obstruct 0.2.1__py3-none-any.whl

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.
obstruct/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .methods import pack, unpack
2
+ from .decorator import struct
3
+ from .struct_type import Struct
4
+ from .types import *
@@ -0,0 +1 @@
1
+ version = "0.2.1"
@@ -0,0 +1,2 @@
1
+ from .base import function_def
2
+ from .aot import *
obstruct/aot/aot.py ADDED
@@ -0,0 +1,2 @@
1
+ from .pack import create_pack_methods
2
+ from .unpack import create_unpack_methods
obstruct/aot/base.py ADDED
@@ -0,0 +1,145 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ import itertools
5
+ import typing
6
+ from typing import Any
7
+
8
+ import typing_inspection.typing_objects
9
+
10
+ from _hydrogenlib_core.typefunc import enhanced_generator
11
+ from obstruct.methods import get_ctype
12
+
13
+
14
+ class _objective_node_operator:
15
+ _node: ast.stmt
16
+
17
+ @property
18
+ def node(self):
19
+ return self._node
20
+
21
+ def add_node(self, node: ast.AST):
22
+ self._node.body.append(node)
23
+ return node
24
+
25
+ def add_code(self, code: str):
26
+ tree = ast.parse(code)
27
+ if isinstance(tree, ast.Module):
28
+ self._node.body.extend(tree.body)
29
+ return tree
30
+ elif isinstance(tree, ast.Expr):
31
+ self._node.body.append(tree.value)
32
+ return tree.value
33
+ else:
34
+ self._node.body.append(tree)
35
+ return tree
36
+
37
+ # def insert_code(self, index, code: str):
38
+ # tree = ast.parse(code)
39
+ # if isinstance(tree, ast.Module):
40
+ # for node in reversed(tree.body):
41
+ # self._node.body.insert(index, node)
42
+ # return tree
43
+ # elif isinstance(tree, ast.Expr):
44
+ # self._node.body.insert(index, tree.value)
45
+ # return tree.value
46
+ # else:
47
+ # self._node.body.insert(index, tree)
48
+ # return tree
49
+
50
+ def compile(self):
51
+ module = ast.Module([self._node])
52
+ ast.fix_missing_locations(module)
53
+ return compile(
54
+ module, filename="<string>", mode="exec"
55
+ )
56
+
57
+ def exec(self, globals=None, locals=None, closure=None):
58
+ globals = globals or {}
59
+ exec(self.compile(), globals, locals, closure=closure)
60
+ return globals[self._node.name]
61
+
62
+ def generate_code(self):
63
+ module = ast.Module([self._node])
64
+ ast.fix_missing_locations(module)
65
+ return ast.unparse(module)
66
+
67
+ def __enter__(self):
68
+ return self
69
+
70
+ def __exit__(self, exc_type, exc_val, exc_tb):
71
+ pass
72
+
73
+
74
+ class function_def(_objective_node_operator):
75
+ def __init__(self, signature: str):
76
+ self._signature = signature
77
+ self._node = ast.parse(f'def {signature}: pass').body[0] # type: ast.FunctionDef
78
+ self._node.body.clear()
79
+
80
+ def add_decorator(self, expr: str | ast.expr):
81
+ if isinstance(expr, str):
82
+ node = ast.parse(expr).body[0]
83
+
84
+ if not isinstance(node, ast.Expr):
85
+ raise ValueError(expr)
86
+
87
+ expr = node.value
88
+
89
+ self._node.decorator_list.append(expr)
90
+ return expr
91
+
92
+ def add_return(self, expr: str | ast.expr):
93
+ if isinstance(expr, str):
94
+ node = ast.parse(expr).body[0]
95
+ if not isinstance(node, ast.Expr):
96
+ raise ValueError(expr)
97
+ self._node.body.append(ast.Return(node.value))
98
+ else:
99
+ self._node.body.append(ast.Return(expr))
100
+
101
+
102
+ class for_stmt(_objective_node_operator):
103
+ def __init__(self, expr):
104
+ self._node = ast.parse(f"for {expr}: ...").body[0] # type: ast.For
105
+ self._node.body.clear()
106
+
107
+ def exec(self, globals=None, locals=None, closure=None):
108
+ return exec(self.compile(), globals, locals, closure=closure)
109
+
110
+
111
+ available_characters = tuple(
112
+ chr(i) for i in range(65, 91)
113
+ )
114
+
115
+
116
+ @enhanced_generator(history=True)
117
+ def unique_name_generator():
118
+ cnt = 0
119
+ while True:
120
+ cnt += 1
121
+ yield from map(lambda x: ''.join(x),
122
+ itertools.combinations(available_characters, cnt))
123
+
124
+
125
+ def is_struct(tp):
126
+ return hasattr(tp, '__cstruct__')
127
+
128
+
129
+ def is_array(tp):
130
+ if typing_inspection.typing_objects.is_annotated(typing.get_origin(tp)):
131
+ return True
132
+ return False
133
+
134
+
135
+ def get_array_spec(tp):
136
+ ls_type, length = typing.get_args(tp)
137
+ return typing.get_args(ls_type)[0], length
138
+
139
+
140
+ def get_array_ctype(tp) -> str | None | Any:
141
+ return get_ctype(get_array_spec(tp)[0])
142
+
143
+
144
+ def to_tuple_expression(names, prefix='self.'):
145
+ return ', '.join(f'{prefix}{name}' for name in names)
obstruct/aot/pack.py ADDED
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ import array
4
+ import typing
5
+
6
+ from .base import unique_name_generator, is_struct, is_array, get_array_ctype, to_tuple_expression, function_def
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from .. import struct_type
10
+
11
+
12
+ def create_pack_methods(cls: struct_type.Struct):
13
+ """
14
+ Generate `pack` and `pack_into` method
15
+ """
16
+ with (
17
+ function_def('pack(self)') as pack,
18
+ function_def('pack_into(self, buffer, offset=0)') as pack_into,
19
+ ):
20
+ ung = unique_name_generator()
21
+ flat_fields = []
22
+
23
+ def add_code(code):
24
+ pack.add_code(code)
25
+ pack_into.add_code(code)
26
+
27
+ def walk(fields, path, parent_name=None):
28
+ # parent name 标识了这个字段的父字段被赋值给了哪个临时变量,这样可以复用已有的临时变量,减少结构体属性访问次数
29
+ name = 'self' # 这里的 name 是默认的 self,如果当前处理的是内嵌的结构体,那么 name 会被替换成临时变量
30
+
31
+ if len(path) > 1: # 当前处理的是内嵌结构体
32
+ name = ung.next() # 生成一个临时变量缓存其值
33
+ add_code(f'{name} = {parent_name}.{path[-1]}')
34
+
35
+ for field in fields: # 遍历结构体的所有字段
36
+ if is_struct(field.type): # 又是一个内嵌的结构体字段
37
+ walk(field.type.__cfields__, path + (field.name,), name) # 递归
38
+ elif is_array(field.type): # 一个数组类型
39
+ d_ctype = get_array_ctype(field.type)
40
+
41
+ _ = ung.next()
42
+ add_code(f'{_} = array({d_ctype!r}, {name}.{field.name})')
43
+ flat_fields.append(f'{_}.tobytes()')
44
+
45
+ else:
46
+ flat_fields.append(f'{name}.{field.name}') # 普通的字段,添加到传递目标列表中
47
+
48
+ walk(cls.__cfields__, ('self',)) # 启动递归函数
49
+ # names = [field.name for field in fields]
50
+
51
+ expression = to_tuple_expression(flat_fields, prefix='')
52
+ pack.add_return(f"xpack({expression})") # 返回打包的结果,将所有字段参数展开传递
53
+ pack_into.add_code(f"xpack_into(buffer, offset, {expression})")
54
+ # print(pack.generate_code()) # Debug
55
+ return (
56
+ pack.exec({'xpack': cls.__cstruct__.pack, 'array': array.array}),
57
+ pack_into.exec({'xpack_into': cls.__cstruct__.pack_into, 'array': array.array})
58
+ )
obstruct/aot/unpack.py ADDED
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ import array
4
+ import typing
5
+
6
+ from .base import unique_name_generator, is_struct, is_array, get_array_ctype, function_def, for_stmt
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from .. import struct_type
10
+
11
+
12
+ def create_unpack_methods(cls: type[struct_type.Struct]):
13
+ with (
14
+ function_def('unpack(cls, buffer)') as unpack,
15
+ function_def('update(self, buffer)') as update,
16
+ function_def('unpack_from(cls, buffer, offset=0)') as unpack_from,
17
+ function_def('update_from(self, buffer, offset=0)') as update_from,
18
+ function_def('iter_unpack(cls, buffer)') as iter_unpack,
19
+ function_def('iter_update(cls, buffer, obj=None)') as iter_update,
20
+ ):
21
+ unpack.add_decorator('classmethod') # unpack 是一个类方法
22
+ unpack_from.add_decorator('classmethod') # unpack_from 也是
23
+ iter_unpack.add_decorator('classmethod')
24
+ iter_update.add_decorator('classmethod')
25
+
26
+ ung = unique_name_generator()
27
+
28
+ def update_add_code(code):
29
+ update.add_code(code)
30
+ update_from.add_code(code)
31
+ iter_update_inner.add_code(code)
32
+
33
+ def unpack_add_code(code):
34
+ unpack.add_code(code)
35
+ unpack_from.add_code(code)
36
+ iter_unpack_inner.add_code(code)
37
+
38
+ post_processors = [] # 后处理代码列表
39
+ assigns = [] # 赋值目标列表
40
+ inner_classes = {} # 内嵌的结构体的类型映射(所有内嵌结构体的类型在生成代码时都会被代替为无意义的名称)
41
+
42
+ iter_update.add_code("self = cls() if obj is None else obj")
43
+
44
+ iter_unpack_inner = for_stmt("values in x_iter_unpack(buffer)")
45
+ iter_update_inner = for_stmt("values in x_iter_unpack(buffer)")
46
+
47
+ iter_unpack.add_node(iter_unpack_inner.node)
48
+ iter_update.add_node(iter_update_inner.node)
49
+
50
+ def walk(fields, path, parent_name=None, current_class_name='cls'):
51
+ name = 'self'
52
+
53
+ if len(path) > 1: # 在处理内嵌的结构体
54
+ name = ung.next() # 临时变量的名称
55
+ update_add_code(f"{name} = {parent_name}.{path[-1]}")
56
+
57
+ unpack_add_code(f"{parent_name}.{path[-1]} = {name} = __new__({current_class_name})") # 组合赋值
58
+ else:
59
+ unpack_add_code(f"{name} = __new__({current_class_name})") # 不需要临时变量存储根对象
60
+ # update 在这里不需要创建新对象
61
+
62
+ for field in fields: # 遍历字段
63
+ field_ref = f'{name}.{field.name}'
64
+
65
+ if is_struct(field.type): # 是个内嵌的结构体
66
+ if field.type not in inner_classes: # 如果这个内嵌的结构体类型不在已知的结构体中(没有分配类名称)
67
+ type_name = ung.next() # 生成一个唯一的类名
68
+ inner_classes[field.type] = type_name # 添加
69
+ else:
70
+ type_name = inner_classes[field.type] # 如果在,那么提取
71
+
72
+ walk(field.type.__cfields__, path + (field.name,), name, type_name)
73
+ elif is_array(field.type):
74
+ d_ctype = get_array_ctype(field.type)
75
+ post_processors.append(
76
+ f'_ = array({d_ctype!r});'
77
+ f'_.frombytes({field_ref});'
78
+ f'{field_ref} = _'
79
+ )
80
+ assigns.append(field_ref)
81
+ else:
82
+ assigns.append(field_ref)
83
+
84
+ walk(cls.__cfields__, ('A',))
85
+
86
+ assign_code = f"{', '.join(assigns)} = x_unpack(buffer)" # 生成普通的 unpack, update 用的赋值语句
87
+ unpack.add_code(assign_code)
88
+ update.add_code(assign_code)
89
+
90
+ assign_code = assign_code.replace("x_unpack(buffer)", "values") # 生成 iter_xxx 的赋值语句
91
+ iter_unpack_inner.add_code(assign_code)
92
+ iter_update_inner.add_code(assign_code)
93
+
94
+ assign_code = assign_code.replace("values", "x_unpack_from(buffer, offset)") # 生成 xxx_from 的赋值语句
95
+ unpack_from.add_code(assign_code)
96
+ update_from.add_code(assign_code)
97
+
98
+ for post_processor in post_processors:
99
+ unpack_add_code(post_processor)
100
+ update_add_code(post_processor)
101
+
102
+ unpack.add_return('self')
103
+ update.add_return('self')
104
+ unpack_from.add_return('self')
105
+ update_from.add_return('self')
106
+
107
+ iter_unpack_inner.add_code('yield self') # iter 方法是 yield,而非 return
108
+ iter_update_inner.add_code('yield self')
109
+
110
+ # print(unpack.generate_code())
111
+ # print(update.generate_code())
112
+ # print(unpack_from.generate_code())
113
+ # print(update_from.generate_code())
114
+ # print(iter_unpack.generate_code())
115
+ print(iter_update.generate_code())
116
+
117
+ globals = {
118
+ v: k for k, v in inner_classes.items() # 翻转字典
119
+ }
120
+
121
+ globals.update(dict(
122
+ __new__=object.__new__,
123
+ x_unpack=cls.__cstruct__.unpack,
124
+ x_unpack_from=cls.__cstruct__.unpack_from,
125
+ x_iter_unpack=cls.__cstruct__.iter_unpack,
126
+ array=array.array
127
+ ))
128
+
129
+ return unpack.exec(
130
+ globals
131
+ ), update.exec(
132
+ globals
133
+ ), unpack_from.exec(
134
+ globals
135
+ ), update_from.exec(
136
+ globals
137
+ ), iter_unpack.exec(
138
+ globals
139
+ ), iter_update.exec(
140
+ globals
141
+ )
obstruct/decorator.py ADDED
@@ -0,0 +1,41 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from . import struct_type
5
+ from .aot import pack, unpack
6
+ from .methods import to_format_string
7
+
8
+ endian_control_characters = tuple('<@=!>')
9
+
10
+
11
+ def struct(maybe_cls=None, *, endian='@'):
12
+ def decorator[T](cls: T) -> type[struct_type.Struct]:
13
+ import struct
14
+
15
+ # module = sys.modules[cls.__module__]
16
+
17
+ fields = dataclasses.fields(cls)
18
+ # namesp = module.__dict__
19
+ # print(namesp)
20
+ real_annotations = typing.get_type_hints(cls)
21
+ # print(cls, real_annotations)
22
+
23
+ for field in fields:
24
+ print(cls, field.name, repr(field.type))
25
+ if isinstance(field.type, str):
26
+ field.type = real_annotations[field.name]
27
+
28
+ cls.__ctype__ = to_format_string([field.type for field in fields])
29
+ # print(cls.__ctype__)
30
+ cls.__cfields__ = tuple(fields) # type: tuple[dataclasses.Field, ...]
31
+ cls.__cstruct__ = struct.Struct(endian + cls.__ctype__) # 构造 Struct 类型
32
+ cls.__cendian__ = endian
33
+
34
+ cls.pack, cls.pack_into = pack.create_pack_methods(cls)
35
+ cls.unpack, cls.update, cls.unpack_from, cls.update_from, cls.iter_unpack, cls.iter_update = (
36
+ unpack.create_unpack_methods(cls)
37
+ )
38
+
39
+ return cls
40
+
41
+ return decorator(maybe_cls) if maybe_cls is not None else decorator
obstruct/methods.py ADDED
@@ -0,0 +1,49 @@
1
+ import struct
2
+ from collections.abc import Buffer
3
+ from typing import Any, Sequence, Iterator, get_args, get_origin
4
+
5
+ from typing_inspection.typing_objects import is_annotated
6
+
7
+ from .struct_type import CType
8
+
9
+ mapping = {
10
+ int: 'i', float: 'f', bool: '?'
11
+ }
12
+
13
+ CTypesFields = Sequence[str | CType | type] | Iterator[str | CType | type]
14
+
15
+
16
+ def get_ctype(tp):
17
+ if isinstance(tp, str):
18
+ return tp
19
+ elif _ := getattr(tp, '__ctype__', None):
20
+ return _
21
+ elif _ := mapping.get(tp):
22
+ return _
23
+ elif is_annotated(get_origin(tp)):
24
+ # 目前需要 Annotated 的类型只有 array
25
+ tp_args = get_args(tp)
26
+ dtype = get_args(tp_args[0])[0]
27
+ length = tp_args[1]
28
+ c_size = struct.calcsize(get_ctype(dtype))
29
+ return f'{length * c_size}s'
30
+ else:
31
+ raise ValueError(f'Unrecognized type: {tp}')
32
+
33
+
34
+ def to_format_string(fields: CTypesFields):
35
+ format = ''.join(
36
+ map(
37
+ get_ctype,
38
+ fields
39
+ )
40
+ )
41
+ return format
42
+
43
+
44
+ def pack(format: CTypesFields, *data: Any) -> bytes:
45
+ return struct.pack(to_format_string(format), *data)
46
+
47
+
48
+ def unpack(format: CTypesFields, data: Buffer):
49
+ return struct.unpack(to_format_string(format), data)
@@ -0,0 +1,52 @@
1
+ import dataclasses
2
+ import struct
3
+ import typing
4
+ from collections.abc import Buffer
5
+ from typing import Self, Generator, Any, ClassVar
6
+
7
+
8
+ class CType(typing.Protocol):
9
+ __ctype__: str
10
+
11
+
12
+ class Struct(CType):
13
+ """
14
+ 代表C语言结构体的Python类。
15
+ 提供了将结构体对象与字节序列之间互相转换的功能,
16
+ 包括打包、解包、更新等操作。
17
+ """
18
+
19
+ __cfields__: ClassVar[tuple[dataclasses.Field]]
20
+ __cstruct__: ClassVar[struct.Struct]
21
+ __cendian__: ClassVar[str]
22
+
23
+ def pack(self) -> bytes:
24
+ ...
25
+
26
+ def pack_into(self, buffer: Buffer, offset=0) -> None:
27
+ ...
28
+
29
+ @classmethod
30
+ def unpack(cls, buffer) -> Self:
31
+ ...
32
+
33
+ @classmethod
34
+ def unpack_from(cls, buffer, offset=0) -> Self:
35
+ ...
36
+
37
+ @classmethod
38
+ def iter_unpack(cls, buffer) -> Generator[Self, None, None]:
39
+ ...
40
+
41
+ @classmethod
42
+ def iter_update(cls, buffer) -> Generator[Self, None, None]:
43
+ ...
44
+
45
+ def update(self, buffer: Buffer) -> None:
46
+ ...
47
+
48
+ def update_from(self, buffer: Buffer, offset=0) -> None:
49
+ ...
50
+
51
+ def to_dict(self) -> dict[str, Any]:
52
+ return dataclasses.asdict(self)
@@ -0,0 +1 @@
1
+ from .types import *
obstruct/types/base.py ADDED
@@ -0,0 +1,6 @@
1
+ def make_struct_type[T](name, pytype: type[T], ctype) -> type[T]:
2
+ return type(
3
+ name, (pytype,), {
4
+ '__ctype__': ctype
5
+ }
6
+ )
@@ -0,0 +1,101 @@
1
+ import builtins
2
+ import sys
3
+ from functools import lru_cache
4
+
5
+ from .base import make_struct_type
6
+
7
+
8
+ @lru_cache(maxsize=32)
9
+ def pad(size: int):
10
+ return make_struct_type(
11
+ f'pad_{size}', builtins.bytes, f'{size}x'
12
+ )
13
+
14
+
15
+ char = make_struct_type(
16
+ 'char', builtins.bytes, 'c'
17
+ )
18
+
19
+ signed_char = make_struct_type(
20
+ 'signed_char', builtins.int, 'b'
21
+ )
22
+
23
+ unsigned_char = make_struct_type(
24
+ 'unsigned_char', builtins.int, 'B'
25
+ )
26
+
27
+ # bool = make_struct_type(
28
+ # 'bool', builtins.int, '?'
29
+ # ) # 布尔类型不能添加子类,也不允许添加新的类属性,只能特殊处理
30
+
31
+ short = make_struct_type(
32
+ 'short', builtins.int, 'h'
33
+ )
34
+
35
+ unsigned_short = make_struct_type(
36
+ 'unsigned_short', builtins.int, 'H'
37
+ )
38
+
39
+ unsigned_int = make_struct_type(
40
+ 'unsigned_int', builtins.int, 'I'
41
+ )
42
+
43
+ long = make_struct_type(
44
+ 'long', builtins.int, 'l'
45
+ )
46
+
47
+ unsigned_long = make_struct_type(
48
+ 'unsigned_long', builtins.int, 'L'
49
+ )
50
+
51
+ long_long = make_struct_type(
52
+ 'long_long', builtins.int, 'q'
53
+ )
54
+
55
+ unsigned_long_long = make_struct_type(
56
+ 'unsigned_long_long', builtins.int, 'Q'
57
+ )
58
+
59
+ double = make_struct_type(
60
+ 'double', builtins.float, 'd'
61
+ )
62
+
63
+ short_float = make_struct_type(
64
+ 'short_float', builtins.float, 'e'
65
+ )
66
+
67
+ size_t = make_struct_type(
68
+ 'size_t', builtins.int, 'n'
69
+ )
70
+
71
+ ssize_t = make_struct_type(
72
+ 'ssize_t', builtins.int, 'N'
73
+ )
74
+
75
+
76
+ # string (char[]), length must be specified when used
77
+ @lru_cache(maxsize=32)
78
+ def string(length):
79
+ return make_struct_type(f'string_{length}', builtins.bytes, f'{length}s')
80
+
81
+
82
+ # pascal string (length-prefixed)
83
+ @lru_cache(maxsize=32)
84
+ def pascal_string(length):
85
+ return make_struct_type(f'pascal_string_{length}', builtins.bytes, f'{length}p')
86
+
87
+
88
+ # pointer-sized integer (platform dependent)
89
+ pointer = make_struct_type(
90
+ 'pointer', builtins.int, 'P'
91
+ )
92
+
93
+ # >= 3.14
94
+ if sys.version_info.major >= 3 and sys.version_info.minor >= 14:
95
+ float_complex = make_struct_type(
96
+ 'complex', builtins.complex, 'F'
97
+ )
98
+
99
+ double_complex = make_struct_type(
100
+ 'double_complex', builtins.complex, 'D'
101
+ )
@@ -0,0 +1,67 @@
1
+ import sys
2
+ from typing import Literal, Sequence
3
+
4
+
5
+ class _ctype:
6
+ __ctype__: str
7
+
8
+
9
+ def pad(size: int) -> type: ...
10
+
11
+
12
+ class char(bytes, _ctype): ...
13
+
14
+
15
+ class signed_char(int, _ctype): ...
16
+
17
+
18
+ class unsigned_char(int, _ctype): ...
19
+
20
+
21
+ class long(int, _ctype): ...
22
+
23
+
24
+ class unsigned_long(int, _ctype): ...
25
+
26
+
27
+ class short(int, _ctype): ...
28
+
29
+
30
+ class unsigned_short(int, _ctype): ...
31
+
32
+
33
+ class unsigned_int(int, _ctype): ...
34
+
35
+
36
+ class long_long(int, _ctype): ...
37
+
38
+
39
+ class unsigned_long_long(int, _ctype): ...
40
+
41
+
42
+ class double(float, _ctype): ...
43
+
44
+
45
+ class short_float(float, _ctype): ...
46
+
47
+
48
+ class size_t(int, _ctype): ...
49
+
50
+
51
+ class ssize_t(int, _ctype): ...
52
+
53
+
54
+ def string(length: int) -> type[bytes]: ...
55
+
56
+
57
+ def pascal_string(length: int) -> type[bytes]: ...
58
+
59
+
60
+ class pointer(int, _ctype): ...
61
+
62
+
63
+ if sys.version_info >= (3, 14, 0):
64
+ class float_complex(complex, _ctype): ...
65
+
66
+
67
+ class double_complex(complex, _ctype): ...
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.5
2
+ Name: obstruct
3
+ Version: 0.2.1
4
+ Author-email: LittleNightSong <LittleNightSongYO@outlook.com>
5
+ Classifier: Development Status :: 3 - Alpha
6
+ Classifier: Programming Language :: Python
7
+ Classifier: Programming Language :: Python :: 3.11
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: Implementation :: CPython
11
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
12
+ Requires-Python: >=3.12
@@ -0,0 +1,17 @@
1
+ obstruct/__init__.py,sha256=zM70fUvOP_Ne_9iAQQBRyST5GGag3yYNis4VAhQLWE4,121
2
+ obstruct/__version__.py,sha256=S7TnOI7A9GMGYPhpcWZGb5luwiex3yTP5q-e9kmMZtw,19
3
+ obstruct/decorator.py,sha256=D7KFh6tfLm-yAE1zhr5aqnyEVNPSOILM9hDguoUCYpI,1389
4
+ obstruct/methods.py,sha256=UIUuvypBo7_SYPqQyknKootLqF-ggYGG39v5rZqvOzU,1278
5
+ obstruct/struct_type.py,sha256=zxByrMqiKkH-i8VCpGM7jQdOyFzKo_edwrDq2RkNj2U,1233
6
+ obstruct/aot/__init__.py,sha256=xj1UpN3cEyramzthzMILYwVWwkFmaAC2OiYxfDsKjk8,52
7
+ obstruct/aot/aot.py,sha256=b1eOn14uy4OTMsjW7T8f-0Lk1vLsVd2dBPGNbWc253s,82
8
+ obstruct/aot/base.py,sha256=L1UpXowWVLaIE3iChvyUdCP6lQx7XC2Qp62QeqziVL0,4115
9
+ obstruct/aot/pack.py,sha256=IfSKYYA9n3n-ZTsnaxgcJFepDR_5rgKmKZO60k3-jVg,2550
10
+ obstruct/aot/unpack.py,sha256=FHHVgJp4wkcnicEg7ZJ2KZ0GVxcSq94_xXgRN2wCRgU,5698
11
+ obstruct/types/__init__.py,sha256=DGan05iQmXP3zYxpjkpv0DAClg41oBCF6WFZD1wSGDE,22
12
+ obstruct/types/base.py,sha256=IZr2JIEtm5I3zxI5JcGM8X5_bZfTcxHyX8YfqPmNRMI,163
13
+ obstruct/types/types.py,sha256=M9M7BCqQZswyHdxEXkOZtt2_lBv96HuicawrOH-o254,2167
14
+ obstruct/types/types.pyi,sha256=p51EY4JjOVQ3HYUQvI0bZCmY9S5BFZO8JXQXCXnUh5A,966
15
+ obstruct-0.2.1.dist-info/METADATA,sha256=U74R9PFbQ_sLLlRHPH_h6EgTt-zpLFgNMx7P80pHszI,519
16
+ obstruct-0.2.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
17
+ obstruct-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any