diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-08-05 18:53:23 +0200 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2017-08-29 17:03:40 +0200 |
commit | 6206809fd09999620c1be5b01ba3444cb284dda9 (patch) | |
tree | a801a9830618fc435942c365fb091791f8febb6c | |
parent | 1aaa8cc527274c606b59399438590c125a370e0f (diff) | |
download | pyasn1-git-6206809fd09999620c1be5b01ba3444cb284dda9.tar.gz |
WIP: forward referenced ASN.1 types
-rw-r--r-- | CHANGES.rst | 6 | ||||
-rw-r--r-- | pyasn1/__init__.py | 2 | ||||
-rw-r--r-- | pyasn1/type/base.py | 2 | ||||
-rw-r--r-- | pyasn1/type/forwardref.py | 45 | ||||
-rw-r--r-- | pyasn1/type/namedtype.py | 2 | ||||
-rw-r--r-- | pyasn1/type/univ.py | 11 | ||||
-rw-r--r-- | tests/type/test_univ.py | 20 |
7 files changed, 84 insertions, 4 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 85870ae..2109016 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,10 @@ +Revision 0.4.1, released XX-08-2017 +----------------------------------- + +- Forward-referencing ASN.1 types implemented to handle + the case of recursive ASN.1 schemas. + Revision 0.3.4, released XX-08-2017 ----------------------------------- diff --git a/pyasn1/__init__.py b/pyasn1/__init__.py index 8b713b4..42f4704 100644 --- a/pyasn1/__init__.py +++ b/pyasn1/__init__.py @@ -1,7 +1,7 @@ import sys # http://www.python.org/dev/peps/pep-0396/ -__version__ = '0.3.4' +__version__ = '0.4.1' if sys.version_info[:2] < (2, 4): raise RuntimeError('PyASN1 requires Python 2.4 or later') diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py index 822f947..07f716e 100644 --- a/pyasn1/type/base.py +++ b/pyasn1/type/base.py @@ -428,7 +428,7 @@ class AbstractConstructedMeta(type): def __init__(cls, name, bases, nmspc): super(AbstractConstructedMeta, cls).__init__(name, bases, nmspc) - forwardref.ForwardRef.newTypeNotification(name, cls) + forwardref.ForwardRef.newTypeNotification(name, cls()) class AbstractConstructedAsn1Item(Asn1ItemBase, metaclass=AbstractConstructedMeta): diff --git a/pyasn1/type/forwardref.py b/pyasn1/type/forwardref.py new file mode 100644 index 0000000..c822419 --- /dev/null +++ b/pyasn1/type/forwardref.py @@ -0,0 +1,45 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com> +# License: http://pyasn1.sf.net/license.html +# +from pyasn1 import error + + +__all__ = ['ForwardRef'] + + +class ForwardRef(object): + # TODO: this is not thread-safe + waitingList = {} + + def __init__(self, symbol, *args, **kwargs): + self.__symbol = symbol + self.args = args + self.kwargs = kwargs + + def callLater(self, cbFun): + if self.__symbol not in self.waitingList: + self.__class__.waitingList[self.__symbol] = [] + self.waitingList[self.__symbol].append((self, cbFun)) + + # TODO: make two callbacks - one for updating types and the other for + # initialization once all updates are done + @classmethod + def newTypeNotification(cls, name, obj): + # TODO: name collision at different modules possible + if name in cls.waitingList: + for waitingObject, cbFun in cls.waitingList.pop(name): + + cbFun(obj.clone(*waitingObject.args, **waitingObject.kwargs)) + + def subtype(self, *args, **kwargs): + # TODO: make a copy; combine kw/args to mimic subtype/clone ops + self.args = args + self.kwargs = kwargs + return self + + def __getattr__(self, item): + raise error.PyAsn1Error('Unresolved forward reference %s (.%s attempted)' % (self.__symbol, item)) + diff --git a/pyasn1/type/namedtype.py b/pyasn1/type/namedtype.py index 0d56f77..c484dfd 100644 --- a/pyasn1/type/namedtype.py +++ b/pyasn1/type/namedtype.py @@ -110,6 +110,7 @@ class NamedTypes(object): def __init__(self, *namedTypes, **kwargs): self.__namedTypes = namedTypes self.__namedTypesLen = len(self.__namedTypes) + self.__nameToPosMap = self.__computeNameToPosMap() def updateNameType(self, idx, name, obj): # TODO: better way to do in-place update? @@ -130,7 +131,6 @@ class NamedTypes(object): return # TODO: verify initialization status self.__minTagSet = self.__computeMinTagSet() - self.__nameToPosMap = self.__computeNameToPosMap() self.__tagToPosMap = self.__computeTagToPosMap() self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {} self.__uniqueTagMap = self.__computeTagMaps(unique=True) diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py index b83c3ec..131c35b 100644 --- a/pyasn1/type/univ.py +++ b/pyasn1/type/univ.py @@ -1723,6 +1723,17 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): base.AbstractConstructedAsn1Item.__init__(self, **kwargs) + def updateComponentType(self, obj): + self._componentType = obj + + import functools + from pyasn1.type import forwardref + + if isinstance(self.componentType, forwardref.ForwardRef): + cbFun = functools.partial(updateComponentType, self) + self.componentType.callLater(cbFun) + + # Python list protocol def clear(self): diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py index b9fcce4..b6ff31e 100644 --- a/tests/type/test_univ.py +++ b/tests/type/test_univ.py @@ -1120,7 +1120,25 @@ class Sequence(unittest.TestCase): assert not s[1][1].isValue # TODO: SequenceOf, Choice, en/decoding - # TODO: dynamic type def use-case + + def testSelfReferencingDynamicDef(self): + + s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.Integer()), + namedtype.OptionalNamedType('selfref', forwardref.ForwardRef('sft')) + ) + ) + + s.componentType['selfref'].asn1Object.newTypeNotification('sft', s) + + s[0] = 0 + s[1][0] = 1 + + assert s[0] == 0 + assert s[1][0] == 1 + assert not s[1][1].isValue + class SetOf(unittest.TestCase): def setUp(self): |