bitspark-bitwire 0.2.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 (25) 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.2.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.2.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/SOURCES.txt +1 -0
  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.2.0/NOTICE +0 -10
  12. bitspark_bitwire-0.2.0/PKG-INFO +0 -116
  13. bitspark_bitwire-0.2.0/README.md +0 -96
  14. bitspark_bitwire-0.2.0/src/bitspark_bitwire.egg-info/PKG-INFO +0 -116
  15. bitspark_bitwire-0.2.0/src/bitwire/__init__.py +0 -150
  16. bitspark_bitwire-0.2.0/tests/test_consumer.py +0 -157
  17. bitspark_bitwire-0.2.0/tests/typecheck.py +0 -46
  18. {bitspark_bitwire-0.2.0 → bitspark_bitwire-0.5.0}/LICENSE +0 -0
  19. {bitspark_bitwire-0.2.0 → bitspark_bitwire-0.5.0}/MANIFEST.in +0 -0
  20. {bitspark_bitwire-0.2.0 → bitspark_bitwire-0.5.0}/check.py +0 -0
  21. {bitspark_bitwire-0.2.0 → bitspark_bitwire-0.5.0}/requirements-dev.txt +0 -0
  22. {bitspark_bitwire-0.2.0 → bitspark_bitwire-0.5.0}/setup.cfg +0 -0
  23. {bitspark_bitwire-0.2.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/dependency_links.txt +0 -0
  24. {bitspark_bitwire-0.2.0 → bitspark_bitwire-0.5.0}/src/bitspark_bitwire.egg-info/top_level.txt +0 -0
  25. {bitspark_bitwire-0.2.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.2.0"
8
- description = "The shared relative-path Wire contract 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,6 +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
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,116 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: bitspark-bitwire
3
- Version: 0.2.0
4
- Summary: The shared relative-path Wire contract 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
- `bitspark-bitwire` provides the shared relative-path Wire contract as the Python
24
- module `bitwire`. It requires Python 3.11 or newer and has no runtime dependencies.
25
- The package contains typed declarations and supporting values; endpoint runtimes,
26
- carriers, codecs and routing helpers belong to implementations.
27
-
28
- ```python
29
- from bitwire import Endpoint, Message, Receiver, ReturnAddress, Wire
30
-
31
-
32
- def call(endpoint: Wire, replies: Wire) -> None:
33
- endpoint.send(
34
- ["cells", "get"],
35
- Message(
36
- {"version": 1, "kind": "request", "id": "request-1", "params": {}},
37
- return_address=ReturnAddress(replies),
38
- ),
39
- )
40
-
41
-
42
- def listen(endpoint: Endpoint):
43
- def receive(path, message):
44
- print(path, message.frame)
45
-
46
- return endpoint.receive(Receiver(message=receive))
47
- ```
48
-
49
- `Wire` is a send-only structural `Protocol`; implementations do not need to inherit
50
- from it. `send` accepts or refuses synchronously; the implementation schedules
51
- delivery. `Endpoint` extends it with `receive(receiver)` and `close`, keeping
52
- attachment and closure authority separate from send access. One receive attachment
53
- is allowed at a time; duplicates are refused. Receivers may return an awaitable.
54
- `receive` returns an idempotent detach function that cannot remove a replacement.
55
- Path matching belongs to routing compositions, not to these interfaces.
56
- Paths are sequences of opaque Unicode-scalar strings and retain empty segments.
57
- Receiver paths are relative to the attached endpoint.
58
-
59
- `ReturnAddress` uses object identity, including when its Wire cannot be compared
60
- or hashed. Preserve this object during routing. The return address is local
61
- capability data and must never be serialized into the profile envelope. Profile
62
- payloads must follow the JSON profile despite their Python `object` annotation.
63
- Runtime-owned received context associated with the return identity must survive
64
- routing. Application-supplied context or metadata does not establish verified
65
- invocation context; that verification remains the runtime's responsibility.
66
-
67
- The [Wire contract](https://github.com/Bitspark/bitwire/blob/main/docs/wire/contract.md)
68
- and [profile boundary](https://github.com/Bitspark/bitwire/blob/main/docs/wire/profile.md)
69
- define the behavior. Runtime protocol checks verify only the presence of methods;
70
- they cannot establish conformance.
71
-
72
- ## Packaging and checks
73
-
74
- From this directory:
75
-
76
- ```sh
77
- python -m pip install -r requirements-dev.txt
78
- python check.py
79
- ```
80
-
81
- This builds a source distribution and a wheel from it, verifies the distribution
82
- contents, installs the wheel into a temporary virtual environment, and runs both
83
- consumer tests and static typing checks against that installed package. The test
84
- endpoint is a recording fixture, not a Bitwire runtime or conformance driver.
85
-
86
- For release artifacts at a chosen location:
87
-
88
- ```sh
89
- python -m build --outdir dist
90
- ```
91
-
92
- ## Optional PyPI publication
93
-
94
- The manual `publish-python.yml` workflow publishes an existing immutable public
95
- release tag. It is separate from the Go/TypeScript release and does not gate that
96
- handover. Its default authentication is a PyPI trusted publisher. Configure these
97
- fields under the PyPI account's **Publishing** page for a pending project:
98
-
99
- | Field | Value |
100
- | --- | --- |
101
- | PyPI project name | `bitspark-bitwire` |
102
- | Owner | `Bitspark` |
103
- | Repository name | `bitwire` |
104
- | Workflow name | `publish-python.yml` |
105
- | Environment name | `pypi` |
106
-
107
- For an existing project, add the same publisher under the project's publishing
108
- settings. As a bootstrap fallback, store an API token in the repository or `pypi`
109
- environment secret named `PYPI_API_TOKEN`, then select `api-token` when dispatching
110
- the workflow from `main`. Tokens are passed only to publication steps. Revoke the
111
- bootstrap token after trusted publishing is configured. The workflow verifies the
112
- packaged artifact before upload and installs the published PyPI package afterward.
113
-
114
- The binding is adapted from Nightseam's Python Wire declarations at commit
115
- [`1c63f1c4`](https://github.com/Bitspark/nightseam/blob/1c63f1c4d7e4b5987d4bd32e294177645c92ed8f/duplex/py/nightseam/duplex/wire.py)
116
- under Apache-2.0. See the included `LICENSE` and `NOTICE` files.
@@ -1,96 +0,0 @@
1
- # Bitwire for Python
2
-
3
- `bitspark-bitwire` provides the shared relative-path Wire contract as the Python
4
- module `bitwire`. It requires Python 3.11 or newer and has no runtime dependencies.
5
- The package contains typed declarations and supporting values; endpoint runtimes,
6
- carriers, codecs and routing helpers belong to implementations.
7
-
8
- ```python
9
- from bitwire import Endpoint, Message, Receiver, ReturnAddress, Wire
10
-
11
-
12
- def call(endpoint: Wire, replies: Wire) -> None:
13
- endpoint.send(
14
- ["cells", "get"],
15
- Message(
16
- {"version": 1, "kind": "request", "id": "request-1", "params": {}},
17
- return_address=ReturnAddress(replies),
18
- ),
19
- )
20
-
21
-
22
- def listen(endpoint: Endpoint):
23
- def receive(path, message):
24
- print(path, message.frame)
25
-
26
- return endpoint.receive(Receiver(message=receive))
27
- ```
28
-
29
- `Wire` is a send-only structural `Protocol`; implementations do not need to inherit
30
- from it. `send` accepts or refuses synchronously; the implementation schedules
31
- delivery. `Endpoint` extends it with `receive(receiver)` and `close`, keeping
32
- attachment and closure authority separate from send access. One receive attachment
33
- is allowed at a time; duplicates are refused. Receivers may return an awaitable.
34
- `receive` returns an idempotent detach function that cannot remove a replacement.
35
- Path matching belongs to routing compositions, not to these interfaces.
36
- Paths are sequences of opaque Unicode-scalar strings and retain empty segments.
37
- Receiver paths are relative to the attached endpoint.
38
-
39
- `ReturnAddress` uses object identity, including when its Wire cannot be compared
40
- or hashed. Preserve this object during routing. The return address is local
41
- capability data and must never be serialized into the profile envelope. Profile
42
- payloads must follow the JSON profile despite their Python `object` annotation.
43
- Runtime-owned received context associated with the return identity must survive
44
- routing. Application-supplied context or metadata does not establish verified
45
- invocation context; that verification remains the runtime's responsibility.
46
-
47
- The [Wire contract](https://github.com/Bitspark/bitwire/blob/main/docs/wire/contract.md)
48
- and [profile boundary](https://github.com/Bitspark/bitwire/blob/main/docs/wire/profile.md)
49
- define the behavior. Runtime protocol checks verify only the presence of methods;
50
- they cannot establish conformance.
51
-
52
- ## Packaging and checks
53
-
54
- From this directory:
55
-
56
- ```sh
57
- python -m pip install -r requirements-dev.txt
58
- python check.py
59
- ```
60
-
61
- This builds a source distribution and a wheel from it, verifies the distribution
62
- contents, installs the wheel into a temporary virtual environment, and runs both
63
- consumer tests and static typing checks against that installed package. The test
64
- endpoint is a recording fixture, not a Bitwire runtime or conformance driver.
65
-
66
- For release artifacts at a chosen location:
67
-
68
- ```sh
69
- python -m build --outdir dist
70
- ```
71
-
72
- ## Optional PyPI publication
73
-
74
- The manual `publish-python.yml` workflow publishes an existing immutable public
75
- release tag. It is separate from the Go/TypeScript release and does not gate that
76
- handover. Its default authentication is a PyPI trusted publisher. Configure these
77
- fields under the PyPI account's **Publishing** page for a pending project:
78
-
79
- | Field | Value |
80
- | --- | --- |
81
- | PyPI project name | `bitspark-bitwire` |
82
- | Owner | `Bitspark` |
83
- | Repository name | `bitwire` |
84
- | Workflow name | `publish-python.yml` |
85
- | Environment name | `pypi` |
86
-
87
- For an existing project, add the same publisher under the project's publishing
88
- settings. As a bootstrap fallback, store an API token in the repository or `pypi`
89
- environment secret named `PYPI_API_TOKEN`, then select `api-token` when dispatching
90
- the workflow from `main`. Tokens are passed only to publication steps. Revoke the
91
- bootstrap token after trusted publishing is configured. The workflow verifies the
92
- packaged artifact before upload and installs the published PyPI package afterward.
93
-
94
- The binding is adapted from Nightseam's Python Wire declarations at commit
95
- [`1c63f1c4`](https://github.com/Bitspark/nightseam/blob/1c63f1c4d7e4b5987d4bd32e294177645c92ed8f/duplex/py/nightseam/duplex/wire.py)
96
- under Apache-2.0. See the included `LICENSE` and `NOTICE` files.
@@ -1,116 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: bitspark-bitwire
3
- Version: 0.2.0
4
- Summary: The shared relative-path Wire contract 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
- `bitspark-bitwire` provides the shared relative-path Wire contract as the Python
24
- module `bitwire`. It requires Python 3.11 or newer and has no runtime dependencies.
25
- The package contains typed declarations and supporting values; endpoint runtimes,
26
- carriers, codecs and routing helpers belong to implementations.
27
-
28
- ```python
29
- from bitwire import Endpoint, Message, Receiver, ReturnAddress, Wire
30
-
31
-
32
- def call(endpoint: Wire, replies: Wire) -> None:
33
- endpoint.send(
34
- ["cells", "get"],
35
- Message(
36
- {"version": 1, "kind": "request", "id": "request-1", "params": {}},
37
- return_address=ReturnAddress(replies),
38
- ),
39
- )
40
-
41
-
42
- def listen(endpoint: Endpoint):
43
- def receive(path, message):
44
- print(path, message.frame)
45
-
46
- return endpoint.receive(Receiver(message=receive))
47
- ```
48
-
49
- `Wire` is a send-only structural `Protocol`; implementations do not need to inherit
50
- from it. `send` accepts or refuses synchronously; the implementation schedules
51
- delivery. `Endpoint` extends it with `receive(receiver)` and `close`, keeping
52
- attachment and closure authority separate from send access. One receive attachment
53
- is allowed at a time; duplicates are refused. Receivers may return an awaitable.
54
- `receive` returns an idempotent detach function that cannot remove a replacement.
55
- Path matching belongs to routing compositions, not to these interfaces.
56
- Paths are sequences of opaque Unicode-scalar strings and retain empty segments.
57
- Receiver paths are relative to the attached endpoint.
58
-
59
- `ReturnAddress` uses object identity, including when its Wire cannot be compared
60
- or hashed. Preserve this object during routing. The return address is local
61
- capability data and must never be serialized into the profile envelope. Profile
62
- payloads must follow the JSON profile despite their Python `object` annotation.
63
- Runtime-owned received context associated with the return identity must survive
64
- routing. Application-supplied context or metadata does not establish verified
65
- invocation context; that verification remains the runtime's responsibility.
66
-
67
- The [Wire contract](https://github.com/Bitspark/bitwire/blob/main/docs/wire/contract.md)
68
- and [profile boundary](https://github.com/Bitspark/bitwire/blob/main/docs/wire/profile.md)
69
- define the behavior. Runtime protocol checks verify only the presence of methods;
70
- they cannot establish conformance.
71
-
72
- ## Packaging and checks
73
-
74
- From this directory:
75
-
76
- ```sh
77
- python -m pip install -r requirements-dev.txt
78
- python check.py
79
- ```
80
-
81
- This builds a source distribution and a wheel from it, verifies the distribution
82
- contents, installs the wheel into a temporary virtual environment, and runs both
83
- consumer tests and static typing checks against that installed package. The test
84
- endpoint is a recording fixture, not a Bitwire runtime or conformance driver.
85
-
86
- For release artifacts at a chosen location:
87
-
88
- ```sh
89
- python -m build --outdir dist
90
- ```
91
-
92
- ## Optional PyPI publication
93
-
94
- The manual `publish-python.yml` workflow publishes an existing immutable public
95
- release tag. It is separate from the Go/TypeScript release and does not gate that
96
- handover. Its default authentication is a PyPI trusted publisher. Configure these
97
- fields under the PyPI account's **Publishing** page for a pending project:
98
-
99
- | Field | Value |
100
- | --- | --- |
101
- | PyPI project name | `bitspark-bitwire` |
102
- | Owner | `Bitspark` |
103
- | Repository name | `bitwire` |
104
- | Workflow name | `publish-python.yml` |
105
- | Environment name | `pypi` |
106
-
107
- For an existing project, add the same publisher under the project's publishing
108
- settings. As a bootstrap fallback, store an API token in the repository or `pypi`
109
- environment secret named `PYPI_API_TOKEN`, then select `api-token` when dispatching
110
- the workflow from `main`. Tokens are passed only to publication steps. Revoke the
111
- bootstrap token after trusted publishing is configured. The workflow verifies the
112
- packaged artifact before upload and installs the published PyPI package afterward.
113
-
114
- The binding is adapted from Nightseam's Python Wire declarations at commit
115
- [`1c63f1c4`](https://github.com/Bitspark/nightseam/blob/1c63f1c4d7e4b5987d4bd32e294177645c92ed8f/duplex/py/nightseam/duplex/wire.py)
116
- under Apache-2.0. See the included `LICENSE` and `NOTICE` files.
@@ -1,150 +0,0 @@
1
- """The shared relative-path Wire contract, without a runtime dependency.
2
-
3
- Adapted from Nightseam's duplex/py/nightseam/duplex/wire.py at
4
- 1c63f1c4d7e4b5987d4bd32e294177645c92ed8f under Apache-2.0.
5
- Dispatch, path encoding, carriers and composition helpers belong to implementations.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- from collections.abc import Awaitable, Callable, Mapping, Sequence
11
- from dataclasses import dataclass
12
- from typing import Literal, NotRequired, Protocol, TypedDict, runtime_checkable
13
-
14
- __all__ = [
15
- "Path",
16
- "ProfileKind",
17
- "ProfileError",
18
- "RequestFrame",
19
- "ResultFrame",
20
- "ErrorFrame",
21
- "EventFrame",
22
- "CancelFrame",
23
- "ProfileFrame",
24
- "ReturnAddress",
25
- "Message",
26
- "Receiver",
27
- "Wire",
28
- "Endpoint",
29
- ]
30
-
31
- # Opaque Unicode-scalar segments: [] != [""] != ["a/b"] != ["a", "b"].
32
- Path = Sequence[str]
33
- ProfileKind = Literal["request", "response", "event", "cancel"]
34
-
35
-
36
- class ProfileError(TypedDict):
37
- """Public error data, independent of any runtime exception class."""
38
-
39
- code: str
40
- message: str
41
- data: NotRequired[object]
42
-
43
-
44
- class _TracedFrame(TypedDict):
45
- version: Literal[1]
46
- traceparent: NotRequired[str]
47
- tracestate: NotRequired[str]
48
-
49
-
50
- class RequestFrame(_TracedFrame):
51
- """The send path supplies the method name; params follow the JSON profile."""
52
-
53
- kind: Literal["request"]
54
- id: str
55
- params: object
56
- meta: NotRequired[Mapping[str, str]]
57
-
58
-
59
- class ResultFrame(_TracedFrame):
60
- kind: Literal["response"]
61
- id: str
62
- result: object
63
-
64
-
65
- class ErrorFrame(_TracedFrame):
66
- kind: Literal["response"]
67
- id: str
68
- error: ProfileError
69
-
70
-
71
- class EventFrame(_TracedFrame):
72
- """The send path supplies the event name; data follow the JSON profile."""
73
-
74
- kind: Literal["event"]
75
- data: object
76
- meta: NotRequired[Mapping[str, str]]
77
-
78
-
79
- class CancelFrame(_TracedFrame):
80
- kind: Literal["cancel"]
81
- id: str
82
-
83
-
84
- ProfileFrame = RequestFrame | ResultFrame | ErrorFrame | EventFrame | CancelFrame
85
-
86
-
87
- @dataclass(frozen=True, eq=False)
88
- class ReturnAddress:
89
- """Local capability with identity equality, never a network-envelope member.
90
-
91
- Different addresses stay distinct even when they hold the same Wire. The
92
- wrapped implementation need not support equality or hashing. Routing must
93
- preserve this address object, rather than construct an equivalent wrapper.
94
- Runtime-owned received context associated with this identity must survive
95
- routing; application payloads are not evidence of verified context.
96
- """
97
-
98
- wire: Wire
99
-
100
-
101
- @dataclass(frozen=True)
102
- class Message:
103
- """A profile frame plus optional local return access.
104
-
105
- Only frame data belongs to the serialized profile. Payloads typed as object
106
- must still satisfy the JSON profile; arbitrary Python objects are not implied.
107
- """
108
-
109
- frame: ProfileFrame
110
- return_address: ReturnAddress | None = None
111
-
112
-
113
- @dataclass(frozen=True)
114
- class Receiver:
115
- """Callbacks receive every path relative to their attached endpoint.
116
-
117
- A receiver may be synchronous or awaitable; endpoints own dispatch.
118
- """
119
-
120
- message: Callable[[Path, Message], None | Awaitable[None]] | None = None
121
- closed: Callable[[int, str], None] | None = None
122
-
123
-
124
- @runtime_checkable
125
- class Wire(Protocol):
126
- """Send-only access with synchronous admission and asynchronous dispatch.
127
-
128
- send returns on acceptance or raises on refusal, without running destination
129
- application code on the sender's stack. It grants no receiving or closure
130
- authority. Structural protocol matching alone does not prove these laws.
131
- """
132
-
133
- def send(self, path: Path, message: Message) -> None: ...
134
-
135
-
136
- @runtime_checkable
137
- class Endpoint(Wire, Protocol):
138
- """Owning endpoint access with one receive attachment and endpoint closure.
139
-
140
- receive refuses a second active attachment or a closed endpoint and returns
141
- an idempotent detach. A stale detach cannot remove a later attachment. Callbacks receive the complete
142
- message and its relative path; routing policy belongs above this boundary.
143
- Detach preserves captured return access and does not close the endpoint.
144
- Closing notifies the active receiver once; detached receivers are not notified.
145
- Closing twice has no additional effect and does not release live bindings.
146
- """
147
-
148
- def receive(self, receiver: Receiver) -> Callable[[], None]: ...
149
-
150
- def close(self, code: int = 1000, reason: str = "") -> None: ...
@@ -1,157 +0,0 @@
1
- """Consumer observations against declarations, not a runtime conformance suite."""
2
-
3
- from __future__ import annotations
4
-
5
- import asyncio
6
- import inspect
7
- import unittest
8
- from collections.abc import Callable
9
-
10
- from bitwire import Endpoint, Message, Path, Receiver, ReturnAddress, Wire
11
-
12
-
13
- class RecordingEndpoint:
14
- """Independent structural consumer fixture; no inheritance or routing runtime."""
15
-
16
- def __init__(self) -> None:
17
- self.sent: list[tuple[tuple[str, ...], Message]] = []
18
- self.receiver: Receiver | None = None
19
- self.attachment: object | None = None
20
- self.ending: tuple[int, str] | None = None
21
-
22
- def __eq__(self, other: object) -> bool:
23
- raise AssertionError("ReturnAddress must not compare the wrapped Wire")
24
-
25
- def send(self, path: Path, message: Message) -> None:
26
- self.sent.append((tuple(path), message))
27
-
28
- def receive(self, receiver: Receiver) -> Callable[[], None]:
29
- if self.ending is not None:
30
- raise RuntimeError("endpoint closed")
31
- if self.receiver is not None:
32
- raise RuntimeError("receiver already attached")
33
- token = object()
34
- self.receiver = receiver
35
- self.attachment = token
36
-
37
- def detach() -> None:
38
- if self.attachment is token:
39
- self.receiver = None
40
- self.attachment = None
41
-
42
- return detach
43
-
44
- def close(self, code: int = 1000, reason: str = "") -> None:
45
- if self.ending is not None:
46
- return
47
- self.ending = (code, reason)
48
- receiver = self.receiver
49
- self.receiver = None
50
- self.attachment = None
51
- if receiver is not None and receiver.closed is not None:
52
- receiver.closed(code, reason)
53
-
54
-
55
- class ConsumerTests(unittest.TestCase):
56
- def test_structural_consumer_keeps_path_frame_and_return_object(self) -> None:
57
- endpoint = RecordingEndpoint()
58
- wire: Wire = endpoint
59
- replies = ReturnAddress(RecordingEndpoint())
60
- request = Message(
61
- {"version": 1, "kind": "request", "id": "r1", "params": {"count": 2**80}},
62
- replies,
63
- )
64
- self.assertIsInstance(endpoint, Wire)
65
- for path in ([], [""], ["a/b"], ["a", "b"], ["\u03bb", "\U0001f30d"]):
66
- wire.send(path, request)
67
-
68
- self.assertEqual(len({path for path, _ in endpoint.sent}), 5)
69
- for _, delivered in endpoint.sent:
70
- self.assertIs(delivered, request)
71
- self.assertIs(delivered.frame, request.frame)
72
- self.assertIs(delivered.return_address, replies)
73
-
74
- def test_return_identity_is_independent_of_wire_equality_and_hashing(self) -> None:
75
- endpoint = RecordingEndpoint()
76
- first, second = ReturnAddress(endpoint), ReturnAddress(endpoint)
77
- self.assertNotEqual(first, second)
78
- self.assertEqual(first, first)
79
- self.assertEqual(len({first, second}), 2)
80
- self.assertIs(first.wire, endpoint)
81
-
82
- def test_async_receiver_and_detach_are_usable_by_a_consumer(self) -> None:
83
- endpoint = RecordingEndpoint()
84
- observed: list[tuple[tuple[str, ...], Message]] = []
85
-
86
- async def delivered(path: Path, message: Message) -> None:
87
- await asyncio.sleep(0)
88
- observed.append((tuple(path), message))
89
-
90
- receiver = Receiver(message=delivered)
91
- detach = endpoint.receive(receiver)
92
- request = Message({"version": 1, "kind": "event", "data": None})
93
-
94
- async def dispatch() -> None:
95
- registered = endpoint.receiver
96
- assert registered is not None and registered.message is not None
97
- result = registered.message(["reply", ""], request)
98
- if inspect.isawaitable(result):
99
- await result
100
-
101
- asyncio.run(dispatch())
102
- self.assertEqual(observed, [(("reply", ""), request)])
103
- detach()
104
- detach()
105
- self.assertIsNone(endpoint.receiver)
106
- self.assertIsNone(endpoint.ending)
107
-
108
- def test_attachment_ownership_and_stale_detach(self) -> None:
109
- endpoint: Endpoint = RecordingEndpoint()
110
- first = Receiver()
111
- detach = endpoint.receive(first)
112
- with self.assertRaises(RuntimeError):
113
- endpoint.receive(first)
114
- detach()
115
- second = Receiver()
116
- second_detach = endpoint.receive(second)
117
- detach()
118
- assert isinstance(endpoint, RecordingEndpoint)
119
- self.assertIs(endpoint.receiver, second)
120
- second_detach()
121
- self.assertIsNone(endpoint.receiver)
122
-
123
- def test_close_notifies_only_active_attachment_once(self) -> None:
124
- endpoint = RecordingEndpoint()
125
- endings: list[tuple[int, str]] = []
126
- detached: list[tuple[int, str]] = []
127
- endpoint.receive(Receiver(closed=lambda c, r: detached.append((c, r))))()
128
- endpoint.receive(Receiver(closed=lambda c, r: endings.append((c, r))))
129
- endpoint.close(1000, "done")
130
- endpoint.close(1001, "again")
131
- self.assertEqual(endings, [(1000, "done")])
132
- self.assertEqual(detached, [])
133
- with self.assertRaises(RuntimeError):
134
- endpoint.receive(Receiver())
135
-
136
- def test_send_only_wire_requires_no_endpoint_control(self) -> None:
137
- class Access:
138
- def send(self, path: Path, message: Message) -> None:
139
- pass
140
-
141
- access: Wire = Access()
142
- self.assertIsInstance(access, Wire)
143
- self.assertNotIsInstance(access, Endpoint)
144
- self.assertIs(ReturnAddress(access).wire, access)
145
-
146
- def test_closed_callback_is_independent_from_message_callback(self) -> None:
147
- endings: list[tuple[int, str]] = []
148
- receiver = Receiver(closed=lambda code, reason: endings.append((code, reason)))
149
- assert receiver.closed is not None
150
- receiver.closed(1000, "complete")
151
- self.assertEqual(endings, [(1000, "complete")])
152
- self.assertIsNone(receiver.message)
153
-
154
-
155
-
156
- if __name__ == "__main__":
157
- unittest.main()
@@ -1,46 +0,0 @@
1
- """Compile-only consumers, including rejections guarded by unused-ignore checks."""
2
-
3
- from typing import assert_type
4
-
5
- from bitwire import (
6
- CancelFrame,
7
- ErrorFrame,
8
- Endpoint,
9
- EventFrame,
10
- Message,
11
- Path,
12
- ProfileFrame,
13
- ProfileKind,
14
- Receiver,
15
- RequestFrame,
16
- ResultFrame,
17
- ReturnAddress,
18
- Wire,
19
- )
20
-
21
-
22
- def consume(wire: Wire, endpoint: Endpoint, frame: ProfileFrame) -> None:
23
- address = ReturnAddress(wire)
24
- wire.send(["scope", ""], Message(frame, address))
25
- assert_type(address.wire, Wire)
26
- kind: ProfileKind = frame["kind"]
27
- assert_type(kind, ProfileKind)
28
- if frame["kind"] == "request":
29
- assert_type(frame["id"], str)
30
-
31
- async def delivered(path: Path, message: Message) -> None:
32
- assert_type(message.return_address, ReturnAddress | None)
33
-
34
- endpoint.receive(Receiver(message=delivered))()
35
- wire.receive(Receiver()) # type: ignore[attr-defined]
36
- wire.close() # type: ignore[attr-defined]
37
- wire.send([42], Message(frame)) # type: ignore[list-item]
38
-
39
-
40
- request: RequestFrame = {"version": 1, "kind": "request", "id": "1", "params": {}}
41
- result: ResultFrame = {"version": 1, "kind": "response", "id": "1", "result": None}
42
- error: ErrorFrame = {"version": 1, "kind": "response", "id": "1", "error": {"code": "no", "message": "No"}}
43
- event: EventFrame = {"version": 1, "kind": "event", "data": None}
44
- cancel: CancelFrame = {"version": 1, "kind": "cancel", "id": "1"}
45
- invalid_version: RequestFrame = {"version": 2, "kind": "request", "id": "1", "params": {}} # type: ignore[typeddict-item]
46
- contradictory: ResultFrame = {"version": 1, "kind": "response", "id": "1", "result": None, "error": {"code": "x", "message": "x"}} # type: ignore[typeddict-unknown-key]