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.
Files changed (26) hide show
  1. bitspark_bitwire-0.5.0/NOTICE +9 -0
  2. bitspark_bitwire-0.5.0/PKG-INFO +30 -0
  3. bitspark_bitwire-0.5.0/README.md +10 -0
  4. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/pyproject.toml +2 -2
  5. bitspark_bitwire-0.5.0/src/bitspark_bitwire.egg-info/PKG-INFO +30 -0
  6. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/SOURCES.txt +1 -1
  7. bitspark_bitwire-0.5.0/src/bitwire/__init__.py +35 -0
  8. bitspark_bitwire-0.5.0/src/bitwire/ontos.py +219 -0
  9. bitspark_bitwire-0.5.0/tests/test_consumer.py +14 -0
  10. bitspark_bitwire-0.5.0/tests/typecheck.py +14 -0
  11. bitspark_bitwire-0.3.0/NOTICE +0 -10
  12. bitspark_bitwire-0.3.0/PKG-INFO +0 -141
  13. bitspark_bitwire-0.3.0/README.md +0 -121
  14. bitspark_bitwire-0.3.0/src/bitspark_bitwire.egg-info/PKG-INFO +0 -141
  15. bitspark_bitwire-0.3.0/src/bitwire/__init__.py +0 -192
  16. bitspark_bitwire-0.3.0/tests/test_consumer.py +0 -157
  17. bitspark_bitwire-0.3.0/tests/test_tree.py +0 -85
  18. bitspark_bitwire-0.3.0/tests/typecheck.py +0 -64
  19. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/LICENSE +0 -0
  20. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/MANIFEST.in +0 -0
  21. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/check.py +0 -0
  22. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/requirements-dev.txt +0 -0
  23. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/setup.cfg +0 -0
  24. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/dependency_links.txt +0 -0
  25. {bitspark_bitwire-0.3.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/top_level.txt +0 -0
  26. {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.3.0"
8
- description = "Addressless Wire primitives, full WireTree structure and addressed carrier contracts for Python"
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()
@@ -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.
@@ -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.
@@ -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.