a3s-code 8.0.4__tar.gz → 8.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: a3s-code
3
- Version: 8.0.4
3
+ Version: 8.2.0
4
4
  Summary: A3S Code Python SDK — pure-Python bootstrap that fetches the native wheel from GitHub Releases
5
5
  Author: A3S Lab
6
6
  License: MIT
@@ -29,17 +29,23 @@ platform from the project's
29
29
  the wheel's sha256 against the release manifest, extracts the compiled
30
30
  extension into a per-user cache, and exposes the normal `a3s_code` API.
31
31
 
32
- The v8.0.3 release uses one CPython 3.10 stable-ABI (`cp310-abi3`) wheel per
32
+ The v8.2.0 release uses one CPython 3.10 stable-ABI (`cp310-abi3`) wheel per
33
33
  platform. That wheel is installable by CPython 3.10, 3.11, 3.12, 3.13, and
34
- 3.14. The published `v8.0.2` assets predate this workflow and still use exact
35
- CPython 3.10–3.13 wheel names; upgrade the bootstrap to v8.0.3 for Python
36
- 3.14. The loader falls back to the exact per-minor names used by older
37
- releases, so upgrading the bootstrap does not invalidate an older release
38
- asset.
34
+ 3.14. Supported native targets are macOS arm64/x86_64, Linux glibc 2.28+
35
+ arm64/x86_64, and Windows arm64/x86_64. Every supported wheel contains its
36
+ target Moli sidecar and provenance record. Linux musl is intentionally omitted
37
+ because upstream Moli publishes no musl asset; use a system/explicit browser
38
+ or another backend there. The loader still falls back to exact per-minor names
39
+ when reading older release manifests.
39
40
 
40
41
  Subsequent imports use the cached extension. Cache lives under a
41
42
  platform-specific directory, `~/.cache/a3s-code/<version>/<platform-tag>/`
42
43
  (or `$A3S_CODE_CACHE_DIR/<version>/<platform-tag>/` when overridden).
44
+ The extracted Moli executable is placed in the same version/target cache and
45
+ is selected through `A3S_CODE_MOLI_EXECUTABLE`; atomic extraction and the
46
+ bootstrap's cross-process installer lock make concurrent first imports
47
+ single-flight. Rust, Node.js, and Go use the Core-managed Moli cache when they
48
+ are not using a package-local sidecar.
43
49
 
44
50
  ## Why
45
51
 
@@ -53,8 +59,8 @@ through PyPI.
53
59
 
54
60
  - macOS arm64 (Apple Silicon, macOS 11+)
55
61
  - macOS x86_64 (Intel, macOS 12+)
56
- - Linux x86_64 (glibc 2.28+)
57
- - Windows x86_64
62
+ - Linux x86_64 and arm64 (glibc 2.28+)
63
+ - Windows x86_64 and arm64
58
64
 
59
65
  CPython 3.10, 3.11, 3.12, 3.13, and 3.14.
60
66
 
@@ -74,6 +80,9 @@ python3.14 -m pip install a3s-code
74
80
  | `A3S_CODE_CACHE_DIR` | Cache root (defaults to `$XDG_CACHE_HOME/a3s-code` or `~/.cache/a3s-code`) |
75
81
  | `A3S_CODE_RELEASES_BASE_URL` | Override the release base URL — useful for air-gapped mirrors |
76
82
  | `A3S_CODE_SKIP_HASH_CHECK` | `1` skips sha256 verification (do not use in production) |
83
+ | `A3S_CODE_MOLI_EXECUTABLE` | Explicit verified Moli executable; skips package discovery |
84
+ | `A3S_CODE_MOLI_CACHE_DIR` | Core-managed shared Moli cache root (the bootstrap uses its version/target cache for a bundled sidecar) |
85
+ | `A3S_CODE_MOLI_RELEASE_BASE_URL` | HTTPS mirror for the pinned Moli release |
77
86
 
78
87
  ## Manual install
79
88
 
@@ -7,17 +7,23 @@ platform from the project's
7
7
  the wheel's sha256 against the release manifest, extracts the compiled
8
8
  extension into a per-user cache, and exposes the normal `a3s_code` API.
9
9
 
10
- The v8.0.3 release uses one CPython 3.10 stable-ABI (`cp310-abi3`) wheel per
10
+ The v8.2.0 release uses one CPython 3.10 stable-ABI (`cp310-abi3`) wheel per
11
11
  platform. That wheel is installable by CPython 3.10, 3.11, 3.12, 3.13, and
12
- 3.14. The published `v8.0.2` assets predate this workflow and still use exact
13
- CPython 3.10–3.13 wheel names; upgrade the bootstrap to v8.0.3 for Python
14
- 3.14. The loader falls back to the exact per-minor names used by older
15
- releases, so upgrading the bootstrap does not invalidate an older release
16
- asset.
12
+ 3.14. Supported native targets are macOS arm64/x86_64, Linux glibc 2.28+
13
+ arm64/x86_64, and Windows arm64/x86_64. Every supported wheel contains its
14
+ target Moli sidecar and provenance record. Linux musl is intentionally omitted
15
+ because upstream Moli publishes no musl asset; use a system/explicit browser
16
+ or another backend there. The loader still falls back to exact per-minor names
17
+ when reading older release manifests.
17
18
 
18
19
  Subsequent imports use the cached extension. Cache lives under a
19
20
  platform-specific directory, `~/.cache/a3s-code/<version>/<platform-tag>/`
20
21
  (or `$A3S_CODE_CACHE_DIR/<version>/<platform-tag>/` when overridden).
22
+ The extracted Moli executable is placed in the same version/target cache and
23
+ is selected through `A3S_CODE_MOLI_EXECUTABLE`; atomic extraction and the
24
+ bootstrap's cross-process installer lock make concurrent first imports
25
+ single-flight. Rust, Node.js, and Go use the Core-managed Moli cache when they
26
+ are not using a package-local sidecar.
21
27
 
22
28
  ## Why
23
29
 
@@ -31,8 +37,8 @@ through PyPI.
31
37
 
32
38
  - macOS arm64 (Apple Silicon, macOS 11+)
33
39
  - macOS x86_64 (Intel, macOS 12+)
34
- - Linux x86_64 (glibc 2.28+)
35
- - Windows x86_64
40
+ - Linux x86_64 and arm64 (glibc 2.28+)
41
+ - Windows x86_64 and arm64
36
42
 
37
43
  CPython 3.10, 3.11, 3.12, 3.13, and 3.14.
38
44
 
@@ -52,6 +58,9 @@ python3.14 -m pip install a3s-code
52
58
  | `A3S_CODE_CACHE_DIR` | Cache root (defaults to `$XDG_CACHE_HOME/a3s-code` or `~/.cache/a3s-code`) |
53
59
  | `A3S_CODE_RELEASES_BASE_URL` | Override the release base URL — useful for air-gapped mirrors |
54
60
  | `A3S_CODE_SKIP_HASH_CHECK` | `1` skips sha256 verification (do not use in production) |
61
+ | `A3S_CODE_MOLI_EXECUTABLE` | Explicit verified Moli executable; skips package discovery |
62
+ | `A3S_CODE_MOLI_CACHE_DIR` | Core-managed shared Moli cache root (the bootstrap uses its version/target cache for a bundled sidecar) |
63
+ | `A3S_CODE_MOLI_RELEASE_BASE_URL` | HTTPS mirror for the pinned Moli release |
55
64
 
56
65
  ## Manual install
57
66
 
@@ -7,7 +7,7 @@ name = "a3s-code"
7
7
  # Keep in sync with crates/code core release. The bootstrap loader fetches
8
8
  # the matching native wheel from `https://github.com/A3S-Lab/Code/releases/tag/v<version>`
9
9
  # at import time.
10
- version = "8.0.4"
10
+ version = "8.2.0"
11
11
  description = "A3S Code Python SDK — pure-Python bootstrap that fetches the native wheel from GitHub Releases"
12
12
  readme = "README.md"
13
13
  license = {text = "MIT"}
@@ -6,7 +6,9 @@ wheel for the current platform, downloads it from the project's GitHub
6
6
  Releases, verifies the wheel's sha256 against the
7
7
  release manifest, extracts the compiled `_native` extension into a
8
8
  per-user cache, and registers it explicitly as `a3s_code._native` because it
9
- lives outside the installed package directory.
9
+ lives outside the installed package directory. A cross-process advisory lock
10
+ keeps the first download and sidecar extraction single-flight for applications
11
+ sharing that cache.
10
12
 
11
13
  Override the cache location via `A3S_CODE_CACHE_DIR`. Override the
12
14
  release source via `A3S_CODE_RELEASES_BASE_URL` (default points at the
@@ -16,28 +18,36 @@ GitHub Releases page for `A3S-Lab/Code`). Skip the integrity check via
16
18
 
17
19
  from __future__ import annotations
18
20
 
21
+ import errno
19
22
  import hashlib
20
23
  import io
21
24
  import json
22
25
  import os
23
26
  import platform
27
+ import stat
24
28
  import sys
25
29
  import threading
30
+ import time
26
31
  import urllib.error
27
32
  import urllib.request
28
33
  import zipfile
34
+ from contextlib import contextmanager
29
35
  from pathlib import Path
30
- from typing import Optional
36
+ from typing import Iterator, Optional
31
37
 
32
38
  # Version is the bootstrap's own version, which equals the matching native
33
39
  # wheel version on GH Releases. Bumped by the release workflow.
34
- __version__ = "8.0.4"
40
+ __version__ = "8.2.0"
35
41
 
36
42
  _DEFAULT_BASE_URL = "https://github.com/A3S-Lab/Code/releases/download"
37
43
  _REQUEST_TIMEOUT_S = 120
38
44
  _USER_AGENT = f"a3s-code-bootstrap/{__version__}"
39
45
  _MIN_PYTHON = (3, 10)
46
+ _MAX_WHEEL_BYTES = 512 * 1024 * 1024
47
+ _MAX_MEMBER_BYTES = 256 * 1024 * 1024
40
48
  _ABI3_PYTHON_TAG = "cp310-abi3"
49
+ _INSTALL_LOCK_TIMEOUT_S = _REQUEST_TIMEOUT_S + 30
50
+ _INSTALL_LOCK_POLL_S = 0.05
41
51
  _LOAD_LOCK = threading.Lock()
42
52
  _LOADED = False
43
53
 
@@ -55,6 +65,91 @@ class BootstrapHttpError(BootstrapError):
55
65
  super().__init__(f"GET {url} failed: HTTP {status_code}")
56
66
 
57
67
 
68
+ def _lock_contention(error: OSError) -> bool:
69
+ """Return whether an advisory-lock failure means another process owns it."""
70
+
71
+ # POSIX flock reports EACCES/EAGAIN. Windows msvcrt.locking commonly uses
72
+ # errno 13 (permission denied) for the same contention condition.
73
+ return isinstance(error, BlockingIOError) or getattr(error, "errno", None) in {
74
+ errno.EAGAIN,
75
+ errno.EACCES, # Windows sharing violation uses the same value
76
+ 35, # EAGAIN on macOS
77
+ 36, # EAGAIN on some BSDs
78
+ }
79
+
80
+
81
+ @contextmanager
82
+ def _install_lock(
83
+ cache_dir: Path, timeout_s: float = _INSTALL_LOCK_TIMEOUT_S
84
+ ) -> Iterator[None]:
85
+ """Acquire a cross-process lock for native wheel and Moli extraction.
86
+
87
+ The lock is advisory and released by the operating system if a bootstrap
88
+ process exits unexpectedly. Keeping it in the same platform/version cache
89
+ means independent Python applications perform one download and one atomic
90
+ extraction, while the in-process ``_LOAD_LOCK`` still protects module
91
+ registration.
92
+ """
93
+
94
+ cache_dir.mkdir(parents=True, exist_ok=True)
95
+ lock_path = cache_dir / ".install.lock"
96
+ try:
97
+ handle = lock_path.open("a+b")
98
+ except OSError as exc:
99
+ raise BootstrapError(f"could not open bootstrap install lock {lock_path}: {exc}") from exc
100
+
101
+ locked = False
102
+ try:
103
+ # Windows locking operates on a byte range and requires at least one
104
+ # byte in the file. The write is harmless on POSIX and races are safe.
105
+ handle.seek(0, os.SEEK_END)
106
+ if handle.tell() == 0:
107
+ handle.write(b"\0")
108
+ handle.flush()
109
+ deadline = time.monotonic() + timeout_s
110
+ while True:
111
+ try:
112
+ handle.seek(0)
113
+ if os.name == "nt":
114
+ import msvcrt
115
+
116
+ msvcrt.locking(handle.fileno(), msvcrt.LK_NBLCK, 1)
117
+ else:
118
+ import fcntl
119
+
120
+ fcntl.flock(handle.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
121
+ locked = True
122
+ break
123
+ except OSError as exc:
124
+ if not _lock_contention(exc):
125
+ raise BootstrapError(
126
+ f"could not acquire bootstrap install lock {lock_path}: {exc}"
127
+ ) from exc
128
+ if time.monotonic() >= deadline:
129
+ raise BootstrapError(
130
+ f"timed out waiting for bootstrap install lock {lock_path}"
131
+ ) from exc
132
+ time.sleep(_INSTALL_LOCK_POLL_S)
133
+ yield
134
+ finally:
135
+ if locked:
136
+ try:
137
+ handle.seek(0)
138
+ if os.name == "nt":
139
+ import msvcrt
140
+
141
+ msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
142
+ else:
143
+ import fcntl
144
+
145
+ fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
146
+ except OSError:
147
+ # Closing the descriptor also releases the advisory lock. Do
148
+ # not hide the caller's extraction/import exception here.
149
+ pass
150
+ handle.close()
151
+
152
+
58
153
  def _base_url() -> str:
59
154
  return os.environ.get("A3S_CODE_RELEASES_BASE_URL", _DEFAULT_BASE_URL).rstrip("/")
60
155
 
@@ -95,13 +190,17 @@ def _platform_tag() -> str:
95
190
  elif sys_plat == "linux":
96
191
  if machine in ("x86_64", "amd64"):
97
192
  return "manylinux_2_28_x86_64"
193
+ if machine in ("arm64", "aarch64"):
194
+ return "manylinux_2_28_aarch64"
98
195
  elif sys_plat == "win32":
99
196
  if machine in ("amd64", "x86_64"):
100
197
  return "win_amd64"
198
+ if machine in ("arm64", "aarch64"):
199
+ return "win_arm64"
101
200
  raise BootstrapError(
102
201
  f"a3s-code: no native wheel published for {sys_plat}/{machine}. "
103
202
  "Supported platforms: macOS arm64 (11+), macOS Intel (12+), "
104
- "Linux x86_64 (glibc 2.28+), Windows x86_64."
203
+ "Linux x86_64/arm64 (glibc 2.28+), Windows x86_64/arm64."
105
204
  )
106
205
 
107
206
 
@@ -207,24 +306,117 @@ def _download_wheel(version: str) -> tuple[str, bytes]:
207
306
  )
208
307
 
209
308
 
309
+ def _safe_wheel_member(name: str) -> tuple[str, ...]:
310
+ """Validate and split a wheel member before reading it.
311
+
312
+ Wheels are ZIP files supplied by a remote release. Do not allow an
313
+ archive entry to escape the cache through absolute paths, parent
314
+ components, or Windows separators (which are accepted by some extraction
315
+ tools even on POSIX).
316
+ """
317
+ if not name or "\\" in name or name.startswith("/"):
318
+ raise BootstrapError(f"downloaded wheel contains an unsafe member path: {name!r}")
319
+ parts = tuple(part for part in name.split("/") if part)
320
+ if not parts or any(part in {".", ".."} for part in parts):
321
+ raise BootstrapError(f"downloaded wheel contains an unsafe member path: {name!r}")
322
+ return parts
323
+
324
+
325
+ def _write_wheel_member(zf: zipfile.ZipFile, info: zipfile.ZipInfo, destination: Path) -> None:
326
+ """Write one regular, bounded wheel member atomically."""
327
+ mode = (info.external_attr >> 16) & 0o170000
328
+ if mode == stat.S_IFLNK:
329
+ raise BootstrapError(f"downloaded wheel contains a symlink member: {info.filename!r}")
330
+ if info.is_dir() or info.file_size > _MAX_MEMBER_BYTES:
331
+ raise BootstrapError(f"downloaded wheel member is too large or not a file: {info.filename!r}")
332
+
333
+ destination.parent.mkdir(parents=True, exist_ok=True)
334
+ temporary = destination.with_name(f".{destination.name}.part-{os.getpid()}-{threading.get_ident()}")
335
+ try:
336
+ with zf.open(info) as source, temporary.open("xb") as target:
337
+ remaining = _MAX_MEMBER_BYTES + 1
338
+ while remaining:
339
+ chunk = source.read(min(1024 * 1024, remaining))
340
+ if not chunk:
341
+ break
342
+ target.write(chunk)
343
+ remaining -= len(chunk)
344
+ if remaining == 0:
345
+ raise BootstrapError(f"downloaded wheel member is too large: {info.filename!r}")
346
+ target.flush()
347
+ os.fsync(target.fileno())
348
+ os.replace(temporary, destination)
349
+ finally:
350
+ try:
351
+ temporary.unlink()
352
+ except FileNotFoundError:
353
+ pass
354
+
355
+
356
+ def _configure_cached_moli(cache_dir: Path) -> None:
357
+ """Expose a wheel-bundled Moli executable to the Rust core.
358
+
359
+ The bootstrap package and the native wheel are separate distributions.
360
+ Without copying this sidecar into the bootstrap cache, a normal
361
+ ``pip install a3s-code`` would silently discard the bundled browser and
362
+ force every process to download it again. An operator-supplied override
363
+ always wins.
364
+ """
365
+ if os.environ.get("A3S_CODE_MOLI_EXECUTABLE"):
366
+ return
367
+ executable = "moli.exe" if os.name == "nt" else "moli"
368
+ candidate = cache_dir / "moli" / executable
369
+ try:
370
+ if candidate.is_file() and (os.name == "nt" or os.access(candidate, os.X_OK)):
371
+ os.environ["A3S_CODE_MOLI_EXECUTABLE"] = str(candidate)
372
+ os.environ.setdefault("A3S_CODE_MOLI_DIR", str(candidate.parent))
373
+ except OSError:
374
+ return
375
+
376
+
210
377
  def _extract_native(wheel_bytes: bytes, target_dir: Path) -> Path:
211
- """Extract the compiled `_native.*` extension from `wheel_bytes` into
212
- `target_dir`. Returns the path to the extracted file.
378
+ """Extract the native extension and bundled Moli sidecar.
379
+
380
+ Returns the path to the extracted extension. The sidecar is placed under
381
+ ``target_dir/moli`` and is selected by :func:`_configure_cached_moli`.
213
382
  """
383
+ if len(wheel_bytes) > _MAX_WHEEL_BYTES:
384
+ raise BootstrapError("downloaded wheel exceeds the bootstrap size limit")
214
385
  target_dir.mkdir(parents=True, exist_ok=True)
386
+ native_path: Optional[Path] = None
387
+ moli_path: Optional[Path] = None
215
388
  with zipfile.ZipFile(io.BytesIO(wheel_bytes)) as zf:
216
- for name in zf.namelist():
217
- base = Path(name).name
218
- # match _native.<abi>.{so,pyd,dylib}
389
+ for info in zf.infolist():
390
+ parts = _safe_wheel_member(info.filename)
391
+ base = parts[-1]
392
+ # Match _native.<abi>.{so,pyd,dylib}; ignore metadata files.
219
393
  if base.startswith("_native.") and not base.endswith(".dist-info"):
220
- out_path = target_dir / base
221
- with zf.open(name) as src, out_path.open("wb") as dst:
222
- dst.write(src.read())
223
- return out_path
224
- raise BootstrapError(
225
- "downloaded wheel did not contain a _native extension; "
226
- "the release artifact appears to be corrupt"
227
- )
394
+ if native_path is not None:
395
+ raise BootstrapError("downloaded wheel contains multiple native extensions")
396
+ native_path = target_dir / base
397
+ _write_wheel_member(zf, info, native_path)
398
+ continue
399
+
400
+ # Maturin places the packaged browser under a3s_code/moli/. Keep
401
+ # the match strict so an unrelated file named `moli` is not run.
402
+ if base in {"moli", "moli.exe"} and len(parts) >= 3 and parts[-3:-1] == ("a3s_code", "moli"):
403
+ if moli_path is not None:
404
+ raise BootstrapError("downloaded wheel contains multiple Moli executables")
405
+ moli_path = target_dir / "moli" / base
406
+ _write_wheel_member(zf, info, moli_path)
407
+
408
+ if native_path is None:
409
+ raise BootstrapError(
410
+ "downloaded wheel did not contain a _native extension; "
411
+ "the release artifact appears to be corrupt"
412
+ )
413
+ if moli_path is not None and os.name != "nt":
414
+ try:
415
+ moli_path.chmod(0o755)
416
+ except OSError as exc:
417
+ raise BootstrapError(f"could not mark bundled Moli executable: {exc}") from exc
418
+ _configure_cached_moli(target_dir)
419
+ return native_path
228
420
 
229
421
 
230
422
  def _find_cached_native(cache_dir: Path) -> Optional[Path]:
@@ -288,24 +480,35 @@ def ensure_native_loaded(version: str = __version__) -> Path:
288
480
 
289
481
  native = _find_cached_native(cache)
290
482
  if native is None:
291
- wheel_name, wheel_bytes = _download_wheel(version)
292
- url = _release_url(wheel_name, version)
293
- sys.stderr.write(
294
- f"a3s-code: fetching native wheel {wheel_name} "
295
- f"from {url} (first import only)...\n"
296
- )
297
-
298
- if os.environ.get("A3S_CODE_SKIP_HASH_CHECK") != "1":
299
- expected = _expected_sha256(wheel_name, version)
300
- if expected is not None:
301
- actual = hashlib.sha256(wheel_bytes).hexdigest()
302
- if actual != expected:
303
- raise BootstrapError(
304
- f"sha256 mismatch for {wheel_name}: "
305
- f"expected {expected}, got {actual}"
306
- )
307
-
308
- native = _extract_native(wheel_bytes, cache)
483
+ # Re-check after taking the process lock: another independent
484
+ # Python application may have completed the download while this
485
+ # process was starting. This keeps both the extension and bundled
486
+ # Moli sidecar single-flight across processes.
487
+ with _install_lock(cache):
488
+ native = _find_cached_native(cache)
489
+ if native is None:
490
+ wheel_name, wheel_bytes = _download_wheel(version)
491
+ url = _release_url(wheel_name, version)
492
+ sys.stderr.write(
493
+ f"a3s-code: fetching native wheel {wheel_name} "
494
+ f"from {url} (first import only)...\n"
495
+ )
496
+
497
+ if os.environ.get("A3S_CODE_SKIP_HASH_CHECK") != "1":
498
+ expected = _expected_sha256(wheel_name, version)
499
+ if expected is not None:
500
+ actual = hashlib.sha256(wheel_bytes).hexdigest()
501
+ if actual != expected:
502
+ raise BootstrapError(
503
+ f"sha256 mismatch for {wheel_name}: "
504
+ f"expected {expected}, got {actual}"
505
+ )
506
+
507
+ native = _extract_native(wheel_bytes, cache)
508
+ else:
509
+ _configure_cached_moli(cache)
510
+ else:
511
+ _configure_cached_moli(cache)
309
512
 
310
513
  _register_native(native)
311
514
  _LOADED = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: a3s-code
3
- Version: 8.0.4
3
+ Version: 8.2.0
4
4
  Summary: A3S Code Python SDK — pure-Python bootstrap that fetches the native wheel from GitHub Releases
5
5
  Author: A3S Lab
6
6
  License: MIT
@@ -29,17 +29,23 @@ platform from the project's
29
29
  the wheel's sha256 against the release manifest, extracts the compiled
30
30
  extension into a per-user cache, and exposes the normal `a3s_code` API.
31
31
 
32
- The v8.0.3 release uses one CPython 3.10 stable-ABI (`cp310-abi3`) wheel per
32
+ The v8.2.0 release uses one CPython 3.10 stable-ABI (`cp310-abi3`) wheel per
33
33
  platform. That wheel is installable by CPython 3.10, 3.11, 3.12, 3.13, and
34
- 3.14. The published `v8.0.2` assets predate this workflow and still use exact
35
- CPython 3.10–3.13 wheel names; upgrade the bootstrap to v8.0.3 for Python
36
- 3.14. The loader falls back to the exact per-minor names used by older
37
- releases, so upgrading the bootstrap does not invalidate an older release
38
- asset.
34
+ 3.14. Supported native targets are macOS arm64/x86_64, Linux glibc 2.28+
35
+ arm64/x86_64, and Windows arm64/x86_64. Every supported wheel contains its
36
+ target Moli sidecar and provenance record. Linux musl is intentionally omitted
37
+ because upstream Moli publishes no musl asset; use a system/explicit browser
38
+ or another backend there. The loader still falls back to exact per-minor names
39
+ when reading older release manifests.
39
40
 
40
41
  Subsequent imports use the cached extension. Cache lives under a
41
42
  platform-specific directory, `~/.cache/a3s-code/<version>/<platform-tag>/`
42
43
  (or `$A3S_CODE_CACHE_DIR/<version>/<platform-tag>/` when overridden).
44
+ The extracted Moli executable is placed in the same version/target cache and
45
+ is selected through `A3S_CODE_MOLI_EXECUTABLE`; atomic extraction and the
46
+ bootstrap's cross-process installer lock make concurrent first imports
47
+ single-flight. Rust, Node.js, and Go use the Core-managed Moli cache when they
48
+ are not using a package-local sidecar.
43
49
 
44
50
  ## Why
45
51
 
@@ -53,8 +59,8 @@ through PyPI.
53
59
 
54
60
  - macOS arm64 (Apple Silicon, macOS 11+)
55
61
  - macOS x86_64 (Intel, macOS 12+)
56
- - Linux x86_64 (glibc 2.28+)
57
- - Windows x86_64
62
+ - Linux x86_64 and arm64 (glibc 2.28+)
63
+ - Windows x86_64 and arm64
58
64
 
59
65
  CPython 3.10, 3.11, 3.12, 3.13, and 3.14.
60
66
 
@@ -74,6 +80,9 @@ python3.14 -m pip install a3s-code
74
80
  | `A3S_CODE_CACHE_DIR` | Cache root (defaults to `$XDG_CACHE_HOME/a3s-code` or `~/.cache/a3s-code`) |
75
81
  | `A3S_CODE_RELEASES_BASE_URL` | Override the release base URL — useful for air-gapped mirrors |
76
82
  | `A3S_CODE_SKIP_HASH_CHECK` | `1` skips sha256 verification (do not use in production) |
83
+ | `A3S_CODE_MOLI_EXECUTABLE` | Explicit verified Moli executable; skips package discovery |
84
+ | `A3S_CODE_MOLI_CACHE_DIR` | Core-managed shared Moli cache root (the bootstrap uses its version/target cache for a bundled sidecar) |
85
+ | `A3S_CODE_MOLI_RELEASE_BASE_URL` | HTTPS mirror for the pinned Moli release |
77
86
 
78
87
  ## Manual install
79
88
 
@@ -13,8 +13,10 @@ import io
13
13
  import json
14
14
  import os
15
15
  import shutil
16
+ import subprocess
16
17
  import sys
17
18
  import tempfile
19
+ import time
18
20
  import unittest
19
21
  import unittest.mock as mock
20
22
  import zipfile
@@ -32,12 +34,16 @@ _bootstrap = importlib.util.module_from_spec(_spec)
32
34
  _spec.loader.exec_module(_bootstrap)
33
35
 
34
36
 
35
- def _make_wheel(native_blob: bytes = b"fake-extension-blob") -> bytes:
36
- """Build a minimal in-memory wheel containing _native.something.so."""
37
+ def _make_wheel(
38
+ native_blob: bytes = b"fake-extension-blob", *, include_moli: bool = False
39
+ ) -> bytes:
40
+ """Build a minimal in-memory wheel containing a native extension."""
37
41
  buf = io.BytesIO()
38
42
  with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
39
43
  zf.writestr("a3s_code/__init__.py", "from ._native import *\n")
40
44
  zf.writestr("a3s_code/_native.cpython-312-x86_64-linux-gnu.so", native_blob)
45
+ if include_moli:
46
+ zf.writestr("a3s_code/moli/moli", b"#!/bin/sh\nexit 0\n")
41
47
  zf.writestr("a3s_code-3.2.1.dist-info/METADATA", "Metadata-Version: 2.1\n")
42
48
  zf.writestr("a3s_code-3.2.1.dist-info/WHEEL", "Wheel-Version: 1.0\n")
43
49
  return buf.getvalue()
@@ -69,6 +75,12 @@ class WheelFilenameTests(unittest.TestCase):
69
75
  "a3s_code-3.2.1-cp312-cp312-manylinux_2_28_x86_64.whl",
70
76
  )
71
77
 
78
+ def test_linux_arm64_cp312(self):
79
+ self.assertEqual(
80
+ self._filename_for("linux", "aarch64", 12),
81
+ "a3s_code-3.2.1-cp312-cp312-manylinux_2_28_aarch64.whl",
82
+ )
83
+
72
84
  def test_macos_arm64_cp311(self):
73
85
  self.assertEqual(
74
86
  self._filename_for("darwin", "arm64", 11),
@@ -136,6 +148,12 @@ class WheelFilenameTests(unittest.TestCase):
136
148
  "a3s_code-3.2.1-cp313-cp313-win_amd64.whl",
137
149
  )
138
150
 
151
+ def test_windows_arm64_cp313(self):
152
+ self.assertEqual(
153
+ self._filename_for("win32", "ARM64", 13),
154
+ "a3s_code-3.2.1-cp313-cp313-win_arm64.whl",
155
+ )
156
+
139
157
  def test_unsupported_platform_raises(self):
140
158
  with self.assertRaises(_bootstrap.BootstrapError) as cm:
141
159
  self._filename_for("freebsd", "x86_64", 12)
@@ -190,6 +208,56 @@ class CacheDirTests(unittest.TestCase):
190
208
  )
191
209
 
192
210
 
211
+ class InstallLockTests(unittest.TestCase):
212
+ def test_lock_releases_for_another_import(self):
213
+ with tempfile.TemporaryDirectory() as tmp:
214
+ cache = Path(tmp) / "cache"
215
+ with _bootstrap._install_lock(cache):
216
+ self.assertTrue((cache / ".install.lock").is_file())
217
+ # A normal close must release the advisory lock, not merely leave
218
+ # an apparently reusable lock file behind.
219
+ with _bootstrap._install_lock(cache, timeout_s=0.2):
220
+ self.assertTrue((cache / ".install.lock").is_file())
221
+
222
+ def test_lock_serializes_independent_processes(self):
223
+ with tempfile.TemporaryDirectory() as tmp:
224
+ cache = Path(tmp) / "cache"
225
+ ready = Path(tmp) / "ready"
226
+ module_path = str(_BOOTSTRAP_PATH)
227
+ child_code = """
228
+ import importlib.util
229
+ import pathlib
230
+ import sys
231
+ import time
232
+
233
+ module_path, cache_path, ready_path = sys.argv[1:]
234
+ spec = importlib.util.spec_from_file_location('_bootstrap_child', module_path)
235
+ module = importlib.util.module_from_spec(spec)
236
+ spec.loader.exec_module(module)
237
+ with module._install_lock(pathlib.Path(cache_path)):
238
+ pathlib.Path(ready_path).write_text('ready', encoding='utf-8')
239
+ time.sleep(0.35)
240
+ """
241
+ child = subprocess.Popen(
242
+ [sys.executable, "-c", child_code, module_path, str(cache), str(ready)],
243
+ stdout=subprocess.PIPE,
244
+ stderr=subprocess.PIPE,
245
+ text=True,
246
+ )
247
+ try:
248
+ deadline = time.monotonic() + 5
249
+ while not ready.exists() and time.monotonic() < deadline:
250
+ time.sleep(0.01)
251
+ self.assertTrue(ready.exists(), "child did not acquire install lock")
252
+ started = time.monotonic()
253
+ with _bootstrap._install_lock(cache, timeout_s=2):
254
+ waited = time.monotonic() - started
255
+ self.assertGreaterEqual(waited, 0.25)
256
+ finally:
257
+ stdout, stderr = child.communicate(timeout=5)
258
+ if child.returncode != 0:
259
+ self.fail(f"lock child failed: {stdout}\n{stderr}")
260
+
193
261
  class ExtractNativeTests(unittest.TestCase):
194
262
  def test_extracts_native_extension(self):
195
263
  wheel_bytes = _make_wheel(b"native-bytes")
@@ -200,6 +268,27 @@ class ExtractNativeTests(unittest.TestCase):
200
268
  self.assertTrue(out.name.startswith("_native."))
201
269
  self.assertEqual(out.read_bytes(), b"native-bytes")
202
270
 
271
+ def test_extracts_and_configures_bundled_moli_sidecar(self):
272
+ wheel_bytes = _make_wheel(include_moli=True)
273
+ with tempfile.TemporaryDirectory() as tmp:
274
+ target = Path(tmp) / "a3s_code"
275
+ with mock.patch.dict(os.environ, {}, clear=False):
276
+ os.environ.pop("A3S_CODE_MOLI_EXECUTABLE", None)
277
+ _bootstrap._extract_native(wheel_bytes, target)
278
+ moli = target / "moli" / "moli"
279
+ self.assertTrue(moli.is_file())
280
+ self.assertEqual(os.environ.get("A3S_CODE_MOLI_EXECUTABLE"), str(moli))
281
+ self.assertTrue(os.access(moli, os.X_OK))
282
+
283
+ def test_rejects_unsafe_wheel_member(self):
284
+ buf = io.BytesIO()
285
+ with zipfile.ZipFile(buf, "w") as zf:
286
+ zf.writestr("../escape", b"bad")
287
+ zf.writestr("a3s_code/_native.so", b"native")
288
+ with tempfile.TemporaryDirectory() as tmp:
289
+ with self.assertRaises(_bootstrap.BootstrapError):
290
+ _bootstrap._extract_native(buf.getvalue(), Path(tmp) / "pkg")
291
+
203
292
  def test_wheel_without_native_raises(self):
204
293
  buf = io.BytesIO()
205
294
  with zipfile.ZipFile(buf, "w") as zf:
File without changes
File without changes