bitspark-bitwire 0.3.0__tar.gz → 0.5.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.
- bitspark_bitwire-0.5.0/NOTICE +9 -0
- bitspark_bitwire-0.5.0/PKG-INFO +30 -0
- bitspark_bitwire-0.5.0/README.md +10 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/pyproject.toml +2 -2
- bitspark_bitwire-0.5.0/src/bitspark_bitwire.egg-info/PKG-INFO +30 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/SOURCES.txt +1 -1
- bitspark_bitwire-0.5.0/src/bitwire/__init__.py +35 -0
- bitspark_bitwire-0.5.0/src/bitwire/ontos.py +219 -0
- bitspark_bitwire-0.5.0/tests/test_consumer.py +14 -0
- bitspark_bitwire-0.5.0/tests/typecheck.py +14 -0
- bitspark_bitwire-0.3.0/NOTICE +0 -10
- bitspark_bitwire-0.3.0/PKG-INFO +0 -141
- bitspark_bitwire-0.3.0/README.md +0 -121
- bitspark_bitwire-0.3.0/src/bitspark_bitwire.egg-info/PKG-INFO +0 -141
- bitspark_bitwire-0.3.0/src/bitwire/__init__.py +0 -192
- bitspark_bitwire-0.3.0/tests/test_consumer.py +0 -157
- bitspark_bitwire-0.3.0/tests/test_tree.py +0 -85
- bitspark_bitwire-0.3.0/tests/typecheck.py +0 -64
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/LICENSE +0 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/MANIFEST.in +0 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/check.py +0 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/requirements-dev.txt +0 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/setup.cfg +0 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/dependency_links.txt +0 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/top_level.txt +0 -0
- {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/src/bitwire/py.typed +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
bitwire
|
|
2
|
+
Copyright 2026 Bitspark and contributors
|
|
3
|
+
|
|
4
|
+
This product includes software developed at Bitspark. The current generic wire
|
|
5
|
+
contract is original work. It includes a checked consumer mirror of ontos v0.9.0
|
|
6
|
+
from 5eed85de5697f53397407f9f82f3b310648827f7: frozen L0 values, canonical codec
|
|
7
|
+
and optional data embeddings. ontos/manifest.json records exact source provenance
|
|
8
|
+
and import adaptations. The prior declarations and drivers derived from nightseam
|
|
9
|
+
were removed; their provenance and immutable releases remain in Git history.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bitspark-bitwire
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Addressless wires and addressed access over ontos values
|
|
5
|
+
Author: Bitspark
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/Bitspark/bitwire
|
|
8
|
+
Project-URL: Documentation, https://github.com/Bitspark/bitwire/tree/main/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/Bitspark/bitwire
|
|
10
|
+
Project-URL: Issues, https://github.com/Bitspark/bitwire/issues
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
License-File: NOTICE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# py wire presentation
|
|
22
|
+
|
|
23
|
+
Addressless Wire sends a ground ontos value. Endpoint adds receive ownership and
|
|
24
|
+
closure. AddressedWire adds an exact byte-path argument; AddressedEndpoint carries
|
|
25
|
+
that layer over one endpoint. WireNode is the complete deixis structure over Wire
|
|
26
|
+
values (a nominal interface in Java), not opaque route access.
|
|
27
|
+
|
|
28
|
+
See the repository's docs/wire/contract.md and docs/wire/carriers.md. Version 0.5.0
|
|
29
|
+
is a clean breaking replacement; compilation and packaged consumers check the
|
|
30
|
+
native surface, while bitruntime supplies independently tested implementations.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# py wire presentation
|
|
2
|
+
|
|
3
|
+
Addressless Wire sends a ground ontos value. Endpoint adds receive ownership and
|
|
4
|
+
closure. AddressedWire adds an exact byte-path argument; AddressedEndpoint carries
|
|
5
|
+
that layer over one endpoint. WireNode is the complete deixis structure over Wire
|
|
6
|
+
values (a nominal interface in Java), not opaque route access.
|
|
7
|
+
|
|
8
|
+
See the repository's docs/wire/contract.md and docs/wire/carriers.md. Version 0.5.0
|
|
9
|
+
is a clean breaking replacement; compilation and packaged consumers check the
|
|
10
|
+
native surface, while bitruntime supplies independently tested implementations.
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bitspark-bitwire"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Addressless
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
description = "Addressless wires and addressed access over ontos values"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
license = "Apache-2.0"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bitspark-bitwire
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Addressless wires and addressed access over ontos values
|
|
5
|
+
Author: Bitspark
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/Bitspark/bitwire
|
|
8
|
+
Project-URL: Documentation, https://github.com/Bitspark/bitwire/tree/main/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/Bitspark/bitwire
|
|
10
|
+
Project-URL: Issues, https://github.com/Bitspark/bitwire/issues
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
License-File: NOTICE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# py wire presentation
|
|
22
|
+
|
|
23
|
+
Addressless Wire sends a ground ontos value. Endpoint adds receive ownership and
|
|
24
|
+
closure. AddressedWire adds an exact byte-path argument; AddressedEndpoint carries
|
|
25
|
+
that layer over one endpoint. WireNode is the complete deixis structure over Wire
|
|
26
|
+
values (a nominal interface in Java), not opaque route access.
|
|
27
|
+
|
|
28
|
+
See the repository's docs/wire/contract.md and docs/wire/carriers.md. Version 0.5.0
|
|
29
|
+
is a clean breaking replacement; compilation and packaged consumers check the
|
|
30
|
+
native surface, while bitruntime supplies independently tested implementations.
|
|
@@ -10,7 +10,7 @@ src/bitspark_bitwire.egg-info/SOURCES.txt
|
|
|
10
10
|
src/bitspark_bitwire.egg-info/dependency_links.txt
|
|
11
11
|
src/bitspark_bitwire.egg-info/top_level.txt
|
|
12
12
|
src/bitwire/__init__.py
|
|
13
|
+
src/bitwire/ontos.py
|
|
13
14
|
src/bitwire/py.typed
|
|
14
15
|
tests/test_consumer.py
|
|
15
|
-
tests/test_tree.py
|
|
16
16
|
tests/typecheck.py
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Addressless interaction, addressed access and complete structure."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Callable, Protocol, TypeVar, Generic
|
|
5
|
+
from .ontos import Atom, Tuple, Value, atom, tuple_, equals
|
|
6
|
+
Path = tuple[Atom, ...]
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class Termination:
|
|
9
|
+
kind: str
|
|
10
|
+
message: str | None = None
|
|
11
|
+
class Wire(Protocol):
|
|
12
|
+
async def send(self, message: Value) -> None: ...
|
|
13
|
+
class Endpoint(Wire, Protocol):
|
|
14
|
+
def receive(self, handler: Callable[[Value], None]) -> Callable[[], None]: ...
|
|
15
|
+
async def closed(self) -> Termination: ...
|
|
16
|
+
async def close(self) -> None: ...
|
|
17
|
+
class AddressedWire(Protocol):
|
|
18
|
+
async def send(self, path: Path, message: Value) -> None: ...
|
|
19
|
+
class AddressedEndpoint(AddressedWire, Protocol):
|
|
20
|
+
def receive(self, handler: Callable[[Path, Value], None]) -> Callable[[], None]: ...
|
|
21
|
+
async def closed(self) -> Termination: ...
|
|
22
|
+
async def close(self) -> None: ...
|
|
23
|
+
T = TypeVar('T')
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class Parts(Generic[T]):
|
|
26
|
+
own: T
|
|
27
|
+
children: tuple[tuple[Atom, DeixisNode[T]], ...]
|
|
28
|
+
class DeixisNode(Protocol[T]):
|
|
29
|
+
def own(self) -> T: ...
|
|
30
|
+
def children(self) -> tuple[tuple[Atom, DeixisNode[T]], ...]: ...
|
|
31
|
+
def at(self, path: Path) -> DeixisNode[T] | None: ...
|
|
32
|
+
def decompose(self) -> Parts[T]: ...
|
|
33
|
+
WireNode = DeixisNode[Wire]
|
|
34
|
+
__all__ = ['Atom', 'Tuple', 'Value', 'atom', 'tuple_', 'equals', 'Path', 'Termination',
|
|
35
|
+
'Wire', 'Endpoint', 'AddressedWire', 'AddressedEndpoint', 'WireNode', 'Parts', 'DeixisNode']
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""ontos/core (L0) — the frozen value model.
|
|
2
|
+
|
|
3
|
+
Bytes = finite octet strings
|
|
4
|
+
Value = Atom(bytes) | Tuple(Value*)
|
|
5
|
+
|
|
6
|
+
A value is finite, immutable, well-founded (a finite tree, never cyclic),
|
|
7
|
+
uninterpreted, and compared by structural identity. This module defines what a
|
|
8
|
+
value is and when two values are the same — nothing else. There is no byte
|
|
9
|
+
encoding here (that is ``ontos_codec``) and no interpretation of atoms or shapes.
|
|
10
|
+
See docs/spec/ontos-core.md.
|
|
11
|
+
|
|
12
|
+
This is the Python peer of the go/rs/ts cores (ontos #60). It exists to
|
|
13
|
+
empirically validate the conformance protocol: it implements the same contract
|
|
14
|
+
and registers as a fourth ``--impl`` in the differential harness with no protocol
|
|
15
|
+
change. It is a validation lane, **not** a published/substrate lane.
|
|
16
|
+
|
|
17
|
+
Common Value API (all four cores)
|
|
18
|
+
---------------------------------
|
|
19
|
+
The Go, Rust, TypeScript, and Python cores expose the same conceptual surface so
|
|
20
|
+
a developer who learns one can predict the others. Every core provides, by its
|
|
21
|
+
own naming convention:
|
|
22
|
+
|
|
23
|
+
- Construct — ``atom(bytes)`` / ``tuple_(items)`` (Go ``NewAtom``/``NewTuple``,
|
|
24
|
+
Rust ``Value::atom``/``tuple``, TS ``atom``/``tuple``).
|
|
25
|
+
- Atom bytes + length — ``Atom.bytes()`` and ``len(atom)``.
|
|
26
|
+
- Tuple children + arity + indexed access — ``Tuple.items()``, ``len(tuple)``,
|
|
27
|
+
and ``Tuple.at(i)``.
|
|
28
|
+
- Structural equality — ``==`` (``__eq__``) and the module-level ``equals``.
|
|
29
|
+
- Diagnostic string — ``str(value)`` (Go ``String``, Rust ``Display``, TS
|
|
30
|
+
``toString``); **not** a canonical encoding.
|
|
31
|
+
|
|
32
|
+
Intentional per-language extras
|
|
33
|
+
--------------------------------
|
|
34
|
+
Python exposes ``is_atom``/``is_tuple`` (idiomatic ``isinstance`` shorthands) and
|
|
35
|
+
hashability (``__hash__``) so values can be dict keys / set members in Python
|
|
36
|
+
code. These are binding conveniences; they confer no new identity (``__hash__`` is
|
|
37
|
+
consistent with structural ``__eq__``) and no canonical form.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
from typing import Iterable, Union
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"Value",
|
|
46
|
+
"Atom",
|
|
47
|
+
"Tuple",
|
|
48
|
+
"atom",
|
|
49
|
+
"tuple_",
|
|
50
|
+
"is_value",
|
|
51
|
+
"equals",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Value:
|
|
56
|
+
"""The abstract base of the two value constructors: ``Atom`` and ``Tuple``.
|
|
57
|
+
|
|
58
|
+
``Value`` itself is never instantiated; it exists only so ``isinstance(v,
|
|
59
|
+
Value)`` and type hints have a single nominal type. The value set is closed:
|
|
60
|
+
every value is exactly one of ``Atom`` or ``Tuple`` (ontos-core §1).
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
__slots__ = ()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Atom(Value):
|
|
67
|
+
"""An opaque byte atom. The bytes have no built-in interpretation.
|
|
68
|
+
|
|
69
|
+
The bytes are copied and stored immutably; ``bytes()`` returns them as an
|
|
70
|
+
(immutable) ``bytes`` object, so a caller can never mutate the atom.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
__slots__ = ("_bytes",)
|
|
74
|
+
|
|
75
|
+
def __init__(self, data: Union[bytes, bytearray, Iterable[int]] = b"") -> None:
|
|
76
|
+
# bytes(...) copies and validates that every element is a 0..255 int, so
|
|
77
|
+
# an out-of-range or non-int element raises ValueError/TypeError here —
|
|
78
|
+
# exactly the "an atom carries octets" invariant (ontos-core §1).
|
|
79
|
+
object.__setattr__(self, "_bytes", bytes(data))
|
|
80
|
+
|
|
81
|
+
def __setattr__(self, name: str, value: object) -> None: # pragma: no cover
|
|
82
|
+
raise AttributeError("ontos values are immutable")
|
|
83
|
+
|
|
84
|
+
def __delattr__(self, name: str) -> None: # pragma: no cover
|
|
85
|
+
raise AttributeError("ontos values are immutable")
|
|
86
|
+
|
|
87
|
+
def bytes(self) -> bytes:
|
|
88
|
+
"""Return the atom's octets. ``bytes`` is itself immutable in Python."""
|
|
89
|
+
return self._bytes
|
|
90
|
+
|
|
91
|
+
def __len__(self) -> int:
|
|
92
|
+
"""The atom's byte length (the analog of TS ``Atom#length``)."""
|
|
93
|
+
return len(self._bytes)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def is_atom(self) -> bool:
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def is_tuple(self) -> bool:
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def __eq__(self, other: object) -> bool:
|
|
104
|
+
# Atom(b) = Atom(c) iff b and c are the same octet string; Atom != Tuple
|
|
105
|
+
# always (ontos-core §3). NotImplemented lets Python try the reflected op
|
|
106
|
+
# for unrelated types rather than declaring inequality prematurely.
|
|
107
|
+
if isinstance(other, Atom):
|
|
108
|
+
return self._bytes == other._bytes
|
|
109
|
+
if isinstance(other, Tuple):
|
|
110
|
+
return False
|
|
111
|
+
return NotImplemented
|
|
112
|
+
|
|
113
|
+
def __ne__(self, other: object) -> bool:
|
|
114
|
+
result = self.__eq__(other)
|
|
115
|
+
if result is NotImplemented:
|
|
116
|
+
return result
|
|
117
|
+
return not result
|
|
118
|
+
|
|
119
|
+
def __hash__(self) -> int:
|
|
120
|
+
# Tagged so an Atom and a Tuple with related structure never share a hash
|
|
121
|
+
# bucket by accident; consistent with __eq__ (equal atoms hash equal).
|
|
122
|
+
return hash(("atom", self._bytes))
|
|
123
|
+
|
|
124
|
+
def __str__(self) -> str:
|
|
125
|
+
return f"Atom(0x{self._bytes.hex()})"
|
|
126
|
+
|
|
127
|
+
def __repr__(self) -> str:
|
|
128
|
+
return self.__str__()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class Tuple(Value):
|
|
132
|
+
"""A finite ordered tuple of values. Arity, order, and multiplicity matter.
|
|
133
|
+
|
|
134
|
+
Children are validated to be ``Value`` instances and stored in an immutable
|
|
135
|
+
builtin tuple, so a ``Tuple`` cannot be mutated and cannot hold a non-value.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
__slots__ = ("_items",)
|
|
139
|
+
|
|
140
|
+
def __init__(self, items: Iterable[Value] = ()) -> None:
|
|
141
|
+
copied = tuple(items)
|
|
142
|
+
for item in copied:
|
|
143
|
+
if not isinstance(item, Value):
|
|
144
|
+
raise TypeError("expected an ontos Value as a tuple child")
|
|
145
|
+
object.__setattr__(self, "_items", copied)
|
|
146
|
+
|
|
147
|
+
def __setattr__(self, name: str, value: object) -> None: # pragma: no cover
|
|
148
|
+
raise AttributeError("ontos values are immutable")
|
|
149
|
+
|
|
150
|
+
def __delattr__(self, name: str) -> None: # pragma: no cover
|
|
151
|
+
raise AttributeError("ontos values are immutable")
|
|
152
|
+
|
|
153
|
+
def items(self) -> tuple:
|
|
154
|
+
"""Return the children as an immutable builtin tuple of ``Value``."""
|
|
155
|
+
return self._items
|
|
156
|
+
|
|
157
|
+
def at(self, index: int) -> Value:
|
|
158
|
+
"""The child at ``index`` (Go ``Tuple.At`` / Rust ``Tuple::at``)."""
|
|
159
|
+
return self._items[index]
|
|
160
|
+
|
|
161
|
+
def __len__(self) -> int:
|
|
162
|
+
"""The arity (number of children)."""
|
|
163
|
+
return len(self._items)
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def is_atom(self) -> bool:
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def is_tuple(self) -> bool:
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
def __eq__(self, other: object) -> bool:
|
|
174
|
+
# Tuple(xs) = Tuple(ys) iff same arity and xs[i] = ys[i] recursively;
|
|
175
|
+
# Tuple != Atom always (ontos-core §3).
|
|
176
|
+
if isinstance(other, Tuple):
|
|
177
|
+
return self._items == other._items
|
|
178
|
+
if isinstance(other, Atom):
|
|
179
|
+
return False
|
|
180
|
+
return NotImplemented
|
|
181
|
+
|
|
182
|
+
def __ne__(self, other: object) -> bool:
|
|
183
|
+
result = self.__eq__(other)
|
|
184
|
+
if result is NotImplemented:
|
|
185
|
+
return result
|
|
186
|
+
return not result
|
|
187
|
+
|
|
188
|
+
def __hash__(self) -> int:
|
|
189
|
+
return hash(("tuple", self._items))
|
|
190
|
+
|
|
191
|
+
def __str__(self) -> str:
|
|
192
|
+
return "Tuple(" + ", ".join(str(item) for item in self._items) + ")"
|
|
193
|
+
|
|
194
|
+
def __repr__(self) -> str:
|
|
195
|
+
return self.__str__()
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def atom(data: Union[bytes, bytearray, Iterable[int]] = b"") -> Atom:
|
|
199
|
+
"""Construct an ``Atom`` from bytes (or an iterable of 0..255 ints)."""
|
|
200
|
+
return Atom(data)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def tuple_(items: Iterable[Value] = ()) -> Tuple:
|
|
204
|
+
"""Construct a ``Tuple`` from an iterable of values.
|
|
205
|
+
|
|
206
|
+
Named ``tuple_`` (trailing underscore) so it does not shadow the builtin
|
|
207
|
+
``tuple``; the type is exported as ``Tuple``.
|
|
208
|
+
"""
|
|
209
|
+
return Tuple(items)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def is_value(value: object) -> bool:
|
|
213
|
+
"""Whether ``value`` is an ontos ``Value`` (an ``Atom`` or a ``Tuple``)."""
|
|
214
|
+
return isinstance(value, (Atom, Tuple))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def equals(left: Value, right: Value) -> bool:
|
|
218
|
+
"""Structural equality (ontos-core §3). The same relation as ``left == right``."""
|
|
219
|
+
return left == right
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from bitwire import Atom, Tuple, Path
|
|
3
|
+
class Contract(unittest.TestCase):
|
|
4
|
+
def test_paths_and_ground_values(self) -> None:
|
|
5
|
+
path: Path = (Atom(),)
|
|
6
|
+
self.assertNotEqual((), path)
|
|
7
|
+
self.assertNotEqual((Atom(b'a/b'),), (Atom(b'a'), Atom(b'b')))
|
|
8
|
+
self.assertNotEqual(Atom(), Tuple())
|
|
9
|
+
def test_value_ownership(self) -> None:
|
|
10
|
+
source = bytearray([0,255])
|
|
11
|
+
value = Atom(source)
|
|
12
|
+
source.clear()
|
|
13
|
+
self.assertEqual(value, Atom(bytes([0,255])))
|
|
14
|
+
if __name__ == '__main__': unittest.main()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from bitwire import Wire, Endpoint, AddressedEndpoint, Atom, Tuple
|
|
2
|
+
async def send_only(wire: Wire) -> None:
|
|
3
|
+
await wire.send(Atom())
|
|
4
|
+
async def consume(endpoint: Endpoint, addressed: AddressedEndpoint) -> None:
|
|
5
|
+
detach = endpoint.receive(lambda message: None)
|
|
6
|
+
await send_only(endpoint)
|
|
7
|
+
await endpoint.send(Tuple())
|
|
8
|
+
detach()
|
|
9
|
+
await addressed.send((Atom(b'leaf'),), Atom())
|
|
10
|
+
addressed.receive(lambda path, message: None)()
|
|
11
|
+
await addressed.close()
|
|
12
|
+
await addressed.closed()
|
|
13
|
+
await endpoint.close()
|
|
14
|
+
await endpoint.closed()
|
bitspark_bitwire-0.3.0/NOTICE
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
Bitwire
|
|
2
|
-
Copyright 2026 Bitspark and the Bitwire contributors
|
|
3
|
-
|
|
4
|
-
This product includes software developed at Bitspark (https://github.com/Bitspark).
|
|
5
|
-
|
|
6
|
-
The Python Wire declarations are adapted from Nightseam, licensed under the
|
|
7
|
-
Apache License, Version 2.0:
|
|
8
|
-
Copyright 2026 Bitspark and the Nightseam contributors.
|
|
9
|
-
Source revision: 1c63f1c4d7e4b5987d4bd32e294177645c92ed8f.
|
|
10
|
-
Source path: duplex/py/nightseam/duplex/wire.py.
|
bitspark_bitwire-0.3.0/PKG-INFO
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: bitspark-bitwire
|
|
3
|
-
Version: 0.3.0
|
|
4
|
-
Summary: Addressless Wire primitives, full WireTree structure and addressed carrier contracts for Python
|
|
5
|
-
Author: Bitspark
|
|
6
|
-
License-Expression: Apache-2.0
|
|
7
|
-
Project-URL: Homepage, https://github.com/Bitspark/bitwire
|
|
8
|
-
Project-URL: Documentation, https://github.com/Bitspark/bitwire/tree/main/docs
|
|
9
|
-
Project-URL: Repository, https://github.com/Bitspark/bitwire
|
|
10
|
-
Project-URL: Issues, https://github.com/Bitspark/bitwire/issues
|
|
11
|
-
Classifier: Development Status :: 3 - Alpha
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
-
Classifier: Typing :: Typed
|
|
15
|
-
Requires-Python: >=3.11
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
License-File: NOTICE
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
|
-
# Bitwire for Python
|
|
22
|
-
|
|
23
|
-
**Source contract: 0.3.0; publication pending.** `Wire` is the addressless
|
|
24
|
-
primitive `send(message)`. `WireTree = DeixisNode<Wire>` provides the complete
|
|
25
|
-
finite, acyclic structure: own value, complete byte-keyed children, partial
|
|
26
|
-
selection and decomposition. Keys are exact arbitrary bytes, including empty
|
|
27
|
-
and non-UTF-8 keys. Empty path selects self; a missing edge is distinct from a
|
|
28
|
-
present refusing primitive. Recomposition preserves own and child identities.
|
|
29
|
-
|
|
30
|
-
This is symmetric with Bitstore's `Data.read()` primitive and
|
|
31
|
-
`DataTree = DeixisNode<Data>`. Derived sending selects the node and invokes its
|
|
32
|
-
own Wire. Construction and derived operators belong in bitruntime; these
|
|
33
|
-
packages publish declarations and criteria, not a production tree runtime.
|
|
34
|
-
See [decision 0012](https://github.com/Bitspark/bitwire/blob/main/docs/decisions/0012-explicit-data-and-wire-trees.md)
|
|
35
|
-
and the [migration guide](https://github.com/Bitspark/bitwire/blob/main/docs/migration-0.3.md).
|
|
36
|
-
|
|
37
|
-
`AddressedWire` explicitly names the former `Wire.send(path, message)` surface.
|
|
38
|
-
It is not a full WireTree. `Endpoint` extends AddressedWire, and return
|
|
39
|
-
capabilities retain AddressedWire so the existing response/lifecycle path
|
|
40
|
-
space, local identity, received context and closure rules remain intact.
|
|
41
|
-
Carrier paths remain exact Unicode-scalar strings under unchanged `bitwire/1`;
|
|
42
|
-
they do not imply support for arbitrary tree byte keys on that carrier.
|
|
43
|
-
|
|
44
|
-
The following addressed-carrier examples use the **0.3 source names**. Older
|
|
45
|
-
0.2.0 artifacts used `Wire` for the addressed interface; their release evidence
|
|
46
|
-
does not validate the renamed declarations or full structural trees.
|
|
47
|
-
|
|
48
|
-
`bitspark-bitwire` provides the shared relative-path AddressedWire contract as the Python
|
|
49
|
-
module `bitwire`. It requires Python 3.11 or newer and has no runtime dependencies.
|
|
50
|
-
The package contains typed declarations and supporting values; endpoint runtimes,
|
|
51
|
-
carriers, codecs and routing helpers belong to implementations.
|
|
52
|
-
|
|
53
|
-
```python
|
|
54
|
-
from bitwire import Endpoint, Message, Receiver, ReturnAddress, AddressedWire
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def call(endpoint: AddressedWire, replies: AddressedWire) -> None:
|
|
58
|
-
endpoint.send(
|
|
59
|
-
["cells", "get"],
|
|
60
|
-
Message(
|
|
61
|
-
{"version": 1, "kind": "request", "id": "request-1", "params": {}},
|
|
62
|
-
return_address=ReturnAddress(replies),
|
|
63
|
-
),
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def listen(endpoint: Endpoint):
|
|
68
|
-
def receive(path, message):
|
|
69
|
-
print(path, message.frame)
|
|
70
|
-
|
|
71
|
-
return endpoint.receive(Receiver(message=receive))
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
`AddressedWire` is a send-only structural `Protocol`; implementations do not need to inherit
|
|
75
|
-
from it. `send` accepts or refuses synchronously; the implementation schedules
|
|
76
|
-
delivery. `Endpoint` extends it with `receive(receiver)` and `close`, keeping
|
|
77
|
-
attachment and closure authority separate from send access. One receive attachment
|
|
78
|
-
is allowed at a time; duplicates are refused. Receivers may return an awaitable.
|
|
79
|
-
`receive` returns an idempotent detach function that cannot remove a replacement.
|
|
80
|
-
Path matching belongs to routing compositions, not to these interfaces.
|
|
81
|
-
Paths are sequences of opaque Unicode-scalar strings and retain empty segments.
|
|
82
|
-
Receiver paths are relative to the attached endpoint.
|
|
83
|
-
|
|
84
|
-
`ReturnAddress` uses object identity, including when its AddressedWire cannot be compared
|
|
85
|
-
or hashed. Preserve this object during routing. The return address is local
|
|
86
|
-
capability data and must never be serialized into the profile envelope. Profile
|
|
87
|
-
payloads must follow the JSON profile despite their Python `object` annotation.
|
|
88
|
-
Runtime-owned received context associated with the return identity must survive
|
|
89
|
-
routing. Application-supplied context or metadata does not establish verified
|
|
90
|
-
invocation context; that verification remains the runtime's responsibility.
|
|
91
|
-
|
|
92
|
-
The [AddressedWire contract](https://github.com/Bitspark/bitwire/blob/main/docs/wire/contract.md)
|
|
93
|
-
and [profile boundary](https://github.com/Bitspark/bitwire/blob/main/docs/wire/profile.md)
|
|
94
|
-
define the behavior. Runtime protocol checks verify only the presence of methods;
|
|
95
|
-
they cannot establish conformance.
|
|
96
|
-
|
|
97
|
-
## Packaging and checks
|
|
98
|
-
|
|
99
|
-
From this directory:
|
|
100
|
-
|
|
101
|
-
```sh
|
|
102
|
-
python -m pip install -r requirements-dev.txt
|
|
103
|
-
python check.py
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
This builds a source distribution and a wheel from it, verifies the distribution
|
|
107
|
-
contents, installs the wheel into a temporary virtual environment, and runs both
|
|
108
|
-
consumer tests and static typing checks against that installed package. The test
|
|
109
|
-
endpoint is a recording fixture, not a Bitwire runtime or conformance driver.
|
|
110
|
-
|
|
111
|
-
For release artifacts at a chosen location:
|
|
112
|
-
|
|
113
|
-
```sh
|
|
114
|
-
python -m build --outdir dist
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Optional PyPI publication
|
|
118
|
-
|
|
119
|
-
The manual `publish-python.yml` workflow publishes an existing immutable public
|
|
120
|
-
release tag. It is separate from the Go/TypeScript release and does not gate that
|
|
121
|
-
handover. Its default authentication is a PyPI trusted publisher. Configure these
|
|
122
|
-
fields under the PyPI account's **Publishing** page for a pending project:
|
|
123
|
-
|
|
124
|
-
| Field | Value |
|
|
125
|
-
| --- | --- |
|
|
126
|
-
| PyPI project name | `bitspark-bitwire` |
|
|
127
|
-
| Owner | `Bitspark` |
|
|
128
|
-
| Repository name | `bitwire` |
|
|
129
|
-
| Workflow name | `publish-python.yml` |
|
|
130
|
-
| Environment name | `pypi` |
|
|
131
|
-
|
|
132
|
-
For an existing project, add the same publisher under the project's publishing
|
|
133
|
-
settings. As a bootstrap fallback, store an API token in the repository or `pypi`
|
|
134
|
-
environment secret named `PYPI_API_TOKEN`, then select `api-token` when dispatching
|
|
135
|
-
the workflow from `main`. Tokens are passed only to publication steps. Revoke the
|
|
136
|
-
bootstrap token after trusted publishing is configured. The workflow verifies the
|
|
137
|
-
packaged artifact before upload and installs the published PyPI package afterward.
|
|
138
|
-
|
|
139
|
-
The binding is adapted from Nightseam's Python AddressedWire declarations at commit
|
|
140
|
-
[`1c63f1c4`](https://github.com/Bitspark/nightseam/blob/1c63f1c4d7e4b5987d4bd32e294177645c92ed8f/duplex/py/nightseam/duplex/wire.py)
|
|
141
|
-
under Apache-2.0. See the included `LICENSE` and `NOTICE` files.
|
bitspark_bitwire-0.3.0/README.md
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# Bitwire for Python
|
|
2
|
-
|
|
3
|
-
**Source contract: 0.3.0; publication pending.** `Wire` is the addressless
|
|
4
|
-
primitive `send(message)`. `WireTree = DeixisNode<Wire>` provides the complete
|
|
5
|
-
finite, acyclic structure: own value, complete byte-keyed children, partial
|
|
6
|
-
selection and decomposition. Keys are exact arbitrary bytes, including empty
|
|
7
|
-
and non-UTF-8 keys. Empty path selects self; a missing edge is distinct from a
|
|
8
|
-
present refusing primitive. Recomposition preserves own and child identities.
|
|
9
|
-
|
|
10
|
-
This is symmetric with Bitstore's `Data.read()` primitive and
|
|
11
|
-
`DataTree = DeixisNode<Data>`. Derived sending selects the node and invokes its
|
|
12
|
-
own Wire. Construction and derived operators belong in bitruntime; these
|
|
13
|
-
packages publish declarations and criteria, not a production tree runtime.
|
|
14
|
-
See [decision 0012](https://github.com/Bitspark/bitwire/blob/main/docs/decisions/0012-explicit-data-and-wire-trees.md)
|
|
15
|
-
and the [migration guide](https://github.com/Bitspark/bitwire/blob/main/docs/migration-0.3.md).
|
|
16
|
-
|
|
17
|
-
`AddressedWire` explicitly names the former `Wire.send(path, message)` surface.
|
|
18
|
-
It is not a full WireTree. `Endpoint` extends AddressedWire, and return
|
|
19
|
-
capabilities retain AddressedWire so the existing response/lifecycle path
|
|
20
|
-
space, local identity, received context and closure rules remain intact.
|
|
21
|
-
Carrier paths remain exact Unicode-scalar strings under unchanged `bitwire/1`;
|
|
22
|
-
they do not imply support for arbitrary tree byte keys on that carrier.
|
|
23
|
-
|
|
24
|
-
The following addressed-carrier examples use the **0.3 source names**. Older
|
|
25
|
-
0.2.0 artifacts used `Wire` for the addressed interface; their release evidence
|
|
26
|
-
does not validate the renamed declarations or full structural trees.
|
|
27
|
-
|
|
28
|
-
`bitspark-bitwire` provides the shared relative-path AddressedWire contract as the Python
|
|
29
|
-
module `bitwire`. It requires Python 3.11 or newer and has no runtime dependencies.
|
|
30
|
-
The package contains typed declarations and supporting values; endpoint runtimes,
|
|
31
|
-
carriers, codecs and routing helpers belong to implementations.
|
|
32
|
-
|
|
33
|
-
```python
|
|
34
|
-
from bitwire import Endpoint, Message, Receiver, ReturnAddress, AddressedWire
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def call(endpoint: AddressedWire, replies: AddressedWire) -> None:
|
|
38
|
-
endpoint.send(
|
|
39
|
-
["cells", "get"],
|
|
40
|
-
Message(
|
|
41
|
-
{"version": 1, "kind": "request", "id": "request-1", "params": {}},
|
|
42
|
-
return_address=ReturnAddress(replies),
|
|
43
|
-
),
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def listen(endpoint: Endpoint):
|
|
48
|
-
def receive(path, message):
|
|
49
|
-
print(path, message.frame)
|
|
50
|
-
|
|
51
|
-
return endpoint.receive(Receiver(message=receive))
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
`AddressedWire` is a send-only structural `Protocol`; implementations do not need to inherit
|
|
55
|
-
from it. `send` accepts or refuses synchronously; the implementation schedules
|
|
56
|
-
delivery. `Endpoint` extends it with `receive(receiver)` and `close`, keeping
|
|
57
|
-
attachment and closure authority separate from send access. One receive attachment
|
|
58
|
-
is allowed at a time; duplicates are refused. Receivers may return an awaitable.
|
|
59
|
-
`receive` returns an idempotent detach function that cannot remove a replacement.
|
|
60
|
-
Path matching belongs to routing compositions, not to these interfaces.
|
|
61
|
-
Paths are sequences of opaque Unicode-scalar strings and retain empty segments.
|
|
62
|
-
Receiver paths are relative to the attached endpoint.
|
|
63
|
-
|
|
64
|
-
`ReturnAddress` uses object identity, including when its AddressedWire cannot be compared
|
|
65
|
-
or hashed. Preserve this object during routing. The return address is local
|
|
66
|
-
capability data and must never be serialized into the profile envelope. Profile
|
|
67
|
-
payloads must follow the JSON profile despite their Python `object` annotation.
|
|
68
|
-
Runtime-owned received context associated with the return identity must survive
|
|
69
|
-
routing. Application-supplied context or metadata does not establish verified
|
|
70
|
-
invocation context; that verification remains the runtime's responsibility.
|
|
71
|
-
|
|
72
|
-
The [AddressedWire contract](https://github.com/Bitspark/bitwire/blob/main/docs/wire/contract.md)
|
|
73
|
-
and [profile boundary](https://github.com/Bitspark/bitwire/blob/main/docs/wire/profile.md)
|
|
74
|
-
define the behavior. Runtime protocol checks verify only the presence of methods;
|
|
75
|
-
they cannot establish conformance.
|
|
76
|
-
|
|
77
|
-
## Packaging and checks
|
|
78
|
-
|
|
79
|
-
From this directory:
|
|
80
|
-
|
|
81
|
-
```sh
|
|
82
|
-
python -m pip install -r requirements-dev.txt
|
|
83
|
-
python check.py
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
This builds a source distribution and a wheel from it, verifies the distribution
|
|
87
|
-
contents, installs the wheel into a temporary virtual environment, and runs both
|
|
88
|
-
consumer tests and static typing checks against that installed package. The test
|
|
89
|
-
endpoint is a recording fixture, not a Bitwire runtime or conformance driver.
|
|
90
|
-
|
|
91
|
-
For release artifacts at a chosen location:
|
|
92
|
-
|
|
93
|
-
```sh
|
|
94
|
-
python -m build --outdir dist
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Optional PyPI publication
|
|
98
|
-
|
|
99
|
-
The manual `publish-python.yml` workflow publishes an existing immutable public
|
|
100
|
-
release tag. It is separate from the Go/TypeScript release and does not gate that
|
|
101
|
-
handover. Its default authentication is a PyPI trusted publisher. Configure these
|
|
102
|
-
fields under the PyPI account's **Publishing** page for a pending project:
|
|
103
|
-
|
|
104
|
-
| Field | Value |
|
|
105
|
-
| --- | --- |
|
|
106
|
-
| PyPI project name | `bitspark-bitwire` |
|
|
107
|
-
| Owner | `Bitspark` |
|
|
108
|
-
| Repository name | `bitwire` |
|
|
109
|
-
| Workflow name | `publish-python.yml` |
|
|
110
|
-
| Environment name | `pypi` |
|
|
111
|
-
|
|
112
|
-
For an existing project, add the same publisher under the project's publishing
|
|
113
|
-
settings. As a bootstrap fallback, store an API token in the repository or `pypi`
|
|
114
|
-
environment secret named `PYPI_API_TOKEN`, then select `api-token` when dispatching
|
|
115
|
-
the workflow from `main`. Tokens are passed only to publication steps. Revoke the
|
|
116
|
-
bootstrap token after trusted publishing is configured. The workflow verifies the
|
|
117
|
-
packaged artifact before upload and installs the published PyPI package afterward.
|
|
118
|
-
|
|
119
|
-
The binding is adapted from Nightseam's Python AddressedWire declarations at commit
|
|
120
|
-
[`1c63f1c4`](https://github.com/Bitspark/nightseam/blob/1c63f1c4d7e4b5987d4bd32e294177645c92ed8f/duplex/py/nightseam/duplex/wire.py)
|
|
121
|
-
under Apache-2.0. See the included `LICENSE` and `NOTICE` files.
|