necoherence 0.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.
Files changed (51) hide show
  1. necoherence-0.2.0/.gitignore +58 -0
  2. necoherence-0.2.0/LICENSE +21 -0
  3. necoherence-0.2.0/PKG-INFO +11 -0
  4. necoherence-0.2.0/README-ja.md +66 -0
  5. necoherence-0.2.0/README.md +66 -0
  6. necoherence-0.2.0/examples/01_fold_arithmetic.py +38 -0
  7. necoherence-0.2.0/examples/02_recurse_gcd.py +46 -0
  8. necoherence-0.2.0/examples/03_closure_reachability.py +40 -0
  9. necoherence-0.2.0/pyproject.toml +34 -0
  10. necoherence-0.2.0/src/necoherence/__init__.py +479 -0
  11. necoherence-0.2.0/src/necoherence/call.py +176 -0
  12. necoherence-0.2.0/src/necoherence/core.py +174 -0
  13. necoherence-0.2.0/src/necoherence/dynamic_admission.py +531 -0
  14. necoherence-0.2.0/src/necoherence/files.py +389 -0
  15. necoherence-0.2.0/src/necoherence/iteration.py +324 -0
  16. necoherence-0.2.0/src/necoherence/network_effect.py +1516 -0
  17. necoherence-0.2.0/src/necoherence/reconcile.py +107 -0
  18. necoherence-0.2.0/src/necoherence/record.py +76 -0
  19. necoherence-0.2.0/src/necoherence/request_progression.py +664 -0
  20. necoherence-0.2.0/src/necoherence/signature_support.py +89 -0
  21. necoherence-0.2.0/src/necoherence/signature_values.py +1283 -0
  22. necoherence-0.2.0/src/necoherence/store.py +102 -0
  23. necoherence-0.2.0/src/necoherence/transition.py +134 -0
  24. necoherence-0.2.0/src/necoherence/type_signatures.py +714 -0
  25. necoherence-0.2.0/tests/test_call.py +139 -0
  26. necoherence-0.2.0/tests/test_core.py +258 -0
  27. necoherence-0.2.0/tests/test_default_materialization.py +112 -0
  28. necoherence-0.2.0/tests/test_dynamic_admission.py +196 -0
  29. necoherence-0.2.0/tests/test_examples.py +38 -0
  30. necoherence-0.2.0/tests/test_files.py +645 -0
  31. necoherence-0.2.0/tests/test_format_rule_inclusion.py +185 -0
  32. necoherence-0.2.0/tests/test_iteration.py +284 -0
  33. necoherence-0.2.0/tests/test_lean_value_decisions.py +126 -0
  34. necoherence-0.2.0/tests/test_mutation_detection.py +142 -0
  35. necoherence-0.2.0/tests/test_network_effect.py +230 -0
  36. necoherence-0.2.0/tests/test_network_effect_progression.py +142 -0
  37. necoherence-0.2.0/tests/test_profile_artifact_admission.py +57 -0
  38. necoherence-0.2.0/tests/test_profile_predicate_vectors.py +245 -0
  39. necoherence-0.2.0/tests/test_profile_wire_vectors.py +119 -0
  40. necoherence-0.2.0/tests/test_reconcile.py +74 -0
  41. necoherence-0.2.0/tests/test_record.py +52 -0
  42. necoherence-0.2.0/tests/test_recursive_values.py +135 -0
  43. necoherence-0.2.0/tests/test_representation_acceptance.py +17 -0
  44. necoherence-0.2.0/tests/test_request_progression.py +184 -0
  45. necoherence-0.2.0/tests/test_semantic_discriminator.py +15 -0
  46. necoherence-0.2.0/tests/test_semantic_grapheme.py +32 -0
  47. necoherence-0.2.0/tests/test_store.py +128 -0
  48. necoherence-0.2.0/tests/test_transition.py +130 -0
  49. necoherence-0.2.0/tests/test_type_signatures.py +330 -0
  50. necoherence-0.2.0/tests/test_wrapper_base64_vectors.py +129 -0
  51. necoherence-0.2.0/tests/v4_samples.py +66 -0
@@ -0,0 +1,58 @@
1
+ # SPDX-FileCopyrightText: 2026 barineco
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ __pycache__/
5
+ *.egg-info/
6
+ .venv/
7
+ lean/.lake
8
+ rust/target/
9
+ typescript/node_modules/
10
+ typescript/dist/
11
+ typescript/.package-consumer/
12
+ typescript/*.tgz
13
+ python/dist/
14
+ python-http-adapter/dist/
15
+ python-http-adapter/.clean-install*/
16
+ python-http-adapter/.pytest_cache/
17
+ python-http-adapter/.mypy_cache/
18
+ typescript-http-adapter/node_modules/
19
+ typescript-http-adapter/dist/
20
+ typescript-http-adapter/*.tgz
21
+ typescript-http-adapter/.package-consumer/
22
+ java-http-adapter/build/
23
+ gleam-http-adapter/build
24
+ gleam-http-adapter/*.tar
25
+ python/.clean-install*/
26
+ python/.verify-*/
27
+ java/build/
28
+ java/lib/
29
+ gleam/build
30
+ gleam/*.tar
31
+ tools/lean/.lake
32
+ tools/rust/target/
33
+ tools/gleam/build
34
+ tools/gleam/*.tar
35
+ tools/typescript/node_modules/
36
+ tools/typescript/dist/
37
+ tools/typescript/.package-consumer/
38
+ tools/typescript/*.tgz
39
+ tools/python/dist/
40
+ tools/python/.clean-install*/
41
+ tools/python/.verify-*/
42
+ tools/python/.pytest_cache/
43
+ tools/python/.mypy_cache/
44
+ tools/java/build/
45
+ tools/java/lib/
46
+ comparison/network_effect/gleam_driver/build
47
+ comparison/network_effect/java_driver/classes/
48
+ comparison/network_effect/rust_driver/target/
49
+ comparison/declaration/gleam_driver/build
50
+ comparison/declaration/rust_driver/target
51
+ comparison/shared_operations/gleam_driver/build
52
+ comparison/shared_operations/lean_driver/.lake
53
+ comparison/shared_operations/rust_driver/target/
54
+ residual-coherence/dist/
55
+ residual-coherence/*.egg-info/
56
+ residual-coherence/.clean-install*/
57
+ residual-coherence/.pytest_cache/
58
+ residual-coherence/.mypy_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 barineco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.5
2
+ Name: necoherence
3
+ Version: 0.2.0
4
+ Summary: Revision-aware state transition utilities for Python
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.12
8
+ Provides-Extra: dev
9
+ Requires-Dist: hypothesis; extra == 'dev'
10
+ Requires-Dist: mypy; extra == 'dev'
11
+ Requires-Dist: pytest; extra == 'dev'
@@ -0,0 +1,66 @@
1
+ # Python 向けの Necoherence
2
+
3
+ [プロジェクトの概要](https://github.com/barineco/necoherence/blob/publish/README-ja.md) | [English](README.md)
4
+
5
+ このパッケージは、Necoherence の Python による実装で、Python 3.12 以降で使えます。
6
+ 実行時に使うのは、標準ライブラリだけです。
7
+
8
+ ## 意味プロファイルの受け入れ
9
+
10
+ 呼び出し側は、宣言を復号するときと、生成したライブラリを受け入れるときに、版の付いた意味プロファイルを渡します。
11
+ 検証、符号化と復号、実行で使う転送形式は、受け入れたプロファイルが定めます。
12
+
13
+ 期待するプロファイルの参照、出典、信頼するバイト列は、入力のバイト列とは別の引数として渡します。
14
+
15
+ ```python
16
+ from necoherence.signature_values import ValueContext
17
+ from necoherence.type_signatures import checkLibrary, decode_profile_artifact
18
+
19
+ def decide(expected, sources, trusted, input_bytes, library, type_id, value):
20
+ profile = decode_profile_artifact(expected, sources, trusted, input_bytes)
21
+ checked = checkLibrary({**library, "profile": profile})
22
+ context = ValueContext({
23
+ "profile": checked.value["profile"], "nodes": checked.value["nodes"],
24
+ })
25
+ return context.validate(type_id, value)
26
+ ```
27
+
28
+ 失敗の型は、原因ごとに次のとおりです。
29
+
30
+ - `WireFailure`: プロファイルの転送値の形式が誤っている
31
+ - `DeclarationFailure`: プロファイルかライブラリが、宣言の条件に違反している
32
+ - `AdmissionFailure`: プロファイルが、期待した参照や出典と食い違っている
33
+ - `MeaningFailure`: 型に意味の定義が欠けている
34
+ - `ValueFailure`: 値が検証で拒否された
35
+
36
+ ## その他の機能
37
+
38
+ [プロジェクトの概要](https://github.com/barineco/necoherence/blob/publish/README-ja.md) に挙げた機能のほかに、次の機能があります。
39
+
40
+ - 宣言の検査: 識別子の重複や、宣言にない型や定義への参照など、型と操作の宣言の誤りを検出する
41
+ - 生成したコードのランタイム: 生成した Python のコードが実行時に呼び出す、値の検証とコーデックの共通の処理
42
+
43
+ 任意の [HTTP アダプター](../python-http-adapter/) は、`necoherence-http-adapter` として独立に配布します。ネットワーク副作用のコマンドを実行し、観測を核の台帳へ返します。
44
+
45
+ ## 付属のツール
46
+
47
+ 型と操作の宣言から Python の型とコーデックを生成する生成器は、[tools/](https://github.com/barineco/necoherence/tree/publish/tools) にあります。
48
+ ライセンスは AGPL-3.0-only です。
49
+
50
+ ## 使用例
51
+
52
+ 使用例は [examples/](https://github.com/barineco/necoherence/tree/publish/python/examples) にあります。
53
+
54
+ ## テストと型検査
55
+
56
+ 次のコマンドで、テストと型検査を実行します。
57
+
58
+ ```bash
59
+ python3 -m pip install ".[dev]"
60
+ python3 -m pytest -q
61
+ python3 -m mypy src tests
62
+ ```
63
+
64
+ ## ライセンス
65
+
66
+ [MIT](LICENSE)
@@ -0,0 +1,66 @@
1
+ # Necoherence for Python
2
+
3
+ [Project overview](https://github.com/barineco/necoherence/blob/publish/README.md) | [日本語](README-ja.md)
4
+
5
+ This package is the Python implementation of Necoherence, for Python 3.12 or later.
6
+ It uses only the standard library at run time.
7
+
8
+ ## Accepting a semantic profile
9
+
10
+ The caller passes a versioned semantic profile when decoding declarations and when accepting a generated library.
11
+ The accepted profile defines the wire format used by validation, encoding and decoding, and execution.
12
+
13
+ The expected profile reference, the sources, and the trusted bytes are passed as arguments separate from the input bytes.
14
+
15
+ ```python
16
+ from necoherence.signature_values import ValueContext
17
+ from necoherence.type_signatures import checkLibrary, decode_profile_artifact
18
+
19
+ def decide(expected, sources, trusted, input_bytes, library, type_id, value):
20
+ profile = decode_profile_artifact(expected, sources, trusted, input_bytes)
21
+ checked = checkLibrary({**library, "profile": profile})
22
+ context = ValueContext({
23
+ "profile": checked.value["profile"], "nodes": checked.value["nodes"],
24
+ })
25
+ return context.validate(type_id, value)
26
+ ```
27
+
28
+ Failures are returned as the following types, one for each cause:
29
+
30
+ - `WireFailure`: the profile's wire value is malformed
31
+ - `DeclarationFailure`: the profile or the library violates a declaration condition
32
+ - `AdmissionFailure`: the profile differs from the expected reference or sources
33
+ - `MeaningFailure`: a type lacks a definition of its meaning
34
+ - `ValueFailure`: validation rejected the value
35
+
36
+ ## Other features
37
+
38
+ Besides the features listed in the [project overview](https://github.com/barineco/necoherence/blob/publish/README.md), the package provides the following:
39
+
40
+ - Declaration checking: detects errors in declarations of types and operations, such as duplicate identifiers and references to undeclared types or definitions
41
+ - Runtime for generated code: the shared value validation and codec routines that generated Python code calls at run time
42
+
43
+ The optional [HTTP adapter](../python-http-adapter/) is distributed separately as `necoherence-http-adapter`. It executes network-effect commands and returns observations to the core ledger.
44
+
45
+ ## Tools
46
+
47
+ The generator, which produces Python types and codecs from declarations of types and operations, is in [tools/](https://github.com/barineco/necoherence/tree/publish/tools).
48
+ Its license is AGPL-3.0-only.
49
+
50
+ ## Examples
51
+
52
+ Examples are in [examples/](https://github.com/barineco/necoherence/tree/publish/python/examples).
53
+
54
+ ## Testing and type checking
55
+
56
+ The following commands run the tests and the type checks:
57
+
58
+ ```bash
59
+ python3 -m pip install ".[dev]"
60
+ python3 -m pytest -q
61
+ python3 -m mypy src tests
62
+ ```
63
+
64
+ ## License
65
+
66
+ [MIT](LICENSE)
@@ -0,0 +1,38 @@
1
+ # SPDX-FileCopyrightText: 2026 barineco
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """fold: accumulate a running sum across a stated number of rounds.
5
+
6
+ The step is one-step-determinate addition; ``count`` is the fold's own
7
+ input field, so the number of rounds is fixed before the first one runs.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass
13
+
14
+ from necoherence import CallOk, FoldReq, FoldTrace, Request, fold, immediate_of, io
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class RunningSum:
19
+ total: int
20
+ next_term: int
21
+
22
+
23
+ def _add_next_term(state: RunningSum) -> RunningSum:
24
+ return RunningSum(total=state.total + state.next_term, next_term=state.next_term + 1)
25
+
26
+
27
+ accumulate = immediate_of("examples.fold.accumulate", (), _add_next_term)
28
+
29
+
30
+ if __name__ == "__main__":
31
+ req = FoldReq(step=accumulate, seed=RunningSum(total=0, next_term=1), count=5, step_fields=())
32
+ result = io(fold, Request(payload=req, fields=("step", "seed", "count", "step_fields")))
33
+ assert isinstance(result, CallOk)
34
+ outcome = result.value
35
+ assert isinstance(outcome, FoldTrace)
36
+ totals = tuple(state.total for state in outcome.states)
37
+ print(totals)
38
+ assert totals == (0, 1, 3, 6, 10, 15)
@@ -0,0 +1,46 @@
1
+ # SPDX-FileCopyrightText: 2026 barineco
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """recurse: Euclid's algorithm, measure = the larger of the two values.
5
+
6
+ Each round's measure strictly decreases (the classic Euclid argument), so
7
+ recurse settles without any stated round count.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from necoherence import (
13
+ CallOk,
14
+ Continue,
15
+ Immediate,
16
+ RecurseReq,
17
+ RecurseSettled,
18
+ Request,
19
+ Settled,
20
+ StepOutcome,
21
+ io,
22
+ recurse,
23
+ )
24
+
25
+
26
+ def _euclid_step(pair: tuple[int, int]) -> StepOutcome[tuple[int, int]]:
27
+ a, b = pair
28
+ if b == 0:
29
+ return Settled(state=(a, b))
30
+ return Continue(state=(b, a % b), measure=max(a, b))
31
+
32
+
33
+ # A tuple carries no field names, so this step declares no required fields.
34
+ euclid_step: Immediate[tuple[int, int], StepOutcome[tuple[int, int]]] = Immediate(
35
+ identity="examples.recurse.euclid_step", required=(), output=_euclid_step
36
+ )
37
+
38
+
39
+ if __name__ == "__main__":
40
+ req = RecurseReq(step=euclid_step, seed=(48, 18), step_fields=())
41
+ result = io(recurse, Request(payload=req, fields=("step", "seed", "step_fields")))
42
+ assert isinstance(result, CallOk)
43
+ outcome = result.value
44
+ assert isinstance(outcome, RecurseSettled)
45
+ print(outcome.value)
46
+ assert outcome.value == (6, 0)
@@ -0,0 +1,40 @@
1
+ # SPDX-FileCopyrightText: 2026 barineco
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """closure: reachable-node set as a monotone step's least fixpoint.
5
+
6
+ Each round adds every node one hop from the current set; the carrier's
7
+ size bounds the number of rounds, since the residual strictly shrinks.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from necoherence import CallOk, ClosureFixpoint, ClosureReq, ClosureState, Request, closure, immediate_of, io
13
+
14
+ _edges: dict[int, frozenset[int]] = {
15
+ 1: frozenset({2, 3}),
16
+ 2: frozenset({4}),
17
+ 3: frozenset({4}),
18
+ 4: frozenset(),
19
+ 5: frozenset(),
20
+ }
21
+
22
+
23
+ def _one_hop(state: ClosureState[int]) -> frozenset[int]:
24
+ grown = set(state.elements)
25
+ for node in state.elements:
26
+ grown |= _edges[node]
27
+ return frozenset(grown)
28
+
29
+
30
+ one_hop = immediate_of("examples.closure.one_hop", ("elements",), _one_hop)
31
+
32
+
33
+ if __name__ == "__main__":
34
+ req = ClosureReq(step=one_hop, carrier=frozenset(_edges), seed=frozenset({1}), step_fields=("elements",))
35
+ result = io(closure, Request(payload=req, fields=("step", "carrier", "seed", "step_fields")))
36
+ assert isinstance(result, CallOk)
37
+ outcome = result.value
38
+ assert isinstance(outcome, ClosureFixpoint)
39
+ print(sorted(outcome.elements))
40
+ assert outcome.elements == frozenset({1, 2, 3, 4})
@@ -0,0 +1,34 @@
1
+ # SPDX-FileCopyrightText: 2026 barineco
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ [build-system]
5
+ requires = ["hatchling"]
6
+ build-backend = "hatchling.build"
7
+
8
+ [project]
9
+ name = "necoherence"
10
+ version = "0.2.0"
11
+ description = "Revision-aware state transition utilities for Python"
12
+ license = "MIT"
13
+ license-files = ["LICENSE"]
14
+ requires-python = ">=3.12"
15
+ dependencies = []
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["pytest", "hypothesis", "mypy"]
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["src/necoherence"]
22
+
23
+ [tool.hatch.build.targets.wheel.force-include]
24
+ "README.md" = "necoherence/README.md"
25
+ "README-ja.md" = "necoherence/README-ja.md"
26
+
27
+ [tool.hatch.build.targets.sdist]
28
+ include = ["src/necoherence", "tests", "examples", "README.md", "README-ja.md", "LICENSE", "pyproject.toml"]
29
+
30
+ [tool.pytest.ini_options]
31
+ pythonpath = ["src"]
32
+
33
+ [tool.mypy]
34
+ mypy_path = "src"