construct-classes 0.1.1__tar.gz → 0.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.
@@ -0,0 +1,50 @@
1
+ Changelog
2
+ =========
3
+
4
+ All notable changes to this project will be documented in this file.
5
+
6
+ The format is based on `Keep a Changelog`_, and this project adheres to
7
+ `Semantic Versioning`_.
8
+
9
+ Unreleased
10
+ ------------
11
+
12
+ Please see all `Unreleased Changes`_ for more information.
13
+
14
+ .. _Unreleased Changes: https://github.com/matejcik/construct-classes/compare/v0.2.0...HEAD
15
+
16
+
17
+ 0.2.0 - 2025-08-25
18
+ --------------------
19
+
20
+ Added
21
+ ~~~~~
22
+
23
+ - Allow pass-through of dataclass arguments via class attributes.
24
+
25
+ Incompatible changes
26
+ ~~~~~~~~~~~~~~~~~~~~
27
+
28
+ - Subclasses of :code:`Struct` are now :code:`kw_only` by default. This will break
29
+ any constructor invocations using positional arguments. You can explicitly
30
+ set :code:`kw_only=False` on your :code:`Struct` subclass to restore the old
31
+ behavior.
32
+
33
+
34
+ 0.1.2 - 2022-10-07
35
+ --------------------
36
+
37
+ Fixed
38
+ ~~~~~
39
+
40
+ - Support for dataclasses that do not contain all the attributes described
41
+ in :code:`SUBCON`.
42
+
43
+
44
+ 0.1.1 - 2022-10-05
45
+ ------------------
46
+
47
+ Initial version.
48
+
49
+ .. _Keep a Changelog: https://keepachangelog.com/en/1.0.0/
50
+ .. _Semantic Versioning: https://semver.org/spec/v2.0.0.html
@@ -0,0 +1,7 @@
1
+ Copyright 2022 matejcik <ja@matejcik.cz>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: construct-classes
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Parse your binary structs into dataclasses
5
5
  Home-page: https://github.com/matejcik/construct-classes
6
6
  License: MIT
@@ -12,13 +12,17 @@ Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: MIT License
13
13
  Classifier: Natural Language :: English
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.6
18
15
  Classifier: Programming Language :: Python :: 3.7
19
16
  Classifier: Programming Language :: Python :: 3.8
20
17
  Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: 3.6
21
24
  Requires-Dist: construct (>=2.10,<3.0)
25
+ Requires-Dist: dataclasses (>=0.8,<0.9) ; python_full_version >= "3.6.0" and python_full_version < "3.7.0"
22
26
  Project-URL: Repository, https://github.com/matejcik/construct-classes
23
27
  Description-Content-Type: text/x-rst
24
28
 
@@ -107,8 +111,21 @@ must match the names of the fields in the Construct struct.
107
111
 
108
112
  Use :code:`dataclasses.field()` to specify attributes on fields that are not subcons.
109
113
 
110
- There are currently no other features. In particular, the resulting class is a Python
111
- dataclass, but you cannot specify its parameters like `frozen` etc.
114
+ By default, subclasses of :code:`Struct` are :code:`kw_only`. This is specifically to
115
+ allow setting default values on any fields regardless of order, so that your attributes
116
+ can be listed in the subcon order.
117
+
118
+ However, you can pass any valid dataclass parameters to the :code:`Struct` class via
119
+ class attributes:
120
+
121
+ .. code-block:: python
122
+
123
+ class MyStruct(Struct, kw_only=False, frozen=True):
124
+ a: int
125
+ b: int
126
+
127
+ my_struct = MyStruct(1, 2) # ok
128
+ my_struct.a = 2 # FrozenInstanceError
112
129
 
113
130
 
114
131
  Installing
@@ -119,6 +136,14 @@ Install using pip:
119
136
  $ pip install construct-classes
120
137
 
121
138
 
139
+ Changelog
140
+ ~~~~~~~~~
141
+
142
+ See `CHANGELOG.rst`_.
143
+
144
+ .. _CHANGELOG.rst: https://github.com/matejcik/construct-classes/blob/master/CHANGELOG.rst
145
+
146
+
122
147
  Footer
123
148
  ------
124
149
 
@@ -83,8 +83,21 @@ must match the names of the fields in the Construct struct.
83
83
 
84
84
  Use :code:`dataclasses.field()` to specify attributes on fields that are not subcons.
85
85
 
86
- There are currently no other features. In particular, the resulting class is a Python
87
- dataclass, but you cannot specify its parameters like `frozen` etc.
86
+ By default, subclasses of :code:`Struct` are :code:`kw_only`. This is specifically to
87
+ allow setting default values on any fields regardless of order, so that your attributes
88
+ can be listed in the subcon order.
89
+
90
+ However, you can pass any valid dataclass parameters to the :code:`Struct` class via
91
+ class attributes:
92
+
93
+ .. code-block:: python
94
+
95
+ class MyStruct(Struct, kw_only=False, frozen=True):
96
+ a: int
97
+ b: int
98
+
99
+ my_struct = MyStruct(1, 2) # ok
100
+ my_struct.a = 2 # FrozenInstanceError
88
101
 
89
102
 
90
103
  Installing
@@ -95,6 +108,14 @@ Install using pip:
95
108
  $ pip install construct-classes
96
109
 
97
110
 
111
+ Changelog
112
+ ~~~~~~~~~
113
+
114
+ See `CHANGELOG.rst`_.
115
+
116
+ .. _CHANGELOG.rst: https://github.com/matejcik/construct-classes/blob/master/CHANGELOG.rst
117
+
118
+
98
119
  Footer
99
120
  ------
100
121
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "construct-classes"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  authors = ["matejcik <ja@matejcik.cz>"]
5
5
  classifiers = [
6
6
  "Development Status :: 2 - Pre-Alpha",
@@ -14,16 +14,25 @@ classifiers = [
14
14
  "Programming Language :: Python :: 3.9",
15
15
  "Programming Language :: Python :: 3.10",
16
16
  "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
17
20
  ]
18
21
  description = "Parse your binary structs into dataclasses"
19
22
  homepage = "https://github.com/matejcik/construct-classes"
20
23
  license = "MIT"
21
24
  repository = "https://github.com/matejcik/construct-classes"
22
25
  readme = "README.rst"
26
+ include = [
27
+ { path = "CHANGELOG.rst", format = "sdist" },
28
+ { path = "LICENSE", format = "sdist" },
29
+ { path = "README.rst", format = "sdist" }
30
+ ]
23
31
 
24
32
  [tool.poetry.dependencies]
25
33
  python = ">=3.6.2,<4.0"
26
34
  construct = "^2.10"
35
+ dataclasses = { version = "^0.8", python = "~3.6.0" }
27
36
 
28
37
  [tool.poetry.dev-dependencies]
29
38
  pytest = ">5"
@@ -31,7 +40,7 @@ black = "^22.8.0"
31
40
  isort = "^5.10.1"
32
41
  flake8 = "^5.0.4"
33
42
  construct-typing = { version = "^0.5.2", python = ">=3.7" }
34
- typing-extensions = ">4"
43
+ typing-extensions = { version = ">4.2", python = ">=3.7" }
35
44
 
36
45
  [build-system]
37
46
  requires = ["poetry-core>=1.0.0"]
@@ -2,7 +2,17 @@ import dataclasses
2
2
  import typing as t
3
3
 
4
4
  import construct as c
5
- from typing_extensions import dataclass_transform
5
+
6
+ if t.TYPE_CHECKING:
7
+ from typing_extensions import dataclass_transform
8
+ else:
9
+
10
+ def dataclass_transform(**kwargs: t.Any) -> t.Any:
11
+ def inner(cls: t.Any) -> t.Any:
12
+ return cls
13
+
14
+ return inner
15
+
6
16
 
7
17
  # workaround for mypy self type bug
8
18
  Self = t.TypeVar("Self", bound="Struct")
@@ -20,13 +30,23 @@ def subcon(
20
30
  return dataclasses.field(metadata=metadata, **kwargs)
21
31
 
22
32
 
23
- @dataclass_transform(field_descriptors=(subcon,))
33
+ @dataclass_transform(field_specifiers=(subcon,), kw_only_default=True)
24
34
  class _StructMeta(type):
25
35
  def __new__(
26
- cls, name: str, bases: t.Tuple[type, ...], namespace: t.Dict[str, t.Any]
36
+ cls,
37
+ name: str,
38
+ bases: t.Tuple[type, ...],
39
+ namespace: t.Dict[str, t.Any],
40
+ *,
41
+ kw_only: bool = True,
42
+ **kwargs: t.Any,
27
43
  ) -> type:
28
44
  new_cls = super().__new__(cls, name, bases, namespace)
29
- return dataclasses.dataclass()(new_cls) # type: ignore # pyright is bad with metaclasses
45
+ if bases:
46
+ assert bases[0].__name__ == "Struct"
47
+ return dataclasses.dataclass(kw_only=kw_only, **kwargs)(new_cls)
48
+ else:
49
+ return new_cls
30
50
 
31
51
 
32
52
  class Struct(metaclass=_StructMeta):
@@ -43,17 +63,18 @@ class Struct(metaclass=_StructMeta):
43
63
 
44
64
  @classmethod
45
65
  def from_parsed(cls: t.Type[Self], data: c.Container) -> Self:
46
- del data["_io"]
66
+ args = {}
47
67
  for field in dataclasses.fields(cls):
68
+ field_data = data.get(field.name)
48
69
  subcls = field.metadata.get("substruct")
49
70
  if subcls is None:
71
+ args[field.name] = field_data
50
72
  continue
51
73
 
52
- field_data = data.get(field.name)
53
74
  if isinstance(field_data, c.ListContainer):
54
- data[field.name] = [subcls.from_parsed(d) for d in field_data]
75
+ args[field.name] = [subcls.from_parsed(d) for d in field_data]
55
76
  elif isinstance(field_data, c.Container):
56
- data[field.name] = subcls.from_parsed(field_data)
77
+ args[field.name] = subcls.from_parsed(field_data)
57
78
  elif field_data is None:
58
79
  continue
59
80
  else:
@@ -61,9 +82,9 @@ class Struct(metaclass=_StructMeta):
61
82
  f"Mismatched type for field {field.name}: expected a struct, found {type(field_data)}"
62
83
  )
63
84
 
64
- for key in data:
65
- data[key] = cls._decontainerize(data[key])
66
- return cls(**data)
85
+ for key in args:
86
+ args[key] = cls._decontainerize(args[key])
87
+ return cls(**args)
67
88
 
68
89
  @classmethod
69
90
  def parse(cls: t.Type[Self], data: bytes) -> Self:
@@ -1,34 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- package_dir = \
5
- {'': 'src'}
6
-
7
- packages = \
8
- ['construct_classes']
9
-
10
- package_data = \
11
- {'': ['*']}
12
-
13
- install_requires = \
14
- ['construct>=2.10,<3.0']
15
-
16
- setup_kwargs = {
17
- 'name': 'construct-classes',
18
- 'version': '0.1.1',
19
- 'description': 'Parse your binary structs into dataclasses',
20
- 'long_description': '=================\nconstruct-classes\n=================\n\n.. image:: https://img.shields.io/pypi/v/construct-classes.svg\n :target: https://pypi.python.org/pypi/construct-classes\n\n.. .. image:: https://readthedocs.org/projects/construct-classes/badge/?version=latest\n.. :target: https://construct-classes.readthedocs.io/en/latest/?badge=latest\n.. :alt: Documentation Status\n\n.. image:: https://pyup.io/repos/github/trezor/construct-classes/shield.svg\n :target: https://pyup.io/repos/github/trezor/construct-classes/\n :alt: Updates\n\n\nParse your binary data into dataclasses. Pack your dataclasses into binary data.\n\n:code:`construct-classes` rely on `construct`_ for parsing and packing. The\nprogrammer needs to manually write the Construct expressions. There is also no type\nverification, so it is the programmer\'s responsibility that the dataclass and the\nConstruct expression match.\n\nFor fully type annotated experience, install `construct-typing`_.\n\nThis package typechecks with `mypy`_ and `pyright`_.\n\n.. _construct: https://construct.readthedocs.io/en/latest/\n.. _construct-typing: https://github.com/timrid/construct-typing\n.. _mypy: https://mypy.readthedocs.io/en/stable/\n.. _pyright: https://github.com/microsoft/pyright\n\nUsage\n-----\n\nAny child of :code:`Struct` is a Python dataclass. It expects a Construct :code:`Struct`\nexpression in the :code:`SUBCON` attribute. The names of the attributes of the dataclass\nmust match the names of the fields in the Construct struct.\n\n.. code-block:: python\n\n import construct as c\n from construct_classes import Struct, subcon\n\n class BasicStruct(Struct):\n x: int\n y: int\n description: str\n\n SUBCON = c.Struct(\n "x" / c.Int32ul,\n "y" / c.Int32ul,\n "description" / c.PascalString(c.Int8ul, "utf8"),\n )\n\n\n data = b"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05hello"\n parsed = BasicStruct.parse(data)\n print(parsed) # BasicStruct(x=1, y=2, description=\'hello\')\n\n new_data = BasicStruct(x=100, y=200, description="world")\n print(new_data.build()) # b\'\\x64\\x00\\x00\\x00\\xc8\\x00\\x00\\x00\\x05world\'\n\n\n:code:`construct-classes` support nested structs, but you need to declare them explicitly:\n\n.. code-block:: python\n\n class LargerStruct(Struct):\n # specify the subclass type:\n basic: BasicStruct = subcon(BasicStruct)\n # in case of a list, specify the item type:\n basic_array: List[BasicStruct] = subcon(BasicStruct)\n # the `subcon()` function supports all arguments of `dataclass.field`:\n default_array: List[BasicStruct] = subcon(BasicStruct, default_factory=list)\n\n # to refer to the subcon, use the `SUBCON` class attribute:\n SUBCON = c.Struct(\n "basic" / BasicStruct.SUBCON,\n "basic_array" / c.Array(2, BasicStruct.SUBCON),\n "default_array" / c.PrefixedArray(c.Int8ul, BasicStruct.SUBCON),\n )\n\nUse :code:`dataclasses.field()` to specify attributes on fields that are not subcons.\n\nThere are currently no other features. In particular, the resulting class is a Python\ndataclass, but you cannot specify its parameters like `frozen` etc.\n\n\nInstalling\n----------\n\nInstall using pip:\n\n $ pip install construct-classes\n\n\nFooter\n------\n\n* Free software: MIT License\n\n.. * Documentation: https://construct-classes.readthedocs.io.\n',
21
- 'author': 'matejcik',
22
- 'author_email': 'ja@matejcik.cz',
23
- 'maintainer': None,
24
- 'maintainer_email': None,
25
- 'url': 'https://github.com/matejcik/construct-classes',
26
- 'package_dir': package_dir,
27
- 'packages': packages,
28
- 'package_data': package_data,
29
- 'install_requires': install_requires,
30
- 'python_requires': '>=3.6.2,<4.0',
31
- }
32
-
33
-
34
- setup(**setup_kwargs)