mscs 2.2.0__tar.gz → 2.3.0__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.
mscs-2.3.0/.gitignore ADDED
@@ -0,0 +1,207 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+ #poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ #pdm.lock
116
+ #pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ #pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # SageMath parsed files
135
+ *.sage.py
136
+
137
+ # Environments
138
+ .env
139
+ .envrc
140
+ .venv
141
+ env/
142
+ venv/
143
+ ENV/
144
+ env.bak/
145
+ venv.bak/
146
+
147
+ # Spyder project settings
148
+ .spyderproject
149
+ .spyproject
150
+
151
+ # Rope project settings
152
+ .ropeproject
153
+
154
+ # mkdocs documentation
155
+ /site
156
+
157
+ # mypy
158
+ .mypy_cache/
159
+ .dmypy.json
160
+ dmypy.json
161
+
162
+ # Pyre type checker
163
+ .pyre/
164
+
165
+ # pytype static type analyzer
166
+ .pytype/
167
+
168
+ # Cython debug symbols
169
+ cython_debug/
170
+
171
+ # PyCharm
172
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
175
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
176
+ #.idea/
177
+
178
+ # Abstra
179
+ # Abstra is an AI-powered process automation framework.
180
+ # Ignore directories containing user credentials, local state, and settings.
181
+ # Learn more at https://abstra.io/docs
182
+ .abstra/
183
+
184
+ # Visual Studio Code
185
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
188
+ # you could uncomment the following to ignore the entire vscode folder
189
+ # .vscode/
190
+
191
+ # Ruff stuff:
192
+ .ruff_cache/
193
+
194
+ # PyPI configuration file
195
+ .pypirc
196
+
197
+ # Cursor
198
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
199
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
200
+ # refer to https://docs.cursor.com/context/ignore-files
201
+ .cursorignore
202
+ .cursorindexingignore
203
+
204
+ # Marimo
205
+ marimo/_static/
206
+ marimo/_lsp/
207
+ __marimo__/
mscs-2.3.0/PKG-INFO ADDED
@@ -0,0 +1,315 @@
1
+ Metadata-Version: 2.4
2
+ Name: mscs
3
+ Version: 2.3.0
4
+ Summary: Safe, fast serialization for Python — a secure replacement for pickle with HMAC authentication, native numpy/PyTorch support.
5
+ Project-URL: Homepage, https://github.com/ElEscribanoSilente/MSC-Serial
6
+ Project-URL: Repository, https://github.com/ElEscribanoSilente/MSC-Serial
7
+ Project-URL: Issues, https://github.com/ElEscribanoSilente/MSC-Serial/issues
8
+ Project-URL: Changelog, https://github.com/ElEscribanoSilente/MSC-Serial/blob/main/CHANGELOG.md
9
+ Author: Esraderey
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: binary,checkpoint,fast,hmac,integrity,numpy,pickle,pytorch,safe,secure,serialization,tensor
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Classifier: Topic :: Security
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.9
29
+ Provides-Extra: all
30
+ Requires-Dist: numpy>=1.20; extra == 'all'
31
+ Requires-Dist: torch>=2.0; extra == 'all'
32
+ Provides-Extra: numpy
33
+ Requires-Dist: numpy>=1.20; extra == 'numpy'
34
+ Provides-Extra: test
35
+ Requires-Dist: hypothesis>=6.0; extra == 'test'
36
+ Requires-Dist: pytest>=7.0; extra == 'test'
37
+ Provides-Extra: torch
38
+ Requires-Dist: numpy>=1.20; extra == 'torch'
39
+ Requires-Dist: torch>=2.0; extra == 'torch'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # MSCS — Safe Serialization for Python
43
+
44
+ **v2.3.0** | [Changelog](CHANGELOG.md) | [PyPI](https://pypi.org/project/mscs/)
45
+
46
+ > **Status: Beta** — API is stable but the format may evolve. Not yet battle-tested in large-scale production.
47
+
48
+ A secure, fast, binary serialization library. Drop-in replacement for `pickle` that **does not execute arbitrary code** during deserialization of unregistered classes.
49
+
50
+ Built for AI/ML workflows — native support for **NumPy arrays** and **PyTorch tensors** with zero-copy performance.
51
+
52
+ ## Why not pickle?
53
+
54
+ ```python
55
+ # pickle: arbitrary code execution on load
56
+ data = pickle.loads(untrusted_bytes) # can run os.system("rm -rf /")
57
+
58
+ # mscs: only reconstructs explicitly registered classes
59
+ data = mscs.loads(untrusted_bytes) # MSCSecurityError if class not registered
60
+ ```
61
+
62
+ ## Comparison with Alternatives
63
+
64
+ | Feature | mscs | pickle | safetensors | torch.save |
65
+ |---------|------|--------|-------------|------------|
66
+ | No arbitrary code execution | Partial* | No | Yes | No |
67
+ | HMAC authentication | Yes | No | No | No |
68
+ | Custom class support | Yes (registry) | Yes | No | Yes |
69
+ | NumPy arrays | Yes | Yes | Yes | Yes |
70
+ | PyTorch tensors | Yes | Yes | Yes | Yes |
71
+ | Circular references | Yes | Yes | No | Yes |
72
+ | Zero dependencies | Yes | Yes | Yes (Rust) | No |
73
+ | Compression built-in | Yes (zlib) | No | No | No |
74
+
75
+ \* **mscs executes `__setstate__`** on registered classes. See [Security Model](#security-model) for details.
76
+
77
+ **When to use safetensors instead:** If you only need to serialize tensors and arrays (model weights, embeddings), [safetensors](https://github.com/huggingface/safetensors) is the industry standard — it's written in Rust, truly zero-code-execution, and widely adopted. Use mscs when you need to serialize **mixed Python objects** (configs, custom classes, nested structures) alongside tensors.
78
+
79
+ ## Install
80
+
81
+ ```bash
82
+ pip install mscs # core (no dependencies)
83
+ pip install mscs[numpy] # + numpy support
84
+ pip install mscs[torch] # + numpy + PyTorch tensor support
85
+ pip install mscs[all] # everything
86
+ ```
87
+
88
+ ## Quick Start
89
+
90
+ ```python
91
+ import mscs
92
+
93
+ # Primitives, collections, nested structures — just works
94
+ data = {"model": "v5.2", "lr": 0.001, "layers": [64, 128, 256]}
95
+ encoded = mscs.dumps(data)
96
+ decoded = mscs.loads(encoded)
97
+
98
+ # NumPy arrays
99
+ import numpy as np
100
+ arr = np.random.randn(100, 100).astype(np.float32)
101
+ encoded = mscs.dumps(arr)
102
+
103
+ # PyTorch tensors — no .numpy() conversion needed
104
+ import torch
105
+ weights = torch.randn(256, 256)
106
+ encoded = mscs.dumps(weights) # safe, no pickle involved
107
+
108
+ # Full model checkpoints
109
+ checkpoint = {
110
+ "epoch": 100,
111
+ "model_state": {k: v for k, v in model.state_dict().items()},
112
+ "optimizer_lr": 0.0003,
113
+ }
114
+ mscs.dump(checkpoint, open("checkpoint.mscs", "wb"))
115
+ restored = mscs.load(open("checkpoint.mscs", "rb"))
116
+ ```
117
+
118
+ ## Custom Classes
119
+
120
+ ```python
121
+ import mscs
122
+ from dataclasses import dataclass
123
+
124
+ @mscs.register
125
+ @dataclass
126
+ class Config:
127
+ state_size: int = 256
128
+ lr: float = 0.001
129
+
130
+ config = Config(512, 0.0003)
131
+ data = mscs.dumps(config)
132
+ restored = mscs.loads(data) # Config(state_size=512, lr=0.0003)
133
+
134
+ # Unregistered classes raise MSCSecurityError in strict mode
135
+ mscs.loads(data_with_unknown_class) # MSCSecurityError
136
+
137
+ # Or get a dict fallback in non-strict mode
138
+ mscs.loads(data_with_unknown_class, strict=False) # {'__class__': '...', '__state__': {...}}
139
+ ```
140
+
141
+ ### Backward Compatibility with Renamed Classes
142
+
143
+ ```python
144
+ mscs.register_alias("my_module.OldConfig", Config)
145
+ ```
146
+
147
+ ### Register All Classes in a Module
148
+
149
+ ```python
150
+ import my_models
151
+ mscs.register_module(my_models)
152
+ ```
153
+
154
+ ## Compression & Integrity
155
+
156
+ ```python
157
+ # zlib compression
158
+ with open("data.mscs.z", "wb") as f:
159
+ mscs.dump_compressed(large_obj, f)
160
+
161
+ with open("data.mscs.z", "rb") as f:
162
+ obj = mscs.load_compressed(f)
163
+
164
+ # CRC32 integrity check (detects accidental corruption, NOT tamper-proof)
165
+ data = mscs.dumps(obj, with_crc=True)
166
+ mscs.loads(data) # verifies CRC, raises MSCDecodeError if corrupted
167
+
168
+ # HMAC-SHA256 authentication (cryptographic, tamper-proof)
169
+ key = b'your-secret-key-here'
170
+ data = mscs.dumps(obj, hmac_key=key)
171
+ mscs.loads(data, hmac_key=key) # verifies HMAC, raises MSCSecurityError if tampered
172
+ mscs.loads(data) # MSCSecurityError: no key provided for signed payload
173
+ mscs.loads(unsigned_data, hmac_key=key) # MSCSecurityError: anti-downgrade protection
174
+ ```
175
+
176
+ ## API Reference
177
+
178
+ ### Core
179
+
180
+ | Function | Description |
181
+ |----------|------------|
182
+ | `dumps(obj, *, with_crc=False, hmac_key=None) -> bytes` | Serialize to bytes |
183
+ | `loads(data, *, strict=True, hmac_key=None) -> Any` | Deserialize from bytes |
184
+ | `dump(obj, file, *, with_crc=False, hmac_key=None)` | Serialize to file (binary mode) |
185
+ | `load(file, *, strict=True, hmac_key=None) -> Any` | Deserialize from file |
186
+ | `dump_compressed(obj, file, level=6)` | Serialize with zlib compression |
187
+ | `load_compressed(file) -> Any` | Deserialize compressed data |
188
+
189
+ ### Registry
190
+
191
+ | Function | Description |
192
+ |----------|------------|
193
+ | `register(cls) -> cls` | Register class as safe (also works as decorator) |
194
+ | `register_alias(old_path, cls)` | Map old class path to new class |
195
+ | `register_module(module) -> list` | Register all classes in a module |
196
+
197
+ ### Utilities
198
+
199
+ | Function | Description |
200
+ |----------|------------|
201
+ | `inspect(data) -> dict` | Get metadata without deserializing |
202
+ | `benchmark(obj, rounds=100) -> dict` | Measure encode/decode performance |
203
+ | `copy(obj) -> obj` | Deep copy via serialization round-trip |
204
+
205
+ ## Supported Types
206
+
207
+ | Type | Notes |
208
+ |------|-------|
209
+ | `None`, `bool`, `int`, `float`, `complex` | Ints up to 8192 bytes (~19,700 digits) |
210
+ | `str`, `bytes`, `bytearray` | UTF-8, ref-tracked |
211
+ | `list`, `tuple`, `dict`, `set`, `frozenset` | Circular refs supported |
212
+ | `datetime`, `date`, `time`, `timedelta` | ISO 8601 |
213
+ | `Decimal`, `UUID`, `Path` | Lossless |
214
+ | `Enum` | Must be registered |
215
+ | `numpy.ndarray` | dtype whitelist enforced |
216
+ | `torch.Tensor` | Auto CPU transfer, preserves requires_grad |
217
+ | `dataclass`, `__slots__`, `__dict__` objects | Must be registered |
218
+
219
+ ## Performance
220
+
221
+ Benchmarked on a single machine (results may vary by hardware and payload):
222
+
223
+ **Payload: `state_dict` with 4 tensors (~57K parameters, dominated by contiguous float32 buffers):**
224
+
225
+ | Method | Roundtrip | Size |
226
+ |--------|-----------|------|
227
+ | **mscs** | **~0.1 ms** | **~65 KB** |
228
+ | pickle | ~0.6 ms | ~68 KB |
229
+ | torch.save | ~0.4 ms | ~67 KB |
230
+
231
+ mscs is fast for tensor-heavy payloads because it writes raw buffers with minimal framing overhead. **For small, nested Python structures (dicts, strings, configs), the speedup is smaller.** Always benchmark with your actual data.
232
+
233
+ Run `python tests/benchmark.py` to reproduce on your machine.
234
+
235
+ ## Security Model
236
+
237
+ mscs provides a **defense-in-depth** approach, but it is **not a sandbox**. Understand the boundaries:
238
+
239
+ ### What mscs prevents
240
+
241
+ 1. **No dynamic imports**: Class names in the binary stream are only used as registry lookup keys — never passed to `importlib`
242
+ 2. **Explicit registry**: Custom classes must be registered before deserialization; unregistered classes raise `MSCSecurityError`
243
+ 3. **NumPy dtype whitelist**: Blocks `object`, `void`, and structured dtypes that could execute code
244
+ 4. **Configurable limits**: `MAX_DEPTH=256`, `MAX_SIZE=512MB`, `MAX_COLLECTION=10M`, `MAX_INT_BYTES=8192`
245
+ 5. **Anti zip-bomb**: `load_compressed` validates both compressed and decompressed sizes with bounded reads
246
+ 6. **Path null byte rejection**: Paths containing null bytes are rejected
247
+ 7. **CRC32 corruption detection**: Optional checksum to detect accidental data corruption (not cryptographic — an attacker can forge CRC32)
248
+ 8. **HMAC-SHA256 authentication**: Optional cryptographic signature to detect intentional tampering. Anti-downgrade protection prevents stripping the HMAC flag.
249
+ 9. **Trailing bytes rejection**: Payloads with unexpected bytes after the serialized object are rejected
250
+ 10. **Integer size limit**: Ints larger than `MAX_INT_BYTES` (8192 bytes, ~19,700 digits) are rejected to prevent CPU exhaustion attacks
251
+
252
+ ### What mscs does NOT prevent
253
+
254
+ 1. **`__setstate__` execution**: If you register a class that implements `__setstate__`, that method **will execute** during deserialization. Only register classes you trust.
255
+ 2. **Path traversal**: Deserialized `Path` objects may contain `../` sequences. The consumer must validate paths before using them for file I/O.
256
+ 3. **Malicious registered classes**: The security boundary is the registry. If you register a class with dangerous behavior in `__init__`, `__setstate__`, or property setters, mscs cannot protect you.
257
+
258
+ **Rule of thumb**: mscs is safe for deserializing untrusted *data* as long as your registry only contains trusted *classes*.
259
+
260
+ ## Binary Format
261
+
262
+ ```
263
+ ┌──────────┬─────────┬───────┬──────────┬────────────────┬──────────────┬──────────────┐
264
+ │ Magic(4) │ Ver.(1) │ Fl(1) │ Tag(1) │ Payload(var) │ CRC32(4)? │ HMAC(32)? │
265
+ │ "MSCS" │ 0x02 │ bits │ type tag │ type-dependent │ if flag 0x01 │ if flag 0x02 │
266
+ └──────────┴─────────┴───────┴──────────┴────────────────┴──────────────┴──────────────┘
267
+ ```
268
+
269
+ **Header** (6 bytes fixed):
270
+ - Bytes 0-3: Magic `MSCS` (0x4D534353)
271
+ - Byte 4: Format version (currently `0x02`)
272
+ - Byte 5: Flags (bit 0 = CRC32 appended, bit 1 = HMAC-SHA256 appended)
273
+
274
+ CRC32 and HMAC are mutually exclusive (HMAC is strictly superior).
275
+
276
+ **Payload**: Recursive type-length-value encoding. Each value starts with a 1-byte type tag:
277
+
278
+ | Tag | Type | Payload format |
279
+ |-----|------|----------------|
280
+ | 0x00 | None | (empty) |
281
+ | 0x01 | bool | 1 byte (0x00/0x01) |
282
+ | 0x02 | int | `<H>` byte count + signed little-endian bytes |
283
+ | 0x03 | float | `<d>` IEEE 754 double |
284
+ | 0x04 | str | `<I>` byte count + UTF-8 |
285
+ | 0x05 | bytes | `<I>` byte count + raw |
286
+ | 0x06 | list | `<I>` item count + items |
287
+ | 0x07 | tuple | `<I>` item count + items |
288
+ | 0x08 | dict | `<I>` pair count + key/value pairs |
289
+ | 0x09 | set | `<I>` item count + items (sorted) |
290
+ | 0x0A | ndarray | str(meta) + `<I>` data size + raw buffer |
291
+ | 0x0B | object | str(class_path) + encoded(state) |
292
+ | 0x0C | complex | `<dd>` real, imag |
293
+ | 0x0D | frozenset | `<I>` item count + items (sorted) |
294
+ | 0x0E | datetime | `<H>` str len + ISO 8601 string |
295
+ | 0x0F | date | `<HBB>` year, month, day |
296
+ | 0x10 | time | `<H>` str len + ISO 8601 string |
297
+ | 0x11 | timedelta (legacy) | `<iiI>` days, seconds, microseconds |
298
+ | 0x12 | Decimal | `<H>` str len + decimal string |
299
+ | 0x13 | Enum | str(class_path) + encoded(value) |
300
+ | 0x14 | bytearray | `<I>` byte count + raw |
301
+ | 0x15 | ref | `<I>` reference ID |
302
+ | 0x16 | UUID | 16 bytes raw |
303
+ | 0x17 | Path | `<I>` str len + UTF-8 path string |
304
+ | 0x18 | Tensor | str(meta) + `<I>` data size + raw buffer |
305
+ | 0x19 | timedelta2 | `<iiI>` days, seconds, microseconds |
306
+
307
+ **ndarray meta**: `"{dtype}|{shape}"` where shape is `"dim0xdim1x..."` (e.g., `"float32|100x100"`).
308
+
309
+ **Tensor meta**: `"{dtype}|{shape}|{requires_grad}"` (e.g., `"float32|256x256|0"`).
310
+
311
+ **Reference tracking**: Mutable containers (list, dict, set, etc.), strings, bytes, and arrays are assigned incrementing IDs. Tag 0x15 refers back to a previously seen object by ID, enabling circular reference support.
312
+
313
+ ## License
314
+
315
+ MIT