bethkit 1.0.0__py3-none-win_amd64.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.
@@ -0,0 +1,522 @@
1
+ """
2
+ Copyright (c) Modding Forge
3
+ """
4
+ from __future__ import annotations
5
+
6
+ import ctypes
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ from .. import _ffi
11
+ from .._error import BethkitClosedError
12
+ from ..enums import StringFileKind
13
+
14
+
15
+ def _buf_from_bytes(data: bytes) -> ctypes.Array[ctypes.c_uint8]:
16
+ """
17
+ Wrap *data* in a ctypes ``c_uint8`` array for FFI calls.
18
+
19
+ Args:
20
+ data (bytes): Byte sequence to wrap.
21
+
22
+ Returns:
23
+ ctypes.Array: A ``c_uint8`` array backed by a copy of *data*.
24
+ """
25
+
26
+ return (ctypes.c_uint8 * len(data)).from_buffer_copy(data)
27
+
28
+
29
+ class StringTable:
30
+ """
31
+ A Bethesda string localisation table (``.STRINGS``, ``.DLSTRINGS``,
32
+ or ``.ILSTRINGS`` files).
33
+
34
+ String tables map numeric IDs to UTF-8 string payloads. They can be
35
+ loaded from disk with :meth:`open`, or created fresh with :meth:`new`
36
+ and written back with :meth:`write_to_file`.
37
+
38
+ Use as a context manager to guarantee that the native handle is
39
+ freed::
40
+
41
+ with StringTable.open(path) as tbl:
42
+ text = tbl.get_str(0x0001)
43
+ """
44
+
45
+ __ptr: int
46
+
47
+ def __init__(self, ptr: int) -> None:
48
+ """
49
+ Args:
50
+ ptr (int): Native handle returned by the FFI open/new call.
51
+ """
52
+
53
+ self.__ptr = ptr
54
+
55
+ def __check_open(self) -> int:
56
+ """
57
+ Return the native pointer, raising if the handle has been closed.
58
+
59
+ Returns:
60
+ int: Valid native pointer.
61
+
62
+ Raises:
63
+ BethkitClosedError: If the table has been closed.
64
+ """
65
+
66
+ if not self.__ptr:
67
+ raise BethkitClosedError("StringTable is closed")
68
+ return self.__ptr
69
+
70
+ @classmethod
71
+ def new(cls, kind: StringFileKind) -> StringTable:
72
+ """
73
+ Create an empty string table of the given kind.
74
+
75
+ Args:
76
+ kind (StringFileKind): Type of string file to create.
77
+
78
+ Returns:
79
+ StringTable: A new, empty ``StringTable``.
80
+
81
+ Raises:
82
+ BethkitNativeError: If the native table cannot be created.
83
+ """
84
+
85
+ lib = _ffi.load_lib()
86
+ ptr = lib.bethkit_string_table_new(int(kind))
87
+ if not ptr:
88
+ _ffi.raise_last_error(lib)
89
+ return cls(ptr)
90
+
91
+ @classmethod
92
+ def open(cls, path: Path) -> StringTable:
93
+ """
94
+ Open a string table file from disk.
95
+
96
+ Args:
97
+ path (Path): Filesystem path to the string file.
98
+
99
+ Returns:
100
+ StringTable: A new ``StringTable`` loaded from *path*.
101
+
102
+ Raises:
103
+ BethkitNativeError: If the file cannot be opened or parsed.
104
+ """
105
+
106
+ lib = _ffi.load_lib()
107
+ ptr = lib.bethkit_string_table_open(_ffi.enc(path))
108
+ if not ptr:
109
+ _ffi.raise_last_error(lib)
110
+ return cls(ptr)
111
+
112
+ def close(self) -> None:
113
+ """
114
+ Release the native string-table handle.
115
+
116
+ Safe to call multiple times; subsequent calls are no-ops.
117
+ """
118
+
119
+ if self.__ptr:
120
+ _ffi.load_lib().bethkit_string_table_free(self.__ptr)
121
+ self.__ptr = 0
122
+
123
+ def __enter__(self) -> StringTable:
124
+ """Return *self* for use as a context manager."""
125
+
126
+ return self
127
+
128
+ def __exit__(self, *_: object) -> None:
129
+ """Free the table when exiting the context."""
130
+
131
+ self.close()
132
+
133
+ def __del__(self) -> None:
134
+ """Free the native handle on garbage collection."""
135
+
136
+ try:
137
+ self.close()
138
+ except Exception:
139
+ pass
140
+
141
+ @property
142
+ def kind(self) -> StringFileKind:
143
+ """
144
+ The string-file format of this table.
145
+
146
+ Returns:
147
+ StringFileKind: ``STRINGS``, ``DL_STRINGS``, or
148
+ ``IL_STRINGS``.
149
+
150
+ Raises:
151
+ BethkitClosedError: If the table has been closed.
152
+ """
153
+
154
+ return StringFileKind(
155
+ _ffi.load_lib().bethkit_string_table_kind(self.__check_open())
156
+ )
157
+
158
+ def __len__(self) -> int:
159
+ """
160
+ Returns:
161
+ int: Number of entries in the table.
162
+ """
163
+
164
+ return _ffi.load_lib().bethkit_string_table_len(
165
+ self.__check_open()
166
+ )
167
+
168
+ def get(self, id: int) -> Optional[bytes]:
169
+ """
170
+ Retrieve a string entry as raw bytes by its ID.
171
+
172
+ Args:
173
+ id (int): Numeric string ID.
174
+
175
+ Returns:
176
+ Optional[bytes]: Raw string bytes, or ``None`` if not found.
177
+
178
+ Raises:
179
+ BethkitClosedError: If the table has been closed.
180
+ """
181
+
182
+ lib = _ffi.load_lib()
183
+ out_len = ctypes.c_size_t(0)
184
+ ptr = lib.bethkit_string_table_get(
185
+ self.__check_open(), id, ctypes.byref(out_len)
186
+ )
187
+ if not ptr:
188
+ return None
189
+ return bytes(ctypes.string_at(ptr, out_len.value))
190
+
191
+ def get_str(self, id: int) -> Optional[str]:
192
+ """
193
+ Retrieve a string entry decoded as UTF-8 by its ID.
194
+
195
+ Args:
196
+ id (int): Numeric string ID.
197
+
198
+ Returns:
199
+ Optional[str]: Decoded string without trailing null, or
200
+ ``None`` if not found.
201
+ """
202
+
203
+ raw = self.get(id)
204
+ if raw is None:
205
+ return None
206
+ return raw.rstrip(b"\x00").decode("utf-8")
207
+
208
+ def insert(self, id: int, data: bytes) -> None:
209
+ """
210
+ Insert or overwrite an entry with the given ID.
211
+
212
+ Args:
213
+ id (int): Numeric string ID.
214
+ data (bytes): String payload (may include trailing null).
215
+
216
+ Raises:
217
+ BethkitClosedError: If the table has been closed.
218
+ BethkitNativeError: If the native call fails.
219
+ """
220
+
221
+ lib = _ffi.load_lib()
222
+ buf = _buf_from_bytes(data)
223
+ if lib.bethkit_string_table_insert(
224
+ self.__check_open(), id, buf, len(data)
225
+ ) != 0:
226
+ _ffi.raise_last_error(lib)
227
+
228
+ def insert_new(self, data: bytes) -> int:
229
+ """
230
+ Insert a new entry and return the auto-assigned ID.
231
+
232
+ Args:
233
+ data (bytes): String payload.
234
+
235
+ Returns:
236
+ int: The ID assigned to the new entry.
237
+
238
+ Raises:
239
+ BethkitClosedError: If the table has been closed.
240
+ BethkitNativeError: If the native call fails.
241
+ """
242
+
243
+ lib = _ffi.load_lib()
244
+ buf = _buf_from_bytes(data)
245
+ out_id = ctypes.c_uint32(0)
246
+ if (
247
+ lib.bethkit_string_table_insert_new(
248
+ self.__check_open(), buf, len(data), ctypes.byref(out_id)
249
+ )
250
+ != 0
251
+ ):
252
+ _ffi.raise_last_error(lib)
253
+ return out_id.value
254
+
255
+ def remove(self, id: int) -> bool:
256
+ """
257
+ Remove the entry with the given ID.
258
+
259
+ Args:
260
+ id (int): Numeric string ID to remove.
261
+
262
+ Returns:
263
+ bool: ``True`` if the entry existed and was removed.
264
+ """
265
+
266
+ return bool(
267
+ _ffi.load_lib().bethkit_string_table_remove(
268
+ self.__check_open(), id
269
+ )
270
+ )
271
+
272
+ def write_to_file(self, path: Path) -> None:
273
+ """
274
+ Serialise the table and write it to *path* on disk.
275
+
276
+ Args:
277
+ path (Path): Destination file path.
278
+
279
+ Raises:
280
+ BethkitClosedError: If the table has been closed.
281
+ BethkitNativeError: If serialisation or the write fails.
282
+ """
283
+
284
+ lib = _ffi.load_lib()
285
+ if lib.bethkit_string_table_write_to_file(
286
+ self.__check_open(), _ffi.enc(path)
287
+ ) != 0:
288
+ _ffi.raise_last_error(lib)
289
+
290
+ def __repr__(self) -> str:
291
+ """
292
+ Returns:
293
+ str: Developer-friendly representation with kind and entry
294
+ count.
295
+ """
296
+
297
+ try:
298
+ return f"<StringTable kind={self.kind.name} len={len(self)}>"
299
+ except BethkitClosedError:
300
+ return "<StringTable closed>"
301
+
302
+
303
+ class LocalizationSet:
304
+ """
305
+ A combined set of all three localisation tables for a single plugin.
306
+
307
+ A :class:`LocalizationSet` bundles the ``.STRINGS``, ``.DLSTRINGS``,
308
+ and ``.ILSTRINGS`` files for a given language. Load them together
309
+ with :meth:`open`, or create an empty set with :meth:`new` and
310
+ populate it manually.
311
+ """
312
+
313
+ __ptr: int
314
+
315
+ def __init__(self, ptr: int) -> None:
316
+ """
317
+ Args:
318
+ ptr (int): Native handle returned by the FFI open/new call.
319
+ """
320
+
321
+ self.__ptr = ptr
322
+
323
+ def __check_open(self) -> int:
324
+ """
325
+ Return the native pointer, raising if the handle has been closed.
326
+
327
+ Returns:
328
+ int: Valid native pointer.
329
+
330
+ Raises:
331
+ BethkitClosedError: If the set has been closed.
332
+ """
333
+
334
+ if not self.__ptr:
335
+ raise BethkitClosedError("LocalizationSet is closed")
336
+ return self.__ptr
337
+
338
+ @classmethod
339
+ def new(cls) -> LocalizationSet:
340
+ """
341
+ Create an empty localisation set.
342
+
343
+ Returns:
344
+ LocalizationSet: A new, empty set with no strings loaded.
345
+
346
+ Raises:
347
+ BethkitNativeError: If the native set cannot be created.
348
+ """
349
+
350
+ lib = _ffi.load_lib()
351
+ ptr = lib.bethkit_localization_set_new()
352
+ if not ptr:
353
+ _ffi.raise_last_error(lib)
354
+ return cls(ptr)
355
+
356
+ @classmethod
357
+ def open(
358
+ cls, plugin_path: Path, language: str
359
+ ) -> LocalizationSet:
360
+ """
361
+ Load all localisation files for the given plugin and language.
362
+
363
+ The method looks for ``<plugin_stem>_<language>.STRINGS`` and
364
+ sibling ``.DLSTRINGS`` / ``.ILSTRINGS`` files next to the plugin.
365
+
366
+ Args:
367
+ plugin_path (Path): Filesystem path to the plugin file.
368
+ language (str): BCP 47-style language code
369
+ (e.g. ``"english"``).
370
+
371
+ Returns:
372
+ LocalizationSet: The loaded set.
373
+
374
+ Raises:
375
+ BethkitNativeError: If any required string file cannot be
376
+ opened.
377
+ """
378
+
379
+ lib = _ffi.load_lib()
380
+ ptr = lib.bethkit_localization_set_open(
381
+ _ffi.enc(plugin_path), _ffi.senc(language)
382
+ )
383
+ if not ptr:
384
+ _ffi.raise_last_error(lib)
385
+ return cls(ptr)
386
+
387
+ def close(self) -> None:
388
+ """
389
+ Release the native localisation-set handle.
390
+
391
+ Safe to call multiple times; subsequent calls are no-ops.
392
+ """
393
+
394
+ if self.__ptr:
395
+ _ffi.load_lib().bethkit_localization_set_free(self.__ptr)
396
+ self.__ptr = 0
397
+
398
+ def __enter__(self) -> LocalizationSet:
399
+ """Return *self* for use as a context manager."""
400
+
401
+ return self
402
+
403
+ def __exit__(self, *_: object) -> None:
404
+ """Free the set when exiting the context."""
405
+
406
+ self.close()
407
+
408
+ def __del__(self) -> None:
409
+ """Free the native handle on garbage collection."""
410
+
411
+ try:
412
+ self.close()
413
+ except Exception:
414
+ pass
415
+
416
+ def get(
417
+ self, kind: StringFileKind, id: int
418
+ ) -> Optional[bytes]:
419
+ """
420
+ Retrieve a string from the specified sub-table by its ID.
421
+
422
+ Args:
423
+ kind (StringFileKind): Which sub-table to query.
424
+ id (int): Numeric string ID.
425
+
426
+ Returns:
427
+ Optional[bytes]: Raw string bytes, or ``None`` if not found.
428
+
429
+ Raises:
430
+ BethkitClosedError: If the set has been closed.
431
+ """
432
+
433
+ lib = _ffi.load_lib()
434
+ out_len = ctypes.c_size_t(0)
435
+ ptr = lib.bethkit_localization_set_get(
436
+ self.__check_open(), int(kind), id, ctypes.byref(out_len)
437
+ )
438
+ if not ptr:
439
+ return None
440
+ return bytes(ctypes.string_at(ptr, out_len.value))
441
+
442
+ def get_str(
443
+ self, kind: StringFileKind, id: int
444
+ ) -> Optional[str]:
445
+ """
446
+ Retrieve a string from the specified sub-table decoded as UTF-8.
447
+
448
+ Args:
449
+ kind (StringFileKind): Which sub-table to query.
450
+ id (int): Numeric string ID.
451
+
452
+ Returns:
453
+ Optional[str]: Decoded string without trailing null, or
454
+ ``None`` if not found.
455
+ """
456
+
457
+ raw = self.get(kind, id)
458
+ if raw is None:
459
+ return None
460
+ return raw.rstrip(b"\x00").decode("utf-8")
461
+
462
+ def set(
463
+ self, kind: StringFileKind, id: int, data: bytes
464
+ ) -> None:
465
+ """
466
+ Insert or overwrite an entry in the specified sub-table.
467
+
468
+ Args:
469
+ kind (StringFileKind): Which sub-table to modify.
470
+ id (int): Numeric string ID.
471
+ data (bytes): String payload.
472
+
473
+ Raises:
474
+ BethkitClosedError: If the set has been closed.
475
+ BethkitNativeError: If the native call fails.
476
+ """
477
+
478
+ lib = _ffi.load_lib()
479
+ buf = _buf_from_bytes(data)
480
+ if (
481
+ lib.bethkit_localization_set_set(
482
+ self.__check_open(), int(kind), id, buf, len(data)
483
+ )
484
+ != 0
485
+ ):
486
+ _ffi.raise_last_error(lib)
487
+
488
+ def write(
489
+ self, plugin_path: Path, language: str
490
+ ) -> None:
491
+ """
492
+ Write all sub-tables to disk next to *plugin_path*.
493
+
494
+ Args:
495
+ plugin_path (Path): Path to the plugin file whose name is
496
+ used to derive the output file names.
497
+ language (str): BCP 47-style language code
498
+ (e.g. ``"english"``).
499
+
500
+ Raises:
501
+ BethkitClosedError: If the set has been closed.
502
+ BethkitNativeError: If any write fails.
503
+ """
504
+
505
+ lib = _ffi.load_lib()
506
+ if (
507
+ lib.bethkit_localization_set_write(
508
+ self.__check_open(),
509
+ _ffi.enc(plugin_path),
510
+ _ffi.senc(language),
511
+ )
512
+ != 0
513
+ ):
514
+ _ffi.raise_last_error(lib)
515
+
516
+ def __repr__(self) -> str:
517
+ """
518
+ Returns:
519
+ str: Developer-friendly representation of the set.
520
+ """
521
+
522
+ return "<LocalizationSet>"
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: bethkit
3
+ Version: 1.0.0
4
+ Summary: Python bindings for bethkit — a Bethesda plugin and archive toolkit
5
+ Project-URL: Homepage, https://moddingforge.com/
6
+ Project-URL: Repository, https://github.com/Modding-Forge/bethkit.py
7
+ Author-email: Modding Forge <info@moddingforge.com>
8
+ License: Apache-2.0
9
+ License-File: LICENSE
10
+ Keywords: ba2,bethesda,bsa,esm,esp,fallout,modding,skyrim
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Games/Entertainment
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: pydantic>=2.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pyright; extra == 'dev'
25
+ Requires-Dist: pytest-cov; extra == 'dev'
26
+ Requires-Dist: pytest-mock; extra == 'dev'
27
+ Requires-Dist: pytest>=7; extra == 'dev'
28
+ Requires-Dist: ruff; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # bethkit.py
32
+
33
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE) [![Python](https://img.shields.io/badge/python-3.10%2B-blue?logo=python)](https://www.python.org) [![PyPI](https://img.shields.io/pypi/v/bethkit?logo=pypi&logoColor=white)](https://pypi.org/project/bethkit/)
34
+
35
+ Python bindings for [bethkit](https://github.com/Modding-Forge/bethkit) - a fast Rust library for reading and writing Bethesda game plugin and archive files. `bethkit.py` wraps the `bethkit_ffi` C ABI via `ctypes`; no compiler or build tools required.
36
+
37
+ ## Features
38
+
39
+ - **Plugin reading** - open `.esp`/`.esm`/`.esl` files by path or from in-memory bytes; iterate groups and records; look up records by FormID or EditorID; inspect sub-records
40
+ - **Plugin writing** - build new plugins from scratch with `PluginWriter`, `WritableGroup`, and `WritableRecord`
41
+ - **BSA / BA2 archives** - open and extract entries from BSA (TES4/SSE) and BA2 (GNRL/DX10) archives; write new archives with `BsaWriter`, `Ba2GnrlWriter`, and `Ba2Dx10Writer`
42
+ - **String tables** - read, edit, and write `.STRINGS`/`.DLSTRINGS`/`.ILSTRINGS` localisation files; apply translation sets with `LocalizationSet`
43
+ - **Schema** - decode sub-records into typed `FieldValue` variants (integers, floats, FormIDs, enums with resolved names, flags, structs, arrays) via `RecordView` and `SchemaRegistry`
44
+ - **Load-order utilities** - `LoadOrder`, `GlobalFormId`, and `PluginCache` for winning-override lookups and EditorID search across multiple plugins
45
+
46
+ ## Requirements
47
+
48
+ | Requirement | Version |
49
+ | ------------ | -------- |
50
+ | Python | ≥ 3.10 |
51
+ | pydantic | ≥ 2.0 |
52
+ | bethkit\_ffi | matching |
53
+
54
+ Place `bethkit_ffi.dll` (Windows), `libbethkit_ffi.so` (Linux), or `libbethkit_ffi.dylib` (macOS) next to the package, or set the `BETHKIT_LIB` environment variable to the full path of the library.
55
+
56
+ ## Installation
57
+
58
+ ```sh
59
+ uv add bethkit
60
+ ```
61
+
62
+ or
63
+
64
+ ```sh
65
+ pip install bethkit
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ ### Reading a plugin
71
+
72
+ ```python
73
+ from pathlib import Path
74
+ from bethkit import Plugin, Game
75
+
76
+ with Plugin.open(Path("Ordinator - Perks of Skyrim.esp"), Game.SKYRIM_SE) as plugin:
77
+ print("Masters:", plugin.masters)
78
+ print("Kind:", plugin.kind)
79
+
80
+ for group in plugin:
81
+ for child in group:
82
+ if hasattr(child, "form_id"):
83
+ print(f" 0x{child.form_id:08X} {child.editor_id}")
84
+ ```
85
+
86
+ ### Building a plugin from scratch
87
+
88
+ ```python
89
+ from pathlib import Path
90
+ from bethkit import Game, PluginWriter, WritableGroup, WritableRecord
91
+
92
+ with PluginWriter(Game.SKYRIM_SE) as writer:
93
+ with WritableGroup.new(b"NPC_") as group:
94
+ rec = WritableRecord.new(b"NPC_", form_id=0x000D62)
95
+ rec.add_subrecord(b"EDID", b"MyNPC\x00")
96
+ group.add_record(rec)
97
+ writer.add_group(group)
98
+ writer.write_to_file(Path("MyMod.esp"))
99
+ ```
100
+
101
+ ### Extracting from an archive
102
+
103
+ ```python
104
+ from pathlib import Path
105
+ from bethkit import Archive
106
+
107
+ with Archive.open(Path("Skyrim - Meshes.bsa")) as arc:
108
+ data = arc.extract("meshes/actors/character/character assets/skeleton.nif")
109
+ if data:
110
+ Path("skeleton.nif").write_bytes(data)
111
+ ```
112
+
113
+ ### Load-order and FormID resolution
114
+
115
+ ```python
116
+ from pathlib import Path
117
+ from bethkit import Game, Plugin, PluginCache, PluginKind, LoadOrder
118
+
119
+ lo = LoadOrder()
120
+ lo.push("Skyrim.esm", PluginKind.FULL)
121
+ lo.push("MyMod.esp", PluginKind.FULL)
122
+
123
+ cache = PluginCache()
124
+ cache.add("Skyrim.esm", Plugin.open(Path("Skyrim.esm"), Game.SKYRIM_SE))
125
+
126
+ hit = cache.find_by_editor_id("ArmorIronCuirass")
127
+ if hit:
128
+ print(hit.global_form_id) # Skyrim.esm:0x012E49
129
+ print(hit.record.editor_id)
130
+ ```
131
+
132
+ ## Development
133
+
134
+ ```sh
135
+ git clone https://github.com/Modding-Forge/bethkit.py
136
+ cd bethkit.py
137
+ uv sync --extra dev
138
+ uv run pytest
139
+ ```
140
+
141
+ Linting and type-checking:
142
+
143
+ ```sh
144
+ uv tool run ruff check src/ tests/
145
+ uv tool run pyright src/ tests/
146
+ ```
147
+
148
+ ## Related projects
149
+
150
+ - [bethkit](https://github.com/Modding-Forge/bethkit) - the underlying Rust library; `bethkit.py` wraps its C ABI
151
+ - [SSE-Auto-Translator](https://github.com/Modding-Forge/SSE-Auto-Translator) - uses `bethkit.py` to patch localised strings
152
+
153
+ ## License
154
+
155
+ Apache-2.0 - see [LICENSE](LICENSE).
@@ -0,0 +1,22 @@
1
+ bethkit/__init__.py,sha256=T6x3LQzo8hV1KcHIoXckWqWxNAgqhFuewcYLqS6zmik,2440
2
+ bethkit/_error.py,sha256=-7fHK_bNxlQTXW-9W6a8b2_6df_mF9vUAwy4PTKsCTU,2498
3
+ bethkit/enums.py,sha256=6yhMv13mCuEenz3hoXNGNFny8JUvvYdLr0CuNg3LSZs,3059
4
+ bethkit/load_order.py,sha256=6S3W-GKGvxPm_7UpnV2aOsynRLMJvmVyljifizvHp9g,5550
5
+ bethkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ bethkit/_ffi/__init__.py,sha256=Wzsh11GwBnOX8ryruuJg2bTMVL-UdgMhidsEuQdlips,905
7
+ bethkit/_ffi/_loader.py,sha256=ehI8DwiUJwDNDPPEj4ewrKu-kbiGbZH_enbuIHiieNc,18508
8
+ bethkit/_ffi/_types.py,sha256=hHPTH8ADrh2-RMwhyGzT0MpZCRZdHCA4L-Ispm1R9ZI,3052
9
+ bethkit/archive/__init__.py,sha256=i41QKSXbSoExxnjO33hu89fj9UKJpyWj6e4iu3fi1Ac,352
10
+ bethkit/archive/archive.py,sha256=4qJkOPKrNOoj6rY_NnbVmg9aMchVhr91hnCZB604I7k,19135
11
+ bethkit/plugin/__init__.py,sha256=XrNzsbXdGYEJ8EUCa143gHX-Vl8KIADgAoqZx4dAZYo,499
12
+ bethkit/plugin/cache.py,sha256=b-KWKeDf26iVVBtlwzu4_zC-6pb6iv4jrETYMgTcU4k,7423
13
+ bethkit/plugin/plugin.py,sha256=C0rPNF5nDABt9KEIafcvYU_XdovX6s2_u1_d0h8jARY,22439
14
+ bethkit/plugin/writer.py,sha256=nkZAitR-AvVRnDIwnVS6nNrWWtUmgrj-7HSnI55lkQA,14571
15
+ bethkit/schema/__init__.py,sha256=XZKez2ApXIaioy6pd3AAZoCujjlf_8QIjrFXOxYEOQc,456
16
+ bethkit/schema/schema.py,sha256=q8MksAXDdfH7hvw6GNOI8u5aqxBDb3YCoOU5wA0z50E,13238
17
+ bethkit/strings/__init__.py,sha256=NK6bWOX2txWMWfd3vE9aT7JYhT-AfNaNVttS-u2OvPs,273
18
+ bethkit/strings/strings.py,sha256=8PMaC_W0m4y20xhMdeFd5upDmnfIHuJ4jYzjdhKXsd0,14409
19
+ bethkit-1.0.0.dist-info/METADATA,sha256=vhR7PuHBFzAyG5LlYSrwFMIqVa1gAniFwYHmAsqPogQ,5383
20
+ bethkit-1.0.0.dist-info/WHEEL,sha256=y3FWwPRFPfRewdaamNJ2TA-_oj6MYMwqJXYgc-1N3nY,93
21
+ bethkit-1.0.0.dist-info/licenses/LICENSE,sha256=zpP_oxrFUIxGN0S1gDhVrmhk75Fytp74iwyh4dXNwGs,11617
22
+ bethkit-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-win_amd64