summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2017-11-19 16:41:26 +0100
committerGitHub <noreply@github.com>2017-11-19 16:41:26 +0100
commit5efe9f213563560d26bd34b6d9b3642642565b41 (patch)
tree87424f2b94fb3360c15ccad18c064518e3328830
parentc156221fce2b2e9061ee5095ce6488c829e62698 (diff)
downloadpyasn1-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.rst10
-rw-r--r--README.md4
-rw-r--r--docs/source/example-use-case.rst2
-rw-r--r--pyasn1/codec/ber/decoder.py4
-rw-r--r--pyasn1/codec/cer/decoder.py4
-rw-r--r--pyasn1/codec/der/decoder.py4
-rw-r--r--pyasn1/codec/native/decoder.py2
-rw-r--r--pyasn1/codec/native/encoder.py2
-rw-r--r--pyasn1/type/base.py30
-rw-r--r--pyasn1/type/char.py7
-rw-r--r--pyasn1/type/univ.py53
-rw-r--r--tests/type/test_univ.py8
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
diff --git a/README.md b/README.md
index cf917e0..90ab094 100644
--- a/README.md
+++ b/README.md
@@ -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))