Skip to content

Commit 471c990

Browse files
committed
Add overloads for dumps and write for BC (v4) (#54)
1 parent 0583275 commit 471c990

File tree

2 files changed

+60
-19
lines changed

2 files changed

+60
-19
lines changed

dissect/cstruct/types/base.py

+49-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from __future__ import annotations
22

3+
import functools
34
from io import BytesIO
4-
from typing import TYPE_CHECKING, Any, BinaryIO, Optional, Union
5+
from typing import TYPE_CHECKING, Any, BinaryIO, Callable, Optional, Union
56

67
from dissect.cstruct.exceptions import ArraySizeError
78

@@ -83,6 +84,31 @@ def read(cls, obj: Union[BinaryIO, bytes]) -> BaseType:
8384

8485
return cls._read(obj)
8586

87+
def write(cls, stream: BinaryIO, value: Any) -> int:
88+
"""Write a value to a writable file-like object.
89+
90+
Args:
91+
stream: File-like objects that supports writing.
92+
value: Value to write.
93+
94+
Returns:
95+
The amount of bytes written.
96+
"""
97+
return cls._write(stream, value)
98+
99+
def dumps(cls, value: Any) -> bytes:
100+
"""Dump a value to a byte string.
101+
102+
Args:
103+
value: Value to dump.
104+
105+
Returns:
106+
The raw bytes of this type.
107+
"""
108+
out = BytesIO()
109+
cls._write(out, value)
110+
return out.getvalue()
111+
86112
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> BaseType:
87113
"""Internal function for reading value.
88114
@@ -152,29 +178,33 @@ def _write_0(cls, stream: BinaryIO, array: list[BaseType]) -> int:
152178
return cls._write_array(stream, array + [cls()])
153179

154180

155-
class BaseType(metaclass=MetaType):
156-
"""Base class for cstruct type classes."""
181+
class _overload:
182+
"""Descriptor to use on the ``write`` and ``dumps`` methods on cstruct types.
157183
158-
def dumps(self) -> bytes:
159-
"""Dump this value to a byte string.
184+
Allows for calling these methods on both the type and instance.
160185
161-
Returns:
162-
The raw bytes of this type.
163-
"""
164-
out = BytesIO()
165-
self.__class__._write(out, self)
166-
return out.getvalue()
186+
Example:
187+
>>> int32.dumps(123)
188+
b'\\x7b\\x00\\x00\\x00'
189+
>>> int32(123).dumps()
190+
b'\\x7b\\x00\\x00\\x00'
191+
"""
167192

168-
def write(self, stream: BinaryIO) -> int:
169-
"""Write this value to a writable file-like object.
193+
def __init__(self, func: Callable[[Any], Any]) -> None:
194+
self.func = func
170195

171-
Args:
172-
fh: File-like objects that supports writing.
196+
def __get__(self, instance: Optional[BaseType], owner: MetaType) -> Callable[[Any], bytes]:
197+
if instance is None:
198+
return functools.partial(self.func, owner)
199+
else:
200+
return functools.partial(self.func, instance.__class__, value=instance)
173201

174-
Returns:
175-
The amount of bytes written.
176-
"""
177-
return self.__class__._write(stream, self)
202+
203+
class BaseType(metaclass=MetaType):
204+
"""Base class for cstruct type classes."""
205+
206+
dumps = _overload(MetaType.dumps)
207+
write = _overload(MetaType.write)
178208

179209

180210
class ArrayMetaType(MetaType):

tests/test_basic.py

+11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
from io import BytesIO
23

34
import pytest
45

@@ -543,3 +544,13 @@ def test_dynamic_substruct_size(cs: cstruct):
543544

544545
assert cs.sub.dynamic
545546
assert cs.test.dynamic
547+
548+
549+
def test_dumps_write_overload(cs: cstruct):
550+
assert cs.uint8.dumps(1) == cs.uint8(1).dumps() == b"\x01"
551+
552+
fh = BytesIO()
553+
cs.uint8.write(fh, 1)
554+
assert fh.getvalue() == b"\x01"
555+
cs.uint8(2).write(fh)
556+
assert fh.getvalue() == b"\x01\x02"

0 commit comments

Comments
 (0)