diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-11-18 15:03:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-18 15:03:06 +0100 |
commit | c156221fce2b2e9061ee5095ce6488c829e62698 (patch) | |
tree | 1d329c6cc6af2babf5c6238a2a397fe1c579acb1 | |
parent | 4cfceb7716d5f553643ad4509de1808989f0fc4d (diff) | |
download | pyasn1-git-c156221fce2b2e9061ee5095ce6488c829e62698.tar.gz |
__repr__() of ASN.1 types reworked for better readability (#102)
At the same time __repr__() stopped being eval()-compliant
-rw-r--r-- | CHANGES.rst | 2 | ||||
-rw-r--r-- | pyasn1/type/base.py | 51 | ||||
-rw-r--r-- | pyasn1/type/constraint.py | 10 | ||||
-rw-r--r-- | pyasn1/type/namedtype.py | 12 | ||||
-rw-r--r-- | pyasn1/type/namedval.py | 8 | ||||
-rw-r--r-- | pyasn1/type/tag.py | 22 | ||||
-rw-r--r-- | pyasn1/type/tagmap.py | 22 | ||||
-rw-r--r-- | pyasn1/type/univ.py | 64 | ||||
-rw-r--r-- | tests/type/test_namedtype.py | 12 | ||||
-rw-r--r-- | tests/type/test_tag.py | 4 | ||||
-rw-r--r-- | tests/type/test_univ.py | 57 |
11 files changed, 103 insertions, 161 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 1db5a70..3e6d96e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,8 @@ 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 + at the cost of not being `eval`-compliant - 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/pyasn1/type/base.py b/pyasn1/type/base.py index 66a2a86..d39b456 100644 --- a/pyasn1/type/base.py +++ b/pyasn1/type/base.py @@ -211,8 +211,6 @@ class NoValue(object): raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr) - def __repr__(self): - return '%s()' % self.__class__.__name__ noValue = NoValue() @@ -238,14 +236,21 @@ class AbstractSimpleAsn1Item(Asn1ItemBase): self._value = value def __repr__(self): - 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.__class__.subtypeSpec: - representation.append('subtypeSpec=%r' % (self.subtypeSpec,)) - return '%s(%s)' % (self.__class__.__name__, ', '.join(representation)) + representation = '%s %s object at 0x%x' % ( + self.__class__.__name__, self.isValue and 'value' or 'schema', id(self) + ) + + for attr, value in self.readOnly.items(): + if value: + representation += ' %s %s' % (attr, value) + + if self.isValue: + value = self.prettyPrint() + if len(value) > 32: + value = value[:16] + '...' + value[-16:] + representation += ' payload [%s]' % value + + return '<%s>' % representation def __str__(self): return str(self._value) @@ -466,20 +471,18 @@ class AbstractConstructedAsn1Item(Asn1ItemBase): self._componentValues = [] def __repr__(self): - representation = [] - 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): - if component is noValue: - continue - representation += '.setComponentByPosition(%d, %s)' % (idx, repr(component)) - return representation + representation = '%s %s object at 0x%x' % ( + self.__class__.__name__, self.isValue and 'value' or 'schema', id(self) + ) + + for attr, value in self.readOnly.items(): + if value is not noValue: + representation += ' %s=%r' % (attr, value) + + if self.isValue and self._componentValues: + representation += ' payload [%s]' % ', '.join([repr(x) for x in self._componentValues]) + + return '<%s>' % representation def __eq__(self, other): return self is other and True or self._componentValues == other diff --git a/pyasn1/type/constraint.py b/pyasn1/type/constraint.py index eda488a..e83e480 100644 --- a/pyasn1/type/constraint.py +++ b/pyasn1/type/constraint.py @@ -34,10 +34,12 @@ class AbstractConstraint(object): ) def __repr__(self): - return '%s(%s)' % ( - self.__class__.__name__, - ', '.join([repr(x) for x in self._values]) - ) + representation = '%s object at 0x%x' % (self.__class__.__name__, id(self)) + + if self._values: + representation += ' consts %s' % ', '.join([repr(x) for x in self._values]) + + return '<%s>' % representation def __eq__(self, other): return self is other and True or self._values == other diff --git a/pyasn1/type/namedtype.py b/pyasn1/type/namedtype.py index 8f1b31b..d118de7 100644 --- a/pyasn1/type/namedtype.py +++ b/pyasn1/type/namedtype.py @@ -43,7 +43,12 @@ class NamedType(object): self.__openType = openType def __repr__(self): - return '%s(%r, %r)' % (self.__class__.__name__, self.__name, self.__type) + representation = '%s=%r' % (self.name, self.asn1Object) + + if self.openType: + representation += ' openType: %r' % self.openType + + return '<%s object at 0x%x type %s>' % (self.__class__.__name__, id(self), representation) def __eq__(self, other): return self.__nameAndType == other @@ -164,9 +169,8 @@ class NamedTypes(object): self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes]) def __repr__(self): - return '%s(%s)' % ( - self.__class__.__name__, ', '.join([repr(x) for x in self.__namedTypes]) - ) + representation = ', '.join(['%r' % x for x in self.__namedTypes]) + return '<%s object at 0x%x types %s>' % (self.__class__.__name__, id(self), representation) def __eq__(self, other): return self.__namedTypes == other diff --git a/pyasn1/type/namedval.py b/pyasn1/type/namedval.py index 0610476..93cd76f 100644 --- a/pyasn1/type/namedval.py +++ b/pyasn1/type/namedval.py @@ -104,10 +104,12 @@ class NamedValues(object): number += 1 def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, tuple(self.items())) + representation = ', '.join(['%s=%d' % x for x in self.items()]) - def __str__(self): - return str(self.items()) + if len(representation) > 64: + representation = representation[:32] + '...' + representation[-32:] + + return '<%s object 0x%x enums %s>' % (self.__class__.__name__, id(self), representation) def __eq__(self, other): return dict(self) == other diff --git a/pyasn1/type/tag.py b/pyasn1/type/tag.py index 2c355a5..d429c18 100644 --- a/pyasn1/type/tag.py +++ b/pyasn1/type/tag.py @@ -63,13 +63,9 @@ class Tag(object): self.__tagClassId = tagClass, tagId self.__hash = hash(self.__tagClassId) - def __str__(self): - return '[%s:%s:%s]' % (self.__tagClass, self.__tagFormat, self.__tagId) - def __repr__(self): - return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % ( - (self.__class__.__name__, self.__tagClass, self.__tagFormat, self.__tagId) - ) + representation = '[%s:%s:%s]' % (self.__tagClass, self.__tagFormat, self.__tagId) + return '<%s object at 0x%x tag %s>' % (self.__class__.__name__, id(self), representation) def __eq__(self, other): return self.__tagClassId == other @@ -195,13 +191,15 @@ class TagSet(object): self.__lenOfSuperTags = len(superTags) self.__hash = hash(self.__superTagsClassId) - def __str__(self): - return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]' - def __repr__(self): - return '%s(%s)' % ( - self.__class__.__name__, '(), ' + ', '.join([repr(x) for x in self.__superTags]) - ) + representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId) + for x in self.__superTags]) + if representation: + representation = 'tags ' + representation + else: + representation = 'untagged' + + return '<%s object at 0x%x %s>' % (self.__class__.__name__, id(self), representation) def __add__(self, superTag): return self.__class__(self.__baseTag, *self.__superTags + (superTag,)) diff --git a/pyasn1/type/tagmap.py b/pyasn1/type/tagmap.py index c03643c..2e266b6 100644 --- a/pyasn1/type/tagmap.py +++ b/pyasn1/type/tagmap.py @@ -56,24 +56,18 @@ class TagMap(object): return iter(self.__presentTypes) def __repr__(self): - s = self.__class__.__name__ + '(' - if self.__presentTypes: - s += 'presentTypes=%r, ' % (self.__presentTypes,) - if self.__skipTypes: - s += 'skipTypes=%r, ' % (self.__skipTypes,) - if self.__defaultType is not None: - s += 'defaultType=%r' % (self.__defaultType,) - return s + ')' + representation = '%s object at 0x%x' % (self.__class__.__name__, id(self)) - def __str__(self): - s = self.__class__.__name__ + ': ' if self.__presentTypes: - s += 'presentTypes: %s, ' % ', '.join([x.prettyPrintType() for x in self.__presentTypes.values()]) + representation += ' present %s' % repr(self.__presentTypes) + if self.__skipTypes: - s += 'skipTypes: %s, ' % ', '.join([x.prettyPrintType() for x in self.__skipTypes.values()]) + representation += ' skip %s' % repr(self.__skipTypes) + if self.__defaultType is not None: - s += 'defaultType: %s, ' % self.__defaultType.prettyPrintType() - return s + representation += ' default %s' % repr(self.__defaultType) + + return '<%s>' % representation @property def presentTypes(self): diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py index 9f66833..056a86f 100644 --- a/pyasn1/type/univ.py +++ b/pyasn1/type/univ.py @@ -93,12 +93,6 @@ class Integer(base.AbstractSimpleAsn1Item): base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs) - def __repr__(self): - if self.namedValues is not self.__class__.namedValues: - return '%s, %r)' % (base.AbstractSimpleAsn1Item.__repr__(self)[:-1], self.namedValues) - else: - return base.AbstractSimpleAsn1Item.__repr__(self) - def __and__(self, value): return self.clone(self._value & value) @@ -261,7 +255,7 @@ class Integer(base.AbstractSimpleAsn1Item): def prettyOut(self, value): try: - return repr(self.namedValues[value]) + return str(self.namedValues[value]) except KeyError: return str(value) @@ -703,9 +697,6 @@ class BitString(base.AbstractSimpleAsn1Item): 'Bad BitString initializer type \'%s\'' % (value,) ) - def prettyOut(self, value): - return '\'%s\'' % str(self) - try: # noinspection PyStatementEffect @@ -965,26 +956,6 @@ class OctetString(base.AbstractSimpleAsn1Item): return octets.ints2octs(r) - def __repr__(self): - r = [] - doHex = False - if self._value is not self.defaultValue: - for x in self.asNumbers(): - if x < 32 or x > 126: - doHex = True - 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.__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)) - # Immutable sequence object protocol def __len__(self): @@ -1161,9 +1132,6 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): def __str__(self): return self.prettyPrint() - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, self.prettyPrint()) - def index(self, suboid): return self._value.index(suboid) @@ -1263,7 +1231,8 @@ class Real(base.AbstractSimpleAsn1Item): try: _plusInf = float('inf') _minusInf = float('-inf') - _inf = (_plusInf, _minusInf) + _inf = _plusInf, _minusInf + except ValueError: # Infinity support is platform and Python dependent _plusInf = _minusInf = None @@ -1332,17 +1301,8 @@ class Real(base.AbstractSimpleAsn1Item): 'Bad real value syntax: %s' % (value,) ) - def prettyOut(self, value): - if value in self._inf: - return "'%s'" % value - else: - return str(value) - def prettyPrint(self, scope=0): - if self.isInf: - return self.prettyOut(self._value) - else: - return str(float(self)) + return self.prettyOut(float(self)) @property def isPlusInf(self): @@ -1907,7 +1867,7 @@ class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): involving regular Python objects (e.g. arithmetic, comprehension etc). """ for componentValue in self._componentValues: - if not componentValue.isValue: + if componentValue is noValue or not componentValue.isValue: return False return True @@ -2436,13 +2396,17 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): for idx, subComponentType in enumerate(componentType.namedTypes): if subComponentType.isDefaulted or subComponentType.isOptional: continue - if (not self._componentValues or - not self._componentValues[idx].isValue): + + if not self._componentValues: + return False + + componentValue = self._componentValues[idx] + if componentValue is noValue or not componentValue.isValue: return False else: for componentValue in self._componentValues: - if not componentValue.isValue: + if componentValue is noValue or not componentValue.isValue: return False return True @@ -2952,7 +2916,9 @@ class Choice(Set): if self._currentIdx is None: return False - return self._componentValues[self._currentIdx].isValue + componentValue = self._componentValues[self._currentIdx] + + return componentValue is not noValue and componentValue.isValue def clear(self): self._currentIdx = None diff --git a/tests/type/test_namedtype.py b/tests/type/test_namedtype.py index 17319d9..b6c38cc 100644 --- a/tests/type/test_namedtype.py +++ b/tests/type/test_namedtype.py @@ -28,7 +28,7 @@ class NamedTypeCaseBase(BaseTestCase): assert n == 'age' or t == univ.Integer(), 'unpack fails' def testRepr(self): - assert eval(repr(self.e), {'NamedType': namedtype.NamedType, 'Integer': univ.Integer}) == self.e, 'repr() fails' + assert 'age' in repr(self.e) class NamedTypesCaseBase(BaseTestCase): @@ -42,15 +42,7 @@ class NamedTypesCaseBase(BaseTestCase): ) def testRepr(self): - assert eval( - repr(self.e), { - 'NamedTypes': namedtype.NamedTypes, - 'NamedType': namedtype.NamedType, - 'OptionalNamedType': namedtype.OptionalNamedType, - 'Integer': univ.Integer, - 'OctetString': univ.OctetString - } - ) == self.e, 'repr() fails' + assert 'first-name' in repr(self.e) def testContains(self): assert 'first-name' in self.e diff --git a/tests/type/test_tag.py b/tests/type/test_tag.py index c3f7b12..815ee27 100644 --- a/tests/type/test_tag.py +++ b/tests/type/test_tag.py @@ -26,7 +26,7 @@ class TagTestCaseBase(BaseTestCase): class TagReprTestCase(TagTestCaseBase): def testRepr(self): - assert eval(repr(self.t1), {'Tag': tag.Tag}) == self.t1, 'repr() fails' + assert 'Tag' in repr(self.t1) class TagCmpTestCase(TagTestCaseBase): @@ -57,7 +57,7 @@ class TagSetTestCaseBase(BaseTestCase): class TagSetReprTestCase(TagSetTestCaseBase): def testRepr(self): - assert eval(repr(self.ts1), {'TagSet': tag.TagSet, 'Tag': tag.Tag}) == self.ts1, 'repr() fails' + assert 'TagSet' in repr(self.ts1) class TagSetCmpTestCase(TagSetTestCaseBase): diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py index 7c3162a..a7183db 100644 --- a/tests/type/test_univ.py +++ b/tests/type/test_univ.py @@ -154,7 +154,7 @@ class IntegerTestCase(BaseTestCase): assert str(univ.Integer(1)) in ('1', '1L'), 'str() fails' def testRepr(self): - assert eval(repr(univ.Integer(123)), {'Integer': univ.Integer}) == univ.Integer(123), 'repr() fails' + assert '123' in repr(univ.Integer(123)) def testAnd(self): assert univ.Integer(1) & 0 == 0, '__and__() fails' @@ -326,7 +326,7 @@ class BooleanTestCase(BaseTestCase): assert str(univ.Boolean(1)) in ('1', '1L'), 'str() fails' def testRepr(self): - assert eval(repr(univ.Boolean(1)), {'Boolean': univ.Boolean}) == univ.Boolean(1), 'repr() fails' + assert 'Boolean' in repr(univ.Boolean(1)) def testTag(self): assert univ.Boolean().tagSet == tag.TagSet( @@ -402,8 +402,7 @@ class BitStringTestCase(BaseTestCase): assert str(self.b.clone('Urgent')) == '01' def testRepr(self): - assert eval(repr(self.b.clone('Urgent,Active')), {'BitString': univ.BitString}) == self.b.clone( - 'Urgent,Active'), 'repr() fails' + assert 'BitString' in repr(self.b.clone('Urgent,Active')) def testTag(self): assert univ.BitString().tagSet == tag.TagSet( @@ -496,7 +495,7 @@ class OctetStringWithUnicodeMixIn(object): assert univ.OctetString(self.encodedPythonString)[0] == self.encodedPythonString[0], '__getitem__() fails' def testRepr(self): - assert eval(repr(univ.OctetString('abc')), {'OctetString': univ.OctetString}) == univ.OctetString('abc'), 'repr() fails' + assert 'abc' in repr(univ.OctetString('abc')) def testAsOctets(self): assert univ.OctetString(self.encodedPythonString).asOctets() == self.encodedPythonString, 'testAsOctets() fails' @@ -576,7 +575,7 @@ class OctetStringTestCase(BaseTestCase): assert univ.OctetString((1, 2, 3, 4, 5)) == ints2octs((1, 2, 3, 4, 5)), 'tuple init failed' def testRepr(self): - assert eval(repr(univ.OctetString('abc')), {'OctetString': univ.OctetString}) == univ.OctetString('abc'), 'repr() fails' + assert 'abc' in repr(univ.OctetString('abc')) def testEmpty(self): try: @@ -643,7 +642,7 @@ class Null(BaseTestCase): assert str(univ.Null('')) == '', 'str() fails' def testRepr(self): - assert eval(repr(univ.Null('')), {'Null': univ.Null}) == univ.Null(''), 'repr() fails' + assert 'Null' in repr(univ.Null('')) def testTag(self): assert univ.Null().tagSet == tag.TagSet( @@ -693,10 +692,10 @@ class RealTestCase(BaseTestCase): assert str(univ.Real(1.0)) == '1.0', 'str() fails' def testRepr(self): - assert eval(repr(univ.Real(-4.1)), {'Real': univ.Real}) == univ.Real(-4.1), 'repr() fails' - assert repr(univ.Real(-4.1)) == 'Real((-41, 10, -1))', 'repr() fails' - assert eval(repr(univ.Real('inf')), {'Real': univ.Real}) == univ.Real('inf'), 'repr() fails' - assert repr(univ.Real('inf')) == 'Real(\'inf\')', 'repr() fails' + assert 'Real' in repr(univ.Real(-4.1)) + assert 'Real' in repr(univ.Real(-4.1)) + assert 'inf' in repr(univ.Real('inf')) + assert '-inf' in repr(univ.Real('-inf')) def testAdd(self): assert univ.Real(-4.1) + 1.4 == -2.7, '__add__() fails' @@ -841,8 +840,7 @@ class ObjectIdentifier(BaseTestCase): assert str(univ.ObjectIdentifier((1, 3, 6))) == '1.3.6', 'str() fails' def testRepr(self): - assert eval(repr(univ.ObjectIdentifier('1.3.6')), - {'ObjectIdentifier': univ.ObjectIdentifier}) == univ.ObjectIdentifier('1.3.6'), 'repr() fails' + assert '1.3.6' in repr(univ.ObjectIdentifier('1.3.6')) def testEq(self): assert univ.ObjectIdentifier((1, 3, 6)) == (1, 3, 6), '__cmp__() fails' @@ -923,10 +921,7 @@ class SequenceOf(BaseTestCase): self.s2 = self.s1.clone() def testRepr(self): - assert eval(repr(self.s1.clone().setComponents('a', 'b')), - {'SequenceOf': univ.SequenceOf, - 'OctetString': univ.OctetString}) == self.s1.clone().setComponents( - 'a', 'b'), 'repr() fails' + assert 'a' in repr(self.s1.clone().setComponents('a', 'b')) def testTag(self): assert self.s1.tagSet == tag.TagSet( @@ -1199,16 +1194,7 @@ class Sequence(BaseTestCase): ) def testRepr(self): - assert eval( - repr(self.s1.clone().setComponents('a', 'b')), - {'Sequence': univ.Sequence, - 'OctetString': univ.OctetString, - 'Integer': univ.Integer, - 'NamedTypes': namedtype.NamedTypes, - 'NamedType': namedtype.NamedType, - 'OptionalNamedType': namedtype.OptionalNamedType, - 'DefaultedNamedType': namedtype.DefaultedNamedType} - ) == self.s1.clone().setComponents('a', 'b'), 'repr() fails' + assert 'name' in repr(self.s1.clone().setComponents('a', 'b')) def testTag(self): assert self.s1.tagSet == tag.TagSet( @@ -1738,18 +1724,11 @@ class Choice(BaseTestCase): assert self.s1.tagSet == tag.TagSet(), 'wrong tagSet' def testRepr(self): - assert eval(repr(self.s1.clone().setComponents('a')), - {'Choice': univ.Choice, 'OctetString': univ.OctetString, 'Integer': univ.Integer, - 'Boolean': univ.Boolean, 'NamedTypes': namedtype.NamedTypes, - 'NamedType': namedtype.NamedType}) == self.s1.clone().setComponents('a'), 'repr() fails' - assert eval(repr(self.s1.clone().setComponents( - sex=self.s1.setComponentByPosition(1).getComponentByPosition(1).clone().setComponents( - count=univ.Integer(123)))), - {'Choice': univ.Choice, 'OctetString': univ.OctetString, 'Integer': univ.Integer, - 'Boolean': univ.Boolean, 'NamedTypes': namedtype.NamedTypes, - 'NamedType': namedtype.NamedType}) == self.s1.clone().setComponents( - sex=self.s1.setComponentByPosition(1).getComponentByPosition(1).clone().setComponents( - count=univ.Integer(123))), 'repr() fails' + assert 'Choice' in repr(self.s1.clone().setComponents('a')) + s = self.s1.clone().setComponents( + sex=self.s1.setComponentByPosition(1).getComponentByPosition(1).clone().setComponents(count=univ.Integer(123)) + ) + assert 'Choice' in repr(s) def testContains(self): self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') |