diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-11-19 16:41:26 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-19 16:41:26 +0100 |
commit | 5efe9f213563560d26bd34b6d9b3642642565b41 (patch) | |
tree | 87424f2b94fb3360c15ccad18c064518e3328830 | |
parent | c156221fce2b2e9061ee5095ce6488c829e62698 (diff) | |
download | pyasn1-git-5efe9f213563560d26bd34b6d9b3642642565b41.tar.gz |
Start `.prettyPrint` deprecation (#103)
* __str__() of ASN.1 types reworked to serve instead of .prettyPrint()
Related changes: `str()` on enumerations and boolean will return
a string label rather than a number.
-rw-r--r-- | CHANGES.rst | 10 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | docs/source/example-use-case.rst | 2 | ||||
-rw-r--r-- | pyasn1/codec/ber/decoder.py | 4 | ||||
-rw-r--r-- | pyasn1/codec/cer/decoder.py | 4 | ||||
-rw-r--r-- | pyasn1/codec/der/decoder.py | 4 | ||||
-rw-r--r-- | pyasn1/codec/native/decoder.py | 2 | ||||
-rw-r--r-- | pyasn1/codec/native/encoder.py | 2 | ||||
-rw-r--r-- | pyasn1/type/base.py | 30 | ||||
-rw-r--r-- | pyasn1/type/char.py | 7 | ||||
-rw-r--r-- | pyasn1/type/univ.py | 53 | ||||
-rw-r--r-- | tests/type/test_univ.py | 8 |
12 files changed, 70 insertions, 60 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 3e6d96e..eaa6013 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,5 +1,5 @@ -Revision 0.4.1, released XX-10-2017 +Revision 0.4.1, released XX-11-2017 ----------------------------------- - ANY DEFINED BY clause support implemented @@ -11,8 +11,14 @@ Revision 0.4.1, released XX-10-2017 parameter to return instead if schema object is to be returned - Constructed types' .getComponentBy*() methods accept the `instantiate` parameter to disable automatic inner component instantiation -- The ASN.1 types `__repr__` implementation reworked for better readability +- The ASN.1 types' `__repr__` implementation reworked for better readability at the cost of not being `eval`-compliant +- Most ASN.1 types' `__str__` magic methods (except for OctetString and + character types) reworked to call `.prettyPrint()` rather than `.prettyPrint` + calling `__str__` as it was before. The intention is to eventually deprecate + `.prettyPrint()` in favor of `str()`. + The other related change is that `str()` of enumerations and boolean types + will return string label instead of number. - Fixed Choice.clear() to fully reset internal state of the object - Sphinx documentation rearranged, simplified and reworded - The `isValue` singleton is now the only way to indicate ASN.1 schema @@ -90,7 +90,7 @@ can use it along the lines of similar Python type (e.g. ASN.1 >>> record = Record() >>> record['id'] = 123 >>> record['room'] = 321 ->>> print(record.prettyPrint()) +>>> str(record) Record: id=123 room=321 @@ -146,7 +146,7 @@ Python objects: ```python >>> from pyasn1.codec.native.decoder import decode >>> record = decode({'id': 123, 'room': 321, 'house': 0}, asn1Spec=Record()) ->>> print(record.prettyPrint()) +>>> str(record) Record: id=123 room=321 diff --git a/docs/source/example-use-case.rst b/docs/source/example-use-case.rst index 0b819c3..4efdc81 100644 --- a/docs/source/example-use-case.rst +++ b/docs/source/example-use-case.rst @@ -110,7 +110,7 @@ Once we have Python ASN.1 structures initialized, we could inspect them: .. code-block:: pycon - >>> print(private_key.prettyPrint()) + >>> print('%s' % private_key) RSAPrivateKey: version=0 modulus=280789907761334970323210643584308373... diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py index e8c439d..2607d51 100644 --- a/pyasn1/codec/ber/decoder.py +++ b/pyasn1/codec/ber/decoder.py @@ -1362,7 +1362,7 @@ class Decoder(object): #: .. code-block:: pycon #: #: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03') -#: >>> print(s.prettyPrint()) +#: >>> str(s) #: SequenceOf: #: 1 2 3 #: @@ -1372,7 +1372,7 @@ class Decoder(object): #: #: >>> seq = SequenceOf(componentType=Integer()) #: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq) -#: >>> print(s.prettyPrint()) +#: >>> str(s) #: SequenceOf: #: 1 2 3 #: diff --git a/pyasn1/codec/cer/decoder.py b/pyasn1/codec/cer/decoder.py index 1e54be2..62b1f82 100644 --- a/pyasn1/codec/cer/decoder.py +++ b/pyasn1/codec/cer/decoder.py @@ -97,7 +97,7 @@ class Decoder(decoder.Decoder): #: .. code-block:: pycon #: #: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00') -#: >>> print(s.prettyPrint()) +#: >>> str(s) #: SequenceOf: #: 1 2 3 #: @@ -107,7 +107,7 @@ class Decoder(decoder.Decoder): #: #: >>> seq = SequenceOf(componentType=Integer()) #: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00', asn1Spec=seq) -#: >>> print(s.prettyPrint()) +#: >>> str(s) #: SequenceOf: #: 1 2 3 #: diff --git a/pyasn1/codec/der/decoder.py b/pyasn1/codec/der/decoder.py index 10a582e..751c5dc 100644 --- a/pyasn1/codec/der/decoder.py +++ b/pyasn1/codec/der/decoder.py @@ -77,7 +77,7 @@ class Decoder(decoder.Decoder): #: .. code-block:: pycon #: #: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03') -#: >>> print(s.prettyPrint()) +#: >>> str(s) #: SequenceOf: #: 1 2 3 #: @@ -87,7 +87,7 @@ class Decoder(decoder.Decoder): #: #: >>> seq = SequenceOf(componentType=Integer()) #: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq) -#: >>> print(s.prettyPrint()) +#: >>> str(s) #: SequenceOf: #: 1 2 3 #: diff --git a/pyasn1/codec/native/decoder.py b/pyasn1/codec/native/decoder.py index e0b1d38..415a185 100644 --- a/pyasn1/codec/native/decoder.py +++ b/pyasn1/codec/native/decoder.py @@ -202,7 +202,7 @@ class Decoder(object): #: #: >>> seq = SequenceOf(componentType=Integer()) #: >>> s, _ = decode([1, 2, 3], asn1Spec=seq) -#: >>> print(s.prettyPrint()) +#: >>> str(s) #: SequenceOf: #: 1 2 3 #: diff --git a/pyasn1/codec/native/encoder.py b/pyasn1/codec/native/encoder.py index 8da9d98..8d4cf6a 100644 --- a/pyasn1/codec/native/encoder.py +++ b/pyasn1/codec/native/encoder.py @@ -43,7 +43,7 @@ class OctetStringEncoder(AbstractItemEncoder): class TextStringEncoder(AbstractItemEncoder): def encode(self, value, encodeFun, **options): - return value.prettyPrint() + return str(value) class NullEncoder(AbstractItemEncoder): diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py index d39b456..600240a 100644 --- a/pyasn1/type/base.py +++ b/pyasn1/type/base.py @@ -52,6 +52,9 @@ class Asn1ItemBase(Asn1Item): self.__dict__[name] = value + def __str__(self): + return self.prettyPrint() + @property def readOnly(self): return self._readOnly @@ -123,6 +126,9 @@ class Asn1ItemBase(Asn1Item): return False return True + def prettyPrint(self, scope=0): + raise NotImplementedError() + # backward compatibility def getTagSet(self): @@ -211,6 +217,9 @@ class NoValue(object): raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr) + def __repr__(self): + return '<%s object at %s>' % (self.__class__.__name__, id(self)) + noValue = NoValue() @@ -252,9 +261,6 @@ class AbstractSimpleAsn1Item(Asn1ItemBase): return '<%s>' % representation - def __str__(self): - return str(self._value) - def __eq__(self, other): return self is other and True or self._value == other @@ -414,17 +420,7 @@ class AbstractSimpleAsn1Item(Asn1ItemBase): return str(value) def prettyPrint(self, scope=0): - """Return human-friendly object representation as a text. - - Returns - ------- - : :class:`str` - human-friendly type and/or value representation. - """ - if self.isValue: - return self.prettyOut(self._value) - else: - return '<no value>' + return self.prettyOut(self._value) # noinspection PyUnusedLocal def prettyPrintType(self, scope=0): @@ -509,6 +505,9 @@ class AbstractConstructedAsn1Item(Asn1ItemBase): def __bool__(self): return self._componentValues and True or False + def __len__(self): + return len(self._componentValues) + def _cloneComponentValues(self, myClone, cloneValueFlag): pass @@ -629,9 +628,6 @@ class AbstractConstructedAsn1Item(Asn1ItemBase): self[k] = kwargs[k] return self - def __len__(self): - return len(self._componentValues) - def clear(self): self._componentValues = [] diff --git a/pyasn1/type/char.py b/pyasn1/type/char.py index 39158ee..277a8bb 100644 --- a/pyasn1/type/char.py +++ b/pyasn1/type/char.py @@ -50,7 +50,9 @@ class AbstractCharacterString(univ.OctetString): if sys.version_info[0] <= 2: def __str__(self): try: + # `str` is Py2 text representation return self._value.encode(self.encoding) + except UnicodeEncodeError: raise error.PyAsn1Error( "Can't encode string '%s' with codec %s" % (self._value, self.encoding) @@ -85,6 +87,7 @@ class AbstractCharacterString(univ.OctetString): else: def __str__(self): + # `unicode` is Py3 text representation return str(self._value) def __bytes__(self): @@ -119,8 +122,8 @@ class AbstractCharacterString(univ.OctetString): def asNumbers(self, padding=True): return tuple(bytes(self)) - def prettyOut(self, value): - return value + def prettyPrint(self, scope=0): + return AbstractCharacterString.__str__(self) def __reversed__(self): return reversed(self._value) diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py index 056a86f..2bb3aa7 100644 --- a/pyasn1/type/univ.py +++ b/pyasn1/type/univ.py @@ -825,7 +825,7 @@ class OctetString(base.AbstractSimpleAsn1Item): return ''.join([chr(x) for x in value]) except ValueError: raise error.PyAsn1Error( - 'Bad %s initializer \'%s\'' % (self.__class__.__name__, value) + "Bad %s initializer '%s'" % (self.__class__.__name__, value) ) else: return str(value) @@ -857,7 +857,7 @@ class OctetString(base.AbstractSimpleAsn1Item): 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() @@ -874,7 +874,7 @@ class OctetString(base.AbstractSimpleAsn1Item): 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): @@ -886,22 +886,29 @@ class OctetString(base.AbstractSimpleAsn1Item): def asNumbers(self): return tuple(self._value) - def prettyOut(self, value): - if sys.version_info[0] <= 2: - numbers = tuple((ord(x) for x in value)) - else: - numbers = tuple(value) + # + # Normally, `.prettyPrint()` is called from `__str__()`. Historically, + # OctetString.prettyPrint() used to return hexified payload + # representation in cases when non-printable content is present. At the + # same time `str()` used to produce either octet-stream (Py2) or + # text (Py3) representations. Therefore `OctetString.__str__()` is + # decoupled from `.prettyPrint` to preserve the original behaviour. + # + # Eventually we should make `__str__` reporting hexified representation + # while both text and octet-stream representation should only be requested + # via the `.asOctets()` method. + # + # Note: ASN.1 OCTET STRING is never mean to contain text! + # + + def prettyPrint(self, scope=0): + numbers = self.asNumbers() + for x in numbers: if x < 32 or x > 126: return '0x' + ''.join(('%.2x' % x for x in numbers)) else: - try: - 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__) - ) + return OctetString.__str__(self) @staticmethod def fromBinaryString(value): @@ -1129,9 +1136,6 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): def __contains__(self, value): return value in self._value - def __str__(self): - return self.prettyPrint() - def index(self, suboid): return self._value.index(suboid) @@ -1302,7 +1306,11 @@ class Real(base.AbstractSimpleAsn1Item): ) def prettyPrint(self, scope=0): - return self.prettyOut(float(self)) + try: + return self.prettyOut(float(self)) + + except OverflowError: + return '<overflow>' @property def isPlusInf(self): @@ -1333,13 +1341,6 @@ class Real(base.AbstractSimpleAsn1Item): def isInf(self): return self._value in self._inf - def __str__(self): - try: - return str(float(self)) - - except OverflowError: - return '<overflow>' - def __add__(self, value): return self.clone(float(self) + value) diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py index a7183db..6819251 100644 --- a/tests/type/test_univ.py +++ b/tests/type/test_univ.py @@ -282,7 +282,8 @@ class IntegerTestCase(BaseTestCase): namedValues = univ.Integer.namedValues.clone(('asn1', 1)) assert Integer('asn1') == 1, 'named val fails' - assert str(Integer('asn1')) != 'asn1', 'named val __str__() fails' + assert int(Integer('asn1')) == 1, 'named val fails' + assert str(Integer('asn1')) == 'asn1', 'named val __str__() fails' def testSubtype(self): assert univ.Integer().subtype( @@ -323,7 +324,10 @@ class BooleanTestCase(BaseTestCase): assert not univ.Boolean(False) and not univ.Boolean(0), 'False initializer fails' def testStr(self): - assert str(univ.Boolean(1)) in ('1', '1L'), 'str() fails' + assert str(univ.Boolean(1)) == 'True', 'str() fails' + + def testInt(self): + assert int(univ.Boolean(1)) == 1, 'int() fails' def testRepr(self): assert 'Boolean' in repr(univ.Boolean(1)) |