Skip to content

Commit 96f8770

Browse files
committed
Move all internal attributes to dunder attributes
1 parent 506d1df commit 96f8770

22 files changed

Lines changed: 329 additions & 234 deletions

dissect/cstruct/bitbuffer.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ def __init__(self, stream: BinaryIO, endian: str):
1919

2020
def read(self, field_type: type[BaseType], bits: int) -> int:
2121
if self._remaining == 0 or self._type != field_type:
22-
if field_type.size is None:
22+
if field_type.__size__ is None:
2323
raise ValueError("Reading variable-length fields is unsupported")
2424

2525
self._type = field_type
26-
self._remaining = field_type.size * 8
26+
self._remaining = field_type.__size__ * 8
2727
self._buffer = field_type._read(self.stream)
2828

2929
if isinstance(self._buffer, bytes):
@@ -51,17 +51,17 @@ def write(self, field_type: type[BaseType], data: int, bits: int) -> None:
5151
if self._type:
5252
self.flush()
5353

54-
if field_type.size is None:
54+
if field_type.__size__ is None:
5555
raise ValueError("Writing variable-length fields is unsupported")
5656

57-
self._remaining = field_type.size * 8
57+
self._remaining = field_type.__size__ * 8
5858
self._type = field_type
5959

60-
if self._type is None or self._type.size is None:
60+
if self._type is None or self._type.__size__ is None:
6161
raise ValueError("Invalid state")
6262

6363
if self.endian == "<":
64-
self._buffer |= data << (self._type.size * 8 - self._remaining)
64+
self._buffer |= data << (self._type.__size__ * 8 - self._remaining)
6565
else:
6666
self._buffer |= data << (self._remaining - bits)
6767

dissect/cstruct/compiler.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161

6262
def compile(structure: type[Structure]) -> type[Structure]:
63-
return Compiler(structure.cs).compile(structure)
63+
return Compiler(structure.__cs__).compile(structure)
6464

6565

6666
class Compiler:
@@ -119,7 +119,7 @@ def generate_source(self) -> str:
119119
"""
120120

121121
if any(field.bits for field in self.fields):
122-
preamble += "bit_reader = BitBuffer(stream, cls.cs.endian)\n"
122+
preamble += "bit_reader = BitBuffer(stream, cls.__cs__.endian)\n"
123123

124124
read_code = "\n".join(self._generate_fields())
125125

@@ -224,22 +224,22 @@ def align_to_field(field: Field) -> Iterator[str]:
224224
yield from flush()
225225

226226
if self.align:
227-
yield f"stream.seek(-stream.tell() & (cls.alignment - 1), {io.SEEK_CUR})"
227+
yield f"stream.seek(-stream.tell() & (cls.__alignment__ - 1), {io.SEEK_CUR})"
228228

229229
def _generate_structure(self, field: Field) -> Iterator[str]:
230230
template = f"""
231-
{"_s = stream.tell()" if field.type.dynamic else ""}
231+
{"_s = stream.tell()" if field.type.__dynamic__ else ""}
232232
r["{field._name}"] = {self._map_field(field)}._read(stream, context=r)
233-
{f's["{field._name}"] = stream.tell() - _s' if field.type.dynamic else ""}
233+
{f's["{field._name}"] = stream.tell() - _s' if field.type.__dynamic__ else ""}
234234
"""
235235

236236
yield dedent(template)
237237

238238
def _generate_array(self, field: Field) -> Iterator[str]:
239239
template = f"""
240-
{"_s = stream.tell()" if field.type.dynamic else ""}
240+
{"_s = stream.tell()" if field.type.__dynamic__ else ""}
241241
r["{field._name}"] = {self._map_field(field)}._read(stream, context=r)
242-
{f's["{field._name}"] = stream.tell() - _s' if field.type.dynamic else ""}
242+
{f's["{field._name}"] = stream.tell() - _s' if field.type.__dynamic__ else ""}
243243
"""
244244

245245
yield dedent(template)
@@ -253,8 +253,8 @@ def _generate_bits(self, field: Field) -> Iterator[str]:
253253
field_type = field_type.type
254254

255255
if issubclass(field_type, Char):
256-
field_type = field_type.cs.uint8
257-
lookup = "cls.cs.uint8"
256+
field_type = field_type.__cs__.uint8
257+
lookup = "cls.__cs__.uint8"
258258

259259
template = f"""
260260
_t = {lookup}
@@ -283,13 +283,13 @@ def _generate_packed(self, fields: list[Field]) -> Iterator[str]:
283283
read_type = _get_read_type(self.cs, field_type.type)
284284

285285
if issubclass(read_type, (Char, Wchar, Int)):
286-
count *= read_type.size
286+
count *= read_type.__size__
287287
getter = f"buf[{size}:{size + count}]"
288288
else:
289289
getter = f"data[{slice_index}:{slice_index + count}]"
290290
slice_index += count
291291
elif issubclass(read_type, (Char, Wchar, Int)):
292-
getter = f"buf[{size}:{size + read_type.size}]"
292+
getter = f"buf[{size}:{size + read_type.__size__}]"
293293
else:
294294
getter = f"data[{slice_index}]"
295295
slice_index += 1
@@ -308,8 +308,8 @@ def _generate_packed(self, fields: list[Field]) -> Iterator[str]:
308308

309309
if issubclass(field_type.type, Int):
310310
reads.append(f"_b = {getter}")
311-
item_parser = parser_template.format(type="_et", getter=f"_b[i:i + {field_type.type.size}]")
312-
list_comp = f"[{item_parser} for i in range(0, {count}, {field_type.type.size})]"
311+
item_parser = parser_template.format(type="_et", getter=f"_b[i:i + {field_type.type.__size__}]")
312+
list_comp = f"[{item_parser} for i in range(0, {count}, {field_type.type.__size__})]"
313313
elif issubclass(field_type.type, Pointer):
314314
item_parser = "_et.__new__(_et, e, stream, r)"
315315
list_comp = f"[{item_parser} for e in {getter}]"
@@ -329,13 +329,13 @@ def _generate_packed(self, fields: list[Field]) -> Iterator[str]:
329329
reads.append(f'r["{field._name}"] = {parser}')
330330
reads.append("") # Generates a newline in the resulting code
331331

332-
size += field_type.size
332+
size += field_type.__size__
333333

334334
fmt = _optimize_struct_fmt(info)
335335
if fmt == "x" or (len(fmt) == 2 and fmt[1] == "x"):
336336
unpack = ""
337337
else:
338-
unpack = f'data = _struct(cls.cs.endian, "{fmt}").unpack(buf)\n'
338+
unpack = f'data = _struct(cls.__cs__.endian, "{fmt}").unpack(buf)\n'
339339

340340
template = f"""
341341
buf = stream.read({size})
@@ -382,9 +382,9 @@ def _generate_struct_info(cs: cstruct, fields: list[Field], align: bool = False)
382382
# Other types are byte based
383383
# We don't actually unpack anything here but slice directly out of the buffer
384384
elif issubclass(read_type, (Char, Wchar, Int)):
385-
yield field, count * read_type.size, "x"
385+
yield field, count * read_type.__size__, "x"
386386

387-
size = count * read_type.size
387+
size = count * read_type.__size__
388388
imaginary_offset += size
389389
if current_offset is not None:
390390
current_offset += size

dissect/cstruct/cstruct.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def __copy__(self) -> cstruct:
220220
# Update types to point to the new cstruct instance
221221
for name, type_ in self.types.items():
222222
new_type = copy.copy(type_)
223-
new_type.cs = cs
223+
new_type.__cs__ = cs
224224
cs.add_type(name, new_type, replace=True)
225225

226226
for name, value in self.consts.items():
@@ -433,10 +433,10 @@ def _make_type(
433433
attrs = attrs or {}
434434
attrs.update(
435435
{
436-
"cs": self,
437-
"size": size,
438-
"dynamic": size is None,
439-
"alignment": alignment or size,
436+
"__cs__": self,
437+
"__size__": size,
438+
"__dynamic__": size is None,
439+
"__alignment__": alignment or size,
440440
}
441441
)
442442
return types.new_class(name, bases, {}, lambda ns: ns.update(attrs))
@@ -446,12 +446,12 @@ def _make_array(self, type_: T, num_entries: int | Expression | None) -> type[Ar
446446
if num_entries is None:
447447
null_terminated = True
448448
size = None
449-
elif isinstance(num_entries, Expression) or type_.dynamic:
449+
elif isinstance(num_entries, Expression) or type_.__dynamic__:
450450
size = None
451451
else:
452-
if type_.size is None:
452+
if type_.__size__ is None:
453453
raise ValueError(f"Cannot create array of dynamic type: {type_.__name__}")
454-
size = num_entries * type_.size
454+
size = num_entries * type_.__size__
455455

456456
name = f"{type_.__name__}[]" if null_terminated else f"{type_.__name__}[{num_entries}]"
457457

@@ -463,7 +463,7 @@ def _make_array(self, type_: T, num_entries: int | Expression | None) -> type[Ar
463463
"null_terminated": null_terminated,
464464
}
465465

466-
return cast("type[Array]", self._make_type(name, bases, size, alignment=type_.alignment, attrs=attrs))
466+
return cast("type[Array]", self._make_type(name, bases, size, alignment=type_.__alignment__, attrs=attrs))
467467

468468
def _make_int_type(self, name: str, size: int, signed: bool, *, alignment: int | None = None) -> type[Int]:
469469
return cast("type[Int]", self._make_type(name, (Int,), size, alignment=alignment, attrs={"signed": signed}))
@@ -490,8 +490,8 @@ def _make_pointer(self, target: type[BaseType]) -> type[Pointer]:
490490
return self._make_type(
491491
f"{target.__name__}*",
492492
(Pointer,),
493-
self.pointer.size,
494-
alignment=self.pointer.alignment,
493+
self.pointer.__size__,
494+
alignment=self.pointer.__alignment__,
495495
attrs={"type": target},
496496
)
497497

dissect/cstruct/tools/stubgen.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,15 @@ def generate_structure_stub(
159159
indent = " " * 4
160160

161161
args = ["self"]
162-
for field_name, field in structure.fields.items():
162+
for field_name, field in structure.__members__.items():
163163
inlined = False
164164

165165
# If it's a structure and not globally defined, add an inline stub for it
166166
nested_type = field.type
167167
while issubclass(nested_type, types.BaseArray):
168168
nested_type = nested_type.type
169169

170-
if issubclass(nested_type, types.Structure) and nested_type.__name__ not in structure.cs.types:
170+
if issubclass(nested_type, types.Structure) and nested_type.__name__ not in structure.__cs__.types:
171171
inlined = True
172172
inline_stub = generate_structure_stub(nested_type, cs_prefix=cs_prefix, module_prefix=module_prefix)
173173

dissect/cstruct/types/base.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import functools
4+
import warnings
45
from io import BytesIO
56
from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, TypeVar
67

@@ -21,13 +22,13 @@
2122
class MetaType(type):
2223
"""Base metaclass for cstruct type classes."""
2324

24-
cs: cstruct
25+
__cs__: cstruct
2526
"""The cstruct instance this type class belongs to."""
26-
size: int | None
27+
__size__: int | None
2728
"""The size of the type in bytes. Can be ``None`` for dynamic sized types."""
28-
dynamic: bool
29+
__dynamic__: bool
2930
"""Whether or not the type is dynamically sized."""
30-
alignment: int | None
31+
__alignment__: int | None
3132
"""The alignment of the type in bytes. A value of ``None`` will be treated as 1-byte aligned."""
3233

3334
# This must be the actual type, but since Array is a subclass of BaseType, we correct this at the bottom of the file
@@ -43,7 +44,7 @@ def __call__(cls, *args, **kwargs) -> Self: # type: ignore
4344
if _is_readable_type(stream):
4445
return cls._read(stream)
4546

46-
if issubclass(cls, bytes) and isinstance(stream, bytes) and len(stream) == cls.size:
47+
if issubclass(cls, bytes) and isinstance(stream, bytes) and len(stream) == cls.__size__:
4748
# Shortcut for char/bytes type
4849
return type.__call__(cls, *args, **kwargs)
4950

@@ -54,18 +55,18 @@ def __call__(cls, *args, **kwargs) -> Self: # type: ignore
5455

5556
def __getitem__(cls, num_entries: int | Expression | None) -> type[BaseArray]:
5657
"""Create a new array with the given number of entries."""
57-
return cls.cs._make_array(cls, num_entries)
58+
return cls.__cs__._make_array(cls, num_entries)
5859

5960
def __bool__(cls) -> bool:
6061
"""Type class is always truthy."""
6162
return True
6263

6364
def __len__(cls) -> int:
6465
"""Return the byte size of the type."""
65-
if cls.size is None:
66+
if cls.__size__ is None:
6667
raise TypeError("Dynamic size")
6768

68-
return cls.size
69+
return cls.__size__
6970

7071
def __default__(cls) -> Self: # type: ignore
7172
"""Return the default value of this type."""
@@ -189,6 +190,46 @@ def _write_0(cls, stream: BinaryIO, array: list[Self]) -> int: # type: ignore
189190
"""
190191
return cls._write_array(stream, [*array, cls.__default__()])
191192

193+
@property
194+
def cs(cls) -> cstruct:
195+
"""Access the cstruct object for this type."""
196+
warnings.warn(
197+
"The 'cs' property is deprecated, use '__cs__' instead.",
198+
DeprecationWarning,
199+
stacklevel=2,
200+
)
201+
return cls.__cs__
202+
203+
@property
204+
def size(cls) -> int | None:
205+
"""Access the size of this type."""
206+
warnings.warn(
207+
"The 'size' property is deprecated, use '__size__', len(cls) or sizeof(cls) instead.",
208+
DeprecationWarning,
209+
stacklevel=2,
210+
)
211+
return cls.__size__
212+
213+
@property
214+
def dynamic(cls) -> bool:
215+
"""Access whether this type is dynamically sized."""
216+
warnings.warn(
217+
"The 'dynamic' property is deprecated, use '__dynamic__' instead.",
218+
DeprecationWarning,
219+
stacklevel=2,
220+
)
221+
return cls.__dynamic__
222+
223+
@property
224+
def alignment(cls) -> int | None:
225+
"""Access the alignment of this type."""
226+
warnings.warn(
227+
"The 'alignment' property is deprecated, use '__alignment__' instead.",
228+
DeprecationWarning,
229+
stacklevel=2,
230+
)
231+
return cls.__alignment__
232+
192233

193234
class _overload:
194235
"""Descriptor to use on the ``write`` and ``dumps`` methods on cstruct types.
@@ -219,10 +260,10 @@ class BaseType(metaclass=MetaType):
219260

220261
def __len__(self) -> int:
221262
"""Return the byte size of the type."""
222-
if self.__class__.size is None:
263+
if self.__class__.__size__ is None:
223264
raise TypeError("Dynamic size")
224265

225-
return self.__class__.size
266+
return self.__class__.__size__
226267

227268

228269
T = TypeVar("T", bound=BaseType)
@@ -260,7 +301,7 @@ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> list[
260301
num = EOF
261302
elif isinstance(cls.num_entries, Expression):
262303
try:
263-
num = max(0, cls.num_entries.evaluate(cls.cs, context))
304+
num = max(0, cls.num_entries.evaluate(cls.__cs__, context))
264305
except Exception:
265306
if cls.num_entries.expression != "EOF":
266307
raise
@@ -273,7 +314,7 @@ def _write(cls, stream: BinaryIO, data: list[Any]) -> int:
273314
if cls.null_terminated:
274315
return cls.type._write_0(stream, data)
275316

276-
if not cls.dynamic and cls.num_entries != (actual_size := len(data)):
317+
if not cls.__dynamic__ and cls.num_entries != (actual_size := len(data)):
277318
raise ArraySizeError(f"Expected static array size {cls.num_entries}, got {actual_size} instead.")
278319

279320
return cls.type._write_array(stream, data)

dissect/cstruct/types/char.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class CharArray(bytes, BaseArray):
1313

1414
@classmethod
1515
def __default__(cls) -> Self:
16-
return type.__call__(cls, b"\x00" * (0 if cls.dynamic or cls.null_terminated else cls.num_entries))
16+
return type.__call__(cls, b"\x00" * (0 if cls.__dynamic__ or cls.null_terminated else cls.num_entries))
1717

1818
@classmethod
1919
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:

dissect/cstruct/types/enum.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ def __call__(
5454
raise TypeError("Enum can only be created from int type")
5555

5656
enum_cls = super().__call__(name, *args, **kwargs)
57-
enum_cls.cs = cs
57+
enum_cls.__cs__ = cs
5858
enum_cls.type = type_
59-
enum_cls.size = type_.size
60-
enum_cls.dynamic = type_.dynamic
61-
enum_cls.alignment = type_.alignment
59+
enum_cls.__size__ = type_.__size__
60+
enum_cls.__dynamic__ = type_.__dynamic__
61+
enum_cls.__alignment__ = type_.__alignment__
6262

6363
_fix_alias_members(enum_cls)
6464

0 commit comments

Comments
 (0)