summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2017-08-05 18:53:23 +0200
committerIlya Etingof <etingof@gmail.com>2017-08-29 17:03:40 +0200
commit6206809fd09999620c1be5b01ba3444cb284dda9 (patch)
treea801a9830618fc435942c365fb091791f8febb6c
parent1aaa8cc527274c606b59399438590c125a370e0f (diff)
downloadpyasn1-git-6206809fd09999620c1be5b01ba3444cb284dda9.tar.gz
WIP: forward referenced ASN.1 types
-rw-r--r--CHANGES.rst6
-rw-r--r--pyasn1/__init__.py2
-rw-r--r--pyasn1/type/base.py2
-rw-r--r--pyasn1/type/forwardref.py45
-rw-r--r--pyasn1/type/namedtype.py2
-rw-r--r--pyasn1/type/univ.py11
-rw-r--r--tests/type/test_univ.py20
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):