dissect.cstruct 4.5.dev3__tar.gz → 4.5.dev5__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.
Files changed (76) hide show
  1. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/PKG-INFO +1 -1
  2. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/compiler.py +3 -1
  3. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/__init__.py +2 -1
  4. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/base.py +4 -0
  5. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/enum.py +1 -1
  6. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/structure.py +10 -10
  7. dissect_cstruct-4.5.dev5/dissect/cstruct/types/void.py +44 -0
  8. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect.cstruct.egg-info/PKG-INFO +1 -1
  9. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_base.py +9 -0
  10. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_structure.py +16 -5
  11. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_union.py +12 -0
  12. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_void.py +9 -7
  13. dissect_cstruct-4.5.dev3/dissect/cstruct/types/void.py +0 -27
  14. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/.git-blame-ignore-revs +0 -0
  15. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/.gitattributes +0 -0
  16. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/COPYRIGHT +0 -0
  17. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/LICENSE +0 -0
  18. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/MANIFEST.in +0 -0
  19. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/README.md +0 -0
  20. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/__init__.py +0 -0
  21. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/bitbuffer.py +0 -0
  22. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/cstruct.py +0 -0
  23. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/exceptions.py +0 -0
  24. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/expression.py +0 -0
  25. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/parser.py +0 -0
  26. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/tools/__init__.py +0 -0
  27. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/tools/stubgen.py +0 -0
  28. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/char.py +0 -0
  29. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/flag.py +0 -0
  30. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/int.py +0 -0
  31. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/leb128.py +0 -0
  32. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/packed.py +0 -0
  33. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/pointer.py +0 -0
  34. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/types/wchar.py +0 -0
  35. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect/cstruct/utils.py +0 -0
  36. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect.cstruct.egg-info/SOURCES.txt +0 -0
  37. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect.cstruct.egg-info/dependency_links.txt +0 -0
  38. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect.cstruct.egg-info/entry_points.txt +0 -0
  39. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect.cstruct.egg-info/requires.txt +0 -0
  40. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/dissect.cstruct.egg-info/top_level.txt +0 -0
  41. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/examples/disk.py +0 -0
  42. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/examples/mirai.py +0 -0
  43. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/examples/pe.py +0 -0
  44. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/examples/protobuf.py +0 -0
  45. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/examples/secdesc.py +0 -0
  46. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/pyproject.toml +0 -0
  47. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/setup.cfg +0 -0
  48. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/__init__.py +0 -0
  49. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/_data/testdef.txt +0 -0
  50. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/_docs/Makefile +0 -0
  51. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/_docs/__init__.py +0 -0
  52. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/_docs/conf.py +0 -0
  53. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/_docs/index.rst +0 -0
  54. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/conftest.py +0 -0
  55. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_align.py +0 -0
  56. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_annotations.py +0 -0
  57. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_basic.py +0 -0
  58. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_bitbuffer.py +0 -0
  59. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_bitfield.py +0 -0
  60. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_compiler.py +0 -0
  61. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_ctypes.py +0 -0
  62. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_expression.py +0 -0
  63. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_parser.py +0 -0
  64. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_tools_stubgen.py +0 -0
  65. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_char.py +0 -0
  66. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_custom.py +0 -0
  67. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_enum.py +0 -0
  68. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_flag.py +0 -0
  69. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_int.py +0 -0
  70. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_leb128.py +0 -0
  71. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_packed.py +0 -0
  72. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_pointer.py +0 -0
  73. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_types_wchar.py +0 -0
  74. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/test_utils.py +0 -0
  75. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tests/utils.py +0 -0
  76. {dissect_cstruct-4.5.dev3 → dissect_cstruct-4.5.dev5}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.5.dev3
3
+ Version: 4.5.dev5
4
4
  Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Apache License 2.0
@@ -21,6 +21,7 @@ from dissect.cstruct.types import (
21
21
  Structure,
22
22
  Union,
23
23
  Void,
24
+ VoidArray,
24
25
  Wchar,
25
26
  WcharArray,
26
27
  )
@@ -48,6 +49,7 @@ SUPPORTED_TYPES = (
48
49
  Void,
49
50
  Wchar,
50
51
  WcharArray,
52
+ VoidArray,
51
53
  )
52
54
 
53
55
  log = logging.getLogger(__name__)
@@ -365,7 +367,7 @@ def _generate_struct_info(cs: cstruct, fields: list[Field], align: bool = False)
365
367
  read_type = _get_read_type(cs, field.type)
366
368
 
367
369
  # Drop voids
368
- if issubclass(read_type, Void):
370
+ if issubclass(read_type, (Void, VoidArray)):
369
371
  continue
370
372
 
371
373
  # Array of more complex types are handled elsewhere
@@ -7,7 +7,7 @@ from dissect.cstruct.types.leb128 import LEB128
7
7
  from dissect.cstruct.types.packed import Packed
8
8
  from dissect.cstruct.types.pointer import Pointer
9
9
  from dissect.cstruct.types.structure import Field, Structure, Union
10
- from dissect.cstruct.types.void import Void
10
+ from dissect.cstruct.types.void import Void, VoidArray
11
11
  from dissect.cstruct.types.wchar import Wchar, WcharArray
12
12
 
13
13
  __all__ = [
@@ -27,6 +27,7 @@ __all__ = [
27
27
  "Structure",
28
28
  "Union",
29
29
  "Void",
30
+ "VoidArray",
30
31
  "Wchar",
31
32
  "WcharArray",
32
33
  ]
@@ -54,6 +54,10 @@ class MetaType(type):
54
54
  """Create a new array with the given number of entries."""
55
55
  return cls.cs._make_array(cls, num_entries)
56
56
 
57
+ def __bool__(cls) -> bool:
58
+ """Type class is always truthy."""
59
+ return True
60
+
57
61
  def __len__(cls) -> int:
58
62
  """Return the byte size of the type."""
59
63
  # Python 3.9 compat thing for bound type vars
@@ -77,7 +77,7 @@ class EnumMetaType(EnumMeta, MetaType):
77
77
  __len__ = MetaType.__len__
78
78
 
79
79
  def __contains__(cls, value: Any) -> bool:
80
- # We used to let stdlib enum handle `__containts``` but this commit is incompatible with our API:
80
+ # We used to let stdlib enum handle `__contains__``` but this commit is incompatible with our API:
81
81
  # https://github.com/python/cpython/commit/8a9aee71268c77867d3cc96d43cbbdcbe8c0e1e8
82
82
  if isinstance(value, cls):
83
83
  return True
@@ -673,7 +673,9 @@ def _make_structure__init__(fields: list[str]) -> str:
673
673
  fields: List of field names.
674
674
  """
675
675
  field_args = ", ".join(f"{field} = None" for field in fields)
676
- field_init = "\n".join(f" self.{name} = {name} if {name} is not None else {i}" for i, name in enumerate(fields))
676
+ field_init = "\n".join(
677
+ f" self.{name} = {name} if {name} is not None else _{i}_default" for i, name in enumerate(fields)
678
+ )
677
679
 
678
680
  code = f"def __init__(self{', ' + field_args or ''}):\n"
679
681
  return code + (field_init or " pass")
@@ -688,7 +690,8 @@ def _make_union__init__(fields: list[str]) -> str:
688
690
  """
689
691
  field_args = ", ".join(f"{field} = None" for field in fields)
690
692
  field_init = "\n".join(
691
- f" object.__setattr__(self, '{name}', {name} if {name} is not None else {i})" for i, name in enumerate(fields)
693
+ f" object.__setattr__(self, '{name}', {name} if {name} is not None else _{i}_default)"
694
+ for i, name in enumerate(fields)
692
695
  )
693
696
 
694
697
  code = f"def __init__(self{', ' + field_args or ''}):\n"
@@ -780,11 +783,10 @@ def _generate_structure__init__(fields: list[Field]) -> FunctionType:
780
783
  template: FunctionType = _make_structure__init__(len(field_names))
781
784
  return type(template)(
782
785
  template.__code__.replace(
783
- co_consts=(None, *[field.type.__default__() for field in fields]),
784
- co_names=(*field_names,),
786
+ co_names=tuple(chain.from_iterable(zip((f"__{name}_default__" for name in field_names), field_names))),
785
787
  co_varnames=("self", *field_names),
786
788
  ),
787
- template.__globals__,
789
+ template.__globals__ | {f"__{field._name}_default__": field.type.__default__() for field in fields},
788
790
  argdefs=template.__defaults__,
789
791
  )
790
792
 
@@ -800,13 +802,11 @@ def _generate_union__init__(fields: list[Field]) -> FunctionType:
800
802
  template: FunctionType = _make_union__init__(len(field_names))
801
803
  return type(template)(
802
804
  template.__code__.replace(
803
- co_consts=(
804
- None,
805
- *sum([(field._name, field.type.__default__()) for field in fields], ()),
806
- ),
805
+ co_consts=(None, *field_names),
806
+ co_names=("object", "__setattr__", *(f"__{name}_default__" for name in field_names)),
807
807
  co_varnames=("self", *field_names),
808
808
  ),
809
- template.__globals__,
809
+ template.__globals__ | {f"__{field._name}_default__": field.type.__default__() for field in fields},
810
810
  argdefs=template.__defaults__,
811
811
  )
812
812
 
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, BinaryIO
4
+
5
+ from dissect.cstruct.types.base import BaseArray, BaseType
6
+
7
+ if TYPE_CHECKING:
8
+ from typing_extensions import Self
9
+
10
+
11
+ class VoidArray(list, BaseArray):
12
+ """Array type representing void elements, primarily used for no-op reading and writing operations."""
13
+
14
+ @classmethod
15
+ def __default__(cls) -> Self:
16
+ return cls()
17
+
18
+ @classmethod
19
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
20
+ return cls()
21
+
22
+ @classmethod
23
+ def _write(cls, stream: BinaryIO, data: bytes) -> int:
24
+ return 0
25
+
26
+
27
+ class Void(BaseType):
28
+ """Void type."""
29
+
30
+ ArrayType = VoidArray
31
+
32
+ def __bool__(self) -> bool:
33
+ return False
34
+
35
+ def __eq__(self, value: object) -> bool:
36
+ return isinstance(value, Void)
37
+
38
+ @classmethod
39
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
40
+ return cls.__new__(cls)
41
+
42
+ @classmethod
43
+ def _write(cls, stream: BinaryIO, data: Void) -> int:
44
+ return 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.5.dev3
3
+ Version: 4.5.dev5
4
4
  Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Apache License 2.0
@@ -137,3 +137,12 @@ def test_custom_array_type(cs: cstruct, compiled: bool) -> None:
137
137
  assert isinstance(result.b, CustomType)
138
138
  assert result.a.value == b"ASDF"
139
139
  assert result.b.value == b"asdf"
140
+
141
+
142
+ def test_truthy_type(cs: cstruct) -> None:
143
+ static_type = cs.uint32
144
+ dynamic_type = cs.uint32[None]
145
+
146
+ assert static_type
147
+ # Should not raise a TypeError: Dynamic size
148
+ assert dynamic_type
@@ -750,11 +750,11 @@ def test_codegen_make_init() -> None:
750
750
  result = _make__init__([f"_{n}" for n in range(5)])
751
751
  expected = """
752
752
  def __init__(self, _0 = None, _1 = None, _2 = None, _3 = None, _4 = None):
753
- self._0 = _0 if _0 is not None else 0
754
- self._1 = _1 if _1 is not None else 1
755
- self._2 = _2 if _2 is not None else 2
756
- self._3 = _3 if _3 is not None else 3
757
- self._4 = _4 if _4 is not None else 4
753
+ self._0 = _0 if _0 is not None else _0_default
754
+ self._1 = _1 if _1 is not None else _1_default
755
+ self._2 = _2 if _2 is not None else _2_default
756
+ self._3 = _3 if _3 is not None else _3_default
757
+ self._4 = _4 if _4 is not None else _4_default
758
758
  """
759
759
  assert result == dedent(expected[1:].rstrip())
760
760
 
@@ -765,3 +765,14 @@ def test_codegen_make_init() -> None:
765
765
  cached = structure._make_structure__init__(5)
766
766
  assert structure._make_structure__init__.cache_info() == (1, 1, 128, 1)
767
767
  assert result is cached
768
+
769
+
770
+ def test_codegen_hashable(cs: cstruct) -> None:
771
+ hashable_fields = [Field("a", cs.uint8), Field("b", cs.uint8)]
772
+ unhashable_fields = [Field("a", cs.uint8[2]), Field("b", cs.uint8)]
773
+
774
+ with pytest.raises(TypeError, match="unhashable type: 'uint8\\[2\\]'"):
775
+ hash(unhashable_fields[0].type.__default__())
776
+
777
+ assert hash(structure._generate_structure__init__(hashable_fields).__code__)
778
+ assert hash(structure._generate_structure__init__(unhashable_fields).__code__)
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
5
5
 
6
6
  import pytest
7
7
 
8
+ from dissect.cstruct.types import structure
8
9
  from dissect.cstruct.types.base import Array, BaseType
9
10
  from dissect.cstruct.types.structure import Field, Union, UnionProxy
10
11
 
@@ -535,3 +536,14 @@ def test_union_partial_initialization_dynamic(cs: cstruct) -> None:
535
536
 
536
537
  with pytest.raises(NotImplementedError, match="Initializing a dynamic union is not yet supported"):
537
538
  cs.test(x=1)
539
+
540
+
541
+ def test_codegen_hashable(cs: cstruct) -> None:
542
+ hashable_fields = [Field("a", cs.uint8), Field("b", cs.uint8)]
543
+ unhashable_fields = [Field("a", cs.uint8[2]), Field("b", cs.uint8)]
544
+
545
+ with pytest.raises(TypeError, match="unhashable type: 'uint8\\[2\\]'"):
546
+ hash(unhashable_fields[0].type.__default__())
547
+
548
+ assert hash(structure._generate_union__init__(hashable_fields).__code__)
549
+ assert hash(structure._generate_union__init__(unhashable_fields).__code__)
@@ -10,7 +10,9 @@ if TYPE_CHECKING:
10
10
 
11
11
 
12
12
  def test_void_read(cs: cstruct) -> None:
13
- assert not cs.void
13
+ # The type itself is truthy, but an instance is not
14
+ assert cs.void
15
+ assert not cs.void()
14
16
 
15
17
  stream = io.BytesIO(b"AAAA")
16
18
  assert not cs.void(stream)
@@ -23,11 +25,11 @@ def test_void_write(cs: cstruct) -> None:
23
25
 
24
26
 
25
27
  def test_void_array_read(cs: cstruct) -> None:
26
- assert not cs.void[4]
28
+ assert not cs.void[4]()
27
29
 
28
30
  stream = io.BytesIO(b"AAAA")
29
- assert not any(cs.void[4](stream))
30
- assert not any(cs.void[None](stream))
31
+ assert not cs.void[4](stream)
32
+ assert not cs.void[None](stream)
31
33
  assert stream.tell() == 0
32
34
 
33
35
 
@@ -41,7 +43,7 @@ def test_void_default(cs: cstruct) -> None:
41
43
  assert not cs.void()
42
44
  assert not cs.void.__default__()
43
45
 
44
- assert cs.void[1].__default__() == [cs.void()]
46
+ assert cs.void[1].__default__() == []
45
47
  assert cs.void[None].__default__() == []
46
48
 
47
49
 
@@ -61,8 +63,8 @@ def test_void_struct(cs: cstruct, compiled: bool) -> None:
61
63
 
62
64
  obj = cs.test(stream)
63
65
  assert not obj.a
64
- assert not any(obj.b)
65
- assert not any(obj.c)
66
+ assert not obj.b
67
+ assert not obj.c
66
68
 
67
69
  assert stream.tell() == 0
68
70
 
@@ -1,27 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any, BinaryIO
4
-
5
- from dissect.cstruct.types.base import BaseType
6
-
7
-
8
- class Void(BaseType):
9
- """Void type."""
10
-
11
- def __bool__(self) -> bool:
12
- return False
13
-
14
- def __eq__(self, value: object) -> bool:
15
- return isinstance(value, Void)
16
-
17
- @classmethod
18
- def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Void:
19
- return cls.__new__(cls)
20
-
21
- @classmethod
22
- def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Void:
23
- return [cls.__new__(cls)]
24
-
25
- @classmethod
26
- def _write(cls, stream: BinaryIO, data: Void) -> int:
27
- return 0