axis-synome 0.1.0.dev16__tar.gz → 0.1.0.dev26__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.
- {axis_synome-0.1.0.dev16/src/axis_synome.egg-info → axis_synome-0.1.0.dev26}/PKG-INFO +26 -1
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/README.md +23 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/WRITING_SPECS.md +30 -34
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/pyproject.toml +8 -2
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/_version.py +2 -2
- axis_synome-0.1.0.dev26/src/axis_synome/spec_support/runtime/base.py +93 -0
- axis_synome-0.1.0.dev26/src/axis_synome/spec_support/runtime/math.py +32 -0
- axis_synome-0.1.0.dev26/src/axis_synome/spec_support/runtime/reference.py +20 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_validator/checker.py +37 -7
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_validator/python_subset.py +2 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26/src/axis_synome.egg-info}/PKG-INFO +26 -1
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome.egg-info/SOURCES.txt +8 -2
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome.egg-info/requires.txt +3 -0
- axis_synome-0.1.0.dev26/tests/axis_synome/risk_capital/formulas/__init__.py +0 -0
- axis_synome-0.1.0.dev26/tests/axis_synome/spec_support/__init__.py +0 -0
- axis_synome-0.1.0.dev26/tests/axis_synome/spec_support/runtime/__init__.py +0 -0
- axis_synome-0.1.0.dev26/tests/axis_synome/spec_support/runtime/test_base.py +52 -0
- axis_synome-0.1.0.dev26/tests/axis_synome/spec_support/runtime/test_math.py +30 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/spec_validator/test_checker.py +89 -5
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/spec_validator/test_flake8_plugin.py +1 -1
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/uv.lock +108 -20
- axis_synome-0.1.0.dev16/src/axis_synome/spec_support/metadata.py +0 -22
- axis_synome-0.1.0.dev16/tests/axis_synome/asc/test_asc_spec_client_parity.py +0 -235
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/.flake8 +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/setup.cfg +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/README.md +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/assets.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/assets_by_prime.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/networks.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/primes.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/protocol_sets.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/tokens.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/entities/types.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/formulas/asc.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/formulas/asc_collateral_ratio.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/formulas/asc_incentive.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/formulas/dab.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/formulas/latent_asc.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/formulas/ratio_latent_asc.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/asc/formulas/resting_asc.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/codegen_test/entities/agents.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/crypto_lending/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/crypto_lending/formulas/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/crypto_lending/formulas/lif.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/risk_capital/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/risk_capital/formulas/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec/risk_capital/formulas/required_risk_capital.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_support/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_support/evm_address.py +0 -0
- {axis_synome-0.1.0.dev16/tests/axis_synome/risk_capital → axis_synome-0.1.0.dev26/src/axis_synome/spec_support/runtime}/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_support/validated_dataclass.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_support/validated_str.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_validator/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_validator/flake8_plugin.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome.egg-info/dependency_links.txt +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome.egg-info/entry_points.txt +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome.egg-info/top_level.txt +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/conftest.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/mocks.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_asc.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_asc_collateral_ratio.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_asc_incentive.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_dab.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_evm_address.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_latent_asc.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_prime_agent_data_validation.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_ratio_latent_asc.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/asc/test_resting_asc.py +0 -0
- {axis_synome-0.1.0.dev16/tests/axis_synome/risk_capital/formulas → axis_synome-0.1.0.dev26/tests/axis_synome/risk_capital}/__init__.py +0 -0
- {axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/tests/axis_synome/risk_capital/formulas/test_loss_given_default.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: axis-synome
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev26
|
|
4
4
|
Summary: Axis specification modules (entities, formulas, validators)
|
|
5
5
|
Author-email: Archon Tech <hello@archontech.ai>
|
|
6
6
|
License: MIT
|
|
@@ -10,6 +10,8 @@ Requires-Python: >=3.11
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
Requires-Dist: eth-hash[pycryptodome]>=0.5.0
|
|
12
12
|
Requires-Dist: pydantic>=2.0.0
|
|
13
|
+
Provides-Extra: runtime
|
|
14
|
+
Requires-Dist: numpy>=1.26; extra == "runtime"
|
|
13
15
|
|
|
14
16
|
# axis-synome
|
|
15
17
|
|
|
@@ -18,6 +20,7 @@ A Python framework for defining and validating financial specifications, with a
|
|
|
18
20
|
- **ASC Specifications** — Actively Stabilizing Capital entities (assets, tokens, networks, protocols) and formulas (collateral ratios, incentives, resting/latent ASC)
|
|
19
21
|
- **Spec Validator** — Flake8 plugin for enforcing Python subset constraints on spec files
|
|
20
22
|
- **Metadata & Utilities** — Support for spec documentation and validated dataclasses
|
|
23
|
+
- **SpecRuntime** — Pluggable runtime abstraction for opaque operations (linear interpolation, ...) that specs can call without depending on a specific implementation library
|
|
21
24
|
|
|
22
25
|
## Installation
|
|
23
26
|
|
|
@@ -27,6 +30,12 @@ Install from PyPI:
|
|
|
27
30
|
pip install axis-synome
|
|
28
31
|
```
|
|
29
32
|
|
|
33
|
+
To enable the built-in `ReferenceSpecRuntime` (numpy-backed), install with the `runtime` extra:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install axis-synome[runtime]
|
|
37
|
+
```
|
|
38
|
+
|
|
30
39
|
Or install from a private registry:
|
|
31
40
|
|
|
32
41
|
```bash
|
|
@@ -47,6 +56,22 @@ from axis_synome.spec.asc.entities import primes, tokens, networks, protocol_set
|
|
|
47
56
|
from axis_synome.spec.asc.formulas import asc, asc_collateral_ratio, asc_incentive
|
|
48
57
|
```
|
|
49
58
|
|
|
59
|
+
### Opaque Operations in Specs
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from axis_synome.spec_support.runtime import math
|
|
63
|
+
|
|
64
|
+
values = math.linear_interp([1.5], [1.0, 2.0], [10.0, 20.0]) # -> [15.0]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The active runtime is selected once at import time. Resolution order:
|
|
68
|
+
|
|
69
|
+
1. `AXIS_SYNOME_SPEC_RUNTIME=module.path:ClassName` env var
|
|
70
|
+
2. A registered `axis_synome.spec_runtimes` entry point (declared by a consumer package's `pyproject.toml`)
|
|
71
|
+
3. The bundled `ReferenceSpecRuntime` (requires `axis-synome[runtime]`)
|
|
72
|
+
|
|
73
|
+
If none is available, importing `axis_synome.spec_support.runtime.math` raises `ImportError` — the runtime is required at import time, not on first call.
|
|
74
|
+
|
|
50
75
|
### Spec Validation
|
|
51
76
|
|
|
52
77
|
```python
|
|
@@ -5,6 +5,7 @@ A Python framework for defining and validating financial specifications, with a
|
|
|
5
5
|
- **ASC Specifications** — Actively Stabilizing Capital entities (assets, tokens, networks, protocols) and formulas (collateral ratios, incentives, resting/latent ASC)
|
|
6
6
|
- **Spec Validator** — Flake8 plugin for enforcing Python subset constraints on spec files
|
|
7
7
|
- **Metadata & Utilities** — Support for spec documentation and validated dataclasses
|
|
8
|
+
- **SpecRuntime** — Pluggable runtime abstraction for opaque operations (linear interpolation, ...) that specs can call without depending on a specific implementation library
|
|
8
9
|
|
|
9
10
|
## Installation
|
|
10
11
|
|
|
@@ -14,6 +15,12 @@ Install from PyPI:
|
|
|
14
15
|
pip install axis-synome
|
|
15
16
|
```
|
|
16
17
|
|
|
18
|
+
To enable the built-in `ReferenceSpecRuntime` (numpy-backed), install with the `runtime` extra:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install axis-synome[runtime]
|
|
22
|
+
```
|
|
23
|
+
|
|
17
24
|
Or install from a private registry:
|
|
18
25
|
|
|
19
26
|
```bash
|
|
@@ -34,6 +41,22 @@ from axis_synome.spec.asc.entities import primes, tokens, networks, protocol_set
|
|
|
34
41
|
from axis_synome.spec.asc.formulas import asc, asc_collateral_ratio, asc_incentive
|
|
35
42
|
```
|
|
36
43
|
|
|
44
|
+
### Opaque Operations in Specs
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from axis_synome.spec_support.runtime import math
|
|
48
|
+
|
|
49
|
+
values = math.linear_interp([1.5], [1.0, 2.0], [10.0, 20.0]) # -> [15.0]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The active runtime is selected once at import time. Resolution order:
|
|
53
|
+
|
|
54
|
+
1. `AXIS_SYNOME_SPEC_RUNTIME=module.path:ClassName` env var
|
|
55
|
+
2. A registered `axis_synome.spec_runtimes` entry point (declared by a consumer package's `pyproject.toml`)
|
|
56
|
+
3. The bundled `ReferenceSpecRuntime` (requires `axis-synome[runtime]`)
|
|
57
|
+
|
|
58
|
+
If none is available, importing `axis_synome.spec_support.runtime.math` raises `ImportError` — the runtime is required at import time, not on first call.
|
|
59
|
+
|
|
37
60
|
### Spec Validation
|
|
38
61
|
|
|
39
62
|
```python
|
|
@@ -175,6 +175,34 @@ a fixed set instiantiated objects. `value_usd` is additionally restricted with a
|
|
|
175
175
|
- Uplift to shared only when needed: introduce shared entities at top level (`axis/entities/`) when the same entities are used by multiple formulas and sharing materially reduces duplication. A pragmatic threshold is when a third, independent formula reuses the same entities.
|
|
176
176
|
- Don’t add empty common folders: only create and populate shared folders when you actually have shared entities.
|
|
177
177
|
- Moving entities is a breaking change: import paths change when uplifting to shared. Provide re‑exports where feasible during transitions and bump the major version when breaking import paths.
|
|
178
|
+
|
|
179
|
+
### SpecRuntime (Opaque Functions)
|
|
180
|
+
|
|
181
|
+
Use only when the operation cannot be expressed in the allowed, parser‑friendly subset, or is so complex (e.g., ARIMA/GARCH/ML/DNN) that a full mathematical spec is infeasible. Prefer parser‑friendly constructs first (comprehensions + sum/min/max/any/all, simple math.*, scalar per‑element helpers).
|
|
182
|
+
|
|
183
|
+
- Use `axis_synome.spec_support.runtime` (Built‑in SpecRuntime)
|
|
184
|
+
- What: a small set of opaque primitives exposed as free functions under `axis_synome.spec_support.runtime.<domain>` (e.g. `runtime.math.linear_interp`). Calls are dispatched to a `SpecRuntime` resolved once at package import time. The parser treats these calls as uninterpreted; the runtime implementation can use NumPy/StatsModels/etc.
|
|
185
|
+
- When: the operation truly requires external libraries (e.g., interpolation, rolling_var, GARCH/ARIMA); you need production‑grade runtime while keeping the spec parseable.
|
|
186
|
+
- How (spec author):
|
|
187
|
+
- Import the relevant submodule and call the function directly:
|
|
188
|
+
- `from axis_synome.spec_support.runtime import math`
|
|
189
|
+
- `math.linear_interp(x, xp, fp)`
|
|
190
|
+
- Do not call `get_runtime()` directly from spec code; treat the `SpecRuntime` class as an internal extension point.
|
|
191
|
+
- How (runtime selection at deploy/test time): in priority order,
|
|
192
|
+
1. `AXIS_SYNOME_SPEC_RUNTIME=my_pkg.module:MyRuntime` env var.
|
|
193
|
+
2. Register an entry point in the consuming package's `pyproject.toml`:
|
|
194
|
+
```toml
|
|
195
|
+
[project.entry-points."axis_synome.spec_runtimes"]
|
|
196
|
+
default = "my_pkg.module:MyRuntime"
|
|
197
|
+
```
|
|
198
|
+
3. Otherwise, the bundled `ReferenceSpecRuntime` (numpy‑backed, requires the `[runtime]` extra) is used.
|
|
199
|
+
- Adding a new operation: extend `SpecRuntime` with a new abstract method, implement it in `ReferenceSpecRuntime`, and add a thin wrapper in `axis_synome/spec_support/runtime/<domain>.py` that simply calls `get_runtime().<op>(...)`.
|
|
200
|
+
|
|
201
|
+
- Authoring Rules
|
|
202
|
+
- Keep wrappers zero‑logic (no exceptions/branching); just delegate to `get_runtime()`.
|
|
203
|
+
- Use precise, stable types and consistent snake_case names.
|
|
204
|
+
- Briefly document new opaque functions in `runtime/README.md` (purpose, signature, runtime notes).
|
|
205
|
+
|
|
178
206
|
### Formulas
|
|
179
207
|
|
|
180
208
|
- Formula functions are pure: `(inputs) -> output`, but process inputs to intermediate values by calling other formula functions (or by utilizing a restricted set of built-in functions and external functions)
|
|
@@ -299,13 +327,6 @@ Assuming that `PrimeAgentAll` is an Enum consisting of prime agent instances, to
|
|
|
299
327
|
- Location: keep tests under `python/axis_synome/tests/` organized by concept/module. Treat these as internal by default; they can be published later based on demand.
|
|
300
328
|
- Declarative first: specs are declarative and amenable to static verification; tests complement by documenting usage and catching regressions in opaque integrations.
|
|
301
329
|
|
|
302
|
-
#### Reference Implementations for Opaque Functions
|
|
303
|
-
|
|
304
|
-
- Opaque functions (e.g., ARIMA) are too complex to fully specify as pure math. To execute happy‑path tests, provide a minimal “test math engine” or reference implementation used only by tests.
|
|
305
|
-
- Do not publish as an official runtime dependency initially. If users request a ready‑made engine, consider publishing a separate package later.
|
|
306
|
-
- Multiple valid approaches may exist; tests should pick one concrete implementation as an example without constraining others.
|
|
307
|
-
|
|
308
|
-
|
|
309
330
|
## Starter Template
|
|
310
331
|
|
|
311
332
|
Start a new spec module with (simplified example; in real modules, entities typically live in their own package folder):
|
|
@@ -391,7 +412,7 @@ def threshold_satisfied(owner: EligibleOwner) -> bool:
|
|
|
391
412
|
- Aggregators: `sum`, `min`, `max`, `any`, `all` over generator expressions.
|
|
392
413
|
- Generator expressions and simple `range(...)` where needed.
|
|
393
414
|
- Selected `math.*` (e.g., `log`, `exp`, `sqrt`, `pow`).
|
|
394
|
-
- Imports from `axis_synome.spec`, `synome`, `typing`, `dataclasses`, `enum`, `pydantic`, and bare `math`.
|
|
415
|
+
- Imports from `axis_synome.spec`, `axis_synome.spec_support`, `synome`, `typing`, `dataclasses`, `enum`, `pydantic`, and bare `math`.
|
|
395
416
|
|
|
396
417
|
- Avoid / Disallowed
|
|
397
418
|
- Mutation, I/O, networking, side effects; exceptions and `try/except`.
|
|
@@ -493,7 +514,7 @@ Use the Makefile targets to run checks consistently:
|
|
|
493
514
|
|
|
494
515
|
- For-loops inside formulas → Replace with generator expressions plus `sum(...)`, `any(...)`, or `all(...)`.
|
|
495
516
|
- Dict/set comprehensions → Move structure-building to entities or use lists/tuples; keep formula bodies expression-centric.
|
|
496
|
-
- Unapproved builtins/imports → Use only builtins in the allowlist and imports under permitted prefixes (`axis_synome.spec`, `typing`, `dataclasses`, `enum`, `pydantic`, `synome`, or `math`).
|
|
517
|
+
- Unapproved builtins/imports → Use only builtins in the allowlist and imports under permitted prefixes (`axis_synome.spec`, `axis_synome.spec_support`, `typing`, `dataclasses`, `enum`, `pydantic`, `synome`, or `math`).
|
|
497
518
|
- Methods in dataclasses/Enums → Move logic into free functions; limit classes to annotated fields (dataclasses) or constant members (Enums).
|
|
498
519
|
- Missing/loose types → Add precise argument and return annotations; prefer closed-world Enums where policy implies eligibility.
|
|
499
520
|
|
|
@@ -646,31 +667,6 @@ def all_positions_above(positions: list[float], threshold: float) -> bool:
|
|
|
646
667
|
return all(p >= threshold for p in positions)
|
|
647
668
|
```
|
|
648
669
|
|
|
649
|
-
## Opaque Functions (Use Sparingly)
|
|
650
|
-
|
|
651
|
-
Use only when the operation cannot be expressed in the allowed, parser‑friendly subset, or is so complex (e.g., ARIMA/GARCH/ML/DNN) that a full mathematical spec is infeasible. Prefer parser‑friendly constructs first (comprehensions + sum/min/max/any/all, simple math.*, scalar per‑element helpers).
|
|
652
|
-
|
|
653
|
-
- Approach 1 — PascalCase (Uninterpreted Function)
|
|
654
|
-
- What: a normal spec function that the parser represents as an uninterpreted SymPy function; prints clearly and composes symbolically.
|
|
655
|
-
- When: you need a symbolic placeholder; runtime can be pure‑Python or evaluation isn’t required in tests. Good for things like ArgMaxIndex(values).
|
|
656
|
-
- How: define a plain function in the spec with a pure‑Python implementation and precise types; try to keep it as declarative as possible even though it is not translated to math, i.e., it still should throw exceptions or have side-effects.
|
|
657
|
-
|
|
658
|
-
- Approach 2 — Spec Wrapper + MathEngine (External Packages)
|
|
659
|
-
- What: a thin spec wrapper that delegates to a pluggable runtime engine (installed via `set_engine`). The parser treats the call as uninterpreted; runtime can use NumPy/StatsModels/etc.
|
|
660
|
-
- When: the operation truly requires external packages or model fitting (e.g., rolling_var, GARCH/ARIMA); you need production‑grade runtime while keeping the spec parseable.
|
|
661
|
-
- How:
|
|
662
|
-
- Define engine contract (add to your project’s `opaque/engine.py`):
|
|
663
|
-
- `class MathEngine(ABC):`
|
|
664
|
-
- `@abstractmethod`
|
|
665
|
-
- `def rolling_var(self, returns: list[float], window: int, confidence: float) -> list[float]: ...`
|
|
666
|
-
- Add thin wrapper in an opaque module (e.g., `axis/.../opaque/statistical.py`) that simply calls `get_engine().rolling_var(...)`.
|
|
667
|
-
- Provide/install an engine in tests/apps: `from axis_synome.spec.ima.opaque import set_engine; set_engine(ReferenceMathEngine())`.
|
|
668
|
-
|
|
669
|
-
- Authoring Rules
|
|
670
|
-
- Keep wrappers zero‑logic (no exceptions/branching); just delegate to `get_engine()`.
|
|
671
|
-
- Use precise, stable types and consistent snake_case names.
|
|
672
|
-
- Briefly document new opaque functions in `opaque/README.md` (purpose, signature, runtime notes).
|
|
673
|
-
|
|
674
670
|
## Atlas Integration
|
|
675
671
|
|
|
676
672
|
- Keep Atlas UUIDs in docstrings using `:source_uuid: ...` when available. If no Atlas section applies, omit the marker entirely.
|
|
@@ -15,6 +15,11 @@ dependencies = [
|
|
|
15
15
|
"pydantic>=2.0.0",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
runtime = [
|
|
20
|
+
"numpy>=1.26",
|
|
21
|
+
]
|
|
22
|
+
|
|
18
23
|
[project.urls]
|
|
19
24
|
Repository = "https://github.com/archon-research/next-gen-atlas"
|
|
20
25
|
Documentation = "https://github.com/archon-research/next-gen-atlas"
|
|
@@ -28,16 +33,17 @@ include = ["axis_synome*"]
|
|
|
28
33
|
|
|
29
34
|
[tool.setuptools_scm]
|
|
30
35
|
root = "../.."
|
|
31
|
-
tag_regex = '^v(?P<version>\d+\.\d
|
|
36
|
+
tag_regex = '^v(?P<version>\d+\.\d+(?:\.\d+)?)'
|
|
32
37
|
version_file = "src/axis_synome/_version.py"
|
|
33
38
|
|
|
34
39
|
[dependency-groups]
|
|
35
40
|
dev = [
|
|
36
41
|
"pytest>=9.0.0",
|
|
37
42
|
"flake8>=7.0.0",
|
|
38
|
-
"ruff
|
|
43
|
+
"ruff>=0.15",
|
|
39
44
|
"ty>=0.0.16",
|
|
40
45
|
"lefthook>=1.9.5",
|
|
46
|
+
"axis-synome[runtime]",
|
|
41
47
|
]
|
|
42
48
|
|
|
43
49
|
[tool.pytest.ini_options]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, 0, '
|
|
21
|
+
__version__ = version = '0.1.0.dev26'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0, 'dev26')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from importlib import import_module, metadata
|
|
5
|
+
|
|
6
|
+
ENV_VAR = "AXIS_SYNOME_SPEC_RUNTIME"
|
|
7
|
+
ENTRY_POINT_GROUP = "axis_synome.spec_runtimes"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SpecRuntime(ABC):
|
|
11
|
+
"""Internal abstraction backing :mod:`axis_synome.spec_support.runtime`.
|
|
12
|
+
|
|
13
|
+
Spec authors should call the free functions in
|
|
14
|
+
``axis_synome.spec_support.runtime.<domain>`` (e.g. ``runtime.math``)
|
|
15
|
+
rather than interacting with this class directly. Custom
|
|
16
|
+
implementations are selected at process start via the
|
|
17
|
+
``AXIS_SYNOME_SPEC_RUNTIME`` env var or an
|
|
18
|
+
``axis_synome.spec_runtimes`` entry point — see
|
|
19
|
+
:func:`_resolve_default_runtime`.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def linear_interp(
|
|
24
|
+
self,
|
|
25
|
+
x: Sequence[float],
|
|
26
|
+
xp: Sequence[float],
|
|
27
|
+
fp: Sequence[float],
|
|
28
|
+
) -> list[float]:
|
|
29
|
+
"""One-dimensional linear interpolation.
|
|
30
|
+
|
|
31
|
+
Mirrors ``numpy.interp`` semantics: for each value in ``x``, return the
|
|
32
|
+
linearly-interpolated value from the ``(xp, fp)`` table. ``xp`` must be
|
|
33
|
+
increasing; values of ``x`` outside ``[xp[0], xp[-1]]`` are clamped to
|
|
34
|
+
the corresponding boundary value of ``fp``.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _instantiate(target: object) -> "SpecRuntime":
|
|
39
|
+
instance = target() if isinstance(target, type) else target
|
|
40
|
+
if not isinstance(instance, SpecRuntime):
|
|
41
|
+
raise TypeError(f"Resolved object {target!r} is not a SpecRuntime instance or subclass.")
|
|
42
|
+
return instance
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _load_from_env() -> "SpecRuntime | None":
|
|
46
|
+
spec = os.environ.get(ENV_VAR)
|
|
47
|
+
if not spec:
|
|
48
|
+
return None
|
|
49
|
+
if ":" not in spec:
|
|
50
|
+
raise RuntimeError(f"Invalid {ENV_VAR}={spec!r}: expected 'module.path:AttrName'.")
|
|
51
|
+
module_path, attr = spec.split(":", 1)
|
|
52
|
+
module = import_module(module_path)
|
|
53
|
+
return _instantiate(getattr(module, attr))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _load_from_entry_points() -> "SpecRuntime | None":
|
|
57
|
+
eps = list(metadata.entry_points(group=ENTRY_POINT_GROUP))
|
|
58
|
+
if not eps:
|
|
59
|
+
return None
|
|
60
|
+
if len(eps) > 1:
|
|
61
|
+
names = sorted(ep.name for ep in eps)
|
|
62
|
+
raise RuntimeError(
|
|
63
|
+
f"Multiple '{ENTRY_POINT_GROUP}' entry points registered: {names}. "
|
|
64
|
+
f"Set {ENV_VAR}=module:Class to disambiguate."
|
|
65
|
+
)
|
|
66
|
+
return _instantiate(eps[0].load())
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _load_reference() -> "SpecRuntime | None":
|
|
70
|
+
try:
|
|
71
|
+
from axis_synome.spec_support.runtime.reference import ReferenceSpecRuntime
|
|
72
|
+
except ImportError:
|
|
73
|
+
return None
|
|
74
|
+
return ReferenceSpecRuntime()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _resolve_default_runtime() -> "SpecRuntime":
|
|
78
|
+
for loader in (_load_from_env, _load_from_entry_points, _load_reference):
|
|
79
|
+
runtime = loader()
|
|
80
|
+
if runtime is not None:
|
|
81
|
+
return runtime
|
|
82
|
+
raise ImportError(
|
|
83
|
+
f"No SpecRuntime available. Install with `pip install axis-synome[runtime]`, "
|
|
84
|
+
f"set {ENV_VAR}=module:Class, or register an entry point in group "
|
|
85
|
+
f"'{ENTRY_POINT_GROUP}'."
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
_runtime: SpecRuntime = _resolve_default_runtime()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_runtime() -> SpecRuntime:
|
|
93
|
+
return _runtime
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Math operations available to spec authors.
|
|
2
|
+
|
|
3
|
+
This is the preferred API: call these free functions directly
|
|
4
|
+
(e.g. ``math.linear_interp(...)``) rather than going through the
|
|
5
|
+
underlying :class:`~axis_synome.spec_support.runtime.base.SpecRuntime`.
|
|
6
|
+
|
|
7
|
+
When ``axis-synome`` is installed with the ``[runtime]`` extra, calls are
|
|
8
|
+
served by ``ReferenceSpecRuntime`` (numpy-backed) by default. To install
|
|
9
|
+
a custom runtime, set ``AXIS_SYNOME_SPEC_RUNTIME=module:Class`` in the
|
|
10
|
+
environment or declare an ``axis_synome.spec_runtimes`` entry point in
|
|
11
|
+
the consuming package's ``pyproject.toml`` — both are resolved once at
|
|
12
|
+
import time.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from collections.abc import Sequence
|
|
16
|
+
|
|
17
|
+
from axis_synome.spec_support.runtime.base import get_runtime
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def linear_interp(
|
|
21
|
+
x: Sequence[float],
|
|
22
|
+
xp: Sequence[float],
|
|
23
|
+
fp: Sequence[float],
|
|
24
|
+
) -> list[float]:
|
|
25
|
+
"""One-dimensional linear interpolation.
|
|
26
|
+
|
|
27
|
+
Mirrors ``numpy.interp`` semantics: for each value in ``x``, return the
|
|
28
|
+
linearly-interpolated value from the ``(xp, fp)`` table. ``xp`` must be
|
|
29
|
+
increasing; values of ``x`` outside ``[xp[0], xp[-1]]`` are clamped to
|
|
30
|
+
the corresponding boundary value of ``fp``.
|
|
31
|
+
"""
|
|
32
|
+
return get_runtime().linear_interp(x, xp, fp)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from axis_synome.spec_support.runtime.base import SpecRuntime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReferenceSpecRuntime(SpecRuntime):
|
|
9
|
+
"""numpy-backed reference implementation of SpecRuntime.
|
|
10
|
+
|
|
11
|
+
Requires the ``runtime`` extra: ``pip install axis-synome[runtime]``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def linear_interp(
|
|
15
|
+
self,
|
|
16
|
+
x: Sequence[float],
|
|
17
|
+
xp: Sequence[float],
|
|
18
|
+
fp: Sequence[float],
|
|
19
|
+
) -> list[float]:
|
|
20
|
+
return np.interp(x, xp, fp).tolist()
|
{axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_validator/checker.py
RENAMED
|
@@ -30,6 +30,10 @@ class SpecChecker(ast.NodeVisitor):
|
|
|
30
30
|
self._in_function = False
|
|
31
31
|
self._module_imported_names: set[str] = set() # Track imports for shadowing detection
|
|
32
32
|
self._function_assigned_names: set[str] = set() # Track assignments for shadowing detection
|
|
33
|
+
# Local names bound to axis_synome.spec_support.runtime submodules
|
|
34
|
+
# (e.g. spec_support.runtime.math). Calls on these bypass the curated
|
|
35
|
+
# stdlib math-function allowlist.
|
|
36
|
+
self._opaque_module_names: set[str] = set()
|
|
33
37
|
|
|
34
38
|
def _error(self, node: ast.AST, msg: str) -> None:
|
|
35
39
|
lineno = getattr(node, "lineno", 0)
|
|
@@ -149,6 +153,23 @@ class SpecChecker(ast.NodeVisitor):
|
|
|
149
153
|
top_level = module.split(".")[0]
|
|
150
154
|
if top_level not in ALLOWED_FROM_IMPORT_PREFIXES:
|
|
151
155
|
self._error(node, f'import from "{module}" is not allowed')
|
|
156
|
+
return
|
|
157
|
+
if module == "axis_synome.spec_support.runtime" or module.startswith(
|
|
158
|
+
"axis_synome.spec_support.runtime."
|
|
159
|
+
):
|
|
160
|
+
for alias in node.names:
|
|
161
|
+
self._opaque_module_names.add(alias.asname or alias.name)
|
|
162
|
+
# Restrict math imports to the allowed function names only.
|
|
163
|
+
if top_level == "math":
|
|
164
|
+
for alias in node.names:
|
|
165
|
+
if alias.name == "*":
|
|
166
|
+
self._error(node, 'wildcard import from "math" is not allowed')
|
|
167
|
+
elif alias.name not in ALLOWED_MATH_FUNCTION_NAMES:
|
|
168
|
+
self._error(
|
|
169
|
+
node,
|
|
170
|
+
f'"{alias.name}" is not an allowed math import; '
|
|
171
|
+
f"permitted: {sorted(ALLOWED_MATH_FUNCTION_NAMES)}",
|
|
172
|
+
)
|
|
152
173
|
|
|
153
174
|
# ------------------------------------------------------------------
|
|
154
175
|
# Names
|
|
@@ -181,6 +202,10 @@ class SpecChecker(ast.NodeVisitor):
|
|
|
181
202
|
and isinstance(func.value, ast.Name)
|
|
182
203
|
and func.value.id in ALLOWED_MODULE_CALL_PREFIXES
|
|
183
204
|
):
|
|
205
|
+
# Calls on axis_synome.spec_support.runtime modules (e.g. runtime.math)
|
|
206
|
+
# are uninterpreted and not subject to the stdlib math allowlist.
|
|
207
|
+
if func.value.id in self._opaque_module_names:
|
|
208
|
+
return
|
|
184
209
|
if func.attr not in ALLOWED_MATH_FUNCTION_NAMES:
|
|
185
210
|
self._error(
|
|
186
211
|
node,
|
|
@@ -214,9 +239,6 @@ class SpecChecker(ast.NodeVisitor):
|
|
|
214
239
|
def visit_Lambda(self, node: ast.Lambda) -> None:
|
|
215
240
|
self._error(node, "lambda expressions are not allowed")
|
|
216
241
|
|
|
217
|
-
def visit_ListComp(self, node: ast.ListComp) -> None:
|
|
218
|
-
self._error(node, "list comprehensions are not allowed (use generator expressions)")
|
|
219
|
-
|
|
220
242
|
def visit_DictComp(self, node: ast.DictComp) -> None:
|
|
221
243
|
self._error(node, "dict comprehensions are not allowed")
|
|
222
244
|
|
|
@@ -262,12 +284,20 @@ class SpecChecker(ast.NodeVisitor):
|
|
|
262
284
|
return
|
|
263
285
|
# Only @dataclass or @validated_dataclass decorated classes are allowed.
|
|
264
286
|
allowed_dataclass_decorators = {"dataclass", "validated_dataclass"}
|
|
287
|
+
|
|
288
|
+
def _decorator_name(d: ast.expr) -> str | None:
|
|
289
|
+
if isinstance(d, ast.Name):
|
|
290
|
+
return d.id
|
|
291
|
+
if isinstance(d, ast.Attribute):
|
|
292
|
+
return d.attr
|
|
293
|
+
if isinstance(d, ast.Call):
|
|
294
|
+
return _decorator_name(d.func)
|
|
295
|
+
return None
|
|
296
|
+
|
|
265
297
|
if not any(
|
|
266
|
-
(
|
|
267
|
-
or (isinstance(d, ast.Attribute) and d.attr in allowed_dataclass_decorators)
|
|
268
|
-
for d in node.decorator_list
|
|
298
|
+
_decorator_name(d) in allowed_dataclass_decorators for d in node.decorator_list
|
|
269
299
|
):
|
|
270
|
-
self._error(node, "only @dataclass classes are allowed")
|
|
300
|
+
self._error(node, "only @dataclass or @validated_dataclass classes are allowed")
|
|
271
301
|
return
|
|
272
302
|
# Validate the class body: only annotated assignments and docstrings.
|
|
273
303
|
for stmt in node.body:
|
{axis_synome-0.1.0.dev16 → axis_synome-0.1.0.dev26}/src/axis_synome/spec_validator/python_subset.py
RENAMED
|
@@ -64,6 +64,7 @@ ALLOWED_FROM_IMPORT_PREFIXES: frozenset[str] = frozenset({
|
|
|
64
64
|
"axis_synome",
|
|
65
65
|
"dataclasses",
|
|
66
66
|
"enum",
|
|
67
|
+
"math",
|
|
67
68
|
"pydantic",
|
|
68
69
|
"synome",
|
|
69
70
|
"typing",
|
|
@@ -92,6 +93,7 @@ ALLOWED_BUILTINS: frozenset[str] = frozenset({
|
|
|
92
93
|
|
|
93
94
|
ALLOWED_MATH_FUNCTION_NAMES: frozenset[str] = frozenset({
|
|
94
95
|
"log",
|
|
96
|
+
"log1p",
|
|
95
97
|
"exp",
|
|
96
98
|
"sqrt",
|
|
97
99
|
"pow",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: axis-synome
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev26
|
|
4
4
|
Summary: Axis specification modules (entities, formulas, validators)
|
|
5
5
|
Author-email: Archon Tech <hello@archontech.ai>
|
|
6
6
|
License: MIT
|
|
@@ -10,6 +10,8 @@ Requires-Python: >=3.11
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
Requires-Dist: eth-hash[pycryptodome]>=0.5.0
|
|
12
12
|
Requires-Dist: pydantic>=2.0.0
|
|
13
|
+
Provides-Extra: runtime
|
|
14
|
+
Requires-Dist: numpy>=1.26; extra == "runtime"
|
|
13
15
|
|
|
14
16
|
# axis-synome
|
|
15
17
|
|
|
@@ -18,6 +20,7 @@ A Python framework for defining and validating financial specifications, with a
|
|
|
18
20
|
- **ASC Specifications** — Actively Stabilizing Capital entities (assets, tokens, networks, protocols) and formulas (collateral ratios, incentives, resting/latent ASC)
|
|
19
21
|
- **Spec Validator** — Flake8 plugin for enforcing Python subset constraints on spec files
|
|
20
22
|
- **Metadata & Utilities** — Support for spec documentation and validated dataclasses
|
|
23
|
+
- **SpecRuntime** — Pluggable runtime abstraction for opaque operations (linear interpolation, ...) that specs can call without depending on a specific implementation library
|
|
21
24
|
|
|
22
25
|
## Installation
|
|
23
26
|
|
|
@@ -27,6 +30,12 @@ Install from PyPI:
|
|
|
27
30
|
pip install axis-synome
|
|
28
31
|
```
|
|
29
32
|
|
|
33
|
+
To enable the built-in `ReferenceSpecRuntime` (numpy-backed), install with the `runtime` extra:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install axis-synome[runtime]
|
|
37
|
+
```
|
|
38
|
+
|
|
30
39
|
Or install from a private registry:
|
|
31
40
|
|
|
32
41
|
```bash
|
|
@@ -47,6 +56,22 @@ from axis_synome.spec.asc.entities import primes, tokens, networks, protocol_set
|
|
|
47
56
|
from axis_synome.spec.asc.formulas import asc, asc_collateral_ratio, asc_incentive
|
|
48
57
|
```
|
|
49
58
|
|
|
59
|
+
### Opaque Operations in Specs
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from axis_synome.spec_support.runtime import math
|
|
63
|
+
|
|
64
|
+
values = math.linear_interp([1.5], [1.0, 2.0], [10.0, 20.0]) # -> [15.0]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The active runtime is selected once at import time. Resolution order:
|
|
68
|
+
|
|
69
|
+
1. `AXIS_SYNOME_SPEC_RUNTIME=module.path:ClassName` env var
|
|
70
|
+
2. A registered `axis_synome.spec_runtimes` entry point (declared by a consumer package's `pyproject.toml`)
|
|
71
|
+
3. The bundled `ReferenceSpecRuntime` (requires `axis-synome[runtime]`)
|
|
72
|
+
|
|
73
|
+
If none is available, importing `axis_synome.spec_support.runtime.math` raises `ImportError` — the runtime is required at import time, not on first call.
|
|
74
|
+
|
|
50
75
|
### Spec Validation
|
|
51
76
|
|
|
52
77
|
```python
|
|
@@ -37,9 +37,12 @@ src/axis_synome/spec/risk_capital/formulas/__init__.py
|
|
|
37
37
|
src/axis_synome/spec/risk_capital/formulas/required_risk_capital.py
|
|
38
38
|
src/axis_synome/spec_support/__init__.py
|
|
39
39
|
src/axis_synome/spec_support/evm_address.py
|
|
40
|
-
src/axis_synome/spec_support/metadata.py
|
|
41
40
|
src/axis_synome/spec_support/validated_dataclass.py
|
|
42
41
|
src/axis_synome/spec_support/validated_str.py
|
|
42
|
+
src/axis_synome/spec_support/runtime/__init__.py
|
|
43
|
+
src/axis_synome/spec_support/runtime/base.py
|
|
44
|
+
src/axis_synome/spec_support/runtime/math.py
|
|
45
|
+
src/axis_synome/spec_support/runtime/reference.py
|
|
43
46
|
src/axis_synome/spec_validator/__init__.py
|
|
44
47
|
src/axis_synome/spec_validator/checker.py
|
|
45
48
|
src/axis_synome/spec_validator/flake8_plugin.py
|
|
@@ -50,7 +53,6 @@ tests/axis_synome/asc/mocks.py
|
|
|
50
53
|
tests/axis_synome/asc/test_asc.py
|
|
51
54
|
tests/axis_synome/asc/test_asc_collateral_ratio.py
|
|
52
55
|
tests/axis_synome/asc/test_asc_incentive.py
|
|
53
|
-
tests/axis_synome/asc/test_asc_spec_client_parity.py
|
|
54
56
|
tests/axis_synome/asc/test_dab.py
|
|
55
57
|
tests/axis_synome/asc/test_evm_address.py
|
|
56
58
|
tests/axis_synome/asc/test_latent_asc.py
|
|
@@ -60,5 +62,9 @@ tests/axis_synome/asc/test_resting_asc.py
|
|
|
60
62
|
tests/axis_synome/risk_capital/__init__.py
|
|
61
63
|
tests/axis_synome/risk_capital/formulas/__init__.py
|
|
62
64
|
tests/axis_synome/risk_capital/formulas/test_loss_given_default.py
|
|
65
|
+
tests/axis_synome/spec_support/__init__.py
|
|
66
|
+
tests/axis_synome/spec_support/runtime/__init__.py
|
|
67
|
+
tests/axis_synome/spec_support/runtime/test_base.py
|
|
68
|
+
tests/axis_synome/spec_support/runtime/test_math.py
|
|
63
69
|
tests/axis_synome/spec_validator/test_checker.py
|
|
64
70
|
tests/axis_synome/spec_validator/test_flake8_plugin.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from axis_synome.spec_support.runtime.base import (
|
|
6
|
+
ENV_VAR,
|
|
7
|
+
SpecRuntime,
|
|
8
|
+
_resolve_default_runtime,
|
|
9
|
+
get_runtime,
|
|
10
|
+
)
|
|
11
|
+
from axis_synome.spec_support.runtime.reference import ReferenceSpecRuntime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _StubRuntime(SpecRuntime):
|
|
15
|
+
def linear_interp(
|
|
16
|
+
self,
|
|
17
|
+
x: Sequence[float],
|
|
18
|
+
xp: Sequence[float], # noqa: ARG002
|
|
19
|
+
fp: Sequence[float], # noqa: ARG002
|
|
20
|
+
) -> list[float]:
|
|
21
|
+
return [0.0 for _ in x]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _NotARuntime:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_default_runtime_is_reference_runtime():
|
|
29
|
+
assert isinstance(get_runtime(), ReferenceSpecRuntime)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_spec_runtime_cannot_be_instantiated_directly():
|
|
33
|
+
with pytest.raises(TypeError):
|
|
34
|
+
SpecRuntime() # type: ignore[abstract]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_resolve_from_env_var(monkeypatch: pytest.MonkeyPatch):
|
|
38
|
+
monkeypatch.setenv(ENV_VAR, f"{__name__}:_StubRuntime")
|
|
39
|
+
runtime = _resolve_default_runtime()
|
|
40
|
+
assert isinstance(runtime, _StubRuntime)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_resolve_from_env_var_rejects_bad_format(monkeypatch: pytest.MonkeyPatch):
|
|
44
|
+
monkeypatch.setenv(ENV_VAR, "no_colon_here")
|
|
45
|
+
with pytest.raises(RuntimeError, match="expected 'module.path:AttrName'"):
|
|
46
|
+
_resolve_default_runtime()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_resolve_from_env_var_rejects_non_spec_runtime(monkeypatch: pytest.MonkeyPatch):
|
|
50
|
+
monkeypatch.setenv(ENV_VAR, f"{__name__}:_NotARuntime")
|
|
51
|
+
with pytest.raises(TypeError, match="not a SpecRuntime"):
|
|
52
|
+
_resolve_default_runtime()
|