asnkit-gen-python 1.0.1__tar.gz → 1.1.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.1.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.1.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.1.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.1.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.1.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.1.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
 
@@ -69,6 +69,7 @@ TAKEN = {
69
69
  "enum",
70
70
  "functools",
71
71
  "spec",
72
+ "use_asni",
72
73
  }
73
74
 
74
75
  # Names of what the methods of the classes are called, which a member
@@ -136,17 +137,7 @@ from asnkitrt.runtime import CodecType
136
137
 
137
138
  DEFAULT_CODEC: CodecType = "{codec}"
138
139
 
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)
140
+ {load}
150
141
 
151
142
 
152
143
  def _type(codec: CodecType, asn1_type: tuple[str, str]) -> Any:
@@ -338,6 +329,7 @@ class Generator:
338
329
  asni: bytes,
339
330
  sources: list[str],
340
331
  codec: str,
332
+ embed: bool = True,
341
333
  ) -> None:
342
334
  from asnkitrt.version import __version__ as asnkit_version # noqa: F401
343
335
 
@@ -347,11 +339,15 @@ class Generator:
347
339
  self.asni = asni
348
340
  self.sources = sources
349
341
  self.codec = codec
342
+ self.embed = embed
350
343
  self.modules: dict[str, Any] = syntax.compiled_to_dict(compiled)
351
344
  self.blocks: list[str] = []
352
345
  self.registrations: list[str] = []
353
346
  self.functions: list[str] = []
354
347
  self.exported: list[str] = ["DEFAULT_CODEC", "spec"]
348
+
349
+ if not embed:
350
+ self.exported.append("use_asni")
355
351
  self.taken: dict[str, str] = {name: "the code of the module" for name in TAKEN}
356
352
  self.names: dict[tuple[str, str], str] = {}
357
353
  self.aliases: dict[tuple[str, str], tuple[str, str]] = {}
@@ -728,7 +724,11 @@ class Generator:
728
724
  version=self.version,
729
725
  sources=", ".join(self.sources) or "a specification",
730
726
  codec=self.codec,
731
- asni=format_bytes(self.asni),
727
+ load=(
728
+ EMBEDDED.format(asni=format_bytes(self.asni))
729
+ if self.embed
730
+ else NOT_EMBEDDED
731
+ ).strip("\n"),
732
732
  )
733
733
  parts = [
734
734
  header,
@@ -745,6 +745,70 @@ class Generator:
745
745
  return "\n\n\n".join(part.strip("\n") for part in parts if part.strip()) + "\n"
746
746
 
747
747
 
748
+ # How a module loads its specification when it is in it.
749
+ EMBEDDED = """\
750
+ # The compiled specification, as the contents of an .asni file.
751
+ _ASNI = (
752
+ {asni}
753
+ )
754
+
755
+
756
+ @functools.cache
757
+ def spec(codec: CodecType = DEFAULT_CODEC) -> asnkitrt.Specification:
758
+ \"\"\"The specification, loaded for the given codec.\"\"\"
759
+
760
+ return asnkitrt.load_asni(_ASNI, codec)
761
+ """
762
+
763
+ # How a module loads its specification when it is not in it, but given
764
+ # by the code that uses it.
765
+ NOT_EMBEDDED = """\
766
+ # The compiled specification is not in this module: give it with use_asni().
767
+ _SPECIFICATION: asnkitrt.syntax.CompiledSpecification | None = None
768
+
769
+
770
+ def use_asni(
771
+ data: bytes | bytearray,
772
+ nonce: asnkitrt.syntax.NonceCheck = None,
773
+ accept_any_nonce: bool = False,
774
+ bound_to: str | None = None,
775
+ accept_any_binding: bool = False,
776
+ verification_keys: asnkitrt.syntax.Keys | None = None,
777
+ accept_any_signature: bool = False,
778
+ decryption_keys: asnkitrt.syntax.Keys | None = None,
779
+ ) -> None:
780
+ \"\"\"Uses the contents of an .asni file, made from the specification this
781
+ module is made from, for all codecs. It is checked once, here, with the
782
+ options of asnkitrt.load_asni().
783
+
784
+ \"\"\"
785
+
786
+ global _SPECIFICATION
787
+
788
+ _SPECIFICATION = asnkitrt.syntax.from_asni(
789
+ data,
790
+ nonce,
791
+ accept_any_nonce,
792
+ bound_to,
793
+ accept_any_binding,
794
+ verification_keys,
795
+ accept_any_signature,
796
+ decryption_keys,
797
+ )
798
+ spec.cache_clear()
799
+
800
+
801
+ @functools.cache
802
+ def spec(codec: CodecType = DEFAULT_CODEC) -> asnkitrt.Specification:
803
+ \"\"\"The specification given with use_asni(), loaded for the given codec.\"\"\"
804
+
805
+ if _SPECIFICATION is None:
806
+ raise RuntimeError("No specification: give it with use_asni().")
807
+
808
+ return asnkitrt.load(_SPECIFICATION, codec)
809
+ """
810
+
811
+
748
812
  REGISTER = """\
749
813
  def _register(
750
814
  class_: Any, asn1_type: tuple[str, str], shape: Any
@@ -769,13 +833,16 @@ def generate(
769
833
  asni: bytes,
770
834
  sources: list[str] | None = None,
771
835
  codec: str = DEFAULT_CODEC,
836
+ embed: bool = True,
772
837
  ) -> str:
773
838
  """Returns the text of a Python module for given contents of an
774
839
  ``.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.
840
+ something or have a nonce to check, as it may be embedded in the
841
+ module and loaded without any of those. `sources` are the names of
842
+ the files it was made from, and `codec` is what the module encodes
843
+ and decodes with if not told otherwise. Without `embed`, the module
844
+ does not have the specification in it, and is given it with its
845
+ ``use_asni()``, as from a file that is signed or encrypted.
779
846
 
780
847
  """
781
848
 
@@ -787,4 +854,4 @@ def generate(
787
854
  "bound to something or has a nonce, but: {}".format(error)
788
855
  )
789
856
 
790
- return Generator(compiled, asni, sources or [], codec).run()
857
+ 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.1.0"