Various additions to documentation
authorSergey Matveev <stargrave@stargrave.org>
Mon, 2 Oct 2017 13:05:21 +0000 (16:05 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 2 Oct 2017 13:05:21 +0000 (16:05 +0300)
MANIFEST.in
NEWS [deleted file]
THANKS [new file with mode: 0644]
pyderasn.py
pyderasn.pyi

index 2c591b27d9d040a4659f6e8d85aa95e60cde897d..d6f4e89eb416e2f24d7231ec499c2652f611f3a1 100644 (file)
@@ -2,7 +2,6 @@ include .coveragerc
 include AUTHORS
 include COPYING*
 include INSTALL
-include NEWS
 include nose.cfg
 include pip-requirements*
 include PUBKEY.asc
diff --git a/NEWS b/NEWS
deleted file mode 100644 (file)
index 1333ed7..0000000
--- a/NEWS
+++ /dev/null
@@ -1 +0,0 @@
-TODO
diff --git a/THANKS b/THANKS
new file mode 100644 (file)
index 0000000..670e586
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,2 @@
+* pyasn1 (http://pyasn1.sourceforge.net/) project
+* Go encoding/asn1 (https://golang.org/pkg/encoding/asn1/) library
index b1f029cdd89e00ec6d99f5400e9bb79ba3f7a2f3..4ae1eb0814182f065fa045d1c89b7a9f6c11e9ba 100755 (executable)
@@ -298,6 +298,17 @@ SetOf
 _____
 .. autoclass:: pyderasn.SetOf
    :members: __init__
+
+Various
+-------
+
+.. autofunction:: pyderasn.hexenc
+.. autofunction:: pyderasn.hexdec
+.. autofunction:: pyderasn.tag_encode
+.. autofunction:: pyderasn.tag_decode
+.. autofunction:: pyderasn.tag_ctxp
+.. autofunction:: pyderasn.tag_ctxc
+.. autoclass:: pyderasn.Obj
 """
 
 from codecs import getdecoder
@@ -392,6 +403,16 @@ TagClassReprs = {
 
 class DecodeError(Exception):
     def __init__(self, msg="", klass=None, decode_path=(), offset=0):
+        """
+        :param str msg: reason of decode failing
+        :param klass: optional exact DecodeError inherited class (like
+                      :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
+                      :py:exc:`InvalidLength`)
+        :param decode_path: tuple of strings. It contains human
+                            readable names of the fields through which
+                            decoding process has passed
+        :param int offset: binary offset where failure happened
+        """
         super(DecodeError, self).__init__()
         self.msg = msg
         self.klass = klass
@@ -496,10 +517,14 @@ _hexencoder = getencoder("hex")
 
 
 def hexdec(data):
+    """Binary data to hexadecimal string convert
+    """
     return _hexdecoder(data)[0]
 
 
 def hexenc(data):
+    """Hexadecimal string to binary data convert
+    """
     return _hexencoder(data)[0].decode("ascii")
 
 
@@ -523,6 +548,16 @@ def zero_ended_encode(num):
 
 
 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
+    """Encode tag to binary form
+
+    :param int num: tag's number
+    :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
+                      :py:data:`pyderasn.TagClassContext`,
+                      :py:data:`pyderasn.TagClassApplication`,
+                      :py:data:`pyderasn.TagClassPrivate`)
+    :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
+                     :py:data:`pyderasn.TagFormConstructed`)
+    """
     if num < 31:
         # [XX|X|.....]
         return int2byte(klass | form | num)
@@ -531,8 +566,14 @@ def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
 
 
 def tag_decode(tag):
-    """
-    assume that data is validated
+    """Decode tag from binary form
+
+    .. warning::
+
+       No validation is performed, assuming that it has already passed.
+
+    It returns tuple with three integers, as
+    :py:func:`pyderasn.tag_encode` accepts.
     """
     first_octet = byte2int(tag)
     klass = first_octet & 0xC0
@@ -547,10 +588,14 @@ def tag_decode(tag):
 
 
 def tag_ctxp(num):
+    """Create CONTEXT PRIMITIVE tag
+    """
     return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
 
 
 def tag_ctxc(num):
+    """Create CONTEXT CONSTRUCTED tag
+    """
     return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
 
 
@@ -612,6 +657,9 @@ def len_decode(data):
 
 class Obj(object):
     """Common ASN.1 object class
+
+    All ASN.1 types are inherited from it. It has metaclass that
+    automatically adds ``__slots__`` to all inherited classes.
     """
     __slots__ = (
         "tag",
@@ -649,6 +697,8 @@ class Obj(object):
 
     @property
     def ready(self):  # pragma: no cover
+        """Is object ready to be encoded?
+        """
         raise NotImplementedError()
 
     def _assert_ready(self):
@@ -657,9 +707,13 @@ class Obj(object):
 
     @property
     def decoded(self):
+        """Is object decoded?
+        """
         return self.llen > 0
 
     def copy(self):  # pragma: no cover
+        """Make a copy of object, safe to be mutated
+        """
         raise NotImplementedError()
 
     @property
@@ -686,6 +740,14 @@ class Obj(object):
         return b"".join((self._expl, len_encode(len(raw)), raw))
 
     def decode(self, data, offset=0, leavemm=False, decode_path=()):
+        """Decode the data
+
+        :param data: either binary or memoryview
+        :param int offset: initial data's offset
+        :param bool leavemm: do we need to leave memoryview of remaining
+                    data as is, or convert it to bytes otherwise
+        :returns: (Obj, remaining data)
+        """
         tlv = memoryview(data)
         if self._expl is None:
             obj, tail = self._decode(
@@ -888,6 +950,15 @@ def pp_console_blob(pp):
 
 
 def pprint(obj, oids=None, big_blobs=False):
+    """Pretty print object
+
+    :param Obj obj: object you want to pretty print
+    :param oids: ``OID <-> humand readable string`` dictionary. When OID
+                 from it is met, then its humand readable form is printed
+    :param big_blobs: if large binary objects are met (like OctetString
+                      values), do we need to print them too, on separate
+                      lines
+    """
     def _pprint_pps(pps):
         for pp in pps:
             if hasattr(pp, "_fields"):
@@ -921,10 +992,6 @@ class Boolean(Obj):
     True
     >>> bool(b)
     True
-    >>> Boolean(optional=True)
-    BOOLEAN OPTIONAL
-    >>> Boolean(impl=tag_ctxp(1), default=False)
-    [1] BOOLEAN False OPTIONAL DEFAULT
     """
     __slots__ = ()
     tag_default = tag_encode(1)
@@ -1119,10 +1186,6 @@ class Integer(Obj):
     True
     >>> int(b)
     -123
-    >>> Integer(optional=True)
-    INTEGER OPTIONAL
-    >>> Integer(impl=tag_ctxp(1), default=123)
-    [1] INTEGER 123 OPTIONAL DEFAULT
 
     >>> Integer(2, bounds=(1, 3))
     INTEGER 2
@@ -3275,6 +3338,86 @@ class Any(Obj):
 ########################################################################
 
 class Sequence(Obj):
+    """``SEQUENCE`` structure type
+
+    You have to make specification of sequence::
+
+        class Extension(Sequence):
+            __slots__ = ()
+            schema = (
+                ("extnID", ObjectIdentifier()),
+                ("critical", Boolean(default=False)),
+                ("extnValue", OctetString()),
+            )
+
+    Then, you can work with it as with dictionary.
+
+    >>> ext = Extension()
+    >>> Extension().specs
+    OrderedDict([
+        ('extnID', OBJECT IDENTIFIER),
+        ('critical', BOOLEAN False OPTIONAL DEFAULT),
+        ('extnValue', OCTET STRING),
+    ])
+    >>> ext["extnID"] = "1.2.3"
+    Traceback (most recent call last):
+    pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
+    >>> ext["extnID"] = ObjectIdentifier("1.2.3")
+
+    You can know if sequence is ready to be encoded:
+
+    >>> ext.ready
+    False
+    >>> ext.encode()
+    Traceback (most recent call last):
+    pyderasn.ObjNotReady: object is not ready: extnValue
+    >>> ext["extnValue"] = OctetString(b"foobar")
+    >>> ext.ready
+    True
+
+    Value you want to assign, must have the same **type** as in
+    corresponding specification, but it can have different tags,
+    optional/default attributes -- they will be taken from specification
+    automatically::
+
+        class TBSCertificate(Sequence):
+            schema = (
+                ("version", Version(expl=tag_ctxc(0), default="v1")),
+            [...]
+
+    >>> tbs = TBSCertificate()
+    >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
+
+    You can know if value exists/set in the sequence and take its value:
+
+    >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
+    (True, True, False)
+    >>> ext["extnID"]
+    OBJECT IDENTIFIER 1.2.3
+
+    But pay attention that if value has default, then it won't be (not
+    in) in the sequence (because ``DEFAULT`` must not be encoded in
+    DER), but you can read its value:
+
+    >>> "critical" in ext, ext["critical"]
+    (False, BOOLEAN False)
+    >>> ext["critical"] = Boolean(True)
+    >>> "critical" in ext, ext["critical"]
+    (True, BOOLEAN True)
+
+    All defaulted values are always optional.
+
+    .. warning::
+
+       When decoded DER contains defaulted value inside, then
+       technically this is not valid DER encoding. But we allow
+       and pass it. Of course reencoding of that kind of DER will
+       result in different binary representation (validly without
+       defaulted value inside).
+
+    Two sequences are equal if they have equal specification (schema),
+    implicit/explicit tagging and the same values.
+    """
     __slots__ = ("specs",)
     tag_default = tag_encode(form=TagFormConstructed, num=16)
     asn1_type_name = "SEQUENCE"
@@ -3518,6 +3661,10 @@ class Sequence(Obj):
 
 
 class Set(Sequence):
+    """``SET`` structure type
+
+    Its usage is identical to :py:class:`pyderasn.Sequence`.
+    """
     __slots__ = ()
     tag_default = tag_encode(form=TagFormConstructed, num=17)
     asn1_type_name = "SET"
@@ -3601,6 +3748,35 @@ class Set(Sequence):
 
 
 class SequenceOf(Obj):
+    """``SEQUENCE OF`` sequence type
+
+    For that kind of type you must specify the object it will carry on
+    (bounds are for example here, not required)::
+
+        class Ints(SequenceOf):
+            schema = Integer()
+            bounds = (0, 2)
+
+    >>> ints = Ints()
+    >>> ints.append(Integer(123))
+    >>> ints.append(Integer(234))
+    >>> ints
+    Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
+    >>> [int(i) for i in ints]
+    [123, 234]
+    >>> ints.append(Integer(345))
+    Traceback (most recent call last):
+    pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
+    >>> ints[1]
+    INTEGER 234
+    >>> ints[1] = Integer(345)
+    >>> ints
+    Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
+
+    Also you can initialize sequence with preinitialized values:
+
+    >>> ints = Ints([Integer(123), Integer(234)])
+    """
     __slots__ = ("spec", "_bound_min", "_bound_max")
     tag_default = tag_encode(form=TagFormConstructed, num=16)
     asn1_type_name = "SEQUENCE OF"
@@ -3841,6 +4017,10 @@ class SequenceOf(Obj):
 
 
 class SetOf(SequenceOf):
+    """``SET OF`` sequence type
+
+    Its usage is identical to :py:class:`pyderasn.SequenceOf`.
+    """
     __slots__ = ()
     tag_default = tag_encode(form=TagFormConstructed, num=17)
     asn1_type_name = "SET OF"
@@ -3884,7 +4064,7 @@ def main():  # pragma: no cover
     parser.add_argument(
         "DERFile",
         type=argparse.FileType("rb"),
-        help="Python path to schema definition to use",
+        help="Path to DER file you want to decode",
     )
     args = parser.parse_args()
     der = memoryview(args.DERFile.read())
index 83f00329d7d65dcb2bcd404a3ba04869d5931918..702a115ab7b59086988f01a39d807ea7896cb104 100644 (file)
@@ -183,6 +183,7 @@ class Boolean(Obj):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "Boolean"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -215,6 +216,7 @@ class Integer(Obj):
     asn1_type_name = ...  # type: str
     specs = ...  # type: Dict[str, int]
     default = ...  # type: "Integer"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -254,6 +256,7 @@ class BitString(Obj):
     asn1_type_name = ...  # type: str
     specs = ...  # type: Dict[str, int]
     default = ...  # type: "BitString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -293,6 +296,7 @@ class OctetString(Obj):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "OctetString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -328,6 +332,7 @@ class Null(Obj):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "Null"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -355,6 +360,7 @@ class ObjectIdentifier(Obj):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "ObjectIdentifier"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -391,6 +397,7 @@ class Enumerated(Integer):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "Enumerated"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -426,6 +433,7 @@ class UTF8String(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "UTF8String"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -445,6 +453,7 @@ class NumericString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "NumericString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -462,6 +471,7 @@ class PrintableString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "PrintableString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -479,6 +489,7 @@ class TeletexString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "TeletexString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -494,6 +505,7 @@ class TeletexString(CommonString):
 class T61String(TeletexString):
     asn1_type_name = ...  # type: str
     default = ...  # type: "T61String"
+    optional = ...  # type: bool
 
 
 class VideotexString(CommonString):
@@ -501,6 +513,7 @@ class VideotexString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "VideotexString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -518,6 +531,7 @@ class IA5String(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "IA5String"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -535,6 +549,7 @@ class UTCTime(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "UTCTime"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -554,6 +569,7 @@ class GeneralizedTime(UTCTime):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "GeneralizedTime"
+    optional = ...  # type: bool
 
     def todatetime(self) -> datetime: ...
 
@@ -563,6 +579,7 @@ class GraphicString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "GraphicString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -580,6 +597,7 @@ class VisibleString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "VisibleString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -595,6 +613,7 @@ class VisibleString(CommonString):
 class ISO646String(VisibleString):
     asn1_type_name = ...  # type: str
     default = ...  # type: "ISO646String"
+    optional = ...  # type: bool
 
 
 class GeneralString(CommonString):
@@ -602,6 +621,7 @@ class GeneralString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "GeneralString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -619,6 +639,7 @@ class UniversalString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "UniversalString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -636,6 +657,7 @@ class BMPString(CommonString):
     encoding = ...  # type: str
     asn1_type_name = ...  # type: str
     default = ...  # type: "BMPString"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -653,6 +675,7 @@ class Choice(Obj):
     asn1_type_name = ...  # type: str
     specs = ...  # type: Dict[str, Obj]
     default = ...  # type: "Choice"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -703,6 +726,7 @@ class Any(Obj):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "Any"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -736,6 +760,7 @@ class Sequence(Obj):
     asn1_type_name = ...  # type: str
     specs = ...  # type: Dict[str, Obj]
     default = ...  # type: "Sequence"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -771,6 +796,7 @@ class Set(Sequence):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "Set"
+    optional = ...  # type: bool
 
 
 class SequenceOf(Obj):
@@ -778,6 +804,7 @@ class SequenceOf(Obj):
     asn1_type_name = ...  # type: str
     spec = ...  # type: Obj
     default = ...  # type: "SequenceOf"
+    optional = ...  # type: bool
 
     def __init__(
             self,
@@ -817,6 +844,7 @@ class SetOf(SequenceOf):
     tag_default = ...  # type: bytes
     asn1_type_name = ...  # type: str
     default = ...  # type: "SetOf"
+    optional = ...  # type: bool
 
 
 def obj_by_path(pypath: str) -> TAny: ...