dissect.cstruct 4.5.dev1__tar.gz → 4.5.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 (75) hide show
  1. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/PKG-INFO +1 -1
  2. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/cstruct.py +0 -3
  3. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/tools/stubgen.py +17 -11
  4. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/enum.py +6 -6
  5. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect.cstruct.egg-info/PKG-INFO +1 -1
  6. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_tools_stubgen.py +73 -6
  7. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/.git-blame-ignore-revs +0 -0
  8. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/.gitattributes +0 -0
  9. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/COPYRIGHT +0 -0
  10. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/LICENSE +0 -0
  11. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/MANIFEST.in +0 -0
  12. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/README.md +0 -0
  13. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/__init__.py +0 -0
  14. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/bitbuffer.py +0 -0
  15. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/compiler.py +0 -0
  16. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/exceptions.py +0 -0
  17. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/expression.py +0 -0
  18. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/parser.py +0 -0
  19. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/tools/__init__.py +0 -0
  20. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/__init__.py +0 -0
  21. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/base.py +0 -0
  22. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/char.py +0 -0
  23. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/flag.py +0 -0
  24. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/int.py +0 -0
  25. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/leb128.py +0 -0
  26. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/packed.py +0 -0
  27. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/pointer.py +0 -0
  28. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/structure.py +0 -0
  29. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/void.py +0 -0
  30. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/types/wchar.py +0 -0
  31. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect/cstruct/utils.py +0 -0
  32. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect.cstruct.egg-info/SOURCES.txt +0 -0
  33. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect.cstruct.egg-info/dependency_links.txt +0 -0
  34. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect.cstruct.egg-info/entry_points.txt +0 -0
  35. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect.cstruct.egg-info/requires.txt +0 -0
  36. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/dissect.cstruct.egg-info/top_level.txt +0 -0
  37. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/examples/disk.py +0 -0
  38. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/examples/mirai.py +0 -0
  39. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/examples/pe.py +0 -0
  40. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/examples/protobuf.py +0 -0
  41. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/examples/secdesc.py +0 -0
  42. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/pyproject.toml +0 -0
  43. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/setup.cfg +0 -0
  44. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/__init__.py +0 -0
  45. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/_data/testdef.txt +0 -0
  46. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/_docs/Makefile +0 -0
  47. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/_docs/__init__.py +0 -0
  48. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/_docs/conf.py +0 -0
  49. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/_docs/index.rst +0 -0
  50. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/conftest.py +0 -0
  51. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_align.py +0 -0
  52. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_annotations.py +0 -0
  53. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_basic.py +0 -0
  54. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_bitbuffer.py +0 -0
  55. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_bitfield.py +0 -0
  56. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_compiler.py +0 -0
  57. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_ctypes.py +0 -0
  58. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_expression.py +0 -0
  59. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_parser.py +0 -0
  60. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_base.py +0 -0
  61. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_char.py +0 -0
  62. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_custom.py +0 -0
  63. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_enum.py +0 -0
  64. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_flag.py +0 -0
  65. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_int.py +0 -0
  66. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_leb128.py +0 -0
  67. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_packed.py +0 -0
  68. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_pointer.py +0 -0
  69. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_structure.py +0 -0
  70. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_union.py +0 -0
  71. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_void.py +0 -0
  72. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_types_wchar.py +0 -0
  73. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/test_utils.py +0 -0
  74. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tests/utils.py +0 -0
  75. {dissect_cstruct-4.5.dev1 → dissect_cstruct-4.5.dev3}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.5.dev1
3
+ Version: 4.5.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
@@ -425,10 +425,7 @@ class cstruct:
425
425
  ) -> type[Structure]:
426
426
  return self._make_struct(name, fields, align=align, anonymous=anonymous, base=Union)
427
427
 
428
- Z = TYPE_CHECKING
429
-
430
428
  if TYPE_CHECKING:
431
- A = 1
432
429
  # ruff: noqa: PYI042
433
430
  _int = int
434
431
  _float = float
@@ -39,9 +39,10 @@ def generate_file_stub(path: Path, base: Path) -> str:
39
39
 
40
40
  header = [
41
41
  "# Generated by cstruct-stubgen",
42
- "from typing import BinaryIO, overload",
42
+ "from typing import BinaryIO, Literal, overload",
43
43
  "",
44
44
  "import dissect.cstruct as __cs__",
45
+ "from typing_extensions import TypeAlias",
45
46
  ]
46
47
  body = []
47
48
 
@@ -49,16 +50,18 @@ def generate_file_stub(path: Path, base: Path) -> str:
49
50
  if isinstance(obj, cstruct):
50
51
  stub = generate_cstruct_stub(obj, module_prefix="__cs__.", cls_name=f"_{name}")
51
52
  body.append(stub)
52
- body.append(f"{name}: _{name}")
53
+
54
+ if body[-1][-1] != "\n":
55
+ body.append("")
56
+
57
+ body.append(f"# Technically `{name}` is an instance of `_{name}`, but then we can't use it in type hints")
58
+ body.append(f"{name}: TypeAlias = _{name}")
59
+ body.append("")
53
60
 
54
61
  if not body:
55
62
  return ""
56
63
 
57
- body_str = "\n".join(body)
58
- if "TypeAlias" in body_str:
59
- header.append("from typing_extensions import TypeAlias")
60
-
61
- return "\n".join([*header, "", body_str, ""])
64
+ return "\n".join([*header, "", "\n".join(body)])
62
65
 
63
66
 
64
67
  def generate_cstruct_stub(cs: cstruct, module_prefix: str = "", cls_name: str = "cstruct") -> str:
@@ -73,7 +76,7 @@ def generate_cstruct_stub(cs: cstruct, module_prefix: str = "", cls_name: str =
73
76
  for name, value in cs.consts.items():
74
77
  if name in empty_cs.consts:
75
78
  continue
76
- body.append(textwrap.indent(f"{name}: {type(value).__name__} = ...", prefix=indent))
79
+ body.append(textwrap.indent(f"{name}: Literal[{value!r}] = ...", prefix=indent))
77
80
 
78
81
  defined_names = set()
79
82
 
@@ -82,10 +85,11 @@ def generate_cstruct_stub(cs: cstruct, module_prefix: str = "", cls_name: str =
82
85
  if name in empty_cs.typedefs:
83
86
  continue
84
87
 
85
- if typedef.__name__ in defined_names:
88
+ if typedef.__name__ in empty_cs.typedefs:
89
+ stub = f"{name}: TypeAlias = {cs_prefix}{typedef.__name__}"
90
+ elif typedef.__name__ in defined_names:
86
91
  # Create an alias to the type if we have already seen it before.
87
92
  stub = f"{name}: TypeAlias = {typedef.__name__}"
88
-
89
93
  elif issubclass(typedef, (types.Enum, types.Flag)):
90
94
  stub = generate_enum_stub(typedef, cs_prefix=cs_prefix, module_prefix=module_prefix)
91
95
  elif issubclass(typedef, types.Structure):
@@ -129,7 +133,7 @@ def generate_generic_stub(
129
133
  cs_prefix: str = "",
130
134
  module_prefix: str = "",
131
135
  ) -> str:
132
- return f"class {name_prefix}{type_.__name__}({module_prefix}{type_.__base__.__name__}): ..."
136
+ return f"class {name_prefix}{type_.__name__}({module_prefix}{type_.__base__.__name__}): ...\n"
133
137
 
134
138
 
135
139
  def generate_enum_stub(
@@ -140,6 +144,7 @@ def generate_enum_stub(
140
144
  ) -> str:
141
145
  result = [f"class {name_prefix}{enum.__name__}({module_prefix}{enum.__base__.__name__}):"]
142
146
  result.extend(f" {key} = ..." for key in enum.__members__)
147
+ result.append("")
143
148
 
144
149
  return "\n".join(result)
145
150
 
@@ -181,6 +186,7 @@ def generate_structure_stub(
181
186
  result.append(
182
187
  textwrap.indent("def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...", prefix=indent)
183
188
  )
189
+ result.append("")
184
190
  return "\n".join(result)
185
191
 
186
192
 
@@ -76,12 +76,12 @@ class EnumMetaType(EnumMeta, MetaType):
76
76
 
77
77
  __len__ = MetaType.__len__
78
78
 
79
- if not PY_312:
80
- # Backport __contains__ from CPython 3.12
81
- def __contains__(cls, value: Any) -> bool:
82
- if isinstance(value, cls):
83
- return True
84
- return value in cls._value2member_map_
79
+ def __contains__(cls, value: Any) -> bool:
80
+ # We used to let stdlib enum handle `__containts``` but this commit is incompatible with our API:
81
+ # https://github.com/python/cpython/commit/8a9aee71268c77867d3cc96d43cbbdcbe8c0e1e8
82
+ if isinstance(value, cls):
83
+ return True
84
+ return value in cls._value2member_map_
85
85
 
86
86
  def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
87
87
  return cls(cls.type._read(stream, context))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.5.dev1
3
+ Version: 4.5.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
@@ -27,6 +27,7 @@ if TYPE_CHECKING:
27
27
  class Test(Enum):
28
28
  A = ...
29
29
  B = ...
30
+
30
31
  """,
31
32
  id="enum",
32
33
  ),
@@ -41,6 +42,7 @@ if TYPE_CHECKING:
41
42
  class Test(Enum):
42
43
  A = ...
43
44
  B = ...
45
+
44
46
  """,
45
47
  id="enum int8",
46
48
  ),
@@ -55,6 +57,7 @@ if TYPE_CHECKING:
55
57
  class Test(Flag):
56
58
  A = ...
57
59
  B = ...
60
+
58
61
  """,
59
62
  id="flag",
60
63
  ),
@@ -63,7 +66,9 @@ if TYPE_CHECKING:
63
66
  def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
64
67
  cs.load(cdef)
65
68
 
66
- assert stubgen.generate_enum_stub(cs.Test) == textwrap.dedent(expected).strip()
69
+ # We don't want to strip all trailing whitespace in case it's part of the intended expected output
70
+ # So just remove one newline from the final """ block
71
+ assert stubgen.generate_enum_stub(cs.Test) == textwrap.dedent(expected).lstrip()[:-1]
67
72
 
68
73
 
69
74
  @pytest.mark.parametrize(
@@ -86,6 +91,7 @@ def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
86
91
  def __init__(self, a: uint8 | None = ..., b: uint8 | None = ..., c: uint16 | None = ...): ...
87
92
  @overload
88
93
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
94
+
89
95
  """,
90
96
  id="basic",
91
97
  ),
@@ -106,6 +112,7 @@ def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
106
112
  def __init__(self, a: Array[uint8] | None = ..., b: CharArray | None = ..., c: WcharArray | None = ...): ...
107
113
  @overload
108
114
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
115
+
109
116
  """, # noqa: E501
110
117
  id="array",
111
118
  ),
@@ -124,6 +131,7 @@ def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
124
131
  def __init__(self, a: Pointer[uint8] | None = ..., b: Array[Pointer[uint8]] | None = ...): ...
125
132
  @overload
126
133
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
134
+
127
135
  """,
128
136
  id="pointer",
129
137
  ),
@@ -144,6 +152,7 @@ def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
144
152
  def __init__(self, a: uint8 | None = ..., b: uint8 | None = ...): ...
145
153
  @overload
146
154
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
155
+
147
156
  """,
148
157
  id="anonymous nested",
149
158
  ),
@@ -165,11 +174,13 @@ def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
165
174
  def __init__(self, a: uint8 | None = ..., b: uint8 | None = ...): ...
166
175
  @overload
167
176
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
177
+
168
178
  x: __anonymous_0__
169
179
  @overload
170
180
  def __init__(self, x: __anonymous_0__ | None = ...): ...
171
181
  @overload
172
182
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
183
+
173
184
  """,
174
185
  id="named nested",
175
186
  ),
@@ -191,11 +202,13 @@ def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
191
202
  def __init__(self, a: uint8 | None = ..., b: uint8 | None = ...): ...
192
203
  @overload
193
204
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
205
+
194
206
  x: Array[__anonymous_0__]
195
207
  @overload
196
208
  def __init__(self, x: Array[__anonymous_0__] | None = ...): ...
197
209
  @overload
198
210
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
211
+
199
212
  """,
200
213
  id="named nested array",
201
214
  ),
@@ -204,7 +217,9 @@ def test_generate_enum_stub(cs: cstruct, cdef: str, expected: str) -> None:
204
217
  def test_generate_structure_stub(cs: cstruct, cdef: str, expected: str) -> None:
205
218
  cs.load(cdef)
206
219
 
207
- assert stubgen.generate_structure_stub(cs.Test) == textwrap.dedent(expected).strip()
220
+ # We don't want to strip all trailing whitespace in case it's part of the intended expected output
221
+ # So just remove one newline from the final """ block
222
+ assert stubgen.generate_structure_stub(cs.Test) == textwrap.dedent(expected).lstrip()[:-1]
208
223
 
209
224
 
210
225
  @pytest.mark.parametrize(
@@ -225,10 +240,11 @@ def test_generate_structure_stub(cs: cstruct, cdef: str, expected: str) -> None:
225
240
  """,
226
241
  """
227
242
  class cstruct(cstruct):
228
- TEST: int = ...
243
+ TEST: Literal[1] = ...
229
244
  class TestEnum(Enum):
230
245
  A = ...
231
246
  B = ...
247
+
232
248
  class TestStruct(Structure):
233
249
  a: cstruct.uint8
234
250
  @overload
@@ -253,16 +269,64 @@ def test_generate_structure_stub(cs: cstruct, cdef: str, expected: str) -> None:
253
269
  def __init__(self, a: cstruct.uint8 | None = ...): ...
254
270
  @overload
255
271
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
272
+
256
273
  _test: TypeAlias = Test
257
274
  """,
258
275
  id="alias stub",
259
276
  ),
277
+ pytest.param(
278
+ """
279
+ typedef __u16 __fs16;
280
+ typedef __u32 __fs32;
281
+ typedef __u64 __fs64;
282
+
283
+ struct Test {
284
+ __fs16 a;
285
+ __fs32 b;
286
+ __fs64 c;
287
+ };
288
+ """,
289
+ """
290
+ class cstruct(cstruct):
291
+ __fs16: TypeAlias = cstruct.uint16
292
+ __fs32: TypeAlias = cstruct.uint32
293
+ __fs64: TypeAlias = cstruct.uint64
294
+ class Test(Structure):
295
+ a: cstruct.uint16
296
+ b: cstruct.uint32
297
+ c: cstruct.uint64
298
+ @overload
299
+ def __init__(self, a: cstruct.uint16 | None = ..., b: cstruct.uint32 | None = ..., c: cstruct.uint64 | None = ...): ...
300
+ @overload
301
+ def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
302
+
303
+ """, # noqa: E501
304
+ id="typedef stub",
305
+ ),
306
+ pytest.param(
307
+ """
308
+ #define INT 1
309
+ #define FLOAT 2.0
310
+ #define STRING "hello"
311
+ #define BYTES b'c'
312
+ """,
313
+ """
314
+ class cstruct(cstruct):
315
+ INT: Literal[1] = ...
316
+ FLOAT: Literal[2.0] = ...
317
+ STRING: Literal['hello'] = ...
318
+ BYTES: Literal[b'c'] = ...
319
+ """,
320
+ id="define literals",
321
+ ),
260
322
  ],
261
323
  )
262
324
  def test_generate_cstruct_stub(cs: cstruct, cdef: str, expected: str) -> None:
263
325
  cs.load(cdef)
264
326
 
265
- assert stubgen.generate_cstruct_stub(cs) == textwrap.dedent(expected).strip()
327
+ # We don't want to strip all trailing whitespace in case it's part of the intended expected output
328
+ # So just remove one newline from the final """ block
329
+ assert stubgen.generate_cstruct_stub(cs) == textwrap.dedent(expected).lstrip()[:-1]
266
330
 
267
331
 
268
332
  def test_generate_cstruct_stub_empty(cs: cstruct) -> None:
@@ -292,9 +356,10 @@ def test_generate_file_stub(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cap
292
356
 
293
357
  expected = """
294
358
  # Generated by cstruct-stubgen
295
- from typing import BinaryIO, overload
359
+ from typing import BinaryIO, Literal, overload
296
360
 
297
361
  import dissect.cstruct as __cs__
362
+ from typing_extensions import TypeAlias
298
363
 
299
364
  class _c_structure(__cs__.cstruct):
300
365
  class Test(__cs__.Structure):
@@ -304,7 +369,9 @@ def test_generate_file_stub(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cap
304
369
  def __init__(self, a: _c_structure.uint32 | None = ..., b: _c_structure.uint32 | None = ...): ...
305
370
  @overload
306
371
  def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
307
- c_structure: _c_structure
372
+
373
+ # Technically `c_structure` is an instance of `_c_structure`, but then we can't use it in type hints
374
+ c_structure: TypeAlias = _c_structure
308
375
  """
309
376
 
310
377
  assert stubgen.generate_file_stub(test_file, tmp_path) == textwrap.dedent(expected).lstrip()