dissect.cstruct 4.6.dev5__tar.gz → 4.6.dev7__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.dev5 → dissect_cstruct-4.6.dev7}/PKG-INFO +1 -1
  2. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/parser.py +69 -5
  3. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/tools/stubgen.py +3 -0
  4. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/PKG-INFO +1 -1
  5. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_parser.py +137 -5
  6. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_tools_stubgen.py +20 -0
  7. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/.git-blame-ignore-revs +0 -0
  8. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/.gitattributes +0 -0
  9. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/CHANGELOG.md +0 -0
  10. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/COPYRIGHT +0 -0
  11. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/LICENSE +0 -0
  12. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/MANIFEST.in +0 -0
  13. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/README.md +0 -0
  14. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/__init__.py +0 -0
  15. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/bitbuffer.py +0 -0
  16. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/compiler.py +0 -0
  17. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/cstruct.py +0 -0
  18. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/exceptions.py +0 -0
  19. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/expression.py +0 -0
  20. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/tools/__init__.py +0 -0
  21. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/__init__.py +0 -0
  22. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/base.py +0 -0
  23. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/char.py +0 -0
  24. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/enum.py +0 -0
  25. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/flag.py +0 -0
  26. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/int.py +0 -0
  27. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/leb128.py +0 -0
  28. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/packed.py +0 -0
  29. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/pointer.py +0 -0
  30. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/structure.py +0 -0
  31. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/void.py +0 -0
  32. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/wchar.py +0 -0
  33. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect/cstruct/utils.py +0 -0
  34. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/SOURCES.txt +0 -0
  35. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/dependency_links.txt +0 -0
  36. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/entry_points.txt +0 -0
  37. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/requires.txt +0 -0
  38. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/top_level.txt +0 -0
  39. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/examples/disk.py +0 -0
  40. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/examples/mirai.py +0 -0
  41. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/examples/pe.py +0 -0
  42. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/examples/protobuf.py +0 -0
  43. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/examples/secdesc.py +0 -0
  44. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/pyproject.toml +0 -0
  45. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/setup.cfg +0 -0
  46. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/__init__.py +0 -0
  47. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/_data/testdef.txt +0 -0
  48. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/_docs/Makefile +0 -0
  49. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/_docs/__init__.py +0 -0
  50. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/_docs/conf.py +0 -0
  51. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/_docs/index.rst +0 -0
  52. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/conftest.py +0 -0
  53. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_align.py +0 -0
  54. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_annotations.py +0 -0
  55. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_basic.py +0 -0
  56. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_bitbuffer.py +0 -0
  57. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_bitfield.py +0 -0
  58. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_compiler.py +0 -0
  59. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_ctypes.py +0 -0
  60. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_expression.py +0 -0
  61. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_base.py +0 -0
  62. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_char.py +0 -0
  63. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_custom.py +0 -0
  64. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_enum.py +0 -0
  65. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_flag.py +0 -0
  66. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_int.py +0 -0
  67. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_leb128.py +0 -0
  68. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_packed.py +0 -0
  69. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_pointer.py +0 -0
  70. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_structure.py +0 -0
  71. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_union.py +0 -0
  72. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_void.py +0 -0
  73. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_types_wchar.py +0 -0
  74. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/test_utils.py +0 -0
  75. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tests/utils.py +0 -0
  76. {dissect_cstruct-4.6.dev5 → dissect_cstruct-4.6.dev7}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.6.dev5
3
+ Version: 4.6.dev7
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
@@ -49,12 +49,18 @@ class TokenParser(Parser):
49
49
  self.compiled = compiled
50
50
  self.align = align
51
51
  self.TOK = self._tokencollection()
52
+ self._conditionals = []
53
+ self._conditionals_depth = 0
52
54
 
53
55
  @staticmethod
54
56
  def _tokencollection() -> TokenCollection:
55
57
  TOK = TokenCollection()
56
58
  TOK.add(r"#\[(?P<values>[^\]]+)\](?=\s*)", "CONFIG_FLAG")
57
- TOK.add(r"#define\s+(?P<name>[^\s]+)\s+(?P<value>[^\r\n]+)\s*", "DEFINE")
59
+ TOK.add(r"#define\s+(?P<name>[^\s]+)(?P<value>[^\r\n]*)", "DEFINE")
60
+ TOK.add(r"#ifdef\s+(?P<name>[^\s]+)\s*", "IFDEF")
61
+ TOK.add(r"#ifndef\s+(?P<name>[^\s]+)\s*", "IFNDEF")
62
+ TOK.add(r"#else\s*", "ELSE")
63
+ TOK.add(r"#endif\s*", "ENDIF")
58
64
  TOK.add(r"typedef(?=\s)", "TYPEDEF")
59
65
  TOK.add(r"(?:struct|union)(?=\s|{)", "STRUCT")
60
66
  TOK.add(
@@ -80,12 +86,61 @@ class TokenParser(Parser):
80
86
  idents.append(tokens.consume())
81
87
  return " ".join([i.value for i in idents])
82
88
 
89
+ def _conditional(self, tokens: TokenConsumer) -> None:
90
+ token = tokens.consume()
91
+ pattern = self.TOK.patterns[token.token]
92
+ match = pattern.match(token.value).groupdict()
93
+
94
+ value = match["name"]
95
+
96
+ if token.token == self.TOK.IFDEF:
97
+ self._conditionals.append(value in self.cstruct.consts)
98
+ elif token.token == self.TOK.IFNDEF:
99
+ self._conditionals.append(value not in self.cstruct.consts)
100
+
101
+ def _check_conditional(self, tokens: TokenConsumer) -> bool:
102
+ """Check and handle conditionals. Return a boolean indicating if we need to continue to the next token."""
103
+ if self._conditionals and self._conditionals_depth == len(self._conditionals):
104
+ # If we have a conditional and the depth matches, handle it accordingly
105
+ if tokens.next == self.TOK.ELSE:
106
+ # Flip the last conditional
107
+ tokens.consume()
108
+ self._conditionals[-1] = not self._conditionals[-1]
109
+ return True
110
+
111
+ if tokens.next == self.TOK.ENDIF:
112
+ # Pop the last conditional
113
+ tokens.consume()
114
+ self._conditionals.pop()
115
+ self._conditionals_depth -= 1
116
+ return True
117
+
118
+ if tokens.next in (self.TOK.IFDEF, self.TOK.IFNDEF):
119
+ # If we encounter a new conditional, increase the depth
120
+ self._conditionals_depth += 1
121
+
122
+ if tokens.next == self.TOK.ENDIF:
123
+ # Similarly, decrease the depth if needed
124
+ self._conditionals_depth -= 1
125
+
126
+ if self._conditionals and not self._conditionals[-1]:
127
+ # If the last conditional evaluated to False, skip the next token
128
+ tokens.consume()
129
+ return True
130
+
131
+ if tokens.next in (self.TOK.IFDEF, self.TOK.IFNDEF):
132
+ # If the next token is a conditional, process it
133
+ self._conditional(tokens)
134
+ return True
135
+
136
+ return False
137
+
83
138
  def _constant(self, tokens: TokenConsumer) -> None:
84
139
  const = tokens.consume()
85
140
  pattern = self.TOK.patterns[self.TOK.DEFINE]
86
141
  match = pattern.match(const.value).groupdict()
87
142
 
88
- value = match["value"]
143
+ value = match["value"].strip()
89
144
  try:
90
145
  value = ast.literal_eval(value)
91
146
  except (ValueError, SyntaxError):
@@ -208,6 +263,9 @@ class TokenParser(Parser):
208
263
  tokens.consume()
209
264
  break
210
265
 
266
+ if self._check_conditional(tokens):
267
+ continue
268
+
211
269
  field = self._parse_field(tokens)
212
270
  fields.append(field)
213
271
 
@@ -266,7 +324,7 @@ class TokenParser(Parser):
266
324
  return Field(None, type_, None)
267
325
 
268
326
  if tokens.next != self.TOK.NAME:
269
- raise ParserError(f"line {self._lineno(tokens.next)}: expected name")
327
+ raise ParserError(f"line {self._lineno(tokens.next)}: expected name, got {tokens.next!r}")
270
328
  nametok = tokens.consume()
271
329
 
272
330
  type_, name, bits = self._parse_field_type(type_, nametok.value)
@@ -314,11 +372,11 @@ class TokenParser(Parser):
314
372
  tokens.eol()
315
373
  break
316
374
 
317
- if tokens.next not in (self.TOK.NAME, self.TOK.DEFS):
375
+ if tokens.next not in (self.TOK.NAME, self.TOK.DEFS, self.TOK.IDENTIFIER):
318
376
  break
319
377
 
320
378
  ntoken = tokens.consume()
321
- if ntoken == self.TOK.NAME:
379
+ if ntoken in (self.TOK.NAME, self.TOK.IDENTIFIER):
322
380
  names.append(ntoken.value.strip())
323
381
  elif ntoken == self.TOK.DEFS:
324
382
  names.extend([name.strip() for name in ntoken.value.strip().split(",")])
@@ -378,6 +436,9 @@ class TokenParser(Parser):
378
436
  if token is None:
379
437
  break
380
438
 
439
+ if self._check_conditional(tokens):
440
+ continue
441
+
381
442
  if token == self.TOK.CONFIG_FLAG:
382
443
  self._config_flag(tokens)
383
444
  elif token == self.TOK.DEFINE:
@@ -395,6 +456,9 @@ class TokenParser(Parser):
395
456
  else:
396
457
  raise ParserError(f"line {self._lineno(token)}: unexpected token {token!r}")
397
458
 
459
+ if self._conditionals:
460
+ raise ParserError(f"line {self._lineno(tokens.previous)}: unclosed conditional statement")
461
+
398
462
 
399
463
  class CStyleParser(Parser):
400
464
  """Definition parser for C-like structure syntax.
@@ -92,6 +92,9 @@ def generate_cstruct_stub(cs: cstruct, module_prefix: str = "", cls_name: str =
92
92
  stub = f"{name}: TypeAlias = {typedef.__name__}"
93
93
  elif issubclass(typedef, (types.Enum, types.Flag)):
94
94
  stub = generate_enum_stub(typedef, cs_prefix=cs_prefix, module_prefix=module_prefix)
95
+ elif issubclass(typedef, types.Pointer):
96
+ typehint = generate_typehint(typedef, prefix=cs_prefix, module_prefix=module_prefix)
97
+ stub = f"{name}: TypeAlias = {typehint}"
95
98
  elif issubclass(typedef, types.Structure):
96
99
  stub = generate_structure_stub(typedef, cs_prefix=cs_prefix, module_prefix=module_prefix)
97
100
  elif issubclass(typedef, types.BaseType):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.6.dev5
3
+ Version: 4.6.dev7
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
@@ -1,18 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
4
3
  from unittest.mock import Mock
5
4
 
6
5
  import pytest
7
6
 
7
+ from dissect.cstruct import cstruct
8
8
  from dissect.cstruct.exceptions import ParserError
9
9
  from dissect.cstruct.parser import TokenParser
10
- from dissect.cstruct.types import BaseArray, Pointer
10
+ from dissect.cstruct.types import BaseArray, Pointer, Structure
11
11
  from tests.utils import verify_compiled
12
12
 
13
- if TYPE_CHECKING:
14
- from dissect.cstruct import cstruct
15
-
16
13
 
17
14
  def test_nested_structs(cs: cstruct, compiled: bool) -> None:
18
15
  cdef = """
@@ -149,3 +146,138 @@ def test_includes(cs: cstruct) -> None:
149
146
  assert cs.myStruct.__name__ == "myStruct"
150
147
  assert len(cs.myStruct.fields) == 1
151
148
  assert cs.myStruct.fields.get("charVal")
149
+
150
+
151
+ def test_typedef_pointer(cs: cstruct) -> None:
152
+ cdef = """
153
+ typedef struct _IMAGE_DATA_DIRECTORY {
154
+ DWORD VirtualAddress;
155
+ DWORD Size;
156
+ } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
157
+ """
158
+ cs.load(cdef)
159
+
160
+ assert issubclass(cs._IMAGE_DATA_DIRECTORY, Structure)
161
+ assert cs.IMAGE_DATA_DIRECTORY is cs._IMAGE_DATA_DIRECTORY
162
+ assert issubclass(cs.PIMAGE_DATA_DIRECTORY, Pointer)
163
+ assert cs.PIMAGE_DATA_DIRECTORY.type == cs._IMAGE_DATA_DIRECTORY
164
+
165
+
166
+ def test_conditional_ifdef(cs: cstruct) -> None:
167
+ cdef = """
168
+ #define MY_CONST 42
169
+
170
+ #ifdef MY_CONST
171
+ struct test {
172
+ uint32 a;
173
+ };
174
+ #endif
175
+ """
176
+ cs.load(cdef)
177
+
178
+ assert "test" in cs.typedefs
179
+
180
+
181
+ def test_conditional_ifndef(cs: cstruct) -> None:
182
+ cdef = """
183
+ #ifndef MYVAR
184
+ #define MYVAR (1)
185
+ #endif
186
+ """
187
+ cs.load(cdef)
188
+
189
+ assert "MYVAR" in cs.consts
190
+ assert cs.consts["MYVAR"] == 1
191
+
192
+
193
+ def test_conditional_ifndef_guard(cs: cstruct) -> None:
194
+ cdef = """
195
+ /* Define Guard */
196
+ #ifndef __MYGUARD
197
+ #define __MYGUARD
198
+
199
+ typedef struct myStruct
200
+ {
201
+ char charVal[16];
202
+ }
203
+ #endif // __MYGUARD
204
+ """
205
+ cs.load(cdef)
206
+
207
+ assert "__MYGUARD" in cs.consts
208
+ assert "myStruct" in cs.typedefs
209
+
210
+
211
+ def test_conditional_nested() -> None:
212
+ cdef = """
213
+ #ifndef MYSWITCH1
214
+ #define MYVAR1 (1)
215
+ #else
216
+ #ifdef MYSWITCH2
217
+ #define MYVAR1 (2)
218
+ #else
219
+ #define MYVAR1 (3)
220
+ #endif
221
+ #endif
222
+ """
223
+ cs = cstruct().load(cdef)
224
+
225
+ assert "MYVAR1" in cs.consts
226
+ assert cs.consts["MYVAR1"] == 1
227
+
228
+ cs = cstruct().load("#define MYSWITCH1")
229
+
230
+ assert "MYSWITCH1" in cs.consts
231
+
232
+ cs.load(cdef)
233
+
234
+ assert "MYVAR1" in cs.consts
235
+ assert cs.consts["MYVAR1"] == 3
236
+
237
+
238
+ def test_conditional_in_struct(cs: cstruct) -> None:
239
+ cdef = """
240
+ struct t_bitfield {
241
+ union {
242
+ struct {
243
+ uint32_t bit0:1;
244
+ uint32_t bit1:1;
245
+ #ifdef MYSWT
246
+ uint32_t bit2:1;
247
+ #endif
248
+ } fval;
249
+ uint32_t bits;
250
+ };
251
+ };
252
+ """
253
+ cs.load(cdef)
254
+
255
+ assert "t_bitfield" in cs.typedefs
256
+ assert "fval" in cs.t_bitfield.fields
257
+ assert "bit0" in cs.t_bitfield.fields["fval"].type.fields
258
+ assert "bit1" in cs.t_bitfield.fields["fval"].type.fields
259
+ assert "bit2" not in cs.t_bitfield.fields["fval"].type.fields
260
+
261
+
262
+ def test_conditional_parsing_error(cs: cstruct) -> None:
263
+ cdef = """
264
+ #ifndef __HELP
265
+ #define __HELP
266
+ #endif
267
+ struct test {
268
+ uint32 a;
269
+ };
270
+ #endif
271
+ """
272
+ with pytest.raises(ParserError, match="line 8: unexpected token .+ENDIF"):
273
+ cs.load(cdef)
274
+
275
+ cdef = """
276
+ #ifndef __HELP
277
+ #define __HELP
278
+ struct test {
279
+ uint32 a;
280
+ };
281
+ """
282
+ with pytest.raises(ParserError, match="line 6: unclosed conditional statement"):
283
+ cs.load(cdef)
@@ -319,6 +319,26 @@ def test_generate_structure_stub(cs: cstruct, cdef: str, expected: str) -> None:
319
319
  """,
320
320
  id="define literals",
321
321
  ),
322
+ pytest.param(
323
+ """
324
+ typedef struct _Test {
325
+ uint8 a;
326
+ } Test, *pTest;
327
+ """,
328
+ """
329
+ class cstruct(cstruct):
330
+ class _Test(Structure):
331
+ a: cstruct.uint8
332
+ @overload
333
+ def __init__(self, a: cstruct.uint8 | None = ...): ...
334
+ @overload
335
+ def __init__(self, fh: bytes | memoryview | bytearray | BinaryIO, /): ...
336
+
337
+ Test: TypeAlias = _Test
338
+ pTest: TypeAlias = Pointer[cstruct._Test]
339
+ """,
340
+ id="pointer alias",
341
+ ),
322
342
  ],
323
343
  )
324
344
  def test_generate_cstruct_stub(cs: cstruct, cdef: str, expected: str) -> None: