Skip to content

Commit 0067f21

Browse files
desty2kclaude
andcommitted
release v2.0.0: version bump, bundle isolation fix, crypto fallback
- Version 2.0.0b1 → 2.0.0 - Fix: encrypted node code scrub no longer mutates caller's bundle dict (tracked via _scrubbed set instead of node["code"] = None) - Add [crypto] optional dependency for pure-Python fallback - Clear error message when both C extension and cryptography are missing - .DS_Store in .gitignore Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0f86859 commit 0067f21

6 files changed

Lines changed: 20 additions & 9 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,6 @@ dmypy.json
120120

121121
# uv
122122
uv.lock
123+
124+
# macOS
125+
.DS_Store

paker/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"ImportErrorPolicy", "AsFileStrategy", "NativeLoaderPolicy",
4848
"DiskPolicy", "virtual_fs", "__version__",
4949
]
50-
__version__ = "2.0.0b1"
50+
__version__ = "2.0.0"
5151

5252

5353
class ImportErrorPolicy(enum.Enum):

paker/_crypto.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,14 @@ def scrub(obj) -> None:
199199

200200
def _aes_ctr_fallback(key: bytes, nonce: bytes, data: bytes) -> bytes:
201201
"""Pure-Python AES-CTR fallback when the C extension is not compiled."""
202-
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
202+
try:
203+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
204+
except ImportError:
205+
raise ImportError(
206+
"paker's C extension (_paker_crypto) is not available and the "
207+
"pure-Python fallback requires the 'cryptography' package. "
208+
"Install it with: pip install paker[crypto]"
209+
) from None
203210
cipher = Cipher(algorithms.AES(key), modes.CTR(nonce))
204211
decryptor = cipher.decryptor()
205212
return decryptor.update(data) + decryptor.finalize()

paker/importers/jsonimporter.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def __init__(
7171
self._as_file_strategy: AsFileStrategy = resolve_strategy(as_file_strategy)
7272
self._owns_virtual_fs = False
7373
self._module_cache: dict[str, object] = {}
74+
self._scrubbed: set[str] = set()
7475
self._logger = logging.getLogger(self.__class__.__name__)
7576
self._context_stack: list[str] = []
7677
sys.meta_path.insert(0, self)
@@ -176,11 +177,7 @@ def exec_module(self, module):
176177
)
177178

178179
if encrypted:
179-
# Stop the bundle from retaining the ciphertext for this module
180-
# once it's been decrypted and executed. Re-imports rely on
181-
# ``sys.modules`` caching; the loader itself won't be asked to
182-
# decrypt this module again.
183-
node["code"] = None
180+
self._scrubbed.add(fullname)
184181
self._module_cache[fullname] = module
185182
self._logger.info("%s imported successfully", fullname)
186183

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "paker"
7-
version = "2.0.0b1"
7+
version = "2.0.0"
88
description = "Encrypted in-memory Python package loader"
99
readme = "README.md"
1010
license = "MIT"
@@ -28,6 +28,9 @@ classifiers = [
2828
]
2929
keywords = ["python", "package-loader", "in-memory", "encrypted", "native-extensions", "memfd", "code-protection", "bundle", "zero-disk", "importlib"]
3030

31+
[project.optional-dependencies]
32+
crypto = ["cryptography>=41.0"]
33+
3134
[project.scripts]
3235
paker = "paker.__main__:main_entry"
3336

tests/test_crypto.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,8 @@ def test_scrub_after_import(self):
318318
imp = JsonImporter(data, key=KEY)
319319
import scrub_test
320320
assert scrub_test.SCRUBBED is True
321-
assert data["scrub_test"]["code"] is None
321+
assert "scrub_test" in imp._scrubbed
322+
assert data["scrub_test"]["code"] is not None
322323
imp.unload()
323324

324325
def test_encrypted_pyc_module(self):

0 commit comments

Comments
 (0)