summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2017-08-05 16:23:29 +0200
committerIlya Etingof <etingof@gmail.com>2017-08-05 16:23:29 +0200
commitb1223af2f402c0fa275dd700f6986cfb90eb27ac (patch)
treef31b5df16aee16617d1bae3ef6d48bd570b54ff9
parentd07ef6cb47c48eae585fc20ed17f804bd5dd9965 (diff)
parent52f60d22db1b1472797831dce9e245419350a216 (diff)
downloadpyasn1-git-b1223af2f402c0fa275dd700f6986cfb90eb27ac.tar.gz
Merge branch 'master' into open-types-support
-rw-r--r--.travis.yml2
-rw-r--r--CHANGES.rst55
-rw-r--r--README.md1
-rw-r--r--doc/source/conf.py4
-rw-r--r--doc/source/docs/api-reference.rst1
-rw-r--r--doc/source/docs/tutorial.rst144
-rw-r--r--doc/source/docs/type/namedval/contents.rst13
-rw-r--r--doc/source/docs/type/namedval/namedval.rst11
-rw-r--r--doc/source/docs/type/univ/bitstring.rst2
-rw-r--r--doc/source/docs/type/univ/choice.rst2
-rw-r--r--doc/source/docs/type/univ/octetstring.rst2
-rw-r--r--doc/source/docs/type/univ/real.rst2
-rw-r--r--doc/source/docs/type/univ/sequence.rst2
-rw-r--r--doc/source/docs/type/univ/sequenceof.rst2
-rw-r--r--doc/source/docs/type/univ/set.rst2
-rw-r--r--doc/source/docs/type/univ/setof.rst6
-rw-r--r--doc/source/docs/type/useful/generalizedtime.rst22
-rw-r--r--doc/source/docs/type/useful/utctime.rst20
-rw-r--r--doc/source/example-use-case.rst13
-rw-r--r--pyasn1/__init__.py3
-rw-r--r--pyasn1/codec/ber/decoder.py31
-rw-r--r--pyasn1/codec/ber/encoder.py59
-rw-r--r--pyasn1/codec/ber/eoo.py4
-rw-r--r--pyasn1/codec/cer/encoder.py181
-rw-r--r--pyasn1/codec/der/encoder.py12
-rw-r--r--pyasn1/codec/native/decoder.py6
-rw-r--r--pyasn1/codec/native/encoder.py2
-rw-r--r--pyasn1/compat/binary.py10
-rw-r--r--pyasn1/compat/calling.py19
-rw-r--r--pyasn1/compat/dateandtime.py22
-rw-r--r--pyasn1/compat/integer.py9
-rw-r--r--pyasn1/compat/string.py26
-rw-r--r--pyasn1/type/base.py295
-rw-r--r--pyasn1/type/char.py57
-rw-r--r--pyasn1/type/constraint.py30
-rw-r--r--pyasn1/type/namedtype.py227
-rw-r--r--pyasn1/type/namedval.py184
-rw-r--r--pyasn1/type/tag.py34
-rw-r--r--pyasn1/type/tagmap.py2
-rw-r--r--pyasn1/type/univ.py671
-rw-r--r--pyasn1/type/useful.py154
-rw-r--r--setup.py5
-rw-r--r--tests/codec/ber/test_decoder.py60
-rw-r--r--tests/codec/ber/test_encoder.py55
-rw-r--r--tests/codec/cer/test_encoder.py272
-rw-r--r--tests/codec/der/test_encoder.py215
-rw-r--r--tests/type/__main__.py3
-rw-r--r--tests/type/test_namedtype.py16
-rw-r--r--tests/type/test_namedval.py34
-rw-r--r--tests/type/test_univ.py186
-rw-r--r--tests/type/test_useful.py97
51 files changed, 2235 insertions, 1052 deletions
diff --git a/.travis.yml b/.travis.yml
index 5c0c478..1c63221 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,7 @@ python:
- "pypy3"
install:
- pip install codecov
- - python setup.py install
+ - pip install --no-deps .
script:
- PYTHONPATH=.:$PYTHONPATH python tests/__main__.py
after_success:
diff --git a/CHANGES.rst b/CHANGES.rst
index a896433..cee10d8 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,8 +1,36 @@
-Revision 0.2.4, released XX-03-2017
+
+Revision 0.4.1, released XX-08-2017
-----------------------------------
- ANY DEFINED BY clause support implemented
+
+Revision 0.3.3, released XX-08-2017
+-----------------------------------
+
+- Fixed SetOf ordering at CER/DER encoder
+
+Revision 0.3.2, released 04-08-2017
+-----------------------------------
+
+- Fixed SequenceOf/SetOf types initialization syntax to remain
+ backward compatible with pyasn1 0.2.*
+- Rectified thread safety issues by moving lazy, run-time computation
+ into object initializer.
+- Fixed .isValue property to return True for empty SetOf/SequenceOf
+ objects
+- Fixed GeneralizedTime/UTCTime CER/DER codecs to actually get invoked
+- Fixed DER/CER encoders handling optional SEQUENCE/SET fields containing
+ nested SEQUENCE/SET with optional fields.
+- Fixed crash in SequenceOf/SetOf pretty printing and decoding (in some
+ cases)
+- Fixed documentation markup issues.
+
+Revision 0.3.1, released 26-07-2017
+-----------------------------------
+
+- ASN.1 types __init__(), .clone() and .subtype() signatures
+ refactored into keyword arguments to simplify their signatures.
- ASN.1 types initialization refactored to minimize the use of
relatively expensive isNoValue() call
- Lazily pre-populate list of values of Sequence/Set/Choice types
@@ -11,17 +39,24 @@ Revision 0.2.4, released XX-03-2017
- The __getitem__() implementation of some ASN.1 types & tag object
refactored for better performance
- BER/CER/DER value encoders refactored to produce either tuple of
- bytes or octetstream depending on what is more optimal
+ bytes or octet-stream depending on what is more optimal
- Reduced the frequency of expensive isinstance() calls
- Tag-related classes optimized, refactored into properties and
documented.
-- NamedType family of classes overhauled, optimized and
- documented.
+- The NamedValues implementation refactored to mimic Python dict, its use
+ in ASN.1 types refactored into properties and better documented.
+ WARNING: this change introduces a deviation from original API.
+- NamedType family of classes overhauled, optimized and documented.
+- The `componentType` attribute of constructed ASN.1 types turned
+ read-only on instances.
- Sequence/Set DER/CER/DER decoder optimized to skip the case of
reordered components handling when not necessary.
-- Tags and constraints-related getter methods refactored into descriptors/properties.
+- Tags and constraints-related getter methods refactored into read-only
+ instance attributes.
- The .hasValue() method refactored into .isValue property. All ASN.1
objects now support them, not just scalars.
+- The Real.{isInfinity, isPlusInfinity, isMinusInfinity} methods
+ refactored into properties and renamed into IsInf, IsPlusInf and isMinusInf
- The end-of-octets type refactored to ensure it is a singleton. Codecs
changed to rely on that for better performance.
- Codecs lookup made more efficient at BER/CER/DER decoder main loop by
@@ -29,17 +64,19 @@ Revision 0.2.4, released XX-03-2017
- The .getComponent*() methods of constructed ASN.1 types changed
to lazily instantiate underlying type rather than return `None`.
This should simplify its API as initialization like `X[0][1] = 2` becomes
- possible. Beware that this change introduces a deviation from
- original API.
+ possible.
+ WARNING: this change introduces a deviation from the original API.
- The .setComponent*() methods of SetOf/SequenceOf types changed not
to allow uninitialized "holes" inside the sequences of their components.
- They now behave similarly to Python lists. Beware that this change
- introduces a deviation from original API.
+ They now behave similarly to Python lists.
+ WARNING: this change introduces a deviation from the original API.
- Default and optional components en/decoding of Constructed type
refactored towards better efficiency and more control.
- OctetsString and Any decoder optimized to avoid creating ASN.1
objects for chunks of substrate. Instead they now join substrate
chunks together and create ASN.1 object from it just once.
+- The GeneralizedTime and UTCTime types now support to/from Python
+ datetime object conversion.
- Unit tests added for the `compat` sub-package.
- Fixed BitString named bits initialization bug.
- Fixed non-functional tag cache (when running Python 2) at DER decoder.
diff --git a/README.md b/README.md
index ee148fc..f0b72b4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
ASN.1 library for Python
------------------------
+[![PyPI](https://img.shields.io/pypi/v/pyasn1.svg?maxAge=2592000)](https://pypi.python.org/pypi/pyasn1)
[![Python Versions](https://img.shields.io/pypi/pyversions/pyasn1.svg)](https://pypi.python.org/pypi/pyasn1/)
[![Build status](https://travis-ci.org/etingof/pyasn1.svg?branch=master)](https://secure.travis-ci.org/etingof/pyasn1)
[![Coverage Status](https://img.shields.io/codecov/c/github/etingof/pyasn1.svg)](https://codecov.io/github/etingof/pyasn1)
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 9fafe47..258e74b 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -62,9 +62,9 @@ author = u'Ilya Etingof <etingof@gmail.com>'
# built documents.
#
# The short X.Y version.
-version = '0.1'
+version = '0.3'
# The full version, including alpha/beta/rc tags.
-release = '0.1.10'
+release = '0.3.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/source/docs/api-reference.rst b/doc/source/docs/api-reference.rst
index fdabd83..0f98527 100644
--- a/doc/source/docs/api-reference.rst
+++ b/doc/source/docs/api-reference.rst
@@ -16,6 +16,7 @@ ASN.1 types
/docs/type/useful/contents
/docs/type/tag/contents
/docs/type/namedtype/contents
+ /docs/type/namedval/contents
Transformation codecs
---------------------
diff --git a/doc/source/docs/tutorial.rst b/doc/source/docs/tutorial.rst
index f7d33d8..a113829 100644
--- a/doc/source/docs/tutorial.rst
+++ b/doc/source/docs/tutorial.rst
@@ -26,7 +26,7 @@ There values could be used just like corresponding native Python
values (integers, strings/bytes etc) and freely mixed with them in
expressions.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> asn1IntegerValue = univ.Integer(12)
@@ -40,7 +40,7 @@ expressions.
It would be an error to perform an operation on a pyasn1 type object
as it holds no value to deal with:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> asn1IntegerType = univ.Integer()
@@ -73,7 +73,7 @@ False.
And here's pyasn1 version of :py:class:`~pyasn1.type.univ.Boolean`:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> class FunFactorPresent(univ.Boolean): pass
@@ -108,7 +108,7 @@ information.
We will explain the CHOICE type later on, meanwhile the
:py:class:`~pyasn1.type.univ.Null` type:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> skip = univ.Null()
@@ -134,7 +134,7 @@ Keep that in mind when designing new data structures.
A rather strigntforward mapping into pyasn1 -
:py:class:`~pyasn1.type.univ.Integer`:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> ageOfUniverse = univ.Integer(13750000000)
@@ -158,7 +158,7 @@ an INTEGER type.
The Temperature type expressed in pyasn1:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, namedval
>>> class Temperature(univ.Integer):
@@ -205,7 +205,7 @@ When constructing :py:class:`~pyasn1.type.univ.Enumerated` type we
will use two pyasn1 features: values labels (as mentioned above) and
value constraint (will be described in more details later on).
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, namedval, constraint
>>> class ErrorStatus(univ.Enumerated):
@@ -249,7 +249,7 @@ initialized with either a three-component tuple or a Python float.
Infinite values could be expressed in a way, compatible with Python
float type.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> pi = univ.Real((314159, 10, -5))
@@ -287,7 +287,7 @@ The pyasn1 :py:class:`~pyasn1.type.univ.BitString` objects can
initialize from native ASN.1 notation (base 2 or base 16 strings) or
from a Python tuple of binary components.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> publicKey = univ.BitString(
@@ -325,7 +325,7 @@ method, all not explicitly mentioned bits are doomed to be zeros.
To express this in pyasn1, we will employ the named values feature (as
with Enumeration type).
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, namedval
>>> class BitMask(univ.BitString):
@@ -376,7 +376,7 @@ STRING style and quoted text initializers for the
:py:class:`~pyasn1.type.univ.OctetString` objects. To avoid possible
collisions, quoted text is the default initialization syntax.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> thumbnail = univ.OctetString(
@@ -394,7 +394,7 @@ collisions, quoted text is the default initialization syntax.
Most frequent usage of the OctetString class is to instantiate it with
a text string.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> welcomeMessage = univ.OctetString('Welcome to ASN.1 wilderness!')
@@ -414,7 +414,7 @@ OctetString instantiation, as it's more reliable and efficient.
Additionally, OctetString's can also be instantiated with a sequence of
8-bit integers (ASCII codes).
-.. code-block:: python
+.. code-block:: pycon
>>> univ.OctetString((77, 101, 101, 103, 111))
OctetString(b'Meego')
@@ -422,7 +422,7 @@ Additionally, OctetString's can also be instantiated with a sequence of
It is sometimes convenient to express OctetString instances as 8-bit
characters (Python 3 bytes or Python 2 strings) or 8-bit integers.
-.. code-block:: python
+.. code-block:: pycon
>>> octetString = univ.OctetString('ABCDEF')
>>> octetString.asNumbers()
@@ -448,7 +448,7 @@ One of the natural ways to map OBJECT IDENTIFIER type into a Python
one is to use Python tuples of integers. So this approach is taken by
pyasn1's :py:class:`~pyasn1.type.univ.ObjectIdentifier` class.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> internetId = univ.ObjectIdentifier((1, 3, 6, 1))
@@ -461,7 +461,7 @@ pyasn1's :py:class:`~pyasn1.type.univ.ObjectIdentifier` class.
A more human-friendly "dotted" notation is also supported.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> univ.ObjectIdentifier('1.3.6.1')
@@ -496,7 +496,7 @@ Values of the ANY type contain serialized ASN.1 value(s) in form of an
octet string. Therefore pyasn1 :py:class:`~pyasn1.type.univ.Any` value
object share the properties of pyasn1 OctetString object.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> someValue = univ.Any(b'\x02\x01\x01')
@@ -542,7 +542,7 @@ Their pyasn1 implementations are
:py:class:`~pyasn1.type.char.PrintableString` and
:py:class:`~pyasn1.type.char.NumericString`:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import char
>>> '%s' % char.PrintableString("Welcome to ASN.1 text types")
@@ -562,7 +562,7 @@ The :py:class:`~pyasn1.type.char.VisibleString`,
:py:class:`~pyasn1.type.char.GeneralString` types came to ASN.1 from
ISO standards on character sets.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import char
>>> char.VisibleString("abc")
@@ -584,7 +584,7 @@ character string types: :py:class:`~pyasn1.type.char.UniversalString`,
:py:class:`~pyasn1.type.char.BMPString` and
:py:class:`~pyasn1.type.char.UTF8String`.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import char
>>> char.UniversalString("abc")
@@ -620,7 +620,7 @@ corresponding instance of OBJECT IDENTIFIER type. There are no formal
linkage between these instances and provision for ObjectDescriptor
uniqueness in the standard.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import useful
>>> descrBER = useful.ObjectDescriptor(
@@ -644,7 +644,7 @@ strict but has Y2K issues.
In pyasn1 parlance:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import useful
>>> moscowTime = useful.GeneralizedTime("20110308120000.0")
@@ -688,7 +688,7 @@ and one for constructed types (will be discussed later on).
In pyasn1, tags are implemented as immutable, tuple-like objects:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import tag
>>> myTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
@@ -724,7 +724,7 @@ and replaced with a new one.
To model both modes of tagging, a specialized container TagSet object
(holding zero, one or more Tag objects) is used in pyasn1.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import tag
>>> tagSet = tag.TagSet(
@@ -761,7 +761,7 @@ Any two TagSet objects could be compared to see if one is a derivative
of the other. Figuring this out is also useful in cases when a type-specific
data processing algorithms are to be chosen.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import tag
>>> tagSet1 = tag.TagSet(
@@ -786,7 +786,7 @@ following ASN.1 tagged type:
could be expressed in pyasn1 like this:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, tag
>>> class MyIntegerType(univ.Integer):
@@ -852,7 +852,7 @@ subtyping in pyasn1, a cloning operation on an existing pyasn1 type
object can be invoked what creates a new instance of original object
with possibly modified properties.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, namedtype, tag
>>> class Record(univ.Sequence):
@@ -888,7 +888,7 @@ the code above returns an implicitly tagged copy of original object.
Once a SEQUENCE or SET type is decleared with pyasn1, it can be
instantiated and initialized (continuing the above code):
-.. code-block:: python
+.. code-block:: pycon
>>> record = Record()
>>> record['id'] = 123
@@ -912,7 +912,7 @@ instantiated and initialized (continuing the above code):
Inner components of pyasn1 Sequence/Set objects could be accessed
using the following methods:
-.. code-block:: python
+.. code-block:: pycon
>>> record['id']
Integer(123)
@@ -931,7 +931,7 @@ The Set type share all the properties of Sequence type, and additionally
support by-tag component addressing (as all Set components have distinct
types).
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, namedtype, tag
>>> class Gamer(univ.Set):
@@ -970,7 +970,7 @@ position and they both have a property of automatic resize.
To specify inner component type, the **componentType** class
attribute should refer to another pyasn1 type object.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> class Progression(univ.SequenceOf):
@@ -1016,7 +1016,7 @@ In pyasn1 implementation,
accepts only a single inner component at a time. It also offers a few
additional methods specific to its behaviour.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, namedtype
>>> class CodeOrMessage(univ.Choice):
@@ -1042,7 +1042,7 @@ Since there could be only a single inner component value in the pyasn1
Choice value object, either of the following methods could be used for
fetching it (continuing previous code):
-.. code-block:: python
+.. code-block:: pycon
>>> codeOrMessage.getName()
'message'
@@ -1084,7 +1084,7 @@ of values.
Its pyasn1 implementation would look like:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import constraint
>>> c = constraint.SingleValueConstraint('0','1','2','3','4','5','6','7','8','9')
@@ -1094,16 +1094,16 @@ Its pyasn1 implementation would look like:
>>> c('A')
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError:
+ ValueConstraintError:
SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
- >>>
+ >>>
As can be seen in the snippet above, if a value violates the
constraint, an exception will be thrown. A constrainted pyasn1 type
object holds a reference to a constraint object (or their combination,
as will be explained later) and calls it for value verification.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, constraint
>>> class DialButton(univ.OctetString):
@@ -1115,7 +1115,7 @@ as will be explained later) and calls it for value verification.
>>> DialButton('A')
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError:
+ ValueConstraintError:
SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
>>>
@@ -1133,7 +1133,7 @@ and upper bounds of allowed range of values of a type.
And in pyasn1 terms:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, constraint
>>> class Teenagers(univ.Integer):
@@ -1143,7 +1143,7 @@ And in pyasn1 terms:
>>> Teenagers(20)
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError:
+ ValueConstraintError:
ValueRangeConstraint(13, 19) failed at: 20
>>>
@@ -1157,7 +1157,7 @@ infinity values.
And in pyasn1 terms:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, constraint
>>> class NegativeInt(univ.Integer):
@@ -1167,7 +1167,7 @@ And in pyasn1 terms:
>>> NegativeInt(0)
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError:
+ ValueConstraintError:
ValueConstraintError: ValueRangeConstraint() failed at: "0" at NegativeInt
>>> class PositiveInt(univ.Integer):
... subtypeSpec = constraint.ValueRangeConstraint(1, float('inf'))
@@ -1178,7 +1178,7 @@ And in pyasn1 terms:
>> PositiveInt(-1)
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError:
+ ValueConstraintError:
ValueConstraintError: ValueRangeConstraint() failed at: "-1" at PositiveInt
Value range constraint usually applies to numeric types.
@@ -1197,7 +1197,7 @@ upper bounds of the size of a valid value.
Express the same grammar in pyasn1:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, constraint
>>> class TwoBits(univ.BitString):
@@ -1207,7 +1207,7 @@ Express the same grammar in pyasn1:
>>> TwoBits((1,1,0))
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError: ValueSizeConstraint(2, 2) failed at: (1, 1, 0)
+ ValueConstraintError: ValueSizeConstraint(2, 2) failed at: (1, 1, 0)
>>>
Size constraint can be applied to potentially massive values - bit or
@@ -1225,7 +1225,7 @@ constraint but constraint applies to individual characters of a value.
And in pyasn1:
-.. code-block:: bash
+.. code-block:: pycon
>>> from pyasn1.type import char, constraint
>>> class MorseCode(char.PrintableString):
@@ -1235,7 +1235,7 @@ And in pyasn1:
>>> MorseCode("?")
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError: PermittedAlphabetConstraint(".", "-", " ") failed at: "?"
+ ValueConstraintError: PermittedAlphabetConstraint(".", "-", " ") failed at: "?"
>>>
Current implementation does not handle ranges of characters in
@@ -1266,7 +1266,7 @@ specification will constitute a valid telephone number:
Constraint intersection object serves the logic above:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import char, constraint
>>> class PhoneNumber(char.NumericString):
@@ -1279,11 +1279,11 @@ Constraint intersection object serves the logic above:
>>> PhoneNumber('?9039343212')
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError: ConstraintsIntersection(PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'), ValueSizeConstraint(11, 11)) failed at: PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9') failed at: "?039343212"
+ ValueConstraintError: ConstraintsIntersection(PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'), ValueSizeConstraint(11, 11)) failed at: PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9') failed at: "?039343212"
>>> PhoneNumber('9343212')
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError:
+ ValueConstraintError:
ConstraintsIntersection(PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'), ValueSizeConstraint(11, 11)) failed at: ValueSizeConstraint(10, 10) failed at: "9343212"
>>>
@@ -1299,7 +1299,7 @@ constraint in a set. In the specification above, a value of all small
or all capital letters is compliant, but a mix of small&capitals is
not. Here's its pyasn1 analogue:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import char, constraint
>>> class CapitalOrSmall(char.IA5String):
@@ -1314,20 +1314,20 @@ not. Here's its pyasn1 analogue:
>>> CapitalOrSmall('Abba')
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError: ConstraintsUnion(PermittedAlphabetConstraint('A', 'B', 'C'), PermittedAlphabetConstraint('a', 'b', 'c')) failed at: failed for "Abba"
+ ValueConstraintError: ConstraintsUnion(PermittedAlphabetConstraint('A', 'B', 'C'), PermittedAlphabetConstraint('a', 'b', 'c')) failed at: failed for "Abba"
>>>
Finally, the exclusion constraint simply negates the logic of value
verification at a constraint. In the following example, any integer
value is allowed in a type but not zero.
-.. code-block:: python
+.. code-block:: bash
NoZero ::= INTEGER (ALL EXCEPT 0)
In pyasn1 the above definition would read:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, constraint
>>> class NoZero(univ.Integer):
@@ -1339,7 +1339,7 @@ In pyasn1 the above definition would read:
>>> NoZero(0)
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError: ConstraintsExclusion(SingleValueConstraint(0)) failed at: 0
+ ValueConstraintError: ConstraintsExclusion(SingleValueConstraint(0)) failed at: 0
>>>
The depth of such a constraints tree, built with constraint
@@ -1360,7 +1360,7 @@ TagSet and Constraint objects are a derivation of one another.
The following example illustrates the concept (we use the same tagset
but different constraints for simplicity):
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, constraint
>>> i1 = univ.Integer(subtypeSpec=constraint.ValueRangeConstraint(3,8))
@@ -1380,7 +1380,7 @@ but different constraints for simplicity):
As can be seen in the above code snippet, there are two methods of any
pyasn1 type/value object that test types for their relationship:
-**isSameTypeWith**() and **isSuperTypeOf**(). The former is
+*isSameTypeWith()* and *isSuperTypeOf()*. The former is
self-descriptive while the latter yields true if the argument appears
to be a pyasn1 object which has tagset and constraints derived from
those of the object being called.
@@ -1428,7 +1428,7 @@ pyasn1 type objects will cause encoder failure.
The following code will create a pyasn1 Integer object and serialize
it with BER encoder:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder
@@ -1455,7 +1455,7 @@ encode data item all at once. However, even in this case, generating
indefinite length encoding may help a low-memory receiver, running a
restartable decoder, to process a large data item.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder
@@ -1480,7 +1480,7 @@ decoder's end.
To use CER or DER encoders one needs to explicitly import and call them - the
APIs are all compatible.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder as ber_encoder
@@ -1501,7 +1501,7 @@ In the process of decoding, pyasn1 value objects are created and
linked to each other, based on the information containted in the
substrate. Thus, the original pyasn1 value object(s) are recovered.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder, decoder
@@ -1519,7 +1519,7 @@ All pyasn1 decoders can handle both definite and indefinite length
encoding modes automatically, explicit switching into one mode to
another is not required.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder, decoder
@@ -1548,7 +1548,7 @@ incomplete] tags recovered from substrate with those found in prototype pyasn1
type objects (also called pyasn1 specification object further in this
document).
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.codec.ber import decoder
>>> decoder.decode(b'\x02\x01\x0c', asn1Spec=univ.Integer())
@@ -1560,7 +1560,7 @@ current values (if it's a pyasn1 value object), but rather use it as a
hint for choosing proper decoder and as a pattern for creating new
objects:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, tag
>>> from pyasn1.codec.ber import encoder, decoder
@@ -1587,7 +1587,7 @@ values constraints possibly present in pyasn1 specification object.
To explain this, we will decode a random integer object into generic Integer
and the constrained one.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, constraint
>>> from pyasn1.codec.ber import encoder, decoder
@@ -1599,14 +1599,14 @@ and the constrained one.
>>> decoder.decode(substrate, asn1Spec=DialDigit())
Traceback (most recent call last):
...
- pyasn1.type.error.ValueConstraintError:
+ ValueConstraintError:
ValueRangeConstraint(0, 9) failed at: 13
>>>
Similarily to encoders, to use CER or DER decoders application has to
explicitly import and call them - all APIs are compatible.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder as ber_encoder
@@ -1642,7 +1642,7 @@ specification object to decoder.
To explain the issue, we will first prepare a Choice object to deal with:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, namedtype
>>> class CodeOrMessage(univ.Choice):
@@ -1661,7 +1661,7 @@ To explain the issue, we will first prepare a Choice object to deal with:
Let's now encode this Choice object and then decode its substrate
with and without pyasn1 specification object:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.codec.ber import encoder, decoder
>>> substrate = encoder.encode(codeOrMessage)
@@ -1704,7 +1704,7 @@ To illustrate the working of Any type, we'll have to make the stage by
encoding a pyasn1 object and then putting its substrate into an any
object.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder, decoder
@@ -1724,7 +1724,7 @@ substrate. Obviously, the substrate we are dealing with, will decode
into the inner [Integer] component, unless pyasn1 specification is
given to guide the decoder. Continuing previous code:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ
>>> from pyasn1.codec.ber import encoder, decoder
@@ -1753,7 +1753,7 @@ convenient then to turn decoder into a recovery mode. Whilst there,
decoder will not bail out when hit an unknown tag but rather treat it
as an Any type.
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.type import univ, tag
>>> from pyasn1.codec.ber import encoder, decoder
diff --git a/doc/source/docs/type/namedval/contents.rst b/doc/source/docs/type/namedval/contents.rst
new file mode 100644
index 0000000..3e5e830
--- /dev/null
+++ b/doc/source/docs/type/namedval/contents.rst
@@ -0,0 +1,13 @@
+
+Enumerating numbers
+-------------------
+
+Some ASN.1 types such as :py:class:`~pyasn1.type.univ.Integer`,
+:py:class:`~pyasn1.type.univ.Enumerated` and
+:py:class:`~pyasn1.type.univ.BitString` may enumerate their values
+with human-friendly labels.
+
+.. toctree::
+ :maxdepth: 2
+
+ /docs/type/namedval/namedval
diff --git a/doc/source/docs/type/namedval/namedval.rst b/doc/source/docs/type/namedval/namedval.rst
new file mode 100644
index 0000000..24847a9
--- /dev/null
+++ b/doc/source/docs/type/namedval/namedval.rst
@@ -0,0 +1,11 @@
+
+.. |NamedValues| replace:: NamedValues
+
+|NamedValues|
+-------------
+
+The |NamedValues| class associates human-friendly names to a set of numbers
+or bits.
+
+.. autoclass:: pyasn1.type.namedval.NamedValues
+ :members:
diff --git a/doc/source/docs/type/univ/bitstring.rst b/doc/source/docs/type/univ/bitstring.rst
index 5e428db..17f6116 100644
--- a/doc/source/docs/type/univ/bitstring.rst
+++ b/doc/source/docs/type/univ/bitstring.rst
@@ -5,7 +5,7 @@
------------
.. autoclass:: pyasn1.type.univ.BitString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues(),binValue=NoValue(), hexValue=NoValue())
- :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, asInteger, asNumbers, asOctets, asBinary
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, asInteger, asNumbers, asOctets, asBinary, fromHexString, fromBinaryString, fromOctetString
.. note::
diff --git a/doc/source/docs/type/univ/choice.rst b/doc/source/docs/type/univ/choice.rst
index e14d3b2..1507e9d 100644
--- a/doc/source/docs/type/univ/choice.rst
+++ b/doc/source/docs/type/univ/choice.rst
@@ -5,7 +5,7 @@
------------
.. autoclass:: pyasn1.type.univ.Choice(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
- :members: isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents,
getComponentByType, setComponentByType, getName, getComponent
diff --git a/doc/source/docs/type/univ/octetstring.rst b/doc/source/docs/type/univ/octetstring.rst
index 6bc4da3..a58476d 100644
--- a/doc/source/docs/type/univ/octetstring.rst
+++ b/doc/source/docs/type/univ/octetstring.rst
@@ -7,7 +7,7 @@
------------
.. autoclass:: pyasn1.type.univ.OctetString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1', binValue=NoValue(),hexValue=NoValue())
- :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, fromHexString, fromBinaryString
.. note::
diff --git a/doc/source/docs/type/univ/real.rst b/doc/source/docs/type/univ/real.rst
index 9c0025a..f80c725 100644
--- a/doc/source/docs/type/univ/real.rst
+++ b/doc/source/docs/type/univ/real.rst
@@ -5,7 +5,7 @@
------------
.. autoclass:: pyasn1.type.univ.Real(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
- :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, isInf, isPlusInf, isMinusInf
.. note::
diff --git a/doc/source/docs/type/univ/sequence.rst b/doc/source/docs/type/univ/sequence.rst
index 6d89b7b..d9123d8 100644
--- a/doc/source/docs/type/univ/sequence.rst
+++ b/doc/source/docs/type/univ/sequence.rst
@@ -5,7 +5,7 @@
------------
.. autoclass:: pyasn1.type.univ.Sequence(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
- :members: isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, getComponentByPosition,
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, getComponentByPosition,
setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents
.. note::
diff --git a/doc/source/docs/type/univ/sequenceof.rst b/doc/source/docs/type/univ/sequenceof.rst
index d76383c..8f5c6f3 100644
--- a/doc/source/docs/type/univ/sequenceof.rst
+++ b/doc/source/docs/type/univ/sequenceof.rst
@@ -5,7 +5,7 @@
------------
.. autoclass:: pyasn1.type.univ.SequenceOf(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
- :members: isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
getComponentByPosition, setComponentByPosition
.. note::
diff --git a/doc/source/docs/type/univ/set.rst b/doc/source/docs/type/univ/set.rst
index e560488..6216210 100644
--- a/doc/source/docs/type/univ/set.rst
+++ b/doc/source/docs/type/univ/set.rst
@@ -5,7 +5,7 @@
------------
.. autoclass:: pyasn1.type.univ.Set(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
- :members: isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents,
getComponentByType, setComponentByType
diff --git a/doc/source/docs/type/univ/setof.rst b/doc/source/docs/type/univ/setof.rst
index a1577db..74fdd42 100644
--- a/doc/source/docs/type/univ/setof.rst
+++ b/doc/source/docs/type/univ/setof.rst
@@ -5,13 +5,13 @@
------------
.. autoclass:: pyasn1.type.univ.SetOf(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
- :members: isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
 getComponentByPosition, setComponentByPosition
.. note::
- The |ASN.1| type models a collection of elements of a single ASN.1 type.
- Ordering of the components **is not** preserved upon de/serialization.
+ The |ASN.1| type models a collection of elements of a single ASN.1 type.
+ Ordering of the components **is not** preserved upon de/serialization.
.. automethod:: pyasn1.type.univ.SetOf.clone(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.SetOf.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())
diff --git a/doc/source/docs/type/useful/generalizedtime.rst b/doc/source/docs/type/useful/generalizedtime.rst
index d6392d1..abf4351 100644
--- a/doc/source/docs/type/useful/generalizedtime.rst
+++ b/doc/source/docs/type/useful/generalizedtime.rst
@@ -7,13 +7,29 @@
------------
.. autoclass:: pyasn1.type.useful.GeneralizedTime(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
- :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, asDateTime, fromDateTime
.. note::
- The |ASN.1| type models a character string representing date and time in many different
- formats. For example, *20170126120000Z* stands for YYYYMMDDHHMMSSZ.
+ The |ASN.1| type models a character string representing date and time
+ in many different formats.
+
+ Formal syntax for the *GeneralizedTime* value is:
+
+ * **YYYYMMDDhh[mm[ss[(.|,)ffff]]]** standing for a local time, four
+ digits for the year, two for the month, two for the day and two
+ for the hour, followed by two digits for the minutes and two
+ for the seconds if required, then a dot (or a comma), and a
+ number for the fractions of second or
+
+ * a string as above followed by the letter “Z” (denoting a UTC
+ time) or
+
+ * a string as above followed by a string **(+|-)hh[mm]** denoting
+ time zone offset relative to UTC
+
+ For example, *20170126120000Z* stands for YYYYMMDDHHMMSSZ.
.. automethod:: pyasn1.type.useful.GeneralizedTime.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.useful.GeneralizedTime.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/doc/source/docs/type/useful/utctime.rst b/doc/source/docs/type/useful/utctime.rst
index a7aed6a..2ad86a9 100644
--- a/doc/source/docs/type/useful/utctime.rst
+++ b/doc/source/docs/type/useful/utctime.rst
@@ -7,12 +7,26 @@
------------
.. autoclass:: pyasn1.type.useful.UTCTime(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
- :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet
+ :members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, asDateTime, fromDateTime
.. note::
- The |ASN.1| type models a character string representing date and time. An example
- format is *170126120000Z* which stands for YYMMDDHHMMSSZ.
+ The |ASN.1| type models a character string representing date and time.
+
+ Formal syntax for the *UTCTime* value is:
+
+ * **YYMMDDhhmm[ss]** standing for UTC time, two
+ digits for the year, two for the month, two for the day and two
+ for the hour, followed by two digits for the minutes and two
+ for the seconds if required or
+
+ * a string as above followed by the letter “Z” (denoting a UTC
+ time) or
+
+ * a string as above followed by a string **(+|-)hhmm** denoting
+ time zone offset relative to UTC
+
+ For example, *170126120000Z* which stands for YYMMDDHHMMSSZ.
.. automethod:: pyasn1.type.useful.UTCTime.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.useful.UTCTime.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
diff --git a/doc/source/example-use-case.rst b/doc/source/example-use-case.rst
index fb076bb..d5be0cd 100644
--- a/doc/source/example-use-case.rst
+++ b/doc/source/example-use-case.rst
@@ -17,7 +17,7 @@ it and weed out data structures specification into a local file:
.. code-block:: python
- $ cat pkcs-1.asn
+ # pkcs-1.asn
PKCS-1 {iso(1) member(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) modules(0) pkcs-1(1)}
@@ -64,7 +64,8 @@ this:
.. code-block:: python
- $ cat rsakey.py
+ # rsakey.py
+
class Version(Integer):
pass
@@ -107,6 +108,8 @@ set on the key file):
Once we have Python ASN.1 structures initialized, we could inspect them:
+.. code-block:: pycon
+
>>> print(private_key.prettyPrint())
RSAPrivateKey:
version=0
@@ -124,7 +127,7 @@ Play with the keys
As well as use them nearly as we do with native Python types:
-.. code-block:: python
+.. code-block:: pycon
>>> pk = private_key
>>>
@@ -147,7 +150,7 @@ ASN.1 data structures exhibit a way more complicated behaviour compared to
Python types. You may wish to simplify things by turning the whole tree of
pyasn1 objects into an analogous tree made of base Python types:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.codec.native.encoder import encode
>>> ...
@@ -160,7 +163,7 @@ pyasn1 objects into an analogous tree made of base Python types:
You can do vice-versa: initialize ASN.1 structure from a dict:
-.. code-block:: python
+.. code-block:: pycon
>>> from pyasn1.codec.native.decoder import decode
>>> py_private_key = {'modulus': 280789907761334970323210643584308373}
diff --git a/pyasn1/__init__.py b/pyasn1/__init__.py
index 091f6c3..5dd7945 100644
--- a/pyasn1/__init__.py
+++ b/pyasn1/__init__.py
@@ -1,8 +1,7 @@
import sys
# http://www.python.org/dev/peps/pep-0396/
-__version__ = '0.2.4'
+__version__ = '0.3.3'
if sys.version_info[:2] < (2, 4):
raise RuntimeError('PyASN1 requires Python 2.4 or later')
-
diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
index c42a576..e479000 100644
--- a/pyasn1/codec/ber/decoder.py
+++ b/pyasn1/codec/ber/decoder.py
@@ -32,11 +32,11 @@ class AbstractSimpleDecoder(AbstractDecoder):
def substrateCollector(asn1Object, substrate, length):
return substrate[:length], substrate[length:]
- def _createComponent(self, asn1Spec, tagSet, value=None):
+ def _createComponent(self, asn1Spec, tagSet, value=base.noValue):
if tagSet[0].tagFormat not in self.tagFormats:
raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType()))
if asn1Spec is None:
- return self.protoComponent.clone(value, tagSet)
+ return self.protoComponent.clone(value, tagSet=tagSet)
elif value is None:
return asn1Spec
else:
@@ -47,11 +47,11 @@ class AbstractConstructedDecoder(AbstractDecoder):
tagFormats = (tag.tagFormatConstructed,)
# noinspection PyUnusedLocal
- def _createComponent(self, asn1Spec, tagSet, value=None):
+ def _createComponent(self, asn1Spec, tagSet, value=base.noValue):
if tagSet[0].tagFormat not in self.tagFormats:
raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType()))
if asn1Spec is None:
- return self.protoComponent.clone(tagSet)
+ return self.protoComponent.clone(tagSet=tagSet)
else:
return asn1Spec.clone()
@@ -107,7 +107,7 @@ class IntegerDecoder(AbstractSimpleDecoder):
class BooleanDecoder(IntegerDecoder):
protoComponent = univ.Boolean(0)
- def _createComponent(self, asn1Spec, tagSet, value=None):
+ def _createComponent(self, asn1Spec, tagSet, value=base.noValue):
return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0)
@@ -372,7 +372,7 @@ class SequenceAndSetDecoderBase(AbstractConstructedDecoder):
if substrateFun:
return substrateFun(asn1Object, substrate, length)
- namedTypes = asn1Object.getComponentType()
+ namedTypes = asn1Object.componentType
if not self.orderedComponents or not namedTypes or namedTypes.hasOptionalOrDefault:
seenIndices = set()
@@ -426,7 +426,7 @@ class SequenceAndSetDecoderBase(AbstractConstructedDecoder):
if substrateFun:
return substrateFun(asn1Object, substrate, length)
- namedTypes = asn1Object.getComponentType()
+ namedTypes = asn1Object.componentType
if not namedTypes or namedTypes.hasOptionalOrDefault:
seenIndices = set()
@@ -511,7 +511,7 @@ class SequenceOfDecoder(AbstractConstructedDecoder):
asn1Object = self._createComponent(asn1Spec, tagSet)
if substrateFun:
return substrateFun(asn1Object, substrate, length)
- asn1Spec = asn1Object.getComponentType()
+ asn1Spec = asn1Object.componentType
idx = 0
while head:
component, head = decodeFun(head, asn1Spec)
@@ -529,7 +529,7 @@ class SequenceOfDecoder(AbstractConstructedDecoder):
asn1Object = self._createComponent(asn1Spec, tagSet)
if substrateFun:
return substrateFun(asn1Object, substrate, length)
- asn1Spec = asn1Object.getComponentType()
+ asn1Spec = asn1Object.componentType
idx = 0
while substrate:
component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True)
@@ -554,14 +554,13 @@ class SetDecoder(SequenceAndSetDecoderBase):
orderedComponents = False
def _getComponentTagMap(self, asn1Object, idx):
- return asn1Object.componentTagMap
+ return asn1Object.componentType.tagMapUnique
def _getComponentPositionByType(self, asn1Object, tagSet, idx):
- nextIdx = asn1Object.getComponentPositionByType(tagSet)
- if nextIdx is None:
- return idx
+ if asn1Object.componentType:
+ return asn1Object.componentType.getPositionByType(tagSet)
else:
- return nextIdx
+ return idx
class SetOfDecoder(SequenceOfDecoder):
@@ -601,14 +600,14 @@ class ChoiceDecoder(AbstractConstructedDecoder):
if substrateFun:
return substrateFun(asn1Object, substrate, length)
if asn1Object.tagSet == tagSet: # explicitly tagged Choice
- component, substrate = decodeFun(substrate, asn1Object.componentTagMap)
+ component, substrate = decodeFun(substrate, asn1Object.componentType.tagMapUnique)
# eat up EOO marker
eooMarker, substrate = decodeFun(substrate, allowEoo=True)
if eooMarker is not eoo.endOfOctets:
raise error.PyAsn1Error('No EOO seen before substrate ends')
else:
component, substrate = decodeFun(
- substrate, asn1Object.componentTagMap, tagSet, length, state
+ substrate, asn1Object.componentType.tagMapUnique, tagSet, length, state
)
effectiveTagSet = component.effectiveTagSet
asn1Object.setComponentByType(
diff --git a/pyasn1/codec/ber/encoder.py b/pyasn1/codec/ber/encoder.py
index 189fd7f..f84effa 100644
--- a/pyasn1/codec/ber/encoder.py
+++ b/pyasn1/codec/ber/encoder.py
@@ -47,7 +47,7 @@ class AbstractItemEncoder(object):
raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
return (0x80 | substrateLen,) + substrate
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
raise error.PyAsn1Error('Not implemented')
def _encodeEndOfOctets(self, encodeFun, defMode):
@@ -56,11 +56,16 @@ class AbstractItemEncoder(object):
else:
return encodeFun(eoo.endOfOctets, defMode)
- def encode(self, encodeFun, value, defMode, maxChunkSize):
+ def encode(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
substrate, isConstructed, isOctets = self.encodeValue(
- encodeFun, value, defMode, maxChunkSize
+ encodeFun, value, defMode, maxChunkSize, ifNotEmpty=ifNotEmpty
)
+
+ if ifNotEmpty and not substrate:
+ return substrate
+
tagSet = value.tagSet
+
# tagged value?
if tagSet:
if not isConstructed: # primitive form implies definite mode
@@ -81,17 +86,17 @@ class AbstractItemEncoder(object):
class EndOfOctetsEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return null, False, True
class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
if isinstance(value, base.AbstractConstructedAsn1Item):
- value = value.clone(tagSet=value.tagSet[:-1], cloneValueFlag=1)
+ value = value.clone(tagSet=value.tagSet[:-1], cloneValueFlag=True)
else:
value = value.clone(tagSet=value.tagSet[:-1])
- return encodeFun(value, defMode, maxChunkSize), True, True
+ return encodeFun(value, defMode, maxChunkSize, ifNotEmpty=ifNotEmpty), True, True
explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
@@ -100,7 +105,7 @@ explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
class BooleanEncoder(AbstractItemEncoder):
supportIndefLenMode = False
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return value and (1,) or (0,), False, False
@@ -108,7 +113,7 @@ class IntegerEncoder(AbstractItemEncoder):
supportIndefLenMode = False
supportCompactZero = False
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
if value == 0:
# de-facto way to encode zero
if self.supportCompactZero:
@@ -120,7 +125,7 @@ class IntegerEncoder(AbstractItemEncoder):
class BitStringEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
valueLength = len(value)
if valueLength % 8:
alignedValue = value << (8 - valueLength % 8)
@@ -136,13 +141,13 @@ class BitStringEncoder(AbstractItemEncoder):
while stop < valueLength:
start = stop
stop = min(start + maxChunkSize * 8, valueLength)
- substrate += encodeFun(alignedValue[start:stop], defMode, maxChunkSize)
+ substrate += encodeFun(alignedValue[start:stop], defMode, maxChunkSize, ifNotEmpty=ifNotEmpty)
return substrate, True, True
class OctetStringEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
if not maxChunkSize or len(value) <= maxChunkSize:
return value.asOctets(), False, True
else:
@@ -152,7 +157,7 @@ class OctetStringEncoder(AbstractItemEncoder):
v = value.clone(value[pos:pos + maxChunkSize])
if not v:
break
- substrate += encodeFun(v, defMode, maxChunkSize)
+ substrate += encodeFun(v, defMode, maxChunkSize, ifNotEmpty=ifNotEmpty)
pos += maxChunkSize
return substrate, True, True
@@ -161,14 +166,14 @@ class OctetStringEncoder(AbstractItemEncoder):
class NullEncoder(AbstractItemEncoder):
supportIndefLenMode = False
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return null, False, True
class ObjectIdentifierEncoder(AbstractItemEncoder):
supportIndefLenMode = False
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
oid = value.asTuple()
# Build the first pair
@@ -266,10 +271,10 @@ class RealEncoder(AbstractItemEncoder):
encbase = encBase[i]
return sign, m, encbase, e
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- if value.isPlusInfinity():
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
+ if value.isPlusInf:
return (0x40,), False, False
- if value.isMinusInfinity():
+ if value.isMinusInf:
return (0x41,), False, False
m, b, e = value
if not m:
@@ -337,9 +342,9 @@ class RealEncoder(AbstractItemEncoder):
class SequenceEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
value.verifySizeSpec()
- namedTypes = value.getComponentType()
+ namedTypes = value.componentType
substrate = null
idx = len(value)
while idx > 0:
@@ -354,23 +359,23 @@ class SequenceEncoder(AbstractItemEncoder):
class SequenceOfEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
value.verifySizeSpec()
substrate = null
idx = len(value)
while idx > 0:
idx -= 1
- substrate = encodeFun(value[idx], defMode, maxChunkSize) + substrate
+ substrate = encodeFun(value[idx], defMode, maxChunkSize, ifNotEmpty=False) + substrate
return substrate, True, True
class ChoiceEncoder(AbstractItemEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
- return encodeFun(value.getComponent(), defMode, maxChunkSize), True, True
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
+ return encodeFun(value.getComponent(), defMode, maxChunkSize, ifNotEmpty=False), True, True
class AnyEncoder(OctetStringEncoder):
- def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return value.asOctets(), defMode == False, True
@@ -450,7 +455,7 @@ class Encoder(object):
self.__tagMap = tagMap
self.__typeMap = typeMap
- def __call__(self, value, defMode=True, maxChunkSize=0):
+ def __call__(self, value, defMode=True, maxChunkSize=0, ifNotEmpty=False):
if not defMode and not self.supportIndefLength:
raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
if debug.logger & debug.flagEncoder:
@@ -475,7 +480,7 @@ class Encoder(object):
if logger:
logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
substrate = concreteEncoder.encode(
- self, value, defMode, maxChunkSize
+ self, value, defMode, maxChunkSize, ifNotEmpty=ifNotEmpty
)
if logger:
logger('built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate)))
diff --git a/pyasn1/codec/ber/eoo.py b/pyasn1/codec/ber/eoo.py
index b02f5cc..28e33c5 100644
--- a/pyasn1/codec/ber/eoo.py
+++ b/pyasn1/codec/ber/eoo.py
@@ -15,9 +15,9 @@ class EndOfOctets(base.AbstractSimpleAsn1Item):
_instance = None
- def __new__(cls, *args):
+ def __new__(cls, *args, **kwargs):
if cls._instance is None:
- cls._instance = object.__new__(cls, *args)
+ cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py
index d04df9d..0d4f566 100644
--- a/pyasn1/codec/cer/encoder.py
+++ b/pyasn1/codec/cer/encoder.py
@@ -7,15 +7,15 @@
from pyasn1.type import univ
from pyasn1.type import useful
from pyasn1.codec.ber import encoder
-from pyasn1.compat.octets import int2oct, str2octs, null
+from pyasn1.compat.octets import str2octs, null
from pyasn1 import error
__all__ = ['encode']
class BooleanEncoder(encoder.IntegerEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- if client == 0:
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
+ if value == 0:
substrate = (0,)
else:
substrate = (255,)
@@ -23,16 +23,16 @@ class BooleanEncoder(encoder.IntegerEncoder):
class BitStringEncoder(encoder.BitStringEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return encoder.BitStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
+ self, encodeFun, value, defMode, 1000, ifNotEmpty=ifNotEmpty
)
class OctetStringEncoder(encoder.OctetStringEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return encoder.OctetStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
+ self, encodeFun, value, defMode, 1000, ifNotEmpty=ifNotEmpty
)
@@ -44,83 +44,132 @@ class RealEncoder(encoder.RealEncoder):
# specialized GeneralStringEncoder here
-class GeneralizedTimeEncoder(OctetStringEncoder):
- zchar = str2octs('Z')
- pluschar = str2octs('+')
- minuschar = str2octs('-')
- zero = str2octs('0')
-
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- octets = client.asOctets()
- # This breaks too many existing data items
- # if '.' not in octets:
- # raise error.PyAsn1Error('Format must include fraction of second: %r' % octets)
- if len(octets) < 15:
- raise error.PyAsn1Error('Bad UTC time length: %r' % octets)
- if self.pluschar in octets or self.minuschar in octets:
- raise error.PyAsn1Error('Must be UTC time: %r' % octets)
- if octets[-1] != self.zchar[0]:
- raise error.PyAsn1Error('Missing timezone specifier: %r' % octets)
- return encoder.OctetStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
- )
+class TimeEncoderMixIn(object):
+ zchar, = str2octs('Z')
+ pluschar, = str2octs('+')
+ minuschar, = str2octs('-')
+ commachar, = str2octs(',')
+ minLength = 12
+ maxLength = 19
+
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
+ # Encoding constraints:
+ # - minutes are mandatory, seconds are optional
+ # - subseconds must NOT be zero
+ # - no hanging fraction dot
+ # - time in UTC (Z)
+ # - only dot is allowed for fractions
+ octets = value.asOctets()
-class UTCTimeEncoder(encoder.OctetStringEncoder):
- zchar = str2octs('Z')
- pluschar = str2octs('+')
- minuschar = str2octs('-')
+ if not self.minLength < len(octets) < self.maxLength:
+ raise error.PyAsn1Error('Length constraint violated: %r' % value)
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- octets = client.asOctets()
if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets)
- if octets and octets[-1] != self.zchar[0]:
- client = client.clone(octets + self.zchar)
- if len(client) != 13:
- raise error.PyAsn1Error('Bad UTC time length: %r' % client)
+
+ if octets[-1] != self.zchar:
+ raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % octets)
+
+ if self.commachar in octets:
+ raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
+
return encoder.OctetStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 1000
+ self, encodeFun, value, defMode, 1000, ifNotEmpty=ifNotEmpty
)
+class GeneralizedTimeEncoder(TimeEncoderMixIn, OctetStringEncoder):
+ minLength = 12
+ maxLength = 19
+
+
+class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
+ minLength = 10
+ maxLength = 14
+
+
class SetOfEncoder(encoder.SequenceOfEncoder):
@staticmethod
def _sortComponents(components):
# sort by tags regardless of the Choice value (static sort)
return sorted(components, key=lambda x: isinstance(x, univ.Choice) and x.minTagSet or x.tagSet)
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
- client.verifySizeSpec()
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
+ value.verifySizeSpec()
substrate = null
- idx = len(client)
+ idx = len(value)
# This is certainly a hack but how else do I distinguish SetOf
# from Set if they have the same tags&constraints?
- if isinstance(client, univ.SequenceAndSetBase):
+ if isinstance(value, univ.SequenceAndSetBase):
# Set
- namedTypes = client.getComponentType()
+ namedTypes = value.componentType
comps = []
+ compsMap = {}
while idx > 0:
idx -= 1
- if namedTypes[idx].isOptional and not client[idx].isValue:
- continue
- if namedTypes[idx].isDefaulted and client[idx] == namedTypes[idx].asn1Object:
- continue
- comps.append(client[idx])
+ if namedTypes:
+ if namedTypes[idx].isOptional and not value[idx].isValue:
+ continue
+ if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
+ continue
+
+ comps.append(value[idx])
+ compsMap[id(value[idx])] = namedTypes[idx].isOptional
+
for comp in self._sortComponents(comps):
- substrate += encodeFun(comp, defMode, maxChunkSize)
+ substrate += encodeFun(comp, defMode, maxChunkSize, ifNotEmpty=compsMap[id(comp)])
else:
# SetOf
- compSubs = []
- while idx > 0:
- idx -= 1
- compSubs.append(
- encodeFun(client[idx], defMode, maxChunkSize)
- )
- compSubs.sort() # perhaps padding's not needed
- substrate = null
- for compSub in compSubs:
- substrate += compSub
+ components = [encodeFun(x, defMode, maxChunkSize) for x in value]
+
+ # sort by serialized and padded components
+ if len(components) > 1:
+ zero = str2octs('\x00')
+ maxLen = max(map(len, components))
+ paddedComponents = [
+ (x.ljust(maxLen, zero), x) for x in components
+ ]
+ paddedComponents.sort(key=lambda x: x[0])
+
+ components = [x[1] for x in paddedComponents]
+
+ substrate = null.join(components)
+
+ return substrate, True, True
+
+
+class SequenceEncoder(encoder.SequenceEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
+ value.verifySizeSpec()
+ namedTypes = value.componentType
+ substrate = null
+ idx = len(value)
+ while idx > 0:
+ idx -= 1
+ if namedTypes:
+ if namedTypes[idx].isOptional and not value[idx].isValue:
+ continue
+ if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
+ continue
+
+ substrate = encodeFun(value[idx], defMode, maxChunkSize, namedTypes[idx].isOptional) + substrate
+
+ return substrate, True, True
+
+
+class SequenceOfEncoder(encoder.SequenceOfEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
+ substrate = null
+ idx = len(value)
+
+ if ifNotEmpty and not idx:
+ return substrate, True, True
+
+ value.verifySizeSpec()
+ while idx > 0:
+ idx -= 1
+ substrate = encodeFun(value[idx], defMode, maxChunkSize, ifNotEmpty=False) + substrate
return substrate, True, True
@@ -132,7 +181,9 @@ tagMap.update({
univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
useful.UTCTime.tagSet: UTCTimeEncoder(),
- univ.SetOf.tagSet: SetOfEncoder() # conflcts with Set
+ # Sequence & Set have same tags as SequenceOf & SetOf
+ univ.SetOf.tagSet: SetOfEncoder(),
+ univ.Sequence.typeId: SequenceEncoder()
})
typeMap = encoder.typeMap.copy()
@@ -143,16 +194,18 @@ typeMap.update({
univ.Real.typeId: RealEncoder(),
useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
useful.UTCTime.typeId: UTCTimeEncoder(),
+ # Sequence & Set have same tags as SequenceOf & SetOf
univ.Set.typeId: SetOfEncoder(),
- univ.SetOf.typeId: SetOfEncoder()
+ univ.SetOf.typeId: SetOfEncoder(),
+ univ.Sequence.typeId: SequenceEncoder(),
+ univ.SequenceOf.typeId: SequenceOfEncoder()
})
class Encoder(encoder.Encoder):
- def __call__(self, client, defMode=False, maxChunkSize=0):
- return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
-
+ def __call__(self, value, defMode=False, maxChunkSize=0, ifNotEmpty=False):
+ return encoder.Encoder.__call__(self, value, defMode, maxChunkSize, ifNotEmpty)
#: Turns ASN.1 object into CER octet stream.
#:
diff --git a/pyasn1/codec/der/encoder.py b/pyasn1/codec/der/encoder.py
index 3751429..6782819 100644
--- a/pyasn1/codec/der/encoder.py
+++ b/pyasn1/codec/der/encoder.py
@@ -12,15 +12,15 @@ __all__ = ['encode']
class BitStringEncoder(encoder.BitStringEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return encoder.BitStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 0
+ self, encodeFun, value, defMode, 0, ifNotEmpty=ifNotEmpty
)
class OctetStringEncoder(encoder.OctetStringEncoder):
- def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
return encoder.OctetStringEncoder.encodeValue(
- self, encodeFun, client, defMode, 0
+ self, encodeFun, value, defMode, 0, ifNotEmpty=ifNotEmpty
)
class SetOfEncoder(encoder.SetOfEncoder):
@@ -50,10 +50,10 @@ typeMap.update({
class Encoder(encoder.Encoder):
supportIndefLength = False
- def __call__(self, client, defMode=True, maxChunkSize=0):
+ def __call__(self, value, defMode=True, maxChunkSize=0, ifNotEmpty=False):
if not defMode:
raise error.PyAsn1Error('DER forbids indefinite length mode')
- return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
+ return encoder.Encoder.__call__(self, value, defMode, maxChunkSize, ifNotEmpty=ifNotEmpty)
#: Turns ASN.1 object into DER octet stream.
#:
diff --git a/pyasn1/codec/native/decoder.py b/pyasn1/codec/native/decoder.py
index ad7d8f5..4c7df95 100644
--- a/pyasn1/codec/native/decoder.py
+++ b/pyasn1/codec/native/decoder.py
@@ -24,7 +24,7 @@ class SequenceOrSetDecoder(object):
def __call__(self, pyObject, asn1Spec, decoderFunc):
asn1Value = asn1Spec.clone()
- componentsTypes = asn1Spec.getComponentType()
+ componentsTypes = asn1Spec.componentType
for field in asn1Value:
if field in pyObject:
@@ -38,7 +38,7 @@ class SequenceOfOrSetOfDecoder(object):
asn1Value = asn1Spec.clone()
for pyValue in pyObject:
- asn1Value.append(decoderFunc(pyValue, asn1Spec.getComponentType()))
+ asn1Value.append(decoderFunc(pyValue, asn1Spec.componentType.asn1Object))
return asn1Value
@@ -47,7 +47,7 @@ class ChoiceDecoder(object):
def __call__(self, pyObject, asn1Spec, decoderFunc):
asn1Value = asn1Spec.clone()
- componentsTypes = asn1Spec.getComponentType()
+ componentsTypes = asn1Spec.componentType
for field in pyObject:
if field in componentsTypes:
diff --git a/pyasn1/codec/native/encoder.py b/pyasn1/codec/native/encoder.py
index 80be329..36ec71b 100644
--- a/pyasn1/codec/native/encoder.py
+++ b/pyasn1/codec/native/encoder.py
@@ -77,7 +77,7 @@ class SetEncoder(AbstractItemEncoder):
protoDict = dict
def encode(self, encodeFun, value):
value.verifySizeSpec()
- namedTypes = value.getComponentType()
+ namedTypes = value.componentType
substrate = self.protoDict()
for idx, (key, subValue) in enumerate(value.items()):
if namedTypes[idx].isOptional and not value[idx].isValue:
diff --git a/pyasn1/compat/binary.py b/pyasn1/compat/binary.py
index 65c42c7..86f6e5d 100644
--- a/pyasn1/compat/binary.py
+++ b/pyasn1/compat/binary.py
@@ -10,6 +10,14 @@ if version_info[0:2] < (2, 6):
def bin(value):
bitstring = []
+ if value > 0:
+ prefix = '0b'
+ elif value < 0:
+ prefix = '-0b'
+ value = abs(value)
+ else:
+ prefix = '0b0'
+
while value:
if value & 1 == 1:
bitstring.append('1')
@@ -20,6 +28,6 @@ if version_info[0:2] < (2, 6):
bitstring.reverse()
- return '0b' + ''.join(bitstring)
+ return prefix + ''.join(bitstring)
else:
bin = bin
diff --git a/pyasn1/compat/calling.py b/pyasn1/compat/calling.py
new file mode 100644
index 0000000..0b2c464
--- /dev/null
+++ b/pyasn1/compat/calling.py
@@ -0,0 +1,19 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
+# License: http://pyasn1.sf.net/license.html
+#
+from sys import version_info
+
+__all__ = ['callable']
+
+
+if (2, 7) < version_info[:2] < (3, 2):
+ import collections
+
+ callable = lambda x: isinstance(x, collections.Callable)
+
+else:
+
+ callable = callable
diff --git a/pyasn1/compat/dateandtime.py b/pyasn1/compat/dateandtime.py
new file mode 100644
index 0000000..646b9e8
--- /dev/null
+++ b/pyasn1/compat/dateandtime.py
@@ -0,0 +1,22 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
+# License: http://pyasn1.sf.net/license.html
+#
+from sys import version_info
+from datetime import datetime
+import time
+
+__all__ = ['strptime']
+
+
+if version_info[:2] <= (2, 4):
+
+ def strptime(text, dateFormat):
+ return datetime(*(time.strptime(text, dateFormat)[0:6]))
+
+else:
+
+ def strptime(text, dateFormat):
+ return datetime.strptime(text, dateFormat)
diff --git a/pyasn1/compat/integer.py b/pyasn1/compat/integer.py
index bb53e64..c3c23e7 100644
--- a/pyasn1/compat/integer.py
+++ b/pyasn1/compat/integer.py
@@ -9,19 +9,22 @@ try:
import platform
implementation = platform.python_implementation()
-except ImportError:
+except (ImportError, AttributeError):
implementation = 'CPython'
if sys.version_info[0:2] < (3, 2):
from binascii import a2b_hex, b2a_hex
-from pyasn1.compat.octets import oct2int, null
+from pyasn1.compat.octets import oct2int, null, ensureString
if sys.version_info[0:2] < (3, 2) or implementation != 'CPython':
+ if sys.version_info[0] > 2:
+ long = int
+
def from_bytes(octets, signed=False):
if not octets:
return 0
- value = long(b2a_hex(str(octets)), 16)
+ value = long(b2a_hex(ensureString(octets)), 16)
if signed and oct2int(octets[0]) & 0x80:
return value - (1 << len(octets) * 8)
diff --git a/pyasn1/compat/string.py b/pyasn1/compat/string.py
new file mode 100644
index 0000000..058eb48
--- /dev/null
+++ b/pyasn1/compat/string.py
@@ -0,0 +1,26 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
+# License: http://pyasn1.sf.net/license.html
+#
+from sys import version_info
+
+if version_info[:2] <= (2, 5):
+
+ def partition(string, sep):
+ try:
+ a, c = string.split(sep, 1)
+
+ except ValueError:
+ a, b, c = string, '', ''
+
+ else:
+ b = sep
+
+ return a, b, c
+
+else:
+
+ def partition(string, sep):
+ return string.partition(sep) \ No newline at end of file
diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py
index 81dfd59..b901123 100644
--- a/pyasn1/type/base.py
+++ b/pyasn1/type/base.py
@@ -6,6 +6,7 @@
#
import sys
from pyasn1.type import constraint, tagmap, tag
+from pyasn1.compat import calling
from pyasn1 import error
__all__ = ['Asn1Item', 'Asn1ItemBase', 'AbstractSimpleAsn1Item', 'AbstractConstructedAsn1Item']
@@ -33,32 +34,58 @@ class Asn1ItemBase(Asn1Item):
# Disambiguation ASN.1 types identification
typeId = None
- def __init__(self, tagSet=None, subtypeSpec=None):
- if tagSet is None:
- self._tagSet = self.__class__.tagSet
- else:
- self._tagSet = tagSet
- if subtypeSpec is None:
- self._subtypeSpec = self.__class__.subtypeSpec
- else:
- self._subtypeSpec = subtypeSpec
+ def __init__(self, **kwargs):
+ for key in ('tagSet', 'subtypeSpec'):
+ if key not in kwargs:
+ kwargs[key] = getattr(self, key)
+
+ for key, value in kwargs.items():
+ setattr(self, key, value)
+ self.readOnly = key
+
+ def __setattr__(self, name, value):
+ if not name.startswith('_'):
+ try:
+ if name in self._readOnly:
+ raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
+
+ except AttributeError:
+ pass
+
+ super(Asn1ItemBase, self).__setattr__(name, value)
+
+ def __getReadOnly(self):
+ try:
+ return self._readOnly
+
+ except AttributeError:
+ self._readOnly = set()
+
+ return frozenset(self._readOnly)
+
+ def __setReadOnly(self, value):
+ try:
+ self._readOnly.add(value)
+
+ except AttributeError:
+ self._readOnly = set()
+
+ self._readOnly.add(value)
+
+ # property.setter is only available past Python 2.5
+ readOnly = property(__getReadOnly, __setReadOnly)
@property
def effectiveTagSet(self):
"""For |ASN.1| type is equivalent to *tagSet*
"""
- return self._tagSet # used by untagged types
+ return self.tagSet # used by untagged types
@property
def tagMap(self):
"""Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
"""
- try:
- return self._tagMap
-
- except AttributeError:
- self._tagMap = tagmap.TagMap({self._tagSet: self})
- return self._tagMap
+ return tagmap.TagMap({self.tagSet: self})
def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
"""Examine |ASN.1| type for equality with other ASN.1 type.
@@ -82,9 +109,9 @@ class Asn1ItemBase(Asn1Item):
"""
return self is other or \
(not matchTags or
- self._tagSet == other.tagSet) and \
+ self.tagSet == other.tagSet) and \
(not matchConstraints or
- self._subtypeSpec == other.subtypeSpec)
+ self.subtypeSpec == other.subtypeSpec)
def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
"""Examine |ASN.1| type for subtype relationship with other ASN.1 type.
@@ -108,9 +135,9 @@ class Asn1ItemBase(Asn1Item):
:class:`False` otherwise.
"""
return (not matchTags or
- self._tagSet.isSuperTagSetOf(other.tagSet)) and \
+ self.tagSet.isSuperTagSetOf(other.tagSet)) and \
(not matchConstraints or
- (self._subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
+ (self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
@staticmethod
def isNoValue(*values):
@@ -162,7 +189,10 @@ class NoValue(object):
op_names = [name
for typ in (str, int, list, dict)
for name in dir(typ)
- if name not in cls.skipMethods and name.startswith('__') and name.endswith('__') and callable(getattr(typ, name))]
+ if (name not in cls.skipMethods and
+ name.startswith('__') and
+ name.endswith('__') and
+ calling.callable(getattr(typ, name)))]
for name in set(op_names):
setattr(cls, name, getPlug(name))
@@ -187,20 +217,19 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
#: Default payload value
defaultValue = noValue
- def __init__(self, value=noValue, tagSet=None, subtypeSpec=None):
- Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
+ def __init__(self, value=noValue, **kwargs):
+ Asn1ItemBase.__init__(self, **kwargs)
if value is None or value is noValue:
value = self.defaultValue
else:
value = self.prettyIn(value)
try:
- self._subtypeSpec(value)
+ self.subtypeSpec(value)
except error.PyAsn1Error:
exType, exValue, exTb = sys.exc_info()
raise exType('%s at %s' % (exValue, self.__class__.__name__))
- self.__hashedValue = None
self._value = value
self._len = None
@@ -208,10 +237,10 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
representation = []
if self._value is not self.defaultValue:
representation.append(self.prettyOut(self._value))
- if self._tagSet is not self.__class__.tagSet:
- representation.append('tagSet=%r' % (self._tagSet,))
- if self._subtypeSpec is not self.subtypeSpec:
- representation.append('subtypeSpec=%r' % (self._subtypeSpec,))
+ if self.tagSet is not self.__class__.tagSet:
+ representation.append('tagSet=%r' % (self.tagSet,))
+ if self.subtypeSpec is not self.__class__.subtypeSpec:
+ representation.append('subtypeSpec=%r' % (self.subtypeSpec,))
return '%s(%s)' % (self.__class__.__name__, ', '.join(representation))
def __str__(self):
@@ -243,20 +272,14 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
return self._value and True or False
def __hash__(self):
- if self.__hashedValue is None:
- self.__hashedValue = hash(self._value)
- return self.__hashedValue
+ return hash(self._value)
@property
def isValue(self):
"""Indicate if |ASN.1| object represents ASN.1 type or ASN.1 value.
- The PyASN1 type objects can only participate in types comparison
- and serve as a blueprint for serialization codecs to resolve
- ambiguous types.
-
- The PyASN1 value objects can additionally participate in most
- of built-in Python operations.
+ In other words, if *isValue* is `True`, then the ASN.1 object is
+ initialized.
Returns
-------
@@ -264,10 +287,19 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
:class:`True` if object represents ASN.1 value and type,
:class:`False` if object represents just ASN.1 type.
+ Note
+ ----
+ There is an important distinction between PyASN1 type and value objects.
+ The PyASN1 type objects can only participate in ASN.1 type
+ operations (subtyping, comparison etc) and serve as a
+ blueprint for serialization codecs to resolve ambiguous types.
+
+ The PyASN1 value objects can additionally participate in most
+ of built-in Python operations.
"""
return self._value is not noValue
- def clone(self, value=noValue, tagSet=None, subtypeSpec=None):
+ def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -290,28 +322,19 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
:
new instance of |ASN.1| type/value
"""
- isModified = False
-
if value is None or value is noValue:
+ if not kwargs:
+ return self
+
value = self._value
- else:
- isModified = True
- if tagSet is None or tagSet is noValue:
- tagSet = self._tagSet
- else:
- isModified = True
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- isModified = True
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec)
- else:
- return self
+ for arg in self.readOnly:
+ if arg not in kwargs:
+ kwargs[arg] = getattr(self, arg)
+
+ return self.__class__(value, **kwargs)
- def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
- subtypeSpec=None):
+ def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -341,31 +364,32 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
-------
:
new instance of |ASN.1| type/value
- """
- isModified = False
-
+ """
if value is None or value is noValue:
+ if not kwargs:
+ return self
+
value = self._value
- else:
- isModified = True
- if implicitTag is not None and implicitTag is not noValue:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- isModified = True
- elif explicitTag is not None and explicitTag is not noValue:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- isModified = True
- else:
- tagSet = self._tagSet
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = self._subtypeSpec + subtypeSpec
- isModified = True
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec)
- else:
- return self
+ for arg in self.readOnly:
+ if arg in kwargs:
+ kwargs[arg] += getattr(self, arg)
+ else:
+ kwargs[arg] = getattr(self, arg)
+
+ try:
+ kwargs['tagSet'] = self.tagSet.tagImplicitly(kwargs['implicitTag'])
+
+ except KeyError:
+ pass
+
+ try:
+ kwargs['tagSet'] = self.tagSet.tagExplicitly(kwargs['explicitTag'])
+
+ except KeyError:
+ pass
+
+ return self.__class__(value, **kwargs)
def prettyIn(self, value):
return value
@@ -436,27 +460,26 @@ class AbstractConstructedAsn1Item(Asn1ItemBase):
#: otherwise subtype relation is only enforced
strictConstraints = False
- def __init__(self, componentType=None, tagSet=None,
- subtypeSpec=None, sizeSpec=None):
- Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
- if componentType is None:
- self._componentType = self.componentType
- else:
- self._componentType = componentType
- if sizeSpec is None:
- self._sizeSpec = self.sizeSpec
- else:
- self._sizeSpec = sizeSpec
+ componentType = None
+ sizeSpec = None
+
+ def __init__(self, **kwargs):
+ for key in ('componentType', 'sizeSpec'):
+ if key not in kwargs:
+ kwargs[key] = getattr(self, key)
+
+ Asn1ItemBase.__init__(self, **kwargs)
+
self._componentValues = []
def __repr__(self):
representation = []
- if self._componentType is not self.componentType:
- representation.append('componentType=%r' % (self._componentType,))
- if self._tagSet is not self.__class__.tagSet:
- representation.append('tagSet=%r' % (self._tagSet,))
- if self._subtypeSpec is not self.subtypeSpec:
- representation.append('subtypeSpec=%r' % (self._subtypeSpec,))
+ if self.componentType is not self.__class__.componentType:
+ representation.append('componentType=%r' % (self.componentType,))
+ if self.tagSet is not self.__class__.tagSet:
+ representation.append('tagSet=%r' % (self.tagSet,))
+ if self.subtypeSpec is not self.__class__.subtypeSpec:
+ representation.append('subtypeSpec=%r' % (self.subtypeSpec,))
representation = '%s(%s)' % (self.__class__.__name__, ', '.join(representation))
if self._componentValues:
for idx, component in enumerate(self._componentValues):
@@ -493,7 +516,7 @@ class AbstractConstructedAsn1Item(Asn1ItemBase):
def _cloneComponentValues(self, myClone, cloneValueFlag):
pass
- def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None, cloneValueFlag=None):
+ def clone(self, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -516,19 +539,20 @@ class AbstractConstructedAsn1Item(Asn1ItemBase):
new instance of |ASN.1| type/value
"""
- if tagSet is None:
- tagSet = self._tagSet
- if subtypeSpec is None:
- subtypeSpec = self._subtypeSpec
- if sizeSpec is None:
- sizeSpec = self._sizeSpec
- clone = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
+ cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+ for arg in self.readOnly:
+ if arg not in kwargs:
+ kwargs[arg] = getattr(self, arg)
+
+ clone = self.__class__(**kwargs)
+
if cloneValueFlag:
self._cloneComponentValues(clone, cloneValueFlag)
+
return clone
- def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None,
- sizeSpec=None, cloneValueFlag=None):
+ def subtype(self, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -551,27 +575,35 @@ class AbstractConstructedAsn1Item(Asn1ItemBase):
new instance of |ASN.1| type/value
"""
- if implicitTag is not None and implicitTag is not noValue:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- elif explicitTag is not None and explicitTag is not noValue:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- else:
- tagSet = self._tagSet
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = self._subtypeSpec + subtypeSpec
- if sizeSpec is None or sizeSpec is noValue:
- sizeSpec = self._sizeSpec
- else:
- sizeSpec += self._sizeSpec
- clone = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
+ cloneValueFlag = kwargs.pop('cloneValueFlag', False)
+
+ for arg in self.readOnly:
+ if arg in kwargs:
+ kwargs[arg] += getattr(self, arg)
+ else:
+ kwargs[arg] = getattr(self, arg)
+
+ try:
+ kwargs['tagSet'] = self.tagSet.tagImplicitly(kwargs['implicitTag'])
+
+ except KeyError:
+ pass
+
+ try:
+ kwargs['tagSet'] = self.tagSet.tagExplicitly(kwargs['explicitTag'])
+
+ except KeyError:
+ pass
+
+ clone = self.__class__(**kwargs)
+
if cloneValueFlag:
self._cloneComponentValues(clone, cloneValueFlag)
+
return clone
def verifySizeSpec(self):
- self._sizeSpec(self)
+ self.sizeSpec(self)
def getComponentByPosition(self, idx):
raise error.PyAsn1Error('Method not implemented')
@@ -586,17 +618,6 @@ class AbstractConstructedAsn1Item(Asn1ItemBase):
self[k] = kwargs[k]
return self
- def getComponentType(self):
- return self._componentType
-
- # backward compatibility -- no-op
- def setDefaultComponents(self):
- pass
-
- @property
- def componentTagMap(self):
- raise error.PyAsn1Error('Method not implemented')
-
def __getitem__(self, idx):
return self.getComponentByPosition(idx)
@@ -610,5 +631,9 @@ class AbstractConstructedAsn1Item(Asn1ItemBase):
self._componentValues = []
# backward compatibility
- def getComponentTagMap(self):
- return self.componentTagMap \ No newline at end of file
+
+ def setDefaultComponents(self):
+ pass
+
+ def getComponentType(self):
+ return self.componentType
diff --git a/pyasn1/type/char.py b/pyasn1/type/char.py
index 039e536..b95e9a7 100644
--- a/pyasn1/type/char.py
+++ b/pyasn1/type/char.py
@@ -50,10 +50,10 @@ class AbstractCharacterString(univ.OctetString):
if sys.version_info[0] <= 2:
def __str__(self):
try:
- return self._value.encode(self._encoding)
+ return self._value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
- 'Can\'t encode string \'%s\' with \'%s\' codec' % (self._value, self._encoding)
+ 'Can\'t encode string \'%s\' with \'%s\' codec' % (self._value, self.encoding)
)
def __unicode__(self):
@@ -64,10 +64,10 @@ class AbstractCharacterString(univ.OctetString):
return value
elif isinstance(value, str):
try:
- return value.decode(self._encoding)
+ return value.decode(self.encoding)
except (LookupError, UnicodeDecodeError):
raise error.PyAsn1Error(
- 'Can\'t decode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+ 'Can\'t decode string \'%s\' with \'%s\' codec' % (value, self.encoding)
)
elif isinstance(value, (tuple, list)):
try:
@@ -96,10 +96,10 @@ class AbstractCharacterString(univ.OctetString):
def __bytes__(self):
try:
- return self._value.encode(self._encoding)
+ return self._value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
- 'Can\'t encode string \'%s\' with \'%s\' codec' % (self._value, self._encoding)
+ 'Can\'t encode string \'%s\' with \'%s\' codec' % (self._value, self.encoding)
)
def prettyIn(self, value):
@@ -107,10 +107,10 @@ class AbstractCharacterString(univ.OctetString):
return value
elif isinstance(value, bytes):
try:
- return value.decode(self._encoding)
+ return value.decode(self.encoding)
except UnicodeDecodeError:
raise error.PyAsn1Error(
- 'Can\'t decode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+ 'Can\'t decode string \'%s\' with \'%s\' codec' % (value, self.encoding)
)
elif isinstance(value, (tuple, list)):
return self.prettyIn(bytes(value))
@@ -134,8 +134,7 @@ class AbstractCharacterString(univ.OctetString):
def __reversed__(self):
return reversed(self._value)
- def clone(self, value=noValue, tagSet=None, subtypeSpec=None,
- encoding=None, binValue=noValue, hexValue=noValue):
+ def clone(self, value=noValue, **kwargs):
"""Creates a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -165,10 +164,9 @@ class AbstractCharacterString(univ.OctetString):
new instance of |ASN.1| type/value
"""
- return univ.OctetString.clone(self, value, tagSet, subtypeSpec, encoding, binValue, hexValue)
+ return univ.OctetString.clone(self, value, **kwargs)
- def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
- subtypeSpec=None, encoding=None, binValue=noValue, hexValue=noValue):
+ def subtype(self, value=noValue, **kwargs):
"""Creates a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -205,13 +203,12 @@ class AbstractCharacterString(univ.OctetString):
new instance of |ASN.1| type/value
"""
- return univ.OctetString.subtype(self, value, implicitTag, explicitTag, subtypeSpec, encoding, binValue, hexValue)
-
+ return univ.OctetString.subtype(self, value, **kwargs)
class NumericString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -226,7 +223,7 @@ class NumericString(AbstractCharacterString):
class PrintableString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -241,7 +238,7 @@ class PrintableString(AbstractCharacterString):
class TeletexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -249,15 +246,21 @@ class TeletexString(AbstractCharacterString):
)
encoding = 'iso-8859-1'
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
class T61String(TeletexString):
__doc__ = TeletexString.__doc__
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
+
class VideotexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -272,7 +275,7 @@ class VideotexString(AbstractCharacterString):
class IA5String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -287,7 +290,7 @@ class IA5String(AbstractCharacterString):
class GraphicString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -302,7 +305,7 @@ class GraphicString(AbstractCharacterString):
class VisibleString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -317,11 +320,13 @@ class VisibleString(AbstractCharacterString):
class ISO646String(VisibleString):
__doc__ = VisibleString.__doc__
+ # Optimization for faster codec lookup
+ typeId = AbstractCharacterString.getTypeId()
class GeneralString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -336,7 +341,7 @@ class GeneralString(AbstractCharacterString):
class UniversalString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -351,7 +356,7 @@ class UniversalString(AbstractCharacterString):
class BMPString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
@@ -366,7 +371,7 @@ class BMPString(AbstractCharacterString):
class UTF8String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
diff --git a/pyasn1/type/constraint.py b/pyasn1/type/constraint.py
index 371e8ce..35bb0e2 100644
--- a/pyasn1/type/constraint.py
+++ b/pyasn1/type/constraint.py
@@ -25,7 +25,7 @@ class AbstractConstraint(object):
def __init__(self, *values):
self._valueMap = set()
self._setValues(values)
- self.__hashedValues = None
+ self.__hash = hash((self.__class__.__name__, self._values))
def __call__(self, value, idx=None):
if not self._values:
@@ -71,33 +71,7 @@ class AbstractConstraint(object):
return self._values and True or False
def __hash__(self):
- if self.__hashedValues is None:
- self.__hashedValues = hash((self.__class__.__name__, self._values))
- return self.__hashedValues
-
- # descriptor protocol
-
- def __get__(self, instance, owner):
- if instance is None:
- return self
-
- # This is a bit of hack: look up instance attribute first,
- # then try class attribute if instance attribute with that
- # name is not available.
- # The rationale is to have `.subtypeSpec`/`.sizeSpec` readable-writeable
- # as a class attribute and read-only as instance attribute.
- try:
- return instance._subtypeSpec
-
- except AttributeError:
- try:
- return instance._sizeSpec
-
- except AttributeError:
- return self
-
- def __set__(self, instance, value):
- raise AttributeError('attribute is read-only')
+ return self.__hash
def _setValues(self, values):
self._values = values
diff --git a/pyasn1/type/namedtype.py b/pyasn1/type/namedtype.py
index f5b99e4..2fc1609 100644
--- a/pyasn1/type/namedtype.py
+++ b/pyasn1/type/namedtype.py
@@ -5,7 +5,7 @@
# License: http://pyasn1.sf.net/license.html
#
import sys
-from pyasn1.type import tagmap
+from pyasn1.type import tag, tagmap
from pyasn1 import error
__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType', 'NamedTypes']
@@ -124,14 +124,17 @@ class NamedTypes(object):
def __init__(self, *namedTypes):
self.__namedTypes = namedTypes
self.__namedTypesLen = len(self.__namedTypes)
- self.__minTagSet = None
- self.__tagToPosMapImpl = None
- self.__nameToPosMapImpl = None
- self.__ambigiousTypesImpl = None
- self.__tagMap = {}
- self.__hasOptionalOrDefault = None
- self.__requiredComponents = None
- self.__holes = None
+ self.__minTagSet = self.__computeMinTagSet()
+ self.__nameToPosMap = self.__computeNameToPosMap()
+ self.__tagToPosMap = self.__computeTagToPosMap()
+ self.__ambiguousTypes = self.__computeAmbiguousTypes()
+ self.__uniqueTagMap = self.__computeTagMaps(unique=True)
+ self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
+ self.__hasOptionalOrDefault = bool([True for namedType in self.__namedTypes
+ if namedType.isDefaulted or namedType.isOptional])
+ self.__requiredComponents = frozenset(
+ [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
+ )
def __repr__(self):
return '%s(%s)' % (
@@ -196,44 +199,50 @@ class NamedTypes(object):
def clone(self):
return self.__class__(*self.__namedTypes)
- @property
- def __tagToPosMap(self):
- if self.__tagToPosMapImpl is None:
- self.__tagToPosMapImpl = {}
- for idx, namedType in enumerate(self.__namedTypes):
- tagMap = namedType.asn1Object.tagMap
- if not tagMap:
- continue
- for _tagSet in tagMap.presentTypes:
- if _tagSet in self.__tagToPosMapImpl:
- raise error.PyAsn1Error('Duplicate type %s in %s' % (_tagSet, namedType))
- self.__tagToPosMapImpl[_tagSet] = idx
-
- return self.__tagToPosMapImpl
-
- @property
- def __nameToPosMap(self):
- if self.__nameToPosMapImpl is None:
- self.__nameToPosMapImpl = {}
- for idx, namedType in enumerate(self.__namedTypes):
- if namedType.name in self.__nameToPosMapImpl:
- raise error.PyAsn1Error('Duplicate name %s in %s' % (namedType.name, namedType))
- self.__nameToPosMapImpl[namedType.name] = idx
-
- return self.__nameToPosMapImpl
-
- @property
- def __ambigiousTypes(self):
- if self.__ambigiousTypesImpl is None:
- self.__ambigiousTypesImpl = {}
- ambigiousTypes = ()
- for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
- if namedType.isOptional or namedType.isDefaulted:
- ambigiousTypes = (namedType,) + ambigiousTypes
- else:
- ambigiousTypes = (namedType,)
- self.__ambigiousTypesImpl[idx] = NamedTypes(*ambigiousTypes)
- return self.__ambigiousTypesImpl
+ class PostponedError(object):
+ def __init__(self, errorMsg):
+ self.__errorMsg = errorMsg
+
+ def __getitem__(self, item):
+ raise error.PyAsn1Error(self.__errorMsg)
+
+ def __computeTagToPosMap(self):
+ tagToPosMap = {}
+ for idx, namedType in enumerate(self.__namedTypes):
+ tagMap = namedType.asn1Object.tagMap
+ if isinstance(tagMap, NamedTypes.PostponedError):
+ return tagMap
+ if not tagMap:
+ continue
+ for _tagSet in tagMap.presentTypes:
+ if _tagSet in tagToPosMap:
+ return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
+ tagToPosMap[_tagSet] = idx
+
+ return tagToPosMap
+
+ def __computeNameToPosMap(self):
+ nameToPosMap = {}
+ for idx, namedType in enumerate(self.__namedTypes):
+ if namedType.name in nameToPosMap:
+ return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
+ nameToPosMap[namedType.name] = idx
+
+ return nameToPosMap
+
+ def __computeAmbiguousTypes(self):
+ ambigiousTypes = {}
+ partialAmbigiousTypes = ()
+ for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
+ if namedType.isOptional or namedType.isDefaulted:
+ partialAmbigiousTypes = (namedType,) + partialAmbigiousTypes
+ else:
+ partialAmbigiousTypes = (namedType,)
+ if len(partialAmbigiousTypes) == len(self.__namedTypes):
+ ambigiousTypes[idx] = self
+ else:
+ ambigiousTypes[idx] = NamedTypes(*partialAmbigiousTypes)
+ return ambigiousTypes
def getTypeByPosition(self, idx):
"""Return ASN.1 type object by its position in fields set.
@@ -355,7 +364,7 @@ class NamedTypes(object):
If given position is out of fields range
"""
try:
- return self.__ambigiousTypes[idx].getTagMap()
+ return self.__ambiguousTypes[idx].tagMap
except KeyError:
raise error.PyAsn1Error('Type position out of range')
@@ -388,11 +397,24 @@ class NamedTypes(object):
or *idx* is out of fields range
"""
try:
- return idx + self.__ambigiousTypes[idx].getPositionByType(tagSet)
+ return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet)
except KeyError:
raise error.PyAsn1Error('Type position out of range')
+ def __computeMinTagSet(self):
+ minTagSet = None
+ for namedType in self.__namedTypes:
+ asn1Object = namedType.asn1Object
+ try:
+ tagSet = asn1Object.minTagSet
+
+ except AttributeError:
+ tagSet = asn1Object.tagSet
+ if minTagSet is None or tagSet < minTagSet:
+ minTagSet = tagSet
+ return minTagSet or tag.TagSet()
+
@property
def minTagSet(self):
"""Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
@@ -406,22 +428,34 @@ class NamedTypes(object):
: :class:`~pyasn1.type.tagset.TagSet`
Minimal TagSet among ASN.1 types in callee *NamedTypes*
"""
- if self.__minTagSet is None:
- for namedType in self.__namedTypes:
- asn1Object = namedType.asn1Object
- try:
- tagSet = asn1Object.minTagSet
-
- except AttributeError:
- tagSet = asn1Object.tagSet
- if self.__minTagSet is None or tagSet < self.__minTagSet:
- self.__minTagSet = tagSet
return self.__minTagSet
- def getTagMap(self, unique=False):
- """Create a *TagMap* object from tags and types recursively.
+ def __computeTagMaps(self, unique):
+ presentTypes = {}
+ skipTypes = {}
+ defaultType = None
+ for namedType in self.__namedTypes:
+ tagMap = namedType.asn1Object.tagMap
+ if isinstance(tagMap, NamedTypes.PostponedError):
+ return tagMap
+ for tagSet in tagMap:
+ if unique and tagSet in presentTypes:
+ return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
+ presentTypes[tagSet] = namedType.asn1Object
+ skipTypes.update(tagMap.skipTypes)
+
+ if defaultType is None:
+ defaultType = tagMap.defaultType
+ elif tagMap.defaultType is not None:
+ raise error.PyAsn1Error('Duplicate default ASN.1 type at %s' % (self,))
+
+ return tagmap.TagMap(presentTypes, skipTypes, defaultType)
+
+ @property
+ def tagMap(self):
+ """Return a *TagMap* object from tags and types recursively.
- Create a new :class:`~pyasn1.type.tagmap.TagMap` object by
+ Return a :class:`~pyasn1.type.tagmap.TagMap` object by
combining tags from *TagMap* objects of children types and
associating them with their immediate child type.
@@ -430,52 +464,51 @@ class NamedTypes(object):
.. code-block:: python
- OuterType ::= CHOICE {
- innerType INTEGER
- }
+ OuterType ::= CHOICE {
+ innerType INTEGER
+ }
- Calling *.getTagMap()* on *OuterType* will yield a map like this:
+ Calling *.tagMap* on *OuterType* will yield a map like this:
.. code-block:: python
- Integer.tagSet -> Choice
+ Integer.tagSet -> Choice
+ """
+ return self.__nonUniqueTagMap
- Parameters
- ----------
- unique: :py:class:`bool`
- If `True`, duplicate *TagSet* objects occurring while building
- new *TagMap* would cause error.
+ @property
+ def tagMapUnique(self):
+ """Return a *TagMap* object from unique tags and types recursively.
- Returns
+ Return a :class:`~pyasn1.type.tagmap.TagMap` object by
+ combining tags from *TagMap* objects of children types and
+ associating them with their immediate child type.
+
+ Example
-------
- : :class:`~pyasn1.type.tagmap.TagMap`
- New *TagMap* holding *TagSet* object gathered from childen types.
- """
- if unique not in self.__tagMap:
- presentTypes = {}
- skipTypes = {}
- defaultType = None
- for namedType in self.__namedTypes:
- tagMap = namedType.asn1Object.tagMap
- for tagSet in tagMap:
- if unique and tagSet in presentTypes:
- raise error.PyAsn1Error('Non-unique tagSet %s' % (tagSet,))
- presentTypes[tagSet] = namedType.asn1Object
- skipTypes.update(tagMap.skipTypes)
- if defaultType is None:
- defaultType = tagMap.defaultType
- elif tagMap.defaultType is not None:
- raise error.PyAsn1Error('Duplicate default ASN.1 type at %s' % (self,))
+ .. code-block:: python
- self.__tagMap[unique] = tagmap.TagMap(presentTypes, skipTypes, defaultType)
+ OuterType ::= CHOICE {
+ innerType INTEGER
+ }
- return self.__tagMap[unique]
+ Calling *.tagMapUnique* on *OuterType* will yield a map like this:
+
+ .. code-block:: python
+
+ Integer.tagSet -> Choice
+
+ Note
+ ----
+
+ Duplicate *TagSet* objects found in the tree of children
+ types would cause error.
+ """
+ return self.__uniqueTagMap
@property
def hasOptionalOrDefault(self):
- if self.__hasOptionalOrDefault is None:
- self.__hasOptionalOrDefault = bool([True for namedType in self.__namedTypes if namedType.isDefaulted or namedType.isOptional])
return self.__hasOptionalOrDefault
@property
@@ -484,10 +517,6 @@ class NamedTypes(object):
@property
def requiredComponents(self):
- if self.__requiredComponents is None:
- self.__requiredComponents = frozenset(
- [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
- )
return self.__requiredComponents
@property
diff --git a/pyasn1/type/namedval.py b/pyasn1/type/namedval.py
index bd61849..dd3ef8e 100644
--- a/pyasn1/type/namedval.py
+++ b/pyasn1/type/namedval.py
@@ -12,84 +12,170 @@ __all__ = ['NamedValues']
class NamedValues(object):
- def __init__(self, *namedValues):
- self.nameToValIdx = {}
- self.valToNameIdx = {}
- self.namedValues = ()
- automaticVal = 1
- for namedValue in namedValues:
- if isinstance(namedValue, tuple):
- name, val = namedValue
+ """Create named values object.
+
+ The |NamedValues| object represents a collection of string names
+ associated with numeric IDs. These objects are used for giving
+ names to otherwise numerical values.
+
+ |NamedValues| objects are immutable and duck-type Python
+ :class:`dict` object mapping ID to name and vice-versa.
+
+ Parameters
+ ----------
+
+ \*args: variable number of two-element :py:class:`tuple`
+ \*\*kwargs: keyword parameters of:
+
+ name: :py:class:`str`
+ Value name
+
+ value: :py:class:`int`
+ A numerical value
+
+ Examples
+ --------
+
+ >>> nv = namedval.NamedValues('a', 'b', ('c', 0), d=1)
+ >>> nv
+ >>> {'c': 0, 'd': 1, 'a': 2, 'b': 3}
+ >>> nv[0]
+ 'c'
+ >>> nv['a']
+ 2
+ """
+ def __init__(self, *args, **kwargs):
+ self.__names = {}
+ self.__numbers = {}
+
+ anonymousNames = []
+
+ for namedValue in args:
+ if isinstance(namedValue, (tuple, list)):
+ try:
+ name, number = namedValue
+
+ except ValueError:
+ raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,))
+
else:
- name = namedValue
- val = automaticVal
- if name in self.nameToValIdx:
+ anonymousNames.append(namedValue)
+ continue
+
+ if name in self.__names:
+ raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+ if number in self.__numbers:
+ raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
+
+ self.__names[name] = number
+ self.__numbers[number] = name
+
+ for name, number in kwargs.items():
+ if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
- self.nameToValIdx[name] = val
- if val in self.valToNameIdx:
- raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val))
- self.valToNameIdx[val] = name
- self.namedValues = self.namedValues + ((name, val),)
- automaticVal += 1
+
+ if number in self.__numbers:
+ raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
+
+ self.__names[name] = number
+ self.__numbers[number] = name
+
+ if anonymousNames:
+
+ number = self.__numbers and max(self.__numbers) + 1 or 0
+
+ for name in anonymousNames:
+
+ if name in self.__names:
+ raise error.PyAsn1Error('Duplicate name %s' % (name,))
+
+ self.__names[name] = number
+ self.__numbers[number] = name
+
+ number += 1
def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(x) for x in self.namedValues]))
+ return '%s(%r)' % (self.__class__.__name__, self.items())
def __str__(self):
- return str(self.namedValues)
+ return str(self.items())
def __eq__(self, other):
- return tuple(self) == tuple(other)
+ return dict(self) == other
def __ne__(self, other):
- return tuple(self) != tuple(other)
+ return dict(self) != other
def __lt__(self, other):
- return tuple(self) < tuple(other)
+ return dict(self) < other
def __le__(self, other):
- return tuple(self) <= tuple(other)
+ return dict(self) <= other
def __gt__(self, other):
- return tuple(self) > tuple(other)
+ return dict(self) > other
def __ge__(self, other):
- return tuple(self) >= tuple(other)
+ return dict(self) >= other
def __hash__(self):
- return hash(tuple(self))
+ return hash(self.items())
- def getName(self, value):
- if value in self.valToNameIdx:
- return self.valToNameIdx[value]
+ # Python dict protocol (read-only)
- def getValue(self, name):
- if name in self.nameToValIdx:
- return self.nameToValIdx[name]
-
- def getValues(self, *names):
+ def __getitem__(self, key):
try:
- return [self.nameToValIdx[name] for name in names]
+ return self.__numbers[key]
except KeyError:
- raise error.PyAsn1Error(
- 'Unknown bit identifier(s): %s' % (set(names).difference(self.nameToValIdx),)
- )
-
- # TODO support by-name subscription
- def __getitem__(self, i):
- return self.namedValues[i]
+ return self.__names[key]
def __len__(self):
- return len(self.namedValues)
+ return len(self.__names)
+
+ def __contains__(self, key):
+ return key in self.__names or key in self.__numbers
+
+ def __iter__(self):
+ return iter(self.__names)
+
+ def values(self):
+ return iter(self.__numbers)
+
+ def keys(self):
+ return iter(self.__names)
+
+ def items(self):
+ for name in self.__names:
+ yield name, self.__names[name]
+
+ # support merging
def __add__(self, namedValues):
- return self.__class__(*self.namedValues + namedValues)
+ return self.__class__(*tuple(self.items()) + tuple(namedValues.items()))
- def __radd__(self, namedValues):
- return self.__class__(*namedValues + tuple(self))
+ # XXX clone/subtype?
- def clone(self, *namedValues):
- return self.__class__(*tuple(self) + namedValues)
+ def clone(self, *args, **kwargs):
+ new = self.__class__(*args, **kwargs)
+ return self + new
-# XXX clone/subtype?
+ # legacy protocol
+
+ def getName(self, value):
+ if value in self.__numbers:
+ return self.__numbers[value]
+
+ def getValue(self, name):
+ if name in self.__names:
+ return self.__names[name]
+
+ def getValues(self, *names):
+ try:
+ return [self.__names[name] for name in names]
+
+ except KeyError:
+ raise error.PyAsn1Error(
+ 'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),)
+ )
diff --git a/pyasn1/type/tag.py b/pyasn1/type/tag.py
index aaf1857..df305a1 100644
--- a/pyasn1/type/tag.py
+++ b/pyasn1/type/tag.py
@@ -61,7 +61,7 @@ class Tag(object):
self.__tagFormat = tagFormat
self.__tagId = tagId
self.__tagClassId = tagClass, tagId
- self.__lazyHash = None
+ self.__hash = hash(self.__tagClassId)
def __str__(self):
return '[%s:%s:%s]' % (self.__tagClass, self.__tagFormat, self.__tagId)
@@ -90,9 +90,7 @@ class Tag(object):
return self.__tagClassId >= other
def __hash__(self):
- if self.__lazyHash is None:
- self.__lazyHash = hash(self.__tagClassId)
- return self.__lazyHash
+ return self.__hash
def __getitem__(self, idx):
if idx == 0:
@@ -178,7 +176,7 @@ class TagSet(object):
[(superTag.tagClass, superTag.tagId) for superTag in superTags]
)
self.__lenOfSuperTags = len(superTags)
- self.__lazyHash = None
+ self.__hash = hash(self.__superTags)
def __str__(self):
return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]'
@@ -219,33 +217,11 @@ class TagSet(object):
return self.__superTagsSignature >= other
def __hash__(self):
- if self.__lazyHash is None:
- self.__lazyHash = hash(self.__superTags)
- return self.__lazyHash
+ return self.__hash
def __len__(self):
return self.__lenOfSuperTags
- # descriptor protocol
-
- def __get__(self, instance, owner):
- if instance is None:
- return self
-
- # This is a bit of hack: look up instance attribute first,
- # then try class attribute if instance attribute with that
- # name is not available.
- # The rationale is to have `.tagSet` readable-writeable
- # as a class attribute and read-only as instance attribute.
- try:
- return instance._tagSet
-
- except AttributeError:
- return self
-
- def __set__(self, instance, value):
- raise AttributeError('attribute is read-only')
-
@property
def baseTag(self):
"""Return base ASN.1 tag
@@ -286,7 +262,7 @@ class TagSet(object):
New *TagSet* object
"""
if superTag.tagClass == tagClassUniversal:
- raise error.PyAsn1Error('Can\'t tag with UNIVERSAL class tag')
+ raise error.PyAsn1Error("Can't tag with UNIVERSAL class tag")
if superTag.tagFormat != tagFormatConstructed:
superTag = Tag(superTag.tagClass, tagFormatConstructed, superTag.tagId)
return self + superTag
diff --git a/pyasn1/type/tagmap.py b/pyasn1/type/tagmap.py
index 8527f33..32b7447 100644
--- a/pyasn1/type/tagmap.py
+++ b/pyasn1/type/tagmap.py
@@ -16,7 +16,7 @@ class TagMap(object):
*TagMap* objects are immutable and duck-type read-only Python
:class:`dict` objects holding *TagSet* objects as keys and ASN.1
- type objects as values.
+ type objects as values.
Parameters
----------
diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py
index db0c4bf..9991fea 100644
--- a/pyasn1/type/univ.py
+++ b/pyasn1/type/univ.py
@@ -21,6 +21,7 @@ __all__ = ['Integer', 'Boolean', 'BitString', 'OctetString', 'Null',
# "Simple" ASN.1 types (yet incomplete)
+
class Integer(base.AbstractSimpleAsn1Item):
"""Create |ASN.1| type or object.
@@ -45,14 +46,14 @@ class Integer(base.AbstractSimpleAsn1Item):
: :py:class:`pyasn1.error.PyAsn1Error`
On constraint violation or bad initializer.
"""
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -64,19 +65,15 @@ class Integer(base.AbstractSimpleAsn1Item):
# Optimization for faster codec lookup
typeId = base.AbstractSimpleAsn1Item.getTypeId()
- def __init__(self, value=noValue, tagSet=None, subtypeSpec=None,
- namedValues=None):
- if namedValues is None:
- self.__namedValues = self.namedValues
- else:
- self.__namedValues = namedValues
- base.AbstractSimpleAsn1Item.__init__(
- self, value, tagSet, subtypeSpec
- )
+ def __init__(self, value=noValue, **kwargs):
+ if 'namedValues' not in kwargs:
+ kwargs['namedValues'] = self.namedValues
+
+ base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
def __repr__(self):
- if self.__namedValues is not self.namedValues:
- return '%s, %r)' % (base.AbstractSimpleAsn1Item.__repr__(self)[:-1], self.__namedValues)
+ if self.namedValues is not self.__class__.namedValues:
+ return '%s, %r)' % (base.AbstractSimpleAsn1Item.__repr__(self)[:-1], self.namedValues)
else:
return base.AbstractSimpleAsn1Item.__repr__(self)
@@ -171,7 +168,8 @@ class Integer(base.AbstractSimpleAsn1Item):
return int(self._value)
if sys.version_info[0] <= 2:
- def __long__(self): return long(self._value)
+ def __long__(self):
+ return long(self._value)
def __float__(self):
return float(self._value)
@@ -231,22 +229,22 @@ class Integer(base.AbstractSimpleAsn1Item):
return int(value)
except ValueError:
- valueOfName = self.__namedValues.getValue(value)
- if valueOfName is not None:
- return valueOfName
+ try:
+ return self.namedValues[value]
- raise error.PyAsn1Error(
- 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1])
- )
+ except KeyError:
+ raise error.PyAsn1Error(
+ 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1])
+ )
def prettyOut(self, value):
- nameOfValue = self.__namedValues.getName(value)
- return nameOfValue is None and str(value) or repr(nameOfValue)
+ try:
+ return repr(self.namedValues[value])
- def getNamedValues(self):
- return self.__namedValues
+ except KeyError:
+ return str(value)
- def clone(self, value=noValue, tagSet=None, subtypeSpec=None, namedValues=None):
+ def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -272,32 +270,9 @@ class Integer(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- isModified = False
-
- if value is None or value is noValue:
- value = self._value
- else:
- isModified = True
- if tagSet is None or tagSet is noValue:
- tagSet = self._tagSet
- else:
- isModified = True
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- isModified = True
- if namedValues is None or namedValues is noValue:
- namedValues = self.__namedValues
- else:
- isModified = True
-
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec, namedValues)
- else:
- return self
+ return base.AbstractSimpleAsn1Item.clone(self, value, **kwargs)
- def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
- subtypeSpec=None, namedValues=None):
+ def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -333,55 +308,32 @@ class Integer(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- isModified = False
+ return base.AbstractSimpleAsn1Item.subtype(self, value, **kwargs)
- if value is None or value is noValue:
- value = self._value
- else:
- isModified = True
- if implicitTag is not None and implicitTag is not noValue:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- isModified = True
- elif explicitTag is not None and explicitTag is not noValue:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- isModified = True
- else:
- tagSet = self._tagSet
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = self._subtypeSpec + subtypeSpec
- isModified = True
- if namedValues is None or namedValues is noValue:
- namedValues = self.__namedValues
- else:
- namedValues = namedValues + self.__namedValues
- isModified = True
+ # backward compatibility
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec, namedValues)
- else:
- return self
+ def getNamedValues(self):
+ return self.namedValues
class Boolean(Integer):
__doc__ = Integer.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01),
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = Integer.subtypeSpec + constraint.SingleValueConstraint(0, 1)
#: Default :py:class:`~pyasn1.type.namedval.NamedValues` object
#: representing symbolic aliases for numbers
- namedValues = Integer.namedValues.clone(('False', 0), ('True', 1))
+ namedValues = namedval.NamedValues(('False', 0), ('True', 1))
# Optimization for faster codec lookup
typeId = Integer.getTypeId()
@@ -421,14 +373,14 @@ class BitString(base.AbstractSimpleAsn1Item):
: :py:class:`pyasn1.error.PyAsn1Error`
On constraint violation or bad initializer.
"""
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -461,25 +413,34 @@ class BitString(base.AbstractSimpleAsn1Item):
return self.bitLength
- def __init__(self, value=noValue, tagSet=None, subtypeSpec=None,
- namedValues=None, binValue=noValue, hexValue=noValue):
- if namedValues is None:
- self.__namedValues = self.namedValues
- else:
- self.__namedValues = namedValues
- if binValue is not noValue:
- value = self.fromBinaryString(binValue)
- elif hexValue is not noValue:
- value = self.fromHexString(hexValue)
- elif value is None or value is noValue:
+ def __init__(self, value=noValue, **kwargs):
+ if value is noValue or value is None:
+ if kwargs:
+ try:
+ value = self.fromBinaryString(kwargs.pop('binValue'))
+
+ except KeyError:
+ pass
+
+ try:
+ value = self.fromHexString(kwargs.pop('hexValue'))
+
+ except KeyError:
+ pass
+
+ if value is noValue or value is None:
if self.defaultBinValue is not noValue:
value = self.fromBinaryString(self.defaultBinValue)
+
elif self.defaultHexValue is not noValue:
value = self.fromHexString(self.defaultHexValue)
- base.AbstractSimpleAsn1Item.__init__(self, value, tagSet, subtypeSpec)
- def clone(self, value=noValue, tagSet=None, subtypeSpec=None,
- namedValues=None, binValue=noValue, hexValue=noValue):
+ if 'namedValues' not in kwargs:
+ kwargs['namedValues'] = self.namedValues
+
+ base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
+ def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -513,32 +474,9 @@ class BitString(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- isModified = False
+ return base.AbstractSimpleAsn1Item.clone(self, value, **kwargs)
- if (value is None or value is noValue) and binValue is noValue and hexValue is noValue:
- value = self._value
- else:
- isModified = True
- if tagSet is None or tagSet is noValue:
- tagSet = self._tagSet
- else:
- isModified = True
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- isModified = True
- if namedValues is None or namedValues is noValue:
- namedValues = self.__namedValues
- else:
- isModified = True
-
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec, namedValues, binValue, hexValue)
- else:
- return self
-
- def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
- subtypeSpec=None, namedValues=None, binValue=noValue, hexValue=noValue):
+ def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -546,7 +484,7 @@ class BitString(base.AbstractSimpleAsn1Item):
Parameters
----------
- value : :class:`int`, :class:`str` or |ASN.1| object
+ value: :class:`int`, :class:`str` or |ASN.1| object
Initialization value to pass to new ASN.1 object instead of
inheriting one from the caller.
@@ -582,35 +520,7 @@ class BitString(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- isModified = False
-
- if (value is None or value is noValue) and binValue is noValue and hexValue is noValue:
- value = self._value
- else:
- isModified = True
- if implicitTag is not None and implicitTag is not noValue:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- isModified = True
- elif explicitTag is not None and explicitTag is not noValue:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- isModified = True
- else:
- tagSet = self._tagSet
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = self._subtypeSpec + subtypeSpec
- isModified = True
- if namedValues is None or namedValues is noValue:
- namedValues = self.__namedValues
- else:
- namedValues = namedValues + self.__namedValues
- isModified = True
-
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec, namedValues, binValue, hexValue)
- else:
- return self
+ return base.AbstractSimpleAsn1Item.subtype(self, value, **kwargs)
def __str__(self):
return self.asBinary()
@@ -724,10 +634,17 @@ class BitString(base.AbstractSimpleAsn1Item):
"""Get |ASN.1| value as a text string of bits.
"""
binString = binary.bin(self._value)[2:]
- return '0'*(len(self._value) - len(binString)) + binString
+ return '0' * (len(self._value) - len(binString)) + binString
@classmethod
def fromHexString(cls, value):
+ """Create a |ASN.1| object initialized from the hex string.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like 'DEADBEEF'
+ """
try:
return cls.SizedInteger(value, 16).setBitLength(len(value) * 4)
@@ -736,6 +653,13 @@ class BitString(base.AbstractSimpleAsn1Item):
@classmethod
def fromBinaryString(cls, value):
+ """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like '1010111'
+ """
try:
return cls.SizedInteger(value or '0', 2).setBitLength(len(value))
@@ -744,6 +668,13 @@ class BitString(base.AbstractSimpleAsn1Item):
@classmethod
def fromOctetString(cls, value, padding=0):
+ """Create a |ASN.1| object initialized from a string.
+
+ Parameters
+ ----------
+ value: :class:`str` (Py2) or :class:`bytes` (Py3)
+ Text string like '\\\\x01\\\\xff' (Py2) or b'\\\\x01\\\\xff' (Py3)
+ """
return cls(cls.SizedInteger(integer.from_bytes(value) >> padding).setBitLength(len(value) * 8 - padding))
def prettyIn(self, value):
@@ -761,8 +692,15 @@ class BitString(base.AbstractSimpleAsn1Item):
'Bad BIT STRING value notation %s' % (value,)
)
- elif self.__namedValues and not value.isdigit(): # named bits like 'Urgent, Active'
- bitPositions = self.__namedValues.getValues(*[x.strip() for x in value.split(',')])
+ elif self.namedValues and not value.isdigit(): # named bits like 'Urgent, Active'
+ names = [x.strip() for x in value.split(',')]
+
+ try:
+
+ bitPositions = [self.namedValues[name] for name in names]
+
+ except KeyError:
+ raise error.PyAsn1Error('unknown bit name(s) in %r' % (names,))
rightmostPosition = max(bitPositions)
@@ -849,14 +787,14 @@ class OctetString(base.AbstractSimpleAsn1Item):
: :py:class:`pyasn1.error.PyAsn1Error`
On constraint violation or bad initializer.
"""
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -867,26 +805,36 @@ class OctetString(base.AbstractSimpleAsn1Item):
defaultBinValue = defaultHexValue = noValue
encoding = 'iso-8859-1'
- def __init__(self, value=noValue, tagSet=None, subtypeSpec=None,
- encoding=None, binValue=noValue, hexValue=noValue):
- if encoding is None:
- self._encoding = self.encoding
- else:
- self._encoding = encoding
- if binValue is not noValue:
- value = self.fromBinaryString(binValue)
- elif hexValue is not noValue:
- value = self.fromHexString(hexValue)
- elif value is None or value is noValue:
+ def __init__(self, value=noValue, **kwargs):
+ if kwargs:
+ if value is noValue or value is None:
+ try:
+ value = self.fromBinaryString(kwargs.pop('binValue'))
+
+ except KeyError:
+ pass
+
+ try:
+ value = self.fromHexString(kwargs.pop('hexValue'))
+
+ except KeyError:
+ pass
+
+ if value is noValue or value is None:
if self.defaultBinValue is not noValue:
value = self.fromBinaryString(self.defaultBinValue)
+
elif self.defaultHexValue is not noValue:
value = self.fromHexString(self.defaultHexValue)
+
+ if 'encoding' not in kwargs:
+ kwargs['encoding'] = self.encoding
+
self.__asNumbersCache = None
- base.AbstractSimpleAsn1Item.__init__(self, value, tagSet, subtypeSpec)
- def clone(self, value=noValue, tagSet=None, subtypeSpec=None,
- encoding=None, binValue=noValue, hexValue=noValue):
+ base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs)
+
+ def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -920,33 +868,9 @@ class OctetString(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- isModified = False
-
- if (value is None or value is noValue) and binValue is noValue and hexValue is noValue:
- value = self._value
- else:
- isModified = True
- if tagSet is None or tagSet is noValue:
- tagSet = self._tagSet
- else:
- isModified = True
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- isModified = True
- if encoding is None or encoding is noValue:
- encoding = self._encoding
- else:
- isModified = True
+ return base.AbstractSimpleAsn1Item.clone(self, value, **kwargs)
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec, encoding, binValue, hexValue)
- else:
- return self
-
- def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
- subtypeSpec=None, encoding=None, binValue=noValue,
- hexValue=noValue):
+ def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -988,34 +912,7 @@ class OctetString(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- isModified = False
-
- if (value is None or value is noValue) and binValue is noValue and hexValue is noValue:
- value = self._value
- else:
- isModified = True
- if implicitTag is not None and implicitTag is not noValue:
- tagSet = self._tagSet.tagImplicitly(implicitTag)
- isModified = True
- elif explicitTag is not None and explicitTag is not noValue:
- tagSet = self._tagSet.tagExplicitly(explicitTag)
- isModified = True
- else:
- tagSet = self._tagSet
- if subtypeSpec is None or subtypeSpec is noValue:
- subtypeSpec = self._subtypeSpec
- else:
- subtypeSpec = self._subtypeSpec + subtypeSpec
- isModified = True
- if encoding is None or encoding is noValue:
- encoding = self._encoding
- else:
- isModified = True
-
- if isModified:
- return self.__class__(value, tagSet, subtypeSpec, encoding, binValue, hexValue)
- else:
- return self
+ return base.AbstractSimpleAsn1Item.subtype(self, value, **kwargs)
if sys.version_info[0] <= 2:
def prettyIn(self, value):
@@ -1023,10 +920,10 @@ class OctetString(base.AbstractSimpleAsn1Item):
return value
elif isinstance(value, unicode):
try:
- return value.encode(self._encoding)
+ return value.encode(self.encoding)
except (LookupError, UnicodeEncodeError):
raise error.PyAsn1Error(
- 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+ 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self.encoding)
)
elif isinstance(value, (tuple, list)):
try:
@@ -1043,20 +940,18 @@ class OctetString(base.AbstractSimpleAsn1Item):
def __unicode__(self):
try:
- return self._value.decode(self._encoding)
+ return self._value.decode(self.encoding)
except UnicodeDecodeError:
raise error.PyAsn1Error(
- 'Can\'t decode string \'%s\' with \'%s\' codec' % (self._value, self._encoding)
+ 'Can\'t decode string \'%s\' with \'%s\' codec' % (self._value, self.encoding)
)
def asOctets(self):
return str(self._value)
def asNumbers(self):
- if self.__asNumbersCache is None:
- self.__asNumbersCache = tuple([ord(x) for x in self._value])
- return self.__asNumbersCache
+ return tuple([ord(x) for x in self._value])
else:
def prettyIn(self, value):
@@ -1064,10 +959,10 @@ class OctetString(base.AbstractSimpleAsn1Item):
return value
elif isinstance(value, str):
try:
- return value.encode(self._encoding)
+ return value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
- 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+ 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self.encoding)
)
elif isinstance(value, OctetString): # a shortcut, bytes() would work the same way
return value.asOctets()
@@ -1080,11 +975,11 @@ class OctetString(base.AbstractSimpleAsn1Item):
def __str__(self):
try:
- return self._value.decode(self._encoding)
+ return self._value.decode(self.encoding)
except UnicodeDecodeError:
raise error.PyAsn1Error(
- 'Can\'t decode string \'%s\' with \'%s\' codec at \'%s\'' % (self._value, self._encoding, self.__class__.__name__)
+ 'Can\'t decode string \'%s\' with \'%s\' codec at \'%s\'' % (self._value, self.encoding, self.__class__.__name__)
)
def __bytes__(self):
@@ -1094,9 +989,7 @@ class OctetString(base.AbstractSimpleAsn1Item):
return bytes(self._value)
def asNumbers(self):
- if self.__asNumbersCache is None:
- self.__asNumbersCache = tuple(self._value)
- return self.__asNumbersCache
+ return tuple(self._value)
def prettyOut(self, value):
if sys.version_info[0] <= 2:
@@ -1105,19 +998,25 @@ class OctetString(base.AbstractSimpleAsn1Item):
numbers = tuple(value)
for x in numbers:
if x < 32 or x > 126:
- return octets.str2octs('0x' + ''.join(('%.2x' % x for x in numbers)))
+ return '0x' + ''.join(('%.2x' % x for x in numbers))
else:
try:
- return value.decode(self._encoding)
+ return value.decode(self.encoding)
except UnicodeDecodeError:
raise error.PyAsn1Error(
- 'Can\'t decode string \'%s\' with \'%s\' codec at \'%s\'' % (
- value, self._encoding, self.__class__.__name__)
+ "Can't decode string '%s' with '%s' codec at '%s'" % (value, self.encoding, self.__class__.__name__)
)
@staticmethod
def fromBinaryString(value):
+ """Create a |ASN.1| object initialized from a string of '0' and '1'.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like '1010111'
+ """
bitNo = 8
byte = 0
r = []
@@ -1142,6 +1041,13 @@ class OctetString(base.AbstractSimpleAsn1Item):
@staticmethod
def fromHexString(value):
+ """Create a |ASN.1| object initialized from the hex string.
+
+ Parameters
+ ----------
+ value: :class:`str`
+ Text string like 'DEADBEEF'
+ """
r = []
p = []
for v in value:
@@ -1165,12 +1071,12 @@ class OctetString(base.AbstractSimpleAsn1Item):
break
if not doHex:
r.append('%r' % (self._value,))
- if self._tagSet is not self.__class__.tagSet:
- r.append('tagSet=%r' % (self._tagSet,))
- if self._subtypeSpec is not self.subtypeSpec:
- r.append('subtypeSpec=%r' % (self._subtypeSpec,))
- if self.encoding is not self._encoding:
- r.append('encoding=%r' % (self._encoding,))
+ if self.tagSet is not self.__class__.tagSet:
+ r.append('tagSet=%r' % (self.tagSet,))
+ if self.subtypeSpec is not self.__class__.subtypeSpec:
+ r.append('subtypeSpec=%r' % (self.subtypeSpec,))
+ if self.encoding is not self.__class__.encoding:
+ r.append('encoding=%r' % (self.encoding,))
if doHex:
r.append('hexValue=%r' % ''.join(['%.2x' % x for x in self.asNumbers()]))
return '%s(%s)' % (self.__class__.__name__, ', '.join(r))
@@ -1236,7 +1142,7 @@ class Null(OctetString):
"""
defaultValue = ''.encode() # This is tightly constrained
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
@@ -1247,7 +1153,7 @@ class Null(OctetString):
# Optimization for faster codec lookup
typeId = OctetString.getTypeId()
- def clone(self, value=noValue, tagSet=None):
+ def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -1267,9 +1173,9 @@ class Null(OctetString):
: :py:class:`~pyasn1.type.univ.Null`
new instance of NULL type/value
"""
- return OctetString.clone(self, value, tagSet)
+ return OctetString.clone(self, value, **kwargs)
- def subtype(self, value=noValue, implicitTag=None, explicitTag=None):
+ def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -1296,7 +1202,7 @@ class Null(OctetString):
: :py:class:`~pyasn1.type.univ.Null`
new instance of NULL type/value
"""
- return OctetString.subtype(self, value, implicitTag, explicitTag)
+ return OctetString.subtype(self, value, **kwargs)
if sys.version_info[0] <= 2:
@@ -1328,14 +1234,14 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item):
: :py:class:`pyasn1.error.PyAsn1Error`
On constraint violation or bad initializer.
"""
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -1468,14 +1374,14 @@ class Real(base.AbstractSimpleAsn1Item):
_plusInf = _minusInf = None
_inf = ()
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -1483,7 +1389,7 @@ class Real(base.AbstractSimpleAsn1Item):
# Optimization for faster codec lookup
typeId = base.AbstractSimpleAsn1Item.getTypeId()
- def clone(self, value=noValue, tagSet=None, subtypeSpec=None):
+ def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
@@ -1506,10 +1412,9 @@ class Real(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- return base.AbstractSimpleAsn1Item.clone(self, value, tagSet, subtypeSpec)
+ return base.AbstractSimpleAsn1Item.clone(self, value, **kwargs)
- def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
- subtypeSpec=None):
+ def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
@@ -1539,7 +1444,7 @@ class Real(base.AbstractSimpleAsn1Item):
:
new instance of |ASN.1| type/value
"""
- return base.AbstractSimpleAsn1Item.subtype(self, value, implicitTag, explicitTag)
+ return base.AbstractSimpleAsn1Item.subtype(self, value, **kwargs)
@staticmethod
def __normalizeBase10(value):
@@ -1596,12 +1501,13 @@ class Real(base.AbstractSimpleAsn1Item):
return str(value)
def prettyPrint(self, scope=0):
- if self.isInfinity():
+ if self.isInf:
return self.prettyOut(self._value)
else:
return str(float(self))
- def isPlusInfinity(self):
+ @property
+ def isPlusInf(self):
"""Indicate PLUS-INFINITY object value
Returns
@@ -1613,7 +1519,8 @@ class Real(base.AbstractSimpleAsn1Item):
"""
return self._value == self._plusInf
- def isMinusInfinity(self):
+ @property
+ def isMinusInf(self):
"""Indicate MINUS-INFINITY object value
Returns
@@ -1624,7 +1531,8 @@ class Real(base.AbstractSimpleAsn1Item):
"""
return self._value == self._minusInf
- def isInfinity(self):
+ @property
+ def isInf(self):
return self._value in self._inf
def __str__(self):
@@ -1683,7 +1591,8 @@ class Real(base.AbstractSimpleAsn1Item):
return int(float(self))
if sys.version_info[0] <= 2:
- def __long__(self): return long(float(self))
+ def __long__(self):
+ return long(float(self))
def __float__(self):
if self._value in self._inf:
@@ -1716,7 +1625,8 @@ class Real(base.AbstractSimpleAsn1Item):
return self.clone(math.ceil(float(self)))
if sys.version_info[0:2] > (2, 5):
- def __trunc__(self): return self.clone(math.trunc(float(self)))
+ def __trunc__(self):
+ return self.clone(math.trunc(float(self)))
def __lt__(self, value):
return float(self) < value
@@ -1751,18 +1661,29 @@ class Real(base.AbstractSimpleAsn1Item):
else:
return self._value[idx]
+ # compatibility stubs
+
+ def isPlusInfinity(self):
+ return self.isPlusInf
+
+ def isMinusInfinity(self):
+ return self.isMinusInf
+
+ def isInfinity(self):
+ return self.isInf
+
class Enumerated(Integer):
__doc__ = Integer.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A)
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -1797,6 +1718,17 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
Object representing collection size constraint
"""
+ def __init__(self, *args, **kwargs):
+ # support positional params for backward compatibility
+ if args:
+ for key, value in zip(('componentType', 'tagSet',
+ 'subtypeSpec', 'sizeSpec'), args):
+ if key in kwargs:
+ raise error.PyAsn1Error('Conflicting positional and keyword params!')
+ kwargs['componentType'] = value
+
+ base.AbstractConstructedAsn1Item.__init__(self, **kwargs)
+
# Python list protocol
def clear(self):
@@ -1845,7 +1777,7 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
----------
idx : :class:`int`
Component index (zero-based). Must either refer to an existing
- component or to N+1 component (of *componentType is set). In the latter
+ component or to N+1 component (if *componentType* is set). In the latter
case a new component type gets instantiated and appended to the |ASN.1|
sequence.
@@ -1899,7 +1831,7 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
IndexError:
When idx > len(self)
"""
- componentType = self._componentType
+ componentType = self.componentType
try:
currentValue = self._componentValues[idx]
@@ -1909,7 +1841,7 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
if len(self._componentValues) < idx:
raise error.PyAsn1Error('Component index out of range')
- if value is None or value is noValue:
+ if value is noValue or value is None:
if componentType is not None:
value = componentType.clone()
elif currentValue is None:
@@ -1931,7 +1863,7 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
if verifyConstraints and value.isValue:
try:
- self._subtypeSpec(value, idx)
+ self.subtypeSpec(value, idx)
except error.PyAsn1Error:
exType, exValue, exTb = sys.exc_info()
@@ -1946,15 +1878,17 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
@property
def componentTagMap(self):
- if self._componentType is not None:
- return self._componentType.tagMap
+ if (self.componentType is not None and
+ self.componentType is not noValue):
+ return self.componentType.tagMap
def prettyPrint(self, scope=0):
scope += 1
representation = self.__class__.__name__ + ':\n'
for idx in range(len(self._componentValues)):
representation += ' ' * scope
- if self._componentValues[idx] is None:
+ if (self._componentValues[idx] is None and
+ self.componentType is not noValue):
representation += '<empty>'
else:
representation += self._componentValues[idx].prettyPrint(scope)
@@ -1963,32 +1897,39 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
def prettyPrintType(self, scope=0):
scope += 1
representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__)
- if self._componentType is not None:
+ if (self.componentType is not None and
+ self.componentType is not noValue):
representation += ' ' * scope
- representation += self._componentType.prettyPrintType(scope)
+ representation += self.componentType.prettyPrintType(scope)
return representation + '\n' + ' ' * (scope - 1) + '}'
@property
def isValue(self):
- """Indicate if |ASN.1| object components represent ASN.1 type or ASN.1 value.
+ """Indicate if |ASN.1| object represents ASN.1 type or ASN.1 value.
- The PyASN1 type objects can only participate in types comparison
- and serve as a blueprint for serialization codecs to resolve
- ambiguous types.
+ In other words, if *isValue* is `True`, then the ASN.1 object is
+ initialized.
- The PyASN1 value objects can additionally participate in most
- of built-in Python operations.
+ For the purpose of this check, empty |ASN.1| object is considered
+ as initialized.
Returns
-------
: :class:`bool`
- :class:`True` if all |ASN.1| components represent value and type,
- :class:`False` if at least one |ASN.1| component represents just ASN.1 type.
- """
- if not self._componentValues:
- return False
+ :class:`True` if object represents ASN.1 value and type,
+ :class:`False` if object represents just ASN.1 type.
+ Note
+ ----
+ There is an important distinction between PyASN1 type and value objects.
+ The PyASN1 type objects can only participate in ASN.1 type
+ operations (subtyping, comparison etc) and serve as a
+ blueprint for serialization codecs to resolve ambiguous types.
+
+ The PyASN1 value objects can additionally participate in most
+ of built-in Python operations.
+ """
for componentValue in self._componentValues:
if not componentValue.isValue:
return False
@@ -1999,7 +1940,7 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item):
class SequenceOf(SequenceOfAndSetOfBase):
__doc__ = SequenceOfAndSetOfBase.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
@@ -2010,7 +1951,7 @@ class SequenceOf(SequenceOfAndSetOfBase):
#: object representing ASN.1 type allowed within |ASN.1| type
componentType = None
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -2026,7 +1967,7 @@ class SequenceOf(SequenceOfAndSetOfBase):
class SetOf(SequenceOfAndSetOfBase):
__doc__ = SequenceOfAndSetOfBase.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
@@ -2037,7 +1978,7 @@ class SetOf(SequenceOfAndSetOfBase):
#: object representing ASN.1 type allowed within |ASN.1| type
componentType = None
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -2073,14 +2014,9 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
#: object representing named ASN.1 types allowed within |ASN.1| type
componentType = namedtype.NamedTypes()
- def __init__(self, componentType=None, tagSet=None,
- subtypeSpec=None, sizeSpec=None):
- if componentType is None:
- componentType = self.componentType
- base.AbstractConstructedAsn1Item.__init__(
- self, componentType, tagSet, subtypeSpec, sizeSpec
- )
- self._componentTypeLen = len(self._componentType)
+ def __init__(self, **kwargs):
+ base.AbstractConstructedAsn1Item.__init__(self, **kwargs)
+ self._componentTypeLen = len(self.componentType)
def __getitem__(self, idx):
if octets.isStringType(idx):
@@ -2095,10 +2031,10 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
base.AbstractConstructedAsn1Item.__setitem__(self, idx, value)
def __contains__(self, key):
- return key in self._componentType
+ return key in self.componentType
def __iter__(self):
- return iter(self._componentType)
+ return iter(self.componentType)
# Python dict protocol
@@ -2107,11 +2043,11 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
yield self[idx]
def keys(self):
- return iter(self._componentType)
+ return iter(self.componentType)
def items(self):
for idx in range(self._componentTypeLen):
- yield self._componentType[idx].getName(), self[idx]
+ yield self.componentType[idx].getName(), self[idx]
def update(self, *iterValue, **mappingValue):
for k, v in iterValue:
@@ -2148,7 +2084,7 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
Instantiate |ASN.1| component type or return existing component value
"""
return self.getComponentByPosition(
- self._componentType.getPositionByName(name)
+ self.componentType.getPositionByName(name)
)
def setComponentByName(self, name, value=noValue,
@@ -2182,7 +2118,7 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
self
"""
return self.setComponentByPosition(
- self._componentType.getPositionByName(name), value, verifyConstraints, matchTags, matchConstraints
+ self.componentType.getPositionByName(name), value, verifyConstraints, matchTags, matchConstraints
)
def getComponentByPosition(self, idx):
@@ -2245,7 +2181,7 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
-------
self
"""
- componentType = self._componentType
+ componentType = self.componentType
componentTypeLen = self._componentTypeLen
try:
@@ -2285,7 +2221,7 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
if verifyConstraints and value.isValue:
try:
- self._subtypeSpec(value, idx)
+ self.subtypeSpec(value, idx)
except error.PyAsn1Error:
exType, exValue, exTb = sys.exc_info()
@@ -2302,36 +2238,43 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
def getNameByPosition(self, idx):
if self._componentTypeLen:
- return self._componentType.getNameByPosition(idx)
-
- def getComponentType(self):
- if self._componentTypeLen:
- return self._componentType
+ return self.componentType.getNameByPosition(idx)
@property
def isValue(self):
- """Indicate if |ASN.1| object components represent ASN.1 type or ASN.1 value.
+ """Indicate if |ASN.1| object represents ASN.1 type or ASN.1 value.
- The PyASN1 type objects can only participate in types comparison
- and serve as a blueprint for serialization codecs to resolve
- ambiguous types.
+ In other words, if *isValue* is `True`, then the ASN.1 object is
+ initialized.
- The PyASN1 value objects can additionally participate in most
- of built-in Python operations.
+ For the purpose of check, the *OPTIONAL* and *DEFAULT* fields are
+ unconditionally considered as initialized.
Returns
-------
: :class:`bool`
- :class:`True` if all |ASN.1| components represent value and type,
- :class:`False` if at least one |ASN.1| component represents just ASN.1 type.
+ :class:`True` if object represents ASN.1 value and type,
+ :class:`False` if object represents just ASN.1 type.
+
+ Note
+ ----
+ There is an important distinction between PyASN1 type and value objects.
+ The PyASN1 type objects can only participate in ASN.1 type
+ operations (subtyping, comparison etc) and serve as a
+ blueprint for serialization codecs to resolve ambiguous types.
+
+ The PyASN1 value objects can additionally participate in most
+ of built-in Python operations.
"""
- componentType = self._componentType
+ componentType = self.componentType
if componentType:
for idx, subComponentType in enumerate(componentType.namedTypes):
if subComponentType.isDefaulted or subComponentType.isOptional:
continue
- if not self._componentValues or self._componentValues[idx] is None or not self._componentValues[idx].isValue:
+ if (not self._componentValues or
+ self._componentValues[idx] is None or
+ not self._componentValues[idx].isValue):
return False
else:
@@ -2354,11 +2297,7 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
for idx in range(len(self._componentValues)):
if self._componentValues[idx] is not None:
representation += ' ' * scope
- componentType = self.getComponentType()
- if componentType is None:
- representation += '<no-name>'
- else:
- representation += componentType.getNameByPosition(idx)
+ representation += self.componentType.getNameByPosition(idx)
representation = '%s=%s\n' % (
representation, self._componentValues[idx].prettyPrint(scope)
)
@@ -2371,26 +2310,31 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
representation += ' ' * scope
representation += '"%s"' % self.componentType.getNameByPosition(idx)
representation = '%s = %s\n' % (
- representation, self._componentType.getTypeByPosition(idx).prettyPrintType(scope)
+ representation, self.componentType.getTypeByPosition(idx).prettyPrintType(scope)
)
return representation + '\n' + ' ' * (scope - 1) + '}'
- # backward compatibility -- no-op
+ # backward compatibility
+
def setDefaultComponents(self):
return self
+ def getComponentType(self):
+ if self._componentTypeLen:
+ return self.componentType
+
class Sequence(SequenceAndSetBase):
__doc__ = SequenceAndSetBase.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
)
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -2407,12 +2351,12 @@ class Sequence(SequenceAndSetBase):
typeId = SequenceAndSetBase.getTypeId()
def getComponentTagMapNearPosition(self, idx):
- if self._componentType:
- return self._componentType.getTagMapNearPosition(idx)
+ if self.componentType:
+ return self.componentType.getTagMapNearPosition(idx)
def getComponentPositionNearType(self, tagSet, idx):
- if self._componentType:
- return self._componentType.getPositionNearType(tagSet, idx)
+ if self.componentType:
+ return self.componentType.getPositionNearType(tagSet, idx)
else:
return idx
@@ -2420,7 +2364,7 @@ class Sequence(SequenceAndSetBase):
class Set(SequenceAndSetBase):
__doc__ = SequenceAndSetBase.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.initTagSet(
@@ -2431,7 +2375,7 @@ class Set(SequenceAndSetBase):
#: object representing ASN.1 type allowed within |ASN.1| type
componentType = namedtype.NamedTypes()
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -2461,7 +2405,7 @@ class Set(SequenceAndSetBase):
a pyasn1 object
"""
component = self.getComponentByPosition(
- self._componentType.getPositionByType(tagSet)
+ self.componentType.getPositionByType(tagSet)
)
if innerFlag and isinstance(component, Set):
# get inner component by inner tagSet
@@ -2503,10 +2447,10 @@ class Set(SequenceAndSetBase):
-------
self
"""
- idx = self._componentType.getPositionByType(tagSet)
+ idx = self.componentType.getPositionByType(tagSet)
if innerFlag: # set inner component by inner tagSet
- componentType = self._componentType.getTypeByPosition(idx)
+ componentType = self.componentType.getTypeByPosition(idx)
if componentType.tagSet:
return self.setComponentByPosition(
@@ -2524,18 +2468,14 @@ class Set(SequenceAndSetBase):
@property
def componentTagMap(self):
- if self._componentType:
- return self._componentType.getTagMap(True)
-
- def getComponentPositionByType(self, tagSet):
- if self._componentType:
- return self._componentType.getPositionByType(tagSet)
+ if self.componentType:
+ return self.componentType.tagMapUnique
class Choice(Set):
__doc__ = Set.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.TagSet() # untagged
@@ -2544,7 +2484,7 @@ class Choice(Set):
#: object representing ASN.1 type allowed within |ASN.1| type
componentType = namedtype.NamedTypes()
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
@@ -2603,12 +2543,12 @@ class Choice(Set):
def __contains__(self, key):
if self._currentIdx is None:
return False
- return key == self._componentType[self._currentIdx].getName()
+ return key == self.componentType[self._currentIdx].getName()
def __iter__(self):
if self._currentIdx is None:
raise StopIteration
- yield self._componentType[self._currentIdx].getName()
+ yield self.componentType[self._currentIdx].getName()
# Python dict protocol
@@ -2618,11 +2558,11 @@ class Choice(Set):
def keys(self):
if self._currentIdx is not None:
- yield self._componentType[self._currentIdx].getName()
+ yield self.componentType[self._currentIdx].getName()
def items(self):
if self._currentIdx is not None:
- yield self._componentType[self._currentIdx].getName(), self[self._currentIdx]
+ yield self.componentType[self._currentIdx].getName(), self[self._currentIdx]
def verifySizeSpec(self):
if self._currentIdx is None:
@@ -2696,16 +2636,16 @@ class Choice(Set):
@property
def minTagSet(self):
- if self._tagSet:
- return self._tagSet
+ if self.tagSet:
+ return self.tagSet
else:
- return self._componentType.minTagSet
+ return self.componentType.minTagSet
@property
def effectiveTagSet(self):
"""Return a :class:`~pyasn1.type.tag.TagSet` object of the currently initialized component or self (if |ASN.1| is tagged)."""
- if self._tagSet:
- return self._tagSet
+ if self.tagSet:
+ return self.tagSet
else:
component = self.getComponent()
return component.effectiveTagSet
@@ -2715,10 +2655,10 @@ class Choice(Set):
""""Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping
ASN.1 tags to ASN.1 objects contained within callee.
"""
- if self._tagSet:
+ if self.tagSet:
return Set.tagMap.fget(self)
else:
- return Set.componentTagMap.fget(self)
+ return self.componentType.tagMapUnique
def getComponent(self, innerFlag=0):
"""Return currently assigned component of the |ASN.1| object.
@@ -2752,7 +2692,7 @@ class Choice(Set):
c = self._componentValues[self._currentIdx]
if isinstance(c, Choice):
return c.getName(innerFlag)
- return self._componentType.getNameByPosition(self._currentIdx)
+ return self.componentType.getNameByPosition(self._currentIdx)
@property
def isValue(self):
@@ -2776,7 +2716,6 @@ class Choice(Set):
return self._componentValues[self._currentIdx].isValue
-
# compatibility stubs
def getMinTagSet(self):
@@ -2786,12 +2725,12 @@ class Choice(Set):
class Any(OctetString):
__doc__ = OctetString.__doc__
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = tag.TagSet() # untagged
- #: Set (class attribute) or return (class or instance attribute) a
+ #: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object
#: imposing constraints on |ASN.1| type initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
diff --git a/pyasn1/type/useful.py b/pyasn1/type/useful.py
index 0b79a98..1338e6a 100644
--- a/pyasn1/type/useful.py
+++ b/pyasn1/type/useful.py
@@ -4,7 +4,10 @@
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
+import datetime
from pyasn1.type import univ, char, tag
+from pyasn1.compat import string, dateandtime
+from pyasn1 import error
__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime']
@@ -20,20 +23,163 @@ class ObjectDescriptor(char.GraphicString):
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
)
+ # Optimization for faster codec lookup
+ typeId = char.GraphicString.getTypeId()
-class GeneralizedTime(char.VisibleString):
- __doc__ = char.GraphicString.__doc__
+
+class TimeMixIn(object):
+
+ _yearsDigits = 4
+ _hasSubsecond = False
+ _optionalMinutes = False
+ _shortTZ = False
+
+ class FixedOffset(datetime.tzinfo):
+ """Fixed offset in minutes east from UTC."""
+
+ def __init__(self, offset, name):
+ self.__offset = datetime.timedelta(minutes=offset)
+ self.__name = name
+
+ def utcoffset(self, dt):
+ return self.__offset
+
+ def tzname(self, dt):
+ return self.__name
+
+ def dst(self, dt):
+ return datetime.timedelta(0)
+
+ UTC = FixedOffset(0, 'UTC')
+
+ @property
+ def asDateTime(self):
+ """Create :py:class:`datetime.datetime` object from a |ASN.1| object.
+
+ Returns
+ -------
+ :
+ new instance of :py:class:`datetime.datetime` object
+ """
+ text = str(self)
+ if text.endswith('Z'):
+ tzinfo = TimeMixIn.UTC
+ text = text[:-1]
+
+ elif '-' in text or '+' in text:
+ if '+' in text:
+ text, plusminus, tz = string.partition(text, '+')
+ else:
+ text, plusminus, tz = string.partition(text, '-')
+
+ if self._shortTZ and len(tz) == 2:
+ tz += '00'
+
+ if len(tz) != 4:
+ raise error.PyAsn1Error('malformed time zone offset %s' % tz)
+
+ try:
+ minutes = int(tz[:2]) * 60 + int(tz[2:])
+ if plusminus == '-':
+ minutes *= -1
+
+ except ValueError:
+ raise error.PyAsn1Error('unknown time specification %s' % self)
+
+ tzinfo = TimeMixIn.FixedOffset(minutes, '?')
+
+ else:
+ tzinfo = None
+
+ if '.' in text or ',' in text:
+ if '.' in text:
+ text, _, ms = string.partition(text, '.')
+ else:
+ text, _, ms = string.partition(text, ',')
+
+ try:
+ ms = int(ms) * 10000
+
+ except ValueError:
+ raise error.PyAsn1Error('bad sub-second time specification %s' % self)
+
+ else:
+ ms = 0
+
+ if self._optionalMinutes and len(text) - self._yearsDigits == 6:
+ text += '0000'
+ elif len(text) - self._yearsDigits == 8:
+ text += '00'
+
+ try:
+ dt = dateandtime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+
+ except ValueError:
+ raise error.PyAsn1Error('malformed datetime format %s' % self)
+
+ return dt.replace(microsecond=ms, tzinfo=tzinfo)
+
+ @classmethod
+ def fromDateTime(cls, dt):
+ """Create |ASN.1| object from a :py:class:`datetime.datetime` object.
+
+ Parameters
+ ----------
+ dt : :py:class:`datetime.datetime` object
+ The `datetime.datetime` object to initialize the |ASN.1| object from
+
+
+ Returns
+ -------
+ :
+ new instance of |ASN.1| value
+ """
+ text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
+ if cls._hasSubsecond:
+ text += '.%d' % (dt.microsecond // 10000)
+
+ if dt.utcoffset():
+ seconds = dt.utcoffset().seconds
+ if seconds < 0:
+ text += '-'
+ else:
+ text += '+'
+ text += '%.2d%.2d' % (seconds // 3600, seconds % 3600)
+ else:
+ text += 'Z'
+
+ return cls(text)
+
+
+class GeneralizedTime(char.VisibleString, TimeMixIn):
+ __doc__ = char.VisibleString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
)
+ # Optimization for faster codec lookup
+ typeId = char.VideotexString.getTypeId()
-class UTCTime(char.VisibleString):
- __doc__ = char.GraphicString.__doc__
+ _yearsDigits = 4
+ _hasSubsecond = True
+ _optionalMinutes = True
+ _shortTZ = True
+
+
+class UTCTime(char.VisibleString, TimeMixIn):
+ __doc__ = char.VisibleString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
)
+
+ # Optimization for faster codec lookup
+ typeId = char.VideotexString.getTypeId()
+
+ _yearsDigits = 2
+ _hasSubsecond = False
+ _optionalMinutes = False
+ _shortTZ = False
diff --git a/setup.py b/setup.py
index 02fa21e..a806c01 100644
--- a/setup.py
+++ b/setup.py
@@ -112,6 +112,9 @@ class PyTest(Command):
unittest.TextTestRunner(verbosity=2).run(suite)
-params['cmdclass'] = {'test': PyTest}
+params['cmdclass'] = {
+ 'test': PyTest,
+ 'tests': PyTest,
+}
setup(**params)
diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py
index 18d7f08..03bfcab 100644
--- a/tests/codec/ber/test_decoder.py
+++ b/tests/codec/ber/test_decoder.py
@@ -482,12 +482,40 @@ class UTF8StringDecoderTestCase(unittest.TestCase):
assert decoder.decode(ints2octs((12, 3, 97, 98, 99))) == (char.UTF8String(sys.version_info[0] == 3 and 'abc' or unicode('abc')), null)
+class SequenceOfDecoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.SequenceOf(componentType=univ.OctetString())
+ self.s.setComponentByPosition(0, univ.OctetString('quick brown'))
+
+ def testDefMode(self):
+ assert decoder.decode(
+ ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+ ) == (self.s, null)
+
+ def testIndefMode(self):
+ assert decoder.decode(
+ ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+ ) == (self.s, null)
+
+ def testDefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+ ) == (self.s, null)
+
+ def testIndefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+ ) == (self.s, null)
+
+
class SequenceDecoderTestCase(unittest.TestCase):
def setUp(self):
- self.s = univ.Sequence(componentType=namedtype.NamedTypes(
- namedtype.NamedType('place-holder', univ.Null(null)),
- namedtype.NamedType('first-name', univ.OctetString(null)),
- namedtype.NamedType('age', univ.Integer(33)))
+ self.s = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null(null)),
+ namedtype.NamedType('first-name', univ.OctetString(null)),
+ namedtype.NamedType('age', univ.Integer(33))
+ )
)
self.s.setComponentByPosition(0, univ.Null(null))
self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
@@ -543,11 +571,13 @@ class SequenceDecoderTestCase(unittest.TestCase):
class GuidedSequenceDecoderTestCase(unittest.TestCase):
def setUp(self):
- self.s = univ.Sequence(componentType=namedtype.NamedTypes(
- namedtype.NamedType('place-holder', univ.Null(null)),
- namedtype.OptionalNamedType('first-name', univ.OctetString(null)),
- namedtype.DefaultedNamedType('age', univ.Integer(33)),
- ))
+ self.s = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null(null)),
+ namedtype.OptionalNamedType('first-name', univ.OctetString()),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ )
+ )
def __init(self):
self.s.clear()
@@ -677,11 +707,13 @@ class GuidedSequenceDecoderTestCase(unittest.TestCase):
class ChoiceDecoderTestCase(unittest.TestCase):
def setUp(self):
- self.s = univ.Choice(componentType=namedtype.NamedTypes(
- namedtype.NamedType('place-holder', univ.Null(null)),
- namedtype.NamedType('number', univ.Integer(0)),
- namedtype.NamedType('string', univ.OctetString())
- ))
+ self.s = univ.Choice(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null(null)),
+ namedtype.NamedType('number', univ.Integer(0)),
+ namedtype.NamedType('string', univ.OctetString())
+ )
+ )
def testBySpec(self):
self.s.setComponentByPosition(0, univ.Null(null))
diff --git a/tests/codec/ber/test_encoder.py b/tests/codec/ber/test_encoder.py
index d0b91c9..dda2596 100644
--- a/tests/codec/ber/test_encoder.py
+++ b/tests/codec/ber/test_encoder.py
@@ -342,13 +342,46 @@ class UTF8StringEncoderTestCase(unittest.TestCase):
(12, 3, 97, 98, 99)), 'Incorrect encoding'
+class SequenceOfEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.SequenceOf(componentType=univ.OctetString())
+
+ def __init(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0, 'quick brown')
+
+ def testDefMode(self):
+ self.__init()
+ assert encoder.encode(self.s) == ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+ def testIndefMode(self):
+ self.__init()
+ assert encoder.encode(
+ self.s, defMode=False
+ ) == ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+ def testDefModeChunked(self):
+ self.__init()
+ assert encoder.encode(
+ self.s, defMode=True, maxChunkSize=4
+ ) == ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+ def testIndefModeChunked(self):
+ self.__init()
+ assert encoder.encode(
+ self.s, defMode=False, maxChunkSize=4
+ ) == ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+
class SequenceEncoderTestCase(unittest.TestCase):
def setUp(self):
- self.s = univ.Sequence(componentType=namedtype.NamedTypes(
- namedtype.NamedType('place-holder', univ.Null()),
- namedtype.OptionalNamedType('first-name', univ.OctetString()),
- namedtype.DefaultedNamedType('age', univ.Integer(33)),
- ))
+ self.s = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null()),
+ namedtype.OptionalNamedType('first-name', univ.OctetString()),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ )
+ )
def __init(self):
self.s.clear()
@@ -466,11 +499,13 @@ class SequenceEncoderTestCase(unittest.TestCase):
class ChoiceEncoderTestCase(unittest.TestCase):
def setUp(self):
- self.s = univ.Choice(componentType=namedtype.NamedTypes(
- namedtype.NamedType('place-holder', univ.Null('')),
- namedtype.NamedType('number', univ.Integer(0)),
- namedtype.NamedType('string', univ.OctetString())
- ))
+ self.s = univ.Choice(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null('')),
+ namedtype.NamedType('number', univ.Integer(0)),
+ namedtype.NamedType('string', univ.OctetString())
+ )
+ )
def testEmpty(self):
try:
diff --git a/tests/codec/cer/test_encoder.py b/tests/codec/cer/test_encoder.py
index 42bff84..a1419fd 100644
--- a/tests/codec/cer/test_encoder.py
+++ b/tests/codec/cer/test_encoder.py
@@ -146,17 +146,31 @@ class GeneralizedTimeEncoderTestCase(unittest.TestCase):
assert 0, 'Missing timezone tolerated'
-# When enabled, this breaks many existing encodings
-#
-# def testDecimalPoint(self):
-# try:
-# assert encoder.encode(
-# useful.GeneralizedTime('20150501120112Z')
-# )
-# except PyAsn1Error:
-# pass
-# else:
-# assert 0, 'Missing decimal point tolerated'
+ def testDecimalCommaPoint(self):
+ try:
+ assert encoder.encode(
+ useful.GeneralizedTime('20150501120112,1Z')
+ )
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'Decimal comma tolerated'
+
+ def testWithSubseconds(self):
+ assert encoder.encode(
+ useful.GeneralizedTime('20170801120112.59Z')
+ ) == ints2octs((24, 18, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 46, 53, 57, 90))
+
+ def testWithSeconds(self):
+ assert encoder.encode(
+ useful.GeneralizedTime('20170801120112Z')
+ ) == ints2octs((24, 15, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90))
+
+ def testWithMinutes(self):
+ assert encoder.encode(
+ useful.GeneralizedTime('201708011201Z')
+ ) == ints2octs((24, 13, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 90))
+
class UTCTimeEncoderTestCase(unittest.TestCase):
def testFractionOfSecond(self):
@@ -170,9 +184,14 @@ class UTCTimeEncoderTestCase(unittest.TestCase):
assert 0, 'Decimal point tolerated'
def testMissingTimezone(self):
- assert encoder.encode(
- useful.UTCTime('150501120112')
- ) == ints2octs((23, 13, 49, 53, 48, 53, 48, 49, 49, 50, 48, 49, 49, 50, 90)), 'Missing timezone not added'
+ try:
+ assert encoder.encode(
+ useful.UTCTime('150501120112')
+ ) == ints2octs((23, 13, 49, 53, 48, 53, 48, 49, 49, 50, 48, 49, 49, 50, 90))
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'Missing timezone tolerated'
def testLocalTimezone(self):
try:
@@ -184,6 +203,231 @@ class UTCTimeEncoderTestCase(unittest.TestCase):
else:
assert 0, 'Local timezone tolerated'
+ def testWithSeconds(self):
+ assert encoder.encode(
+ useful.UTCTime('990801120112Z')
+ ) == ints2octs((23, 13, 57, 57, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90))
+
+ def testWithMinutes(self):
+ assert encoder.encode(
+ useful.UTCTime('9908011201Z')
+ ) == ints2octs((23, 11, 57, 57, 48, 56, 48, 49, 49, 50, 48, 49, 90))
+
+
+class SetOfEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.SetOf(componentType=univ.OctetString())
+
+ def testIndefMode1(self):
+ self.s.clear()
+ self.s.append('a')
+ self.s.append('ab')
+
+ assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+ def testIndefMode2(self):
+ self.s.clear()
+ self.s.append('ab')
+ self.s.append('a')
+
+ assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0))
+
+ def testIndefMode3(self):
+ self.s.clear()
+ self.s.append('b')
+ self.s.append('a')
+
+ assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+ def testIndefMode4(self):
+ self.s.clear()
+ self.s.append('a')
+ self.s.append('b')
+
+ assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0))
+
+
+class NestedOptionalSequenceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ inner = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('first-name', univ.OctetString()),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ )
+ )
+
+ outerWithOptional = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('inner', inner),
+ )
+ )
+
+ outerWithDefault = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.DefaultedNamedType('inner', inner),
+ )
+ )
+
+ self.s1 = outerWithOptional
+ self.s2 = outerWithDefault
+
+ def __initOptionalWithDefaultAndOptional(self):
+ self.s1.clear()
+ self.s1[0][0] = 'test'
+ self.s1[0][1] = 123
+ return self.s1
+
+ def __initOptionalWithDefault(self):
+ self.s1.clear()
+ self.s1[0][1] = 123
+ return self.s1
+
+ def __initOptionalWithOptional(self):
+ self.s1.clear()
+ self.s1[0][0] = 'test'
+ return self.s1
+
+ def __initOptional(self):
+ self.s1.clear()
+ return self.s1
+
+ def __initDefaultWithDefaultAndOptional(self):
+ self.s2.clear()
+ self.s2[0][0] = 'test'
+ self.s2[0][1] = 123
+ return self.s2
+
+ def __initDefaultWithDefault(self):
+ self.s2.clear()
+ self.s2[0][0] = 'test'
+ return self.s2
+
+ def __initDefaultWithOptional(self):
+ self.s2.clear()
+ self.s2[0][1] = 123
+ return self.s2
+
+ def testOptionalWithDefaultAndOptional(self):
+ s = self.__initOptionalWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+
+ def testOptionalWithDefault(self):
+ s = self.__initOptionalWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
+ def testOptionalWithOptional(self):
+ s = self.__initOptionalWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+ def testOptional(self):
+ s = self.__initOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
+ def testDefaultWithDefaultAndOptional(self):
+ s = self.__initDefaultWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+
+ def testDefaultWithDefault(self):
+ s = self.__initDefaultWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+ def testDefaultWithOptional(self):
+ s = self.__initDefaultWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
+
+class NestedOptionalChoiceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ layer3 = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('first-name', univ.OctetString()),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ )
+ )
+
+ layer2 = univ.Choice(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('inner', layer3),
+ namedtype.NamedType('first-name', univ.OctetString())
+ )
+ )
+
+ layer1 = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('inner', layer2),
+ )
+ )
+
+ self.s = layer1
+
+ def __initOptionalWithDefaultAndOptional(self):
+ self.s.clear()
+ self.s[0][0][0] = 'test'
+ self.s[0][0][1] = 123
+ return self.s
+
+ def __initOptionalWithDefault(self):
+ self.s.clear()
+ self.s[0][0][1] = 123
+ return self.s
+
+ def __initOptionalWithOptional(self):
+ self.s.clear()
+ self.s[0][0][0] = 'test'
+ return self.s
+
+ def __initOptional(self):
+ self.s.clear()
+ return self.s
+
+ def testOptionalWithDefaultAndOptional(self):
+ s = self.__initOptionalWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+
+ def testOptionalWithDefault(self):
+ s = self.__initOptionalWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
+ def testOptionalWithOptional(self):
+ s = self.__initOptionalWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+ def testOptional(self):
+ s = self.__initOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
+
+class NestedOptionalSequenceOfEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ layer2 = univ.SequenceOf(
+ componentType=univ.OctetString()
+ )
+
+ layer1 = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('inner', layer2),
+ )
+ )
+
+ self.s = layer1
+
+ def __initOptionalWithValue(self):
+ self.s.clear()
+ self.s[0][0] = 'test'
+ return self.s
+
+ def __initOptional(self):
+ self.s.clear()
+ return self.s
+
+ def testOptionalWithValue(self):
+ s = self.__initOptionalWithValue()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+ def testOptional(self):
+ s = self.__initOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
diff --git a/tests/codec/der/test_encoder.py b/tests/codec/der/test_encoder.py
index a148763..d30dfc1 100644
--- a/tests/codec/der/test_encoder.py
+++ b/tests/codec/der/test_encoder.py
@@ -43,6 +43,38 @@ class BitStringEncoderTestCase(unittest.TestCase):
) == ints2octs((3, 2, 7, 128))
+class SetOfEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.SetOf(componentType=univ.OctetString())
+
+ def testDefMode1(self):
+ self.s.clear()
+ self.s.append('a')
+ self.s.append('ab')
+
+ assert encoder.encode(self.s) == ints2octs((49, 7, 4, 1, 97, 4, 2, 97, 98))
+
+ def testDefMode2(self):
+ self.s.clear()
+ self.s.append('ab')
+ self.s.append('a')
+
+ assert encoder.encode(self.s) == ints2octs((49, 7, 4, 1, 97, 4, 2, 97, 98))
+
+ def testDefMode3(self):
+ self.s.clear()
+ self.s.append('b')
+ self.s.append('a')
+
+ assert encoder.encode(self.s) == ints2octs((49, 6, 4, 1, 97, 4, 1, 98))
+
+ def testDefMode4(self):
+ self.s.clear()
+ self.s.append('a')
+ self.s.append('b')
+
+ assert encoder.encode(self.s) == ints2octs((49, 6, 4, 1, 97, 4, 1, 98))
+
class SetWithChoiceEncoderTestCase(unittest.TestCase):
def setUp(self):
c = univ.Choice(componentType=namedtype.NamedTypes(
@@ -64,6 +96,189 @@ class SetWithChoiceEncoderTestCase(unittest.TestCase):
self.s.getComponentByName('status').setComponentByPosition(1, True)
assert encoder.encode(self.s) == ints2octs((49, 6, 1, 1, 255, 2, 1, 5))
+
+class NestedOptionalSequenceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ inner = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('first-name', univ.OctetString()),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ )
+ )
+
+ outerWithOptional = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('inner', inner),
+ )
+ )
+
+ outerWithDefault = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.DefaultedNamedType('inner', inner),
+ )
+ )
+
+ self.s1 = outerWithOptional
+ self.s2 = outerWithDefault
+
+ def __initOptionalWithDefaultAndOptional(self):
+ self.s1.clear()
+ self.s1[0][0] = 'test'
+ self.s1[0][1] = 123
+ return self.s1
+
+ def __initOptionalWithDefault(self):
+ self.s1.clear()
+ self.s1[0][1] = 123
+ return self.s1
+
+ def __initOptionalWithOptional(self):
+ self.s1.clear()
+ self.s1[0][0] = 'test'
+ return self.s1
+
+ def __initOptional(self):
+ self.s1.clear()
+ return self.s1
+
+ def __initDefaultWithDefaultAndOptional(self):
+ self.s2.clear()
+ self.s2[0][0] = 'test'
+ self.s2[0][1] = 123
+ return self.s2
+
+ def __initDefaultWithDefault(self):
+ self.s2.clear()
+ self.s2[0][0] = 'test'
+ return self.s2
+
+ def __initDefaultWithOptional(self):
+ self.s2.clear()
+ self.s2[0][1] = 123
+ return self.s2
+
+ def testDefModeOptionalWithDefaultAndOptional(self):
+ s = self.__initOptionalWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123))
+
+ def testDefModeOptionalWithDefault(self):
+ s = self.__initOptionalWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123))
+
+ def testDefModeOptionalWithOptional(self):
+ s = self.__initOptionalWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+ def testDefModeOptional(self):
+ s = self.__initOptional()
+ assert encoder.encode(s) == ints2octs((48, 0))
+
+ def testDefModeDefaultWithDefaultAndOptional(self):
+ s = self.__initDefaultWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123))
+
+ def testDefModeDefaultWithDefault(self):
+ s = self.__initDefaultWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+ def testDefModeDefaultWithOptional(self):
+ s = self.__initDefaultWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123))
+
+
+class NestedOptionalChoiceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ layer3 = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('first-name', univ.OctetString()),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ )
+ )
+
+ layer2 = univ.Choice(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('inner', layer3),
+ namedtype.NamedType('first-name', univ.OctetString())
+ )
+ )
+
+ layer1 = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('inner', layer2),
+ )
+ )
+
+ self.s = layer1
+
+ def __initOptionalWithDefaultAndOptional(self):
+ self.s.clear()
+ self.s[0][0][0] = 'test'
+ self.s[0][0][1] = 123
+ return self.s
+
+ def __initOptionalWithDefault(self):
+ self.s.clear()
+ self.s[0][0][1] = 123
+ return self.s
+
+ def __initOptionalWithOptional(self):
+ self.s.clear()
+ self.s[0][0][0] = 'test'
+ return self.s
+
+ def __initOptional(self):
+ self.s.clear()
+ return self.s
+
+ def testDefModeOptionalWithDefaultAndOptional(self):
+ s = self.__initOptionalWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123))
+
+ def testDefModeOptionalWithDefault(self):
+ s = self.__initOptionalWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123))
+
+ def testDefModeOptionalWithOptional(self):
+ s = self.__initOptionalWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+ def testDefModeOptional(self):
+ s = self.__initOptional()
+ assert encoder.encode(s) == ints2octs((48, 0))
+
+
+class NestedOptionalSequenceOfEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ layer2 = univ.SequenceOf(
+ componentType=univ.OctetString()
+ )
+
+ layer1 = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('inner', layer2),
+ )
+ )
+
+ self.s = layer1
+
+ def __initOptionalWithValue(self):
+ self.s.clear()
+ self.s[0][0] = 'test'
+ return self.s
+
+ def __initOptional(self):
+ self.s.clear()
+ return self.s
+
+ def testDefModeOptionalWithValue(self):
+ s = self.__initOptionalWithValue()
+ assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116))
+
+ def testDefModeOptional(self):
+ s = self.__initOptional()
+ assert encoder.encode(s) == ints2octs((48, 0))
+
+
suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
if __name__ == '__main__':
diff --git a/tests/type/__main__.py b/tests/type/__main__.py
index 15036ea..0ad51ce 100644
--- a/tests/type/__main__.py
+++ b/tests/type/__main__.py
@@ -16,7 +16,8 @@ suite = unittest.TestLoader().loadTestsFromNames(
'tests.type.test_namedval.suite',
'tests.type.test_tag.suite',
'tests.type.test_univ.suite',
- 'tests.type.test_char.suite']
+ 'tests.type.test_char.suite',
+ 'tests.type.test_useful.suite']
)
diff --git a/tests/type/test_namedtype.py b/tests/type/test_namedtype.py
index a4186ca..be46e3c 100644
--- a/tests/type/test_namedtype.py
+++ b/tests/type/test_namedtype.py
@@ -75,24 +75,24 @@ class NamedTypesCaseBase(unittest.TestCase):
}
def testGetTagMap(self):
- assert self.e.getTagMap().presentTypes == {
+ assert self.e.tagMap.presentTypes == {
univ.OctetString.tagSet: univ.OctetString(''),
univ.Integer.tagSet: univ.Integer(0)
}
def testStrTagMap(self):
- assert 'TagMap' in str(self.e.getTagMap())
- assert 'OctetString' in str(self.e.getTagMap())
- assert 'Integer' in str(self.e.getTagMap())
+ assert 'TagMap' in str(self.e.tagMap)
+ assert 'OctetString' in str(self.e.tagMap)
+ assert 'Integer' in str(self.e.tagMap)
def testReprTagMap(self):
- assert 'TagMap' in repr(self.e.getTagMap())
- assert 'OctetString' in repr(self.e.getTagMap())
- assert 'Integer' in repr(self.e.getTagMap())
+ assert 'TagMap' in repr(self.e.tagMap)
+ assert 'OctetString' in repr(self.e.tagMap)
+ assert 'Integer' in repr(self.e.tagMap)
def testGetTagMapWithDups(self):
try:
- self.e.getTagMap(True)
+ self.e.tagMapUnique[0]
except PyAsn1Error:
pass
else:
diff --git a/tests/type/test_namedval.py b/tests/type/test_namedval.py
index e52f724..6504b34 100644
--- a/tests/type/test_namedval.py
+++ b/tests/type/test_namedval.py
@@ -18,12 +18,34 @@ class NamedValuesCaseBase(unittest.TestCase):
def setUp(self):
self.e = namedval.NamedValues(('off', 0), ('on', 1))
- def testIter(self):
- off, on = self.e
- assert off == ('off', 0) or on == ('on', 1), 'unpack fails'
-
- def testRepr(self):
- assert eval(repr(self.e), {'NamedValues': namedval.NamedValues}) == self.e, 'repr() fails'
+ def testDict(self):
+ assert set(self.e.items()) == set([('off', 0), ('on', 1)])
+ assert set(self.e.keys()) == set(['off', 'on'])
+ assert set(self.e) == set(['off', 'on'])
+ assert set(self.e.values()) == set([0, 1])
+ assert 'on' in self.e and 'off' in self.e and 'xxx' not in self.e
+ assert 0 in self.e and 1 in self.e and 2 not in self.e
+
+ def testInit(self):
+ assert namedval.NamedValues(off=0, on=1) == {'off': 0, 'on': 1}
+ assert namedval.NamedValues('off', 'on') == {'off': 0, 'on': 1}
+ assert namedval.NamedValues(('c', 0)) == {'c': 0}
+ assert namedval.NamedValues('a', 'b', ('c', 0), d=1) == {'c': 0, 'd': 1, 'a': 2, 'b': 3}
+
+ def testLen(self):
+ assert len(self.e) == 2
+ assert len(namedval.NamedValues()) == 0
+
+ def testAdd(self):
+ assert namedval.NamedValues(off=0) + namedval.NamedValues(on=1) == {'off': 0, 'on': 1}
+
+ def testClone(self):
+ assert namedval.NamedValues(off=0).clone(('on', 1)) == {'off': 0, 'on': 1}
+ assert namedval.NamedValues(off=0).clone(on=1) == {'off': 0, 'on': 1}
+
+ def testStrRepr(self):
+ assert str(self.e)
+ assert repr(self.e)
suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py
index 339f965..48c9271 100644
--- a/tests/type/test_univ.py
+++ b/tests/type/test_univ.py
@@ -272,11 +272,24 @@ class IntegerTestCase(unittest.TestCase):
)
def testNamedVals(self):
- i = univ.Integer(
- 'asn1', namedValues=univ.Integer.namedValues.clone(('asn1', 1))
+
+ class Integer(univ.Integer):
+ namedValues = univ.Integer.namedValues.clone(('asn1', 1))
+
+ assert Integer('asn1') == 1, 'named val fails'
+ assert str(Integer('asn1')) != 'asn1', 'named val __str__() fails'
+
+ def testSubtype(self):
+ assert univ.Integer().subtype(
+ value=1,
+ implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2),
+ subtypeSpec=constraint.SingleValueConstraint(1, 3)
+ ) == univ.Integer(
+ value=1,
+ tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate,
+ tag.tagFormatSimple, 2)),
+ subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1, 3))
)
- assert i == 1, 'named val fails'
- assert str(i) != 'asn1', 'named val __str__() fails'
class BooleanTestCase(unittest.TestCase):
@@ -299,25 +312,17 @@ class BooleanTestCase(unittest.TestCase):
)
def testConstraints(self):
+
+ class Boolean(univ.Boolean):
+ pass
+
try:
- univ.Boolean(2)
+ Boolean(2)
except error.ValueConstraintError:
pass
else:
assert 0, 'constraint fail'
- def testSubtype(self):
- assert univ.Integer().subtype(
- value=1,
- implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2),
- subtypeSpec=constraint.SingleValueConstraint(1, 3)
- ) == univ.Integer(
- value=1,
- tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate,
- tag.tagFormatSimple, 2)),
- subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1, 3))
- )
-
class BitStringTestCase(unittest.TestCase):
def setUp(self):
@@ -388,6 +393,13 @@ class BitStringTestCase(unittest.TestCase):
assert self.b.clone('11000000011001').asInteger() == 12313
assert self.b.clone('1100110011011111').asInteger() == 52447
+ def testStaticDef(self):
+
+ class BitString(univ.BitString):
+ pass
+
+ assert BitString('11000000011001').asInteger() == 12313
+
class OctetStringWithUnicodeMixIn(object):
@@ -519,6 +531,13 @@ class OctetStringTestCase(unittest.TestCase):
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
)
+ def testStaticDef(self):
+
+ class OctetString(univ.OctetString):
+ pass
+
+ assert OctetString(hexValue="FA9823C43E43510DE3422") == ints2octs((250, 152, 35, 196, 62, 67, 81, 13, 227, 66, 32))
+
class Null(unittest.TestCase):
def testStr(self):
@@ -541,6 +560,13 @@ class Null(unittest.TestCase):
else:
assert 0, 'constraint fail'
+ def testStaticDef(self):
+
+ class Null(univ.Null):
+ pass
+
+ assert not Null()
+
class RealTestCase(unittest.TestCase):
def testFloat4BinEnc(self):
@@ -637,10 +663,10 @@ class RealTestCase(unittest.TestCase):
assert univ.Real(float('inf')) == float('inf'), 'prettyIn() fails'
def testPlusInf(self):
- assert univ.Real('inf').isPlusInfinity(), 'isPlusInfinity failed'
+ assert univ.Real('inf').isPlusInf, 'isPlusInfinity failed'
def testMinusInf(self):
- assert univ.Real('-inf').isMinusInfinity(), 'isMinusInfinity failed'
+ assert univ.Real('-inf').isMinusInf, 'isMinusInfinity failed'
def testPos(self):
assert +univ.Real(1.0) == 1.0, '__pos__() fails'
@@ -667,6 +693,13 @@ class RealTestCase(unittest.TestCase):
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
)
+ def testStaticDef(self):
+
+ class Real(univ.Real):
+ pass
+
+ assert Real(1.0) == 1.0
+
class ObjectIdentifier(unittest.TestCase):
def testStr(self):
@@ -720,6 +753,13 @@ class ObjectIdentifier(unittest.TestCase):
assert 1234 in s
assert 4321 not in s
+ def testStaticDef(self):
+
+ class ObjectIdentifier(univ.ObjectIdentifier):
+ pass
+
+ assert str(ObjectIdentifier((1, 3, 6))) == '1.3.6'
+
class SequenceOf(unittest.TestCase):
def setUp(self):
@@ -730,7 +770,8 @@ class SequenceOf(unittest.TestCase):
def testRepr(self):
assert eval(repr(self.s1.clone().setComponents('a', 'b')),
- {'SequenceOf': univ.SequenceOf, 'OctetString': univ.OctetString}) == self.s1.clone().setComponents(
+ {'SequenceOf': univ.SequenceOf,
+ 'OctetString': univ.OctetString}) == self.s1.clone().setComponents(
'a', 'b'), 'repr() fails'
def testTag(self):
@@ -819,7 +860,7 @@ class SequenceOf(unittest.TestCase):
assert 0, 'size spec fails'
def testGetComponentTagMap(self):
- assert self.s1.getComponentTagMap().presentTypes == {
+ assert self.s1.componentType.tagMap.presentTypes == {
univ.OctetString.tagSet: univ.OctetString('')
}
@@ -888,6 +929,30 @@ class SequenceOf(unittest.TestCase):
self.s1.sort()
assert list(self.s1) == [str2octs('a'), str2octs('b')]
+ def testStaticDef(self):
+
+ class SequenceOf(univ.SequenceOf):
+ componentType = univ.OctetString('')
+
+ s = SequenceOf()
+ s[0] = 'abc'
+ assert len(s) == 1
+ assert s == [str2octs('abc')]
+
+ def testLegacyInitializer(self):
+ n = univ.SequenceOf(
+ componentType=univ.OctetString()
+ )
+ o = univ.SequenceOf(
+ univ.OctetString() # this is the old way
+ )
+
+ assert n.isSameTypeWith(o) and o.isSameTypeWith(n)
+
+ n[0] = 'fox'
+ o[0] = 'fox'
+
+ assert n == o
class Sequence(unittest.TestCase):
def setUp(self):
@@ -1022,6 +1087,19 @@ class Sequence(unittest.TestCase):
self.s1.update(name='CBA')
assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'CBA'), ('nick', 'def')]] + [('age', 123)]
+ def testStaticDef(self):
+
+ class Sequence(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString('')),
+ namedtype.OptionalNamedType('nick', univ.OctetString('')),
+ namedtype.DefaultedNamedType('age', univ.Integer(34))
+ )
+
+ s = Sequence()
+ s['name'] = 'abc'
+ assert s['name'] == str2octs('abc')
+
class SetOf(unittest.TestCase):
def setUp(self):
@@ -1039,6 +1117,16 @@ class SetOf(unittest.TestCase):
self.s1.setComponentByPosition(0, self.s1[0].clone('cba'))
assert self.s1[0] == str2octs('cba'), 'set by idx fails'
+ def testStaticDef(self):
+
+ class SetOf(univ.SequenceOf):
+ componentType = univ.OctetString('')
+
+ s = SetOf()
+ s[0] = 'abc'
+ assert len(s) == 1
+ assert s == [str2octs('abc')]
+
class Set(unittest.TestCase):
def setUp(self):
@@ -1073,14 +1161,14 @@ class Set(unittest.TestCase):
}
def testGetComponentTagMap(self):
- assert self.s1.getComponentTagMap().presentTypes == {
+ assert self.s1.componentType.tagMapUnique.presentTypes == {
univ.OctetString.tagSet: univ.OctetString(''),
univ.Null.tagSet: univ.Null(''),
univ.Integer.tagSet: univ.Integer(34)
}
def testGetPositionByType(self):
- assert self.s1.getComponentPositionByType(univ.Null().tagSet) == 1
+ assert self.s1.componentType.getPositionByType(univ.Null().tagSet) == 1
def testSetToDefault(self):
self.s1.setComponentByName('name', univ.noValue)
@@ -1089,17 +1177,34 @@ class Set(unittest.TestCase):
def testIter(self):
assert list(self.s1) == ['name', 'null', 'age']
+ def testStaticDef(self):
+
+ class Set(univ.Set):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString('')),
+ namedtype.OptionalNamedType('nick', univ.OctetString('')),
+ namedtype.DefaultedNamedType('age', univ.Integer(34))
+ )
+
+ s = Set()
+ s['name'] = 'abc'
+ assert s['name'] == str2octs('abc')
+
class Choice(unittest.TestCase):
def setUp(self):
- innerComp = univ.Choice(componentType=namedtype.NamedTypes(
- namedtype.NamedType('count', univ.Integer()),
- namedtype.NamedType('flag', univ.Boolean())
- ))
- self.s1 = univ.Choice(componentType=namedtype.NamedTypes(
- namedtype.NamedType('name', univ.OctetString()),
- namedtype.NamedType('sex', innerComp)
- ))
+ innerComp = univ.Choice(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('count', univ.Integer()),
+ namedtype.NamedType('flag', univ.Boolean())
+ )
+ )
+ self.s1 = univ.Choice(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString()),
+ namedtype.NamedType('sex', innerComp)
+ )
+ )
def testTag(self):
assert self.s1.tagSet == tag.TagSet(), 'wrong tagSet'
@@ -1190,6 +1295,25 @@ class Choice(unittest.TestCase):
s.setComponentByName('sex', univ.noValue)
assert s['sex'] is not univ.noValue
+ def testStaticDef(self):
+
+ class InnerChoice(univ.Choice):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('count', univ.Integer()),
+ namedtype.NamedType('flag', univ.Boolean())
+ )
+
+ class OuterChoice(univ.Choice):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString()),
+ namedtype.NamedType('sex', InnerChoice())
+ )
+
+ c = OuterChoice()
+
+ c.setComponentByType(univ.OctetString.tagSet, 'abc')
+ assert c.getName() == 'name'
+
suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
diff --git a/tests/type/test_useful.py b/tests/type/test_useful.py
new file mode 100644
index 0000000..dbd6fe0
--- /dev/null
+++ b/tests/type/test_useful.py
@@ -0,0 +1,97 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
+# License: http://pyasn1.sf.net/license.html
+#
+import sys
+import datetime
+from pyasn1.type import useful
+from pyasn1.error import PyAsn1Error
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+
+class FixedOffset(datetime.tzinfo):
+ def __init__(self, offset, name):
+ self.__offset = datetime.timedelta(minutes=offset)
+ self.__name = name
+
+ def utcoffset(self, dt):
+ return self.__offset
+
+ def tzname(self, dt):
+ return self.__name
+
+ def dst(self, dt):
+ return datetime.timedelta(0)
+
+
+UTC = FixedOffset(0, 'UTC')
+UTC2 = FixedOffset(120, 'UTC')
+
+
+class ObjectDescriptorTestCase(unittest.TestCase):
+ pass
+
+
+class GeneralizedTimeTestCase(unittest.TestCase):
+
+ def testFromDateTime(self):
+ assert useful.GeneralizedTime.fromDateTime(datetime.datetime(2017, 7, 11, 0, 1, 2, 30000, tzinfo=UTC)) == '20170711000102.3Z'
+
+ def testToDateTime0(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2) == useful.GeneralizedTime('20170711000102').asDateTime
+
+ def testToDateTime1(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.GeneralizedTime('20170711000102Z').asDateTime
+
+ def testToDateTime2(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, 30000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102.3Z').asDateTime
+
+ def testToDateTime3(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, 30000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102,3Z').asDateTime
+
+ def testToDateTime4(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, 30000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102.3+0000').asDateTime
+
+ def testToDateTime5(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, 30000, tzinfo=UTC2) == useful.GeneralizedTime('20170711000102.3+0200').asDateTime
+
+ def testToDateTime6(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, 30000, tzinfo=UTC2) == useful.GeneralizedTime('20170711000102.3+02').asDateTime
+
+ def testToDateTime7(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1) == useful.GeneralizedTime('201707110001').asDateTime
+
+ def testToDateTime8(self):
+ assert datetime.datetime(2017, 7, 11, 0) == useful.GeneralizedTime('2017071100').asDateTime
+
+
+class UTCTimeTestCase(unittest.TestCase):
+
+ def testFromDateTime(self):
+ assert useful.UTCTime.fromDateTime(datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC)) == '170711000102Z'
+
+ def testToDateTime0(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2) == useful.UTCTime('170711000102').asDateTime
+
+ def testToDateTime1(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.UTCTime('170711000102Z').asDateTime
+
+ def testToDateTime2(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.UTCTime('170711000102+0000').asDateTime
+
+ def testToDateTime3(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC2) == useful.UTCTime('170711000102+0200').asDateTime
+
+ def testToDateTime4(self):
+ assert datetime.datetime(2017, 7, 11, 0, 1) == useful.UTCTime('1707110001').asDateTime
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+ unittest.TextTestRunner(verbosity=2).run(suite)