asnkit-gen-python 1.0.1__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asnkit-gen-python
3
- Version: 1.0.1
3
+ Version: 1.2.0
4
4
  Summary: asnkit plugin that makes Python code with data classes from ASN.1
5
5
  Author: xadaemon
6
6
  Author-email: xadaemon <mxavier@boxtheta.com>
@@ -10,7 +10,7 @@ Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Topic :: Software Development :: Code Generators
13
- Requires-Dist: asnkitrt==1.0.1
13
+ Requires-Dist: asnkitrt==1.2.0
14
14
  Requires-Python: >=3.13
15
15
  Project-URL: source, https://github.com/boxtheta/asnkit
16
16
  Description-Content-Type: text/x-rst
@@ -19,7 +19,7 @@ asnkit-gen-python
19
19
  =================
20
20
 
21
21
  A plugin for `asnkitcc`_ that generates Python code from ASN.1. The
22
- generated module imports `asnkitrt`_ and embeds the compiled
22
+ generated module imports `asnkitrt`_ and, by default, embeds the compiled
23
23
  specification as bytes. It has a data class for each ``SEQUENCE`` and
24
24
  ``SET``, an enumeration for each ``ENUMERATED`` and a class for each
25
25
  alternative of a ``CHOICE``, all with methods to encode and decode.
@@ -45,6 +45,16 @@ Options, given with ``--plugin-opt KEY=VALUE``:
45
45
  ``codec``
46
46
  The codec used when none is given. Default: ``ber``.
47
47
 
48
+ ``embed``
49
+ ``false`` to leave the compiled specification out of the module, as
50
+ for one sent signed or encrypted. The module then has
51
+ ``use_asni(data, ...)``, which takes the contents of the ``.asni``
52
+ file with the options of ``asnkitrt.load_asni()`` (``nonce``,
53
+ ``bound_to``, ``verification_keys``, ``decryption_keys`` and those
54
+ to accept any), checks it once and uses it for every codec. Until it
55
+ is called, encoding and decoding raise ``RuntimeError``. The file has
56
+ to be compiled from the same specification. Default: ``true``.
57
+
48
58
  Names are converted to Python style: ``fooBar`` becomes ``foo_bar`` and
49
59
  a type ``Foo-Bar`` becomes ``FooBar``. If two names end up the same in
50
60
  Python, generation fails with an error.
@@ -2,7 +2,7 @@ asnkit-gen-python
2
2
  =================
3
3
 
4
4
  A plugin for `asnkitcc`_ that generates Python code from ASN.1. The
5
- generated module imports `asnkitrt`_ and embeds the compiled
5
+ generated module imports `asnkitrt`_ and, by default, embeds the compiled
6
6
  specification as bytes. It has a data class for each ``SEQUENCE`` and
7
7
  ``SET``, an enumeration for each ``ENUMERATED`` and a class for each
8
8
  alternative of a ``CHOICE``, all with methods to encode and decode.
@@ -28,6 +28,16 @@ Options, given with ``--plugin-opt KEY=VALUE``:
28
28
  ``codec``
29
29
  The codec used when none is given. Default: ``ber``.
30
30
 
31
+ ``embed``
32
+ ``false`` to leave the compiled specification out of the module, as
33
+ for one sent signed or encrypted. The module then has
34
+ ``use_asni(data, ...)``, which takes the contents of the ``.asni``
35
+ file with the options of ``asnkitrt.load_asni()`` (``nonce``,
36
+ ``bound_to``, ``verification_keys``, ``decryption_keys`` and those
37
+ to accept any), checks it once and uses it for every codec. Until it
38
+ is called, encoding and decoding raise ``RuntimeError``. The file has
39
+ to be compiled from the same specification. Default: ``true``.
40
+
31
41
  Names are converted to Python style: ``fooBar`` becomes ``foo_bar`` and
32
42
  a type ``Foo-Bar`` becomes ``FooBar``. If two names end up the same in
33
43
  Python, generation fails with an error.
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "asnkit-gen-python"
3
- version = "1.0.1"
3
+ version = "1.2.0"
4
4
  description = "asnkit plugin that makes Python code with data classes from ASN.1"
5
5
  readme = "README.rst"
6
6
  requires-python = ">=3.13"
7
- dependencies = ["asnkitrt==1.0.1"]
7
+ dependencies = ["asnkitrt==1.2.0"]
8
8
  license = "Apache-2.0"
9
9
  license-files = ["LICENSE-COMPILER"]
10
10
  classifiers = [
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "asnkit-gen-python"
3
- version = "1.0.1"
3
+ version = "1.2.0"
4
4
  description = "asnkit plugin that makes Python code with data classes from ASN.1"
5
5
  readme = "README.rst"
6
6
  authors = [
@@ -8,7 +8,7 @@ authors = [
8
8
  ]
9
9
  requires-python = ">=3.13"
10
10
  dependencies = [
11
- "asnkitrt==1.0.1",
11
+ "asnkitrt==1.2.0",
12
12
  ]
13
13
  license = "Apache-2.0"
14
14
  license-files = ["LICENSE-COMPILER"]
@@ -10,9 +10,11 @@
10
10
 
11
11
  It writes one Python module to the folder. The options are ``name``,
12
12
  which is the name of the module without ``.py`` (default: that of
13
- the first file the specification is from), and ``codec``, which is
13
+ the first file the specification is from), ``codec``, which is
14
14
  what the module encodes and decodes with if not told otherwise
15
- (default: ``ber``).
15
+ (default: ``ber``), and ``embed``, which is ``false`` for a module that
16
+ does not have the compiled specification in it, and is given it with
17
+ its ``use_asni()`` (default: ``true``).
16
18
 
17
19
  """
18
20
 
@@ -26,7 +28,7 @@ from asnkitrt.errors import Error
26
28
  from .generate import DEFAULT_CODEC, GenerateError, generate
27
29
  from .version import __version__
28
30
 
29
- OPTIONS = {"name", "codec"}
31
+ OPTIONS = {"name", "codec", "embed"}
30
32
 
31
33
 
32
34
  def read_options(items: list[str]) -> dict[str, str]:
@@ -71,7 +73,14 @@ def run(args: argparse.Namespace) -> str:
71
73
  if not name.isidentifier():
72
74
  raise GenerateError("Expected a name of a module, but got '{}'.".format(name))
73
75
 
74
- text = generate(asni, sources, options.get("codec", DEFAULT_CODEC))
76
+ embed = options.get("embed", "true")
77
+
78
+ if embed not in ("true", "false"):
79
+ raise GenerateError(
80
+ "Expected embed=true or embed=false, but got '{}'.".format(embed)
81
+ )
82
+
83
+ text = generate(asni, sources, options.get("codec", DEFAULT_CODEC), embed == "true")
75
84
  os.makedirs(args.out, exist_ok=True)
76
85
  path = os.path.join(args.out, name + ".py")
77
86
 
@@ -18,6 +18,10 @@ from typing import Any
18
18
 
19
19
  from asnkitrt import syntax
20
20
 
21
+ # The resolved dictionaries are not part of the API of asnkitrt, which is
22
+ # released with this plugin.
23
+ from asnkitrt.syntax.convert import _compiled_to_dict
24
+
21
25
  DEFAULT_CODEC = "ber"
22
26
 
23
27
  # What asnkitrt encodes and decodes a type as, by the name of the type.
@@ -69,6 +73,7 @@ TAKEN = {
69
73
  "enum",
70
74
  "functools",
71
75
  "spec",
76
+ "use_asni",
72
77
  }
73
78
 
74
79
  # Names of what the methods of the classes are called, which a member
@@ -136,17 +141,7 @@ from asnkitrt.runtime import CodecType
136
141
 
137
142
  DEFAULT_CODEC: CodecType = "{codec}"
138
143
 
139
- # The compiled specification, as the contents of an .asni file.
140
- _ASNI = (
141
- {asni}
142
- )
143
-
144
-
145
- @functools.cache
146
- def spec(codec: CodecType = DEFAULT_CODEC) -> asnkitrt.Specification:
147
- """The specification, loaded for the given codec."""
148
-
149
- return asnkitrt.load_asni(_ASNI, codec)
144
+ {load}
150
145
 
151
146
 
152
147
  def _type(codec: CodecType, asn1_type: tuple[str, str]) -> Any:
@@ -338,6 +333,7 @@ class Generator:
338
333
  asni: bytes,
339
334
  sources: list[str],
340
335
  codec: str,
336
+ embed: bool = True,
341
337
  ) -> None:
342
338
  from asnkitrt.version import __version__ as asnkit_version # noqa: F401
343
339
 
@@ -347,11 +343,15 @@ class Generator:
347
343
  self.asni = asni
348
344
  self.sources = sources
349
345
  self.codec = codec
350
- self.modules: dict[str, Any] = syntax.compiled_to_dict(compiled)
346
+ self.embed = embed
347
+ self.modules: dict[str, Any] = _compiled_to_dict(compiled)
351
348
  self.blocks: list[str] = []
352
349
  self.registrations: list[str] = []
353
350
  self.functions: list[str] = []
354
351
  self.exported: list[str] = ["DEFAULT_CODEC", "spec"]
352
+
353
+ if not embed:
354
+ self.exported.append("use_asni")
355
355
  self.taken: dict[str, str] = {name: "the code of the module" for name in TAKEN}
356
356
  self.names: dict[tuple[str, str], str] = {}
357
357
  self.aliases: dict[tuple[str, str], tuple[str, str]] = {}
@@ -728,7 +728,11 @@ class Generator:
728
728
  version=self.version,
729
729
  sources=", ".join(self.sources) or "a specification",
730
730
  codec=self.codec,
731
- asni=format_bytes(self.asni),
731
+ load=(
732
+ EMBEDDED.format(asni=format_bytes(self.asni))
733
+ if self.embed
734
+ else NOT_EMBEDDED
735
+ ).strip("\n"),
732
736
  )
733
737
  parts = [
734
738
  header,
@@ -745,6 +749,70 @@ class Generator:
745
749
  return "\n\n\n".join(part.strip("\n") for part in parts if part.strip()) + "\n"
746
750
 
747
751
 
752
+ # How a module loads its specification when it is in it.
753
+ EMBEDDED = """\
754
+ # The compiled specification, as the contents of an .asni file.
755
+ _ASNI = (
756
+ {asni}
757
+ )
758
+
759
+
760
+ @functools.cache
761
+ def spec(codec: CodecType = DEFAULT_CODEC) -> asnkitrt.Specification:
762
+ \"\"\"The specification, loaded for the given codec.\"\"\"
763
+
764
+ return asnkitrt.load_asni(_ASNI, codec)
765
+ """
766
+
767
+ # How a module loads its specification when it is not in it, but given
768
+ # by the code that uses it.
769
+ NOT_EMBEDDED = """\
770
+ # The compiled specification is not in this module: give it with use_asni().
771
+ _SPECIFICATION: asnkitrt.syntax.CompiledSpecification | None = None
772
+
773
+
774
+ def use_asni(
775
+ data: bytes | bytearray,
776
+ nonce: asnkitrt.syntax.NonceCheck = None,
777
+ accept_any_nonce: bool = False,
778
+ bound_to: str | None = None,
779
+ accept_any_binding: bool = False,
780
+ verification_keys: asnkitrt.syntax.Keys | None = None,
781
+ accept_any_signature: bool = False,
782
+ decryption_keys: asnkitrt.syntax.Keys | None = None,
783
+ ) -> None:
784
+ \"\"\"Uses the contents of an .asni file, made from the specification this
785
+ module is made from, for all codecs. It is checked once, here, with the
786
+ options of asnkitrt.load_asni().
787
+
788
+ \"\"\"
789
+
790
+ global _SPECIFICATION
791
+
792
+ _SPECIFICATION = asnkitrt.syntax.from_asni(
793
+ data,
794
+ nonce,
795
+ accept_any_nonce,
796
+ bound_to,
797
+ accept_any_binding,
798
+ verification_keys,
799
+ accept_any_signature,
800
+ decryption_keys,
801
+ )
802
+ spec.cache_clear()
803
+
804
+
805
+ @functools.cache
806
+ def spec(codec: CodecType = DEFAULT_CODEC) -> asnkitrt.Specification:
807
+ \"\"\"The specification given with use_asni(), loaded for the given codec.\"\"\"
808
+
809
+ if _SPECIFICATION is None:
810
+ raise RuntimeError("No specification: give it with use_asni().")
811
+
812
+ return asnkitrt.load(_SPECIFICATION, codec)
813
+ """
814
+
815
+
748
816
  REGISTER = """\
749
817
  def _register(
750
818
  class_: Any, asn1_type: tuple[str, str], shape: Any
@@ -769,13 +837,16 @@ def generate(
769
837
  asni: bytes,
770
838
  sources: list[str] | None = None,
771
839
  codec: str = DEFAULT_CODEC,
840
+ embed: bool = True,
772
841
  ) -> str:
773
842
  """Returns the text of a Python module for given contents of an
774
843
  ``.asni`` file, which must not be signed, encrypted, bound to
775
- something or have a nonce to check, as it is embedded in the module
776
- and loaded without any of those. `sources` are the names of the
777
- files it was made from, and `codec` is what the module encodes and
778
- decodes with if not told otherwise.
844
+ something or have a nonce to check, as it may be embedded in the
845
+ module and loaded without any of those. `sources` are the names of
846
+ the files it was made from, and `codec` is what the module encodes
847
+ and decodes with if not told otherwise. Without `embed`, the module
848
+ does not have the specification in it, and is given it with its
849
+ ``use_asni()``, as from a file that is signed or encrypted.
779
850
 
780
851
  """
781
852
 
@@ -787,4 +858,4 @@ def generate(
787
858
  "bound to something or has a nonce, but: {}".format(error)
788
859
  )
789
860
 
790
- return Generator(compiled, asni, sources or [], codec).run()
861
+ return Generator(compiled, asni, sources or [], codec, embed).run()
@@ -2,4 +2,4 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- __version__ = "1.0.1"
5
+ __version__ = "1.2.0"