worldcut 0.1.1__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.
@@ -0,0 +1,12 @@
1
+ /.venv/
2
+ /.wheel-venv/
3
+ /.sdist-venv/
4
+ /.sdist-test/
5
+ /dist/
6
+ /.pytest_cache/
7
+ /.mypy_cache/
8
+ /.ruff_cache/
9
+ /.hypothesis/
10
+ *.egg-info/
11
+ __pycache__/
12
+ *.py[cod]
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.5
2
+ Name: worldcut
3
+ Version: 0.1.1
4
+ Summary: Independent Python verifier for the WorldCut decision-coherence protocol
5
+ Project-URL: Homepage, https://github.com/Jason-Doyle/WorldCut
6
+ Project-URL: Issues, https://github.com/Jason-Doyle/WorldCut/issues
7
+ Project-URL: Repository, https://github.com/Jason-Doyle/WorldCut
8
+ Author: Jason Doyle
9
+ License-Expression: Apache-2.0
10
+ Keywords: consistency,distributed-systems,provenance,verification
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: rfc8785==0.1.4
22
+ Provides-Extra: dev
23
+ Requires-Dist: build>=1.2.2; extra == 'dev'
24
+ Requires-Dist: hypothesis>=6.130; extra == 'dev'
25
+ Requires-Dist: mypy>=1.15; extra == 'dev'
26
+ Requires-Dist: pytest>=8.3; extra == 'dev'
27
+ Requires-Dist: ruff>=0.11; extra == 'dev'
28
+ Requires-Dist: twine>=6.1; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # WorldCut Python
32
+
33
+ Independent Python implementation of WorldCut protocol **0.1**, engine
34
+ **0.1.2**, and canonicalization **worldcut-json-v1**. Python 3.11 or newer is
35
+ required.
36
+
37
+ ## Install
38
+
39
+ Install from PyPI after the `0.1.1` registry release is live:
40
+
41
+ ```sh
42
+ python -m pip install worldcut==0.1.1
43
+ ```
44
+
45
+ Until then, or when a source install is preferred, use the protected tag:
46
+
47
+ ```sh
48
+ python -m pip install "worldcut @ git+https://github.com/Jason-Doyle/WorldCut.git@ports/python/v0.1.1#subdirectory=ports/python"
49
+ ```
50
+
51
+ ## Library
52
+
53
+ ```python
54
+ from pathlib import Path
55
+
56
+ from worldcut import parse_input, verify
57
+
58
+ parsed = parse_input(Path("verification.json").read_bytes())
59
+ result = verify(parsed)
60
+ print(result["verdict"])
61
+ print(result["verificationRecordDigest"])
62
+ ```
63
+
64
+ `ParsedInput` is an immutable validated snapshot. Every verification returns a
65
+ fresh result, so mutating one result cannot affect later verification.
66
+
67
+ ## CLI
68
+
69
+ ```sh
70
+ worldcut-py verification.json
71
+ worldcut-py --require-satisfied verification.json
72
+ ```
73
+
74
+ The second form exits with status 2 unless the verdict is
75
+ `CONTRACT_SATISFIED`.
76
+
77
+ ## Validate
78
+
79
+ ```sh
80
+ python -m pip install -e ".[dev]"
81
+ ruff format --check .
82
+ ruff check .
83
+ mypy src
84
+ pytest
85
+ python -m build
86
+ twine check dist/*
87
+ ```
88
+
89
+ The source distribution includes the complete mirrored conformance corpus and
90
+ can run its tests without files from the parent repository. This port includes
91
+ the verifier and CLI only; integrations are intentionally not included yet.
@@ -0,0 +1,61 @@
1
+ # WorldCut Python
2
+
3
+ Independent Python implementation of WorldCut protocol **0.1**, engine
4
+ **0.1.2**, and canonicalization **worldcut-json-v1**. Python 3.11 or newer is
5
+ required.
6
+
7
+ ## Install
8
+
9
+ Install from PyPI after the `0.1.1` registry release is live:
10
+
11
+ ```sh
12
+ python -m pip install worldcut==0.1.1
13
+ ```
14
+
15
+ Until then, or when a source install is preferred, use the protected tag:
16
+
17
+ ```sh
18
+ python -m pip install "worldcut @ git+https://github.com/Jason-Doyle/WorldCut.git@ports/python/v0.1.1#subdirectory=ports/python"
19
+ ```
20
+
21
+ ## Library
22
+
23
+ ```python
24
+ from pathlib import Path
25
+
26
+ from worldcut import parse_input, verify
27
+
28
+ parsed = parse_input(Path("verification.json").read_bytes())
29
+ result = verify(parsed)
30
+ print(result["verdict"])
31
+ print(result["verificationRecordDigest"])
32
+ ```
33
+
34
+ `ParsedInput` is an immutable validated snapshot. Every verification returns a
35
+ fresh result, so mutating one result cannot affect later verification.
36
+
37
+ ## CLI
38
+
39
+ ```sh
40
+ worldcut-py verification.json
41
+ worldcut-py --require-satisfied verification.json
42
+ ```
43
+
44
+ The second form exits with status 2 unless the verdict is
45
+ `CONTRACT_SATISFIED`.
46
+
47
+ ## Validate
48
+
49
+ ```sh
50
+ python -m pip install -e ".[dev]"
51
+ ruff format --check .
52
+ ruff check .
53
+ mypy src
54
+ pytest
55
+ python -m build
56
+ twine check dist/*
57
+ ```
58
+
59
+ The source distribution includes the complete mirrored conformance corpus and
60
+ can run its tests without files from the parent repository. This port includes
61
+ the verifier and CLI only; integrations are intentionally not included yet.
@@ -0,0 +1,70 @@
1
+ [build-system]
2
+ requires = ["hatchling==1.32.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "worldcut"
7
+ version = "0.1.1"
8
+ description = "Independent Python verifier for the WorldCut decision-coherence protocol"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "Apache-2.0"
12
+ authors = [{ name = "Jason Doyle" }]
13
+ keywords = ["consistency", "distributed-systems", "provenance", "verification"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: Apache Software License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = ["rfc8785==0.1.4"]
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "build>=1.2.2",
30
+ "hypothesis>=6.130",
31
+ "mypy>=1.15",
32
+ "pytest>=8.3",
33
+ "ruff>=0.11",
34
+ "twine>=6.1",
35
+ ]
36
+
37
+ [project.scripts]
38
+ worldcut-py = "worldcut.cli:main"
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/Jason-Doyle/WorldCut"
42
+ Issues = "https://github.com/Jason-Doyle/WorldCut/issues"
43
+ Repository = "https://github.com/Jason-Doyle/WorldCut"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/worldcut"]
47
+
48
+ [tool.hatch.build.targets.sdist]
49
+ include = [
50
+ "/src/worldcut",
51
+ "/tests",
52
+ "/README.md",
53
+ "/pyproject.toml",
54
+ ]
55
+
56
+ [tool.pytest.ini_options]
57
+ addopts = "-ra --strict-config --strict-markers"
58
+ testpaths = ["tests"]
59
+
60
+ [tool.mypy]
61
+ python_version = "3.11"
62
+ strict = true
63
+ files = ["src"]
64
+
65
+ [tool.ruff]
66
+ target-version = "py311"
67
+ line-length = 88
68
+
69
+ [tool.ruff.lint]
70
+ select = ["B", "E", "F", "I", "RUF", "SIM", "UP"]
@@ -0,0 +1,30 @@
1
+ """Independent Python implementation of the WorldCut 0.1 verifier."""
2
+
3
+ from .canonicalization import canonical_json, sha256_digest
4
+ from .errors import WorldCutError, WorldCutErrorCode, WorldCutInputError
5
+ from .models import ParsedInput, VerificationResult
6
+ from .validation import PROTOCOL_VERSION, parse_input
7
+ from .verifier import (
8
+ CANONICALIZATION,
9
+ ENGINE_VERSION,
10
+ verify,
11
+ verify_json,
12
+ )
13
+
14
+ __all__ = [
15
+ "CANONICALIZATION",
16
+ "ENGINE_VERSION",
17
+ "PROTOCOL_VERSION",
18
+ "ParsedInput",
19
+ "VerificationResult",
20
+ "WorldCutError",
21
+ "WorldCutErrorCode",
22
+ "WorldCutInputError",
23
+ "canonical_json",
24
+ "parse_input",
25
+ "sha256_digest",
26
+ "verify",
27
+ "verify_json",
28
+ ]
29
+
30
+ __version__ = "0.1.1"
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import math
5
+
6
+ import rfc8785
7
+
8
+ from .models import JsonValue
9
+
10
+ _MAX_SAFE_INTEGER = 9_007_199_254_740_991
11
+
12
+
13
+ def _assert_valid_unicode(value: str, field: str) -> None:
14
+ for character in value:
15
+ code_point = ord(character)
16
+ if 0xD800 <= code_point <= 0xDFFF:
17
+ raise TypeError(f"{field} contains an unpaired surrogate")
18
+
19
+
20
+ def _snapshot_json(value: object, field: str, ancestors: set[int]) -> JsonValue:
21
+ if value is None or isinstance(value, (str, bool)):
22
+ if isinstance(value, str):
23
+ _assert_valid_unicode(value, field)
24
+ return value
25
+ if isinstance(value, int) and not isinstance(value, bool):
26
+ if abs(value) > _MAX_SAFE_INTEGER:
27
+ raise TypeError(f"{field} contains an integer outside the safe domain")
28
+ return value
29
+ if isinstance(value, float):
30
+ if not math.isfinite(value):
31
+ raise TypeError(f"{field} contains a non-finite number")
32
+ return value
33
+ if type(value) is list:
34
+ identity = id(value)
35
+ if identity in ancestors:
36
+ raise TypeError(f"{field} must not contain cycles")
37
+ ancestors.add(identity)
38
+ array_result = [
39
+ _snapshot_json(item, f"{field}[{index}]", ancestors)
40
+ for index, item in enumerate(value)
41
+ ]
42
+ ancestors.remove(identity)
43
+ return array_result
44
+ if type(value) is dict:
45
+ identity = id(value)
46
+ if identity in ancestors:
47
+ raise TypeError(f"{field} must not contain cycles")
48
+ ancestors.add(identity)
49
+ object_result: dict[str, JsonValue] = {}
50
+ for raw_key, item in value.items():
51
+ if not isinstance(raw_key, str):
52
+ raise TypeError(f"{field} must use string object keys")
53
+ _assert_valid_unicode(raw_key, f"{field} key")
54
+ object_result[raw_key] = _snapshot_json(
55
+ item, f"{field}.{raw_key}", ancestors
56
+ )
57
+ ancestors.remove(identity)
58
+ return object_result
59
+ raise TypeError(f"{field} contains unsupported {type(value).__name__} data")
60
+
61
+
62
+ def canonical_json(value: JsonValue) -> str:
63
+ """Return the worldcut-json-v1 canonical representation of JSON data."""
64
+
65
+ snapshot = _snapshot_json(value, "value", set())
66
+ try:
67
+ return rfc8785.dumps(snapshot).decode("utf-8")
68
+ except (rfc8785.CanonicalizationError, UnicodeError) as error:
69
+ raise TypeError(f"cannot canonicalize JSON data: {error}") from error
70
+
71
+
72
+ def sha256_digest(value: JsonValue) -> str:
73
+ """Return the lowercase SHA-256 digest of canonical JSON data."""
74
+
75
+ return hashlib.sha256(canonical_json(value).encode("utf-8")).hexdigest()
76
+
77
+
78
+ def utf16_sort_key(value: str) -> bytes:
79
+ """Produce the protocol's raw UTF-16 code-unit ordering key."""
80
+
81
+ _assert_valid_unicode(value, "text")
82
+ return value.encode("utf-16-be")
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from .errors import WorldCutError
8
+ from .verifier import verify_json
9
+
10
+
11
+ def _usage() -> str:
12
+ return "\n".join(
13
+ [
14
+ "Usage: worldcut-py [--require-satisfied] <verification.json>",
15
+ "",
16
+ "Options:",
17
+ " --require-satisfied Exit with code 2 unless the contract is satisfied",
18
+ " --help Show this help",
19
+ ]
20
+ )
21
+
22
+
23
+ def _write_error(code: str, message: str) -> None:
24
+ print(
25
+ json.dumps({"error": {"code": code, "message": message}}),
26
+ file=sys.stderr,
27
+ )
28
+
29
+
30
+ def run(arguments: list[str] | None = None) -> int:
31
+ """Run the WorldCut CLI and return its process exit status."""
32
+
33
+ arguments = list(sys.argv[1:] if arguments is None else arguments)
34
+ if "--help" in arguments:
35
+ print(_usage())
36
+ return 0
37
+ require_satisfied = False
38
+ positional: list[str] = []
39
+ for argument in arguments:
40
+ if argument == "--require-satisfied":
41
+ require_satisfied = True
42
+ elif argument.startswith("-"):
43
+ _write_error("WORLDCUT_INVALID_ARGUMENT", f"Unknown option: {argument}")
44
+ return 1
45
+ else:
46
+ positional.append(argument)
47
+ if len(positional) != 1:
48
+ _write_error(
49
+ "WORLDCUT_INVALID_ARGUMENT",
50
+ "Exactly one verification JSON file is required",
51
+ )
52
+ return 1
53
+ path = Path(positional[0]).resolve()
54
+ try:
55
+ source = path.read_bytes()
56
+ except OSError:
57
+ _write_error("WORLDCUT_FILE_READ_FAILED", f"Unable to read {path}")
58
+ return 1
59
+ try:
60
+ result = verify_json(source)
61
+ except WorldCutError as error:
62
+ _write_error(error.code, str(error))
63
+ return 1
64
+ print(json.dumps(result, indent=2))
65
+ if require_satisfied and result["verdict"] != "CONTRACT_SATISFIED":
66
+ return 2
67
+ return 0
68
+
69
+
70
+ def main() -> None:
71
+ """Console-script entry point."""
72
+
73
+ raise SystemExit(run())
74
+
75
+
76
+ if __name__ == "__main__":
77
+ main()
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, TypeAlias
4
+
5
+ WorldCutErrorCode: TypeAlias = Literal[
6
+ "WORLDCUT_INVALID_INPUT",
7
+ "WORLDCUT_INVALID_ARGUMENT",
8
+ "WORLDCUT_FILE_READ_FAILED",
9
+ "WORLDCUT_RUNTIME_ERROR",
10
+ ]
11
+
12
+
13
+ class WorldCutError(Exception):
14
+ """Base exception carrying a stable WorldCut error code."""
15
+
16
+ code: WorldCutErrorCode
17
+
18
+ def __init__(self, code: WorldCutErrorCode, message: str) -> None:
19
+ super().__init__(message)
20
+ self.code = code
21
+
22
+
23
+ class WorldCutInputError(WorldCutError):
24
+ """Raised when transport data violates the WorldCut protocol."""
25
+
26
+ def __init__(self, message: str) -> None:
27
+ super().__init__("WORLDCUT_INVALID_INPUT", message)