dissect.cstruct 4.6.dev1__tar.gz → 4.6.dev3__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.6.dev1 → dissect_cstruct-4.6.dev3}/PKG-INFO +1 -1
  2. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/compiler.py +4 -4
  3. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/cstruct.py +4 -3
  4. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/expression.py +1 -1
  5. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/parser.py +12 -2
  6. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/structure.py +1 -1
  7. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect.cstruct.egg-info/PKG-INFO +1 -1
  8. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/pyproject.toml +2 -2
  9. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_annotations.py +1 -1
  10. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_parser.py +20 -0
  11. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_structure.py +36 -2
  12. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tox.ini +4 -2
  13. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/.git-blame-ignore-revs +0 -0
  14. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/.gitattributes +0 -0
  15. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/CHANGELOG.md +0 -0
  16. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/COPYRIGHT +0 -0
  17. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/LICENSE +0 -0
  18. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/MANIFEST.in +0 -0
  19. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/README.md +0 -0
  20. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/__init__.py +0 -0
  21. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/bitbuffer.py +0 -0
  22. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/exceptions.py +0 -0
  23. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/tools/__init__.py +0 -0
  24. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/tools/stubgen.py +0 -0
  25. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/__init__.py +0 -0
  26. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/base.py +0 -0
  27. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/char.py +0 -0
  28. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/enum.py +0 -0
  29. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/flag.py +0 -0
  30. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/int.py +0 -0
  31. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/leb128.py +0 -0
  32. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/packed.py +0 -0
  33. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/pointer.py +0 -0
  34. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/void.py +0 -0
  35. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/types/wchar.py +0 -0
  36. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect/cstruct/utils.py +0 -0
  37. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect.cstruct.egg-info/SOURCES.txt +0 -0
  38. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect.cstruct.egg-info/dependency_links.txt +0 -0
  39. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect.cstruct.egg-info/entry_points.txt +0 -0
  40. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect.cstruct.egg-info/requires.txt +0 -0
  41. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/dissect.cstruct.egg-info/top_level.txt +0 -0
  42. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/examples/disk.py +0 -0
  43. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/examples/mirai.py +0 -0
  44. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/examples/pe.py +0 -0
  45. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/examples/protobuf.py +0 -0
  46. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/examples/secdesc.py +0 -0
  47. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/setup.cfg +0 -0
  48. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/__init__.py +0 -0
  49. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/_data/testdef.txt +0 -0
  50. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/_docs/Makefile +0 -0
  51. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/_docs/__init__.py +0 -0
  52. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/_docs/conf.py +0 -0
  53. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/_docs/index.rst +0 -0
  54. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/conftest.py +0 -0
  55. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_align.py +0 -0
  56. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_basic.py +0 -0
  57. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_bitbuffer.py +0 -0
  58. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_bitfield.py +0 -0
  59. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_compiler.py +0 -0
  60. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_ctypes.py +0 -0
  61. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_expression.py +0 -0
  62. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_tools_stubgen.py +0 -0
  63. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_base.py +0 -0
  64. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_char.py +0 -0
  65. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_custom.py +0 -0
  66. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_enum.py +0 -0
  67. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_flag.py +0 -0
  68. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_int.py +0 -0
  69. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_leb128.py +0 -0
  70. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_packed.py +0 -0
  71. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_pointer.py +0 -0
  72. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_union.py +0 -0
  73. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_void.py +0 -0
  74. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_types_wchar.py +0 -0
  75. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/test_utils.py +0 -0
  76. {dissect_cstruct-4.6.dev1 → dissect_cstruct-4.6.dev3}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.6.dev1
3
+ Version: 4.6.dev3
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
@@ -226,18 +226,18 @@ class _ReadSourceGenerator:
226
226
 
227
227
  def _generate_structure(self, field: Field) -> Iterator[str]:
228
228
  template = f"""
229
- {'_s = stream.tell()' if field.type.dynamic else ''}
229
+ {"_s = stream.tell()" if field.type.dynamic else ""}
230
230
  r["{field._name}"] = {self._map_field(field)}._read(stream, context=r)
231
- {f's["{field._name}"] = stream.tell() - _s' if field.type.dynamic else ''}
231
+ {f's["{field._name}"] = stream.tell() - _s' if field.type.dynamic else ""}
232
232
  """
233
233
 
234
234
  yield dedent(template)
235
235
 
236
236
  def _generate_array(self, field: Field) -> Iterator[str]:
237
237
  template = f"""
238
- {'_s = stream.tell()' if field.type.dynamic else ''}
238
+ {"_s = stream.tell()" if field.type.dynamic else ""}
239
239
  r["{field._name}"] = {self._map_field(field)}._read(stream, context=r)
240
- {f's["{field._name}"] = stream.tell() - _s' if field.type.dynamic else ''}
240
+ {f's["{field._name}"] = stream.tell() - _s' if field.type.dynamic else ""}
241
241
  """
242
242
 
243
243
  yield dedent(template)
@@ -53,6 +53,7 @@ class cstruct:
53
53
 
54
54
  self.consts = {}
55
55
  self.lookups = {}
56
+ self.includes = []
56
57
  # fmt: off
57
58
  self.typedefs = {
58
59
  # Internal types
@@ -368,14 +369,14 @@ class cstruct:
368
369
  "null_terminated": null_terminated,
369
370
  }
370
371
 
371
- return cast(type[Array], self._make_type(name, bases, size, alignment=type_.alignment, attrs=attrs))
372
+ return cast("type[Array]", self._make_type(name, bases, size, alignment=type_.alignment, attrs=attrs))
372
373
 
373
374
  def _make_int_type(self, name: str, size: int, signed: bool, *, alignment: int | None = None) -> type[Int]:
374
- return cast(type[Int], self._make_type(name, (Int,), size, alignment=alignment, attrs={"signed": signed}))
375
+ return cast("type[Int]", self._make_type(name, (Int,), size, alignment=alignment, attrs={"signed": signed}))
375
376
 
376
377
  def _make_packed_type(self, name: str, packchar: str, base: type, *, alignment: int | None = None) -> type[Packed]:
377
378
  return cast(
378
- type[Packed],
379
+ "type[Packed]",
379
380
  self._make_type(
380
381
  name,
381
382
  (base, Packed),
@@ -141,7 +141,7 @@ class ExpressionTokenizer:
141
141
  self.tokens.append(">>")
142
142
  elif self.match(expected="<", append=False) and self.match(expected="<", append=False):
143
143
  self.tokens.append("<<")
144
- elif self.match(expected={" ", "\t"}, append=False):
144
+ elif self.match(expected={" ", "\n", "\t"}, append=False):
145
145
  continue
146
146
  else:
147
147
  raise ExpressionTokenizerError(
@@ -63,7 +63,8 @@ class TokenParser(Parser):
63
63
  "ENUM",
64
64
  )
65
65
  TOK.add(r"(?<=})\s*(?P<defs>(?:[a-zA-Z0-9_]+\s*,\s*)+[a-zA-Z0-9_]+)\s*(?=;)", "DEFS")
66
- TOK.add(r"(?P<name>\**?\s*[a-zA-Z0-9_]+)(?:\s*:\s*(?P<bits>\d+))?(?:\[(?P<count>[^;\n]*)\])?\s*(?=;)", "NAME")
66
+ TOK.add(r"(?P<name>\**?\s*[a-zA-Z0-9_]+)(?:\s*:\s*(?P<bits>\d+))?(?:\[(?P<count>[^;]*)\])?\s*(?=;)", "NAME")
67
+ TOK.add(r"#include\s+(?P<name>[^\s]+)\s*", "INCLUDE")
67
68
  TOK.add(r"[a-zA-Z_][a-zA-Z0-9_]*", "IDENTIFIER")
68
69
  TOK.add(r"[{}]", "BLOCK")
69
70
  TOK.add(r"\$(?P<name>[^\s]+) = (?P<value>{[^}]+})\w*[\r\n]+", "LOOKUP")
@@ -193,7 +194,7 @@ class TokenParser(Parser):
193
194
  if tokens.next == self.TOK.NAME:
194
195
  # As part of a struct field
195
196
  # struct type_name field_name;
196
- if not len(names):
197
+ if not names:
197
198
  raise ParserError(f"line {self._lineno(tokens.next)}: unexpected anonymous struct")
198
199
  return self.cstruct.resolve(names[0])
199
200
 
@@ -324,6 +325,13 @@ class TokenParser(Parser):
324
325
 
325
326
  return names
326
327
 
328
+ def _include(self, tokens: TokenConsumer) -> None:
329
+ include = tokens.consume()
330
+ pattern = self.TOK.patterns[self.TOK.INCLUDE]
331
+ match = pattern.match(include.value).groupdict()
332
+
333
+ self.cstruct.includes.append(match["name"].strip().strip("'\""))
334
+
327
335
  @staticmethod
328
336
  def _remove_comments(string: str) -> str:
329
337
  # https://stackoverflow.com/a/18381470
@@ -382,6 +390,8 @@ class TokenParser(Parser):
382
390
  self._enum(tokens)
383
391
  elif token == self.TOK.LOOKUP:
384
392
  self._lookup(tokens)
393
+ elif token == self.TOK.INCLUDE:
394
+ self._include(tokens)
385
395
  else:
386
396
  raise ParserError(f"line {self._lineno(token)}: unexpected token {token!r}")
387
397
 
@@ -139,7 +139,7 @@ class StructureMetaType(MetaType):
139
139
 
140
140
  if cls.__compiled__:
141
141
  # If the previous class was compiled try to compile this too
142
- from dissect.cstruct import compiler
142
+ from dissect.cstruct import compiler # noqa: PLC0415
143
143
 
144
144
  try:
145
145
  classdict["_read"] = compiler.Compiler(cls.cs).compile_read(fields, cls.__name__, align=cls.__align__)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.6.dev1
3
+ Version: 4.6.dev3
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
@@ -42,7 +42,7 @@ cstruct-stubgen = "dissect.cstruct.tools.stubgen:main"
42
42
 
43
43
  [tool.ruff]
44
44
  line-length = 120
45
- required-version = ">=0.9.0"
45
+ required-version = ">=0.12.0"
46
46
 
47
47
  [tool.ruff.format]
48
48
  docstring-code-format = true
@@ -85,7 +85,7 @@ select = [
85
85
  ignore = ["E203", "B904", "UP024", "ANN002", "ANN003", "ANN204", "ANN401", "SIM105", "TRY003"]
86
86
 
87
87
  [tool.ruff.lint.per-file-ignores]
88
- "tests/docs/**" = ["INP001"]
88
+ "tests/_docs/**" = ["INP001"]
89
89
 
90
90
  [tool.ruff.lint.isort]
91
91
  known-first-party = ["dissect.cstruct"]
@@ -21,7 +21,7 @@ def test_cstruct_type_annotation(name: str, monkeypatch: pytest.MonkeyPatch) ->
21
21
  for module in [module for module in sys.modules if module in ("dissect.cstruct.cstruct")]:
22
22
  monkeypatch.delitem(sys.modules, module)
23
23
 
24
- from dissect.cstruct import cstruct
24
+ from dissect.cstruct import cstruct # noqa: PLC0415
25
25
 
26
26
  if name.startswith("__"):
27
27
  name = f"_cstruct{name}"
@@ -129,3 +129,23 @@ def test_structure_names(cs: cstruct) -> None:
129
129
  assert cs.c.__name__ == "c"
130
130
  assert cs.d.__name__ == "c"
131
131
  assert cs.e.__name__ == "e"
132
+
133
+
134
+ def test_includes(cs: cstruct) -> None:
135
+ cdef = """
136
+ /* Standard libs */
137
+ #include <stdint.h> // defines fixed data types: int8_t...
138
+ /* user libs */
139
+ #include "myLib.h" // my own header
140
+
141
+ typedef struct myStruct
142
+ {
143
+ char charVal[16];
144
+ }
145
+ """
146
+ cs.load(cdef)
147
+
148
+ assert cs.includes == ["<stdint.h>", "myLib.h"]
149
+ assert cs.myStruct.__name__ == "myStruct"
150
+ assert len(cs.myStruct.fields) == 1
151
+ assert cs.myStruct.fields.get("charVal")
@@ -265,8 +265,8 @@ def test_structure_definition_simple(cs: cstruct, compiled: bool) -> None:
265
265
  with pytest.raises(AttributeError):
266
266
  obj.nope # noqa: B018
267
267
 
268
- assert obj.__dynamic_sizes__ == { "string": 7, "wstring": 10 }
269
- assert obj.__sizes__ == { "magic": 4, "wmagic": 8, "a": 1, "b": 2, "c": 4, "string": 7, "wstring": 10 }
268
+ assert obj.__dynamic_sizes__ == {"string": 7, "wstring": 10}
269
+ assert obj.__sizes__ == {"magic": 4, "wmagic": 8, "a": 1, "b": 2, "c": 4, "string": 7, "wstring": 10}
270
270
  assert len(obj) == len(buf)
271
271
  assert obj.dumps() == buf
272
272
 
@@ -276,6 +276,7 @@ def test_structure_definition_simple(cs: cstruct, compiled: bool) -> None:
276
276
  obj.write(fh)
277
277
  assert fh.getvalue() == buf
278
278
 
279
+
279
280
  def test_structure_values_dict(cs: cstruct, compiled: bool) -> None:
280
281
  cdef = """
281
282
  struct test {
@@ -810,3 +811,36 @@ def test_codegen_hashable(cs: cstruct) -> None:
810
811
 
811
812
  assert hash(structure._generate_structure__init__(hashable_fields).__code__)
812
813
  assert hash(structure._generate_structure__init__(unhashable_fields).__code__)
814
+
815
+
816
+ def test_structure_definition_newline(cs: cstruct, compiled: bool) -> None:
817
+ cdef = """
818
+ struct test {
819
+ char magic[4
820
+ ];
821
+
822
+ wchar wmagic[4];
823
+ uint8 a;
824
+ uint16 b;
825
+ uint32 c;
826
+ char string[];
827
+ wchar wstring[];
828
+ };
829
+ """
830
+ cs.endian = ">"
831
+ cs.load(cdef, compiled=compiled)
832
+
833
+ assert verify_compiled(cs.test, compiled)
834
+
835
+ buf = b"test\x00t\x00e\x00s\x00t\x01\x02\x03\x04\x05\x06\x07lalala\x00\x00t\x00e\x00s\x00t\x00\x00"
836
+
837
+ obj = cs.test()
838
+ obj.magic = b"test"
839
+ obj.wmagic = "test"
840
+ obj.a = 0x01
841
+ obj.b = 0x0203
842
+ obj.c = 0x04050607
843
+ obj.string = b"lalala"
844
+ obj.wstring = "test"
845
+
846
+ assert obj.dumps() == buf
@@ -32,16 +32,18 @@ commands =
32
32
  [testenv:fix]
33
33
  package = skip
34
34
  deps =
35
- ruff==0.9.2
35
+ ruff==0.12.4
36
36
  commands =
37
37
  ruff format dissect tests
38
+ ruff check --fix dissect tests
38
39
 
39
40
  [testenv:lint]
40
41
  package = skip
41
42
  deps =
42
- ruff==0.9.2
43
+ ruff==0.12.4
43
44
  vermin
44
45
  commands =
46
+ ruff format --check dissect tests
45
47
  ruff check dissect tests
46
48
  vermin -t=3.9- --no-tips --lint dissect tests
47
49