aether-dsc 0.2.0__tar.gz → 0.2.2__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.
Files changed (23) hide show
  1. aether_dsc-0.2.2/NOTICE +8 -0
  2. {aether_dsc-0.2.0/aether_dsc.egg-info → aether_dsc-0.2.2}/PKG-INFO +5 -1
  3. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/README.md +4 -0
  4. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc/__init__.py +14 -2
  5. aether_dsc-0.2.2/aether_dsc/_payload/c19a2ae533cbef09f605b99cce9c58d42fa00928b99327a361023a3f8f3cc7d1.bin +0 -0
  6. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc/payload_headers_host.txt +1 -0
  7. {aether_dsc-0.2.0 → aether_dsc-0.2.2/aether_dsc.egg-info}/PKG-INFO +5 -1
  8. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc.egg-info/SOURCES.txt +3 -2
  9. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/pyproject.toml +4 -1
  10. aether_dsc-0.2.2/tests/test_ft_payload.py +109 -0
  11. aether_dsc-0.2.0/NOTICE +0 -2
  12. aether_dsc-0.2.0/aether_dsc/_payload/a9c89146169d59183c41dba2a050a420a5f3dd40729154ae547b61a2239c38b8.bin +0 -0
  13. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/LICENSE +0 -0
  14. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc/_core.py +0 -0
  15. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc/_rtc_check.py +0 -0
  16. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc/_seal_impl.py +0 -0
  17. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc/payload_headers.txt +0 -0
  18. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc/seal.py +0 -0
  19. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc.egg-info/dependency_links.txt +0 -0
  20. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc.egg-info/requires.txt +0 -0
  21. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/aether_dsc.egg-info/top_level.txt +0 -0
  22. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/setup.cfg +0 -0
  23. {aether_dsc-0.2.0 → aether_dsc-0.2.2}/tests/test_aether_dsc.py +0 -0
@@ -0,0 +1,8 @@
1
+ aether
2
+ Copyright 2026 Alessandro Masat
3
+
4
+ The table-driven exp and log in aether/backend/cpu/simd/math/PacketExpLog.h
5
+ follow the algorithms of ARM optimized-routines
6
+ (https://github.com/ARM-software/optimized-routines, math/exp.c and math/log.c),
7
+ Copyright (c) Arm Limited, MIT OR Apache-2.0 WITH LLVM-exception. The lookup
8
+ tables in PacketExpLogTables.h were regenerated for aether.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-dsc
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: aether-dsc — the sealed AETHER header payload: opaque, digest-named, no readable source in the wheel
5
5
  Author: Alessandro Masat
6
6
  License: Apache-2.0
@@ -58,3 +58,7 @@ refused, never shipped silently broken.
58
58
  The blob is a zlib-compressed tar — not obfuscation against a determined
59
59
  reader, just enough that `strings`/`cat` on the wheel does not hand back
60
60
  readable C++ source.
61
+
62
+ ## Threading
63
+
64
+ Free-threaded CPython (3.13t/3.14t) is supported: the compiled modules declare GIL-free operation and the GIL stays disabled after import. Any number of threads may call module-level functions, build, compile, plan and cache concurrently. Distinct objects may be used from distinct threads without synchronisation. One stateful object (a stream, capture, launcher, graph, composer, plan, pipeline, active set, host kernel or arg block) shared by several threads is memory-safe — its calls serialise and a consumed object raises — but the ORDER of those calls is the caller's responsibility, exactly as for a NumPy array or a CuPy stream. CUDA adds two rules of its own: a stream capture is begun, filled and ended by one thread, and while any capture is open no thread may synchronise the whole device (stream-level synchronisation is fine). GPU routes are supported on free-threaded 3.14; on 3.13t the CPU route runs.
@@ -39,3 +39,7 @@ refused, never shipped silently broken.
39
39
  The blob is a zlib-compressed tar — not obfuscation against a determined
40
40
  reader, just enough that `strings`/`cat` on the wheel does not hand back
41
41
  readable C++ source.
42
+
43
+ ## Threading
44
+
45
+ Free-threaded CPython (3.13t/3.14t) is supported: the compiled modules declare GIL-free operation and the GIL stays disabled after import. Any number of threads may call module-level functions, build, compile, plan and cache concurrently. Distinct objects may be used from distinct threads without synchronisation. One stateful object (a stream, capture, launcher, graph, composer, plan, pipeline, active set, host kernel or arg block) shared by several threads is memory-safe — its calls serialise and a consumed object raises — but the ORDER of those calls is the caller's responsibility, exactly as for a NumPy array or a CuPy stream. CUDA adds two rules of its own: a stream capture is begun, filled and ended by one thread, and while any capture is open no thread may synchronise the whole device (stream-level synchronisation is fine). GPU routes are supported on free-threaded 3.14; on 3.13t the CPU route runs.
@@ -31,6 +31,8 @@ goes through :func:`seal`; see :mod:`aether_dsc.seal`'s module docstring.
31
31
  """
32
32
  from __future__ import annotations
33
33
 
34
+ import threading
35
+
34
36
  from ._core import Payload, PAYLOAD_DIR, _unpack, digest_of
35
37
  # Imported from `_seal_impl`, deliberately NOT from `.seal`:
36
38
  # `aether_dsc/seal.py` is `python -m aether_dsc.seal`'s CLI file, and
@@ -48,9 +50,10 @@ __all__ = ["Payload", "seal", "payload", "version", "PAYLOAD_DIR"]
48
50
  #: `constexpr version()`), because a sealed payload speaks for exactly one
49
51
  #: aether checkout and a version skew between the two would silently claim
50
52
  #: otherwise.
51
- version = "0.2.0"
53
+ version = "0.2.2"
52
54
 
53
55
  _PAYLOAD_CACHE: Payload | None = None
56
+ _PAYLOAD_LOCK = threading.Lock()
54
57
 
55
58
 
56
59
  def payload() -> Payload:
@@ -66,6 +69,14 @@ def payload() -> Payload:
66
69
  `$HAWK_AETHER_INCLUDE`: a caller that wants "seal if there is no
67
70
  blob" asks for it explicitly, `payload()` never guesses.
68
71
  """
72
+ cached = _PAYLOAD_CACHE
73
+ if cached is not None:
74
+ return cached
75
+ with _PAYLOAD_LOCK:
76
+ return _payload_locked()
77
+
78
+
79
+ def _payload_locked() -> Payload:
69
80
  global _PAYLOAD_CACHE
70
81
  if _PAYLOAD_CACHE is not None:
71
82
  return _PAYLOAD_CACHE
@@ -96,4 +107,5 @@ def _reset_payload_cache() -> None:
96
107
  freshly written blob in a test's tmp `_payload/` dir) is picked up
97
108
  without restarting the process."""
98
109
  global _PAYLOAD_CACHE
99
- _PAYLOAD_CACHE = None
110
+ with _PAYLOAD_LOCK:
111
+ _PAYLOAD_CACHE = None
@@ -24,6 +24,7 @@
24
24
  # ever observed.
25
25
  aether/backend/cpu/simd/math/PacketArcTrig.h
26
26
  aether/backend/cpu/simd/math/PacketExpLog.h
27
+ aether/backend/cpu/simd/math/PacketExpLogTables.h
27
28
  aether/backend/cpu/simd/math/PacketHyperbolic.h
28
29
  aether/backend/cpu/simd/math/PacketMathCore.h
29
30
  aether/backend/cpu/simd/math/PacketMisc.h
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-dsc
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: aether-dsc — the sealed AETHER header payload: opaque, digest-named, no readable source in the wheel
5
5
  Author: Alessandro Masat
6
6
  License: Apache-2.0
@@ -58,3 +58,7 @@ refused, never shipped silently broken.
58
58
  The blob is a zlib-compressed tar — not obfuscation against a determined
59
59
  reader, just enough that `strings`/`cat` on the wheel does not hand back
60
60
  readable C++ source.
61
+
62
+ ## Threading
63
+
64
+ Free-threaded CPython (3.13t/3.14t) is supported: the compiled modules declare GIL-free operation and the GIL stays disabled after import. Any number of threads may call module-level functions, build, compile, plan and cache concurrently. Distinct objects may be used from distinct threads without synchronisation. One stateful object (a stream, capture, launcher, graph, composer, plan, pipeline, active set, host kernel or arg block) shared by several threads is memory-safe — its calls serialise and a consumed object raises — but the ORDER of those calls is the caller's responsibility, exactly as for a NumPy array or a CuPy stream. CUDA adds two rules of its own: a stream capture is begun, filled and ended by one thread, and while any capture is open no thread may synchronise the whole device (stream-level synchronisation is fine). GPU routes are supported on free-threaded 3.14; on 3.13t the CPU route runs.
@@ -14,5 +14,6 @@ aether_dsc.egg-info/SOURCES.txt
14
14
  aether_dsc.egg-info/dependency_links.txt
15
15
  aether_dsc.egg-info/requires.txt
16
16
  aether_dsc.egg-info/top_level.txt
17
- aether_dsc/_payload/a9c89146169d59183c41dba2a050a420a5f3dd40729154ae547b61a2239c38b8.bin
18
- tests/test_aether_dsc.py
17
+ aether_dsc/_payload/c19a2ae533cbef09f605b99cce9c58d42fa00928b99327a361023a3f8f3cc7d1.bin
18
+ tests/test_aether_dsc.py
19
+ tests/test_ft_payload.py
@@ -14,7 +14,7 @@ name = "aether-dsc"
14
14
  # aether-dsc ships a SEALED payload of that exact checkout, so a version skew
15
15
  # between the two would silently claim a payload speaks for a different
16
16
  # aether than it does.
17
- version = "0.2.0"
17
+ version = "0.2.2"
18
18
  description = "aether-dsc — the sealed AETHER header payload: opaque, digest-named, no readable source in the wheel"
19
19
  readme = "README.md"
20
20
  requires-python = ">=3.9"
@@ -67,6 +67,9 @@ aether_dsc = ["py.typed", "_payload/*.bin", "payload_headers.txt",
67
67
 
68
68
  [tool.pytest.ini_options]
69
69
  testpaths = ["tests"]
70
+ markers = [
71
+ "ft: free-threading rows (raptor.conformance.freethreading) -- skip on a GIL build; select with -m ft on 3.13t/3.14t.",
72
+ ]
70
73
  # Makes `import aether_dsc` work against the checked-out tree without an
71
74
  # install — this repo root, prepended to sys.path before collection (mirrors
72
75
  # raptor's/hawk's own pyproject convention).
@@ -0,0 +1,109 @@
1
+ # Copyright 2026 Alessandro Masat
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Free-threading rows for aether-dsc (FT-1 and FT-3: one ``Payload`` identity).
5
+
6
+ On a GIL build the rows SKIP with a reason; the name manifest keeps "skipped"
7
+ from degrading into "silently not collected".
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import sys
13
+ import threading
14
+ import time
15
+
16
+ import pytest
17
+
18
+ import aether_dsc
19
+ from raptor.conformance import freethreading as ft
20
+ from raptor.conformance.freethreading import declare_ft_row, register_ft_row
21
+
22
+ pytestmark = pytest.mark.ft
23
+
24
+ declare_ft_row(
25
+ "FT-1-GIL-FREE-AETHER-DSC", "aether-dsc",
26
+ "importing aether_dsc leaves the GIL off")
27
+ declare_ft_row(
28
+ "FT-3-PAYLOAD-IDENTITY", "aether-dsc",
29
+ "16 threads on a cold payload() cache get one Payload object")
30
+
31
+ EXPECTED_TESTS = frozenset(
32
+ {
33
+ "test_manifest_all_tests_collected",
34
+ "test_rows_declared_and_collected",
35
+ "test_gil_free_import",
36
+ "test_payload_cold_cache_single_identity",
37
+ "test_payload_cold_cache_single_identity_slow_unpack",
38
+ }
39
+ )
40
+
41
+ THREADS = 16
42
+
43
+
44
+ def _cold_race(rounds: int) -> None:
45
+ for _ in range(rounds):
46
+ aether_dsc._reset_payload_cache()
47
+ barrier = threading.Barrier(THREADS)
48
+ got: list[object] = []
49
+ errors: list[BaseException] = []
50
+
51
+ def work() -> None:
52
+ try:
53
+ barrier.wait()
54
+ got.append(aether_dsc.payload())
55
+ except BaseException as exc: # noqa: BLE001 - checked below
56
+ errors.append(exc)
57
+
58
+ pool = [threading.Thread(target=work) for _ in range(THREADS)]
59
+ for t in pool:
60
+ t.start()
61
+ for t in pool:
62
+ t.join()
63
+ assert not errors, errors
64
+ assert len(got) == THREADS
65
+ assert len({id(p) for p in got}) == 1, "payload() returned distinct objects"
66
+ assert all(p is got[0] for p in got)
67
+ assert aether_dsc.payload() is got[0]
68
+
69
+
70
+ def test_manifest_all_tests_collected(request):
71
+ here = {
72
+ i.name for i in request.session.items if i.module is sys.modules[__name__]
73
+ }
74
+ assert EXPECTED_TESTS <= here, sorted(EXPECTED_TESTS - here)
75
+ assert {n for n in here if "[" not in n} <= EXPECTED_TESTS
76
+
77
+
78
+ def test_rows_declared_and_collected():
79
+ ft.assert_ft_rows_complete("aether-dsc")
80
+
81
+
82
+ @register_ft_row("FT-1-GIL-FREE-AETHER-DSC")
83
+ def test_gil_free_import():
84
+ ft.require_free_threaded()
85
+ ft.assert_gil_free("aether_dsc")
86
+
87
+
88
+ @register_ft_row("FT-3-PAYLOAD-IDENTITY")
89
+ def test_payload_cold_cache_single_identity():
90
+ try:
91
+ _cold_race(50)
92
+ finally:
93
+ aether_dsc._reset_payload_cache()
94
+
95
+
96
+ def test_payload_cold_cache_single_identity_slow_unpack(monkeypatch):
97
+ # Widen the check-then-set window: a slow unpack makes an unlocked
98
+ # cold-cache race certain on every interpreter, GIL or not.
99
+ real = aether_dsc._unpack
100
+
101
+ def slow(data):
102
+ time.sleep(0.02)
103
+ return real(data)
104
+
105
+ monkeypatch.setattr(aether_dsc, "_unpack", slow)
106
+ try:
107
+ _cold_race(3)
108
+ finally:
109
+ aether_dsc._reset_payload_cache()
aether_dsc-0.2.0/NOTICE DELETED
@@ -1,2 +0,0 @@
1
- aether
2
- Copyright 2026 Alessandro Masat
File without changes
File without changes