oep-client-python 0.0.42__py3-none-any.whl → 0.0.44__py3-none-any.whl
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.
- oep_client/__init__.py +1 -1
- oep_client/conformance/__init__.py +1 -1
- oep_client/conformance/core_smoke.py +1 -1
- oep_client/conformance/host_core.py +1 -1
- oep_client/conformance/host_deadline.py +1 -1
- oep_client/conformance/host_requests.py +3 -3
- oep_client/conformance/host_wait.py +1 -1
- oep_client/conformance/oep_smoke.py +2 -2
- oep_client/core.py +4 -3
- oep_client/dump.py +3 -3
- oep_client/endpoint.py +6 -6
- oep_client/frames.py +1 -1
- oep_client/host.py +21 -13
- oep_client/link.py +22 -23
- oep_client/pins.py +1 -1
- oep_client/riscv.py +1 -1
- oep_client/speed_record.py +1 -1
- oep_client/targets.py +6 -5
- oep_client/virtual_bench.py +4 -4
- oep_client/virtual_bench_serve.py +2 -2
- oep_client/virtual_core.py +3 -4
- {oep_client_python-0.0.42.dist-info → oep_client_python-0.0.44.dist-info}/METADATA +5 -7
- {oep_client_python-0.0.42.dist-info → oep_client_python-0.0.44.dist-info}/RECORD +26 -26
- {oep_client_python-0.0.42.dist-info → oep_client_python-0.0.44.dist-info}/WHEEL +0 -0
- {oep_client_python-0.0.42.dist-info → oep_client_python-0.0.44.dist-info}/entry_points.txt +0 -0
- {oep_client_python-0.0.42.dist-info → oep_client_python-0.0.44.dist-info}/licenses/LICENSE +0 -0
oep_client/__init__.py
CHANGED
|
@@ -144,7 +144,7 @@ def checker_sources_sha256():
|
|
|
144
144
|
for name in CHECKER_SOURCES}
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
REVIEWED_CORE_SHA256 = '
|
|
147
|
+
REVIEWED_CORE_SHA256 = '699f7eb6333b6eb6678ddcf27074c03544f0f0a6ee0ef6ccdf02edd3757f962e' # oep-spec docs/oep-core.ja.md
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def require_reviewed_contract(registry, spec=None):
|
|
@@ -8,7 +8,7 @@ import struct
|
|
|
8
8
|
from . import ops, require, tlvs
|
|
9
9
|
from .. import conformance as _helpers
|
|
10
10
|
|
|
11
|
-
REVIEWED_CORE_SHA256 = '
|
|
11
|
+
REVIEWED_CORE_SHA256 = '699f7eb6333b6eb6678ddcf27074c03544f0f0a6ee0ef6ccdf02edd3757f962e' # oep-spec docs/oep-core.ja.md
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class CoreSmokeChecks:
|
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
import struct
|
|
5
5
|
from . import require,spec_identity
|
|
6
6
|
|
|
7
|
-
REVIEWED_CORE_SHA256='
|
|
7
|
+
REVIEWED_CORE_SHA256='699f7eb6333b6eb6678ddcf27074c03544f0f0a6ee0ef6ccdf02edd3757f962e'
|
|
8
8
|
CORE_OPS={'confirm':1,'list':2,'describe':3,'clock':4,'open':16,'end':17,'keepalive':18,'lock_state':19}
|
|
9
9
|
# the settings' ops (core §13.5 / §13.7, transports §7.3): their numbers reviewed, their results not read here
|
|
10
10
|
SETTINGS_OPS={'get':32,'set':33,'unset':34,'save':35,'state':36,'network':37}
|
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
from . import require,spec_identity
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
REVIEWED={'docs/oep-core.ja.md':'
|
|
12
|
+
REVIEWED={'docs/oep-core.ja.md':'699f7eb6333b6eb6678ddcf27074c03544f0f0a6ee0ef6ccdf02edd3757f962e',
|
|
13
13
|
'docs/oep-transports.ja.md':'2117de20712f7dfad6c81e247cf1b0b206ccf7528fcdf70702cbfce1c17dbadc'}
|
|
14
14
|
QUICK_MS=1000 # core_quick_op_max_ms
|
|
15
15
|
|
|
@@ -12,11 +12,11 @@ from . import require, spec_identity
|
|
|
12
12
|
REVIEWED = {
|
|
13
13
|
'interfaces/oep-if-plan.ja.md': 'da8bd60af53499573c07bc33dbd8c4df93eb61a93b91dc57907f26620ae36bbf',
|
|
14
14
|
'interfaces/oep-if-fixture.ja.md': '780beb7a3f9adb51fccf3441930e4c04d2bb64ef738e302195cf5a4e7c2a60a6',
|
|
15
|
-
'docs/oep-core.ja.md': '
|
|
16
|
-
'interfaces/oep-if-debug.ja.md': '
|
|
15
|
+
'docs/oep-core.ja.md': '699f7eb6333b6eb6678ddcf27074c03544f0f0a6ee0ef6ccdf02edd3757f962e',
|
|
16
|
+
'interfaces/oep-if-debug.ja.md': '58843357aae5afd31082b068c76c754cbd047446fe4b0aa77b5c22cbedf1f3f8',
|
|
17
17
|
'interfaces/oep-if-capture.ja.md': 'ffd89a6a1abf006aea788a6b47b270e6ac0e6d8190a716f4123c4f499bd3a61e',
|
|
18
18
|
}
|
|
19
|
-
REVIEWED_REGISTRY_SHA256='
|
|
19
|
+
REVIEWED_REGISTRY_SHA256='278114efc9235b4c6026cd66e11ed558e0ac5afd789011973f6293ae1abffb9c'
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def reviewed_identity(spec):
|
|
@@ -6,7 +6,7 @@ from .. import registry as _reg
|
|
|
6
6
|
SAVE=_reg.CORE.op['save'] # fn 0's save (core §13.7)
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
REVIEWED={'docs/oep-core.ja.md':'
|
|
9
|
+
REVIEWED={'docs/oep-core.ja.md':'699f7eb6333b6eb6678ddcf27074c03544f0f0a6ee0ef6ccdf02edd3757f962e',
|
|
10
10
|
'docs/oep-transports.ja.md':'2117de20712f7dfad6c81e247cf1b0b206ccf7528fcdf70702cbfce1c17dbadc'}
|
|
11
11
|
QUICK_MS=1000 # core_quick_op_max_ms
|
|
12
12
|
|
|
@@ -6,10 +6,10 @@ import struct
|
|
|
6
6
|
|
|
7
7
|
from . import ops, require, tlvs
|
|
8
8
|
|
|
9
|
-
REVIEWED_INTERFACES = { # oep-spec
|
|
9
|
+
REVIEWED_INTERFACES = { # oep-spec interfaces/
|
|
10
10
|
'oep-if-fixture.ja.md': '780beb7a3f9adb51fccf3441930e4c04d2bb64ef738e302195cf5a4e7c2a60a6',
|
|
11
11
|
'oep-if-plan.ja.md': 'da8bd60af53499573c07bc33dbd8c4df93eb61a93b91dc57907f26620ae36bbf',
|
|
12
|
-
'oep-if-debug.ja.md': '
|
|
12
|
+
'oep-if-debug.ja.md': '58843357aae5afd31082b068c76c754cbd047446fe4b0aa77b5c22cbedf1f3f8',
|
|
13
13
|
'oep-if-common.ja.md': '25fed0b1b8b7f246a8b71d02cea810249803b1c3c1e9c3df466c04f38687f70c',
|
|
14
14
|
}
|
|
15
15
|
|
oep_client/core.py
CHANGED
|
@@ -216,15 +216,16 @@ def transports(hst: h.Host) -> list[tuple[int, int, int]]:
|
|
|
216
216
|
|
|
217
217
|
|
|
218
218
|
def take(hst: h.Host, lease_ms: int = 3000, *, owner: str | None = None, wait_s: float = 5.0,
|
|
219
|
-
force: bool = False) -> h.Opened:
|
|
219
|
+
force: bool = False, extensions=()) -> h.Opened:
|
|
220
220
|
"""Take the lock as host guide §6 says: when the probe's only transport is a serial port and this host opened it
|
|
221
221
|
exclusively, the previous holder cannot be there any more - force at once; otherwise wait out the holder's lease
|
|
222
|
-
(up to wait_s), and name it (InUse) if it keeps it going. force: the user asked for it.
|
|
222
|
+
(up to wait_s), and name it (InUse) if it keeps it going. force: the user asked for it. extensions: named
|
|
223
|
+
extensions for the open, sent critical (Host.open; a broker's sharing, host guide §7)."""
|
|
223
224
|
link = getattr(hst, "link", None)
|
|
224
225
|
ways = transports(hst)
|
|
225
226
|
only = len(ways) == 1 and ways[0][1] in SERIAL_KINDS and getattr(link, "framing", None) == "cobs" \
|
|
226
227
|
and getattr(link, "transport", None) == "serial"
|
|
227
|
-
return hst.take(lease_ms, owner=owner, only_way_in=only, wait_s=wait_s, force=force)
|
|
228
|
+
return hst.take(lease_ms, owner=owner, only_way_in=only, wait_s=wait_s, force=force, extensions=extensions)
|
|
228
229
|
|
|
229
230
|
|
|
230
231
|
def plan_fn(hst: h.Host) -> int:
|
oep_client/dump.py
CHANGED
|
@@ -27,7 +27,7 @@ class Capabilities:
|
|
|
27
27
|
max_frame: int
|
|
28
28
|
offers: list[Offer] = field(default_factory=list)
|
|
29
29
|
requests: dict[str, int] = field(default_factory=dict)
|
|
30
|
-
missing: list[str] = field(default_factory=list) # what core §1.2 requires and the probe did not give
|
|
30
|
+
missing: list[str] = field(default_factory=list) # what core §1.2 requires and the probe did not give
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
_REQUIRED_CORE_TAGS = {reg.CORE.tlv["describe"][k]: k for k in ("unit_id", "transport", "max_op_ms")}
|
|
@@ -44,7 +44,7 @@ CORE_REQUIRED_OPS = ("confirm", "list", "describe", "clock", "open", "end", "kee
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def required_missing(confirm_payload: bytes, core_describe: list[tuple[int, bytes]]) -> list[str]:
|
|
47
|
-
"""What every probe must give, as far as a lock-free look on one transport shows it (core §1.2
|
|
47
|
+
"""What every probe must give, as far as a lock-free look on one transport shows it (core §1.2; oep-spec
|
|
48
48
|
docs/conformance.md section 1): confirm's answer carries TLV transport, naming an entry of fn 0's describe or 0xFF
|
|
49
49
|
(§7.1); fn 0's describe carries unit_id, transport, max_op_ms (§1.2, §7.5) and ops in core §7.4's form with every
|
|
50
50
|
required op set (core §11; a probe whose fn 0 ops break the form is not used). -> what is missing (empty: nothing seen missing). The bounds of confirm's values and of
|
|
@@ -95,7 +95,7 @@ def _describe_all(call, fn: int, caps: Capabilities) -> bytes:
|
|
|
95
95
|
def collect(call, prefix: str = "", exact: bool = False, confirm: tuple[int, int] = (0, 1)) -> Capabilities:
|
|
96
96
|
"""confirm, then fn 0's describe (the core: no name, never listed, core §7.2) and every listed interface's (the
|
|
97
97
|
ones under `prefix` on label boundaries, `exact`: that name only - list itself returns every one, core §7.2). `confirm`: the revision range the confirm asks for - a host that confirmed already passes the
|
|
98
|
-
revision in use (core §7.1
|
|
98
|
+
revision in use (core §7.1: every later confirm on a transport asks for it alone). fn 0 is shown first, as
|
|
99
99
|
an Offer with fn 0 and no name."""
|
|
100
100
|
p = call(CORE_FN, OP_CONFIRM, m.CONFIRM_REQUEST + bytes(confirm)) # v0 and v1 both answer (0..1)
|
|
101
101
|
magic, revision = struct.unpack_from("<4sB", p)
|
oep_client/endpoint.py
CHANGED
|
@@ -629,7 +629,7 @@ class Endpoint(FixtureDrive):
|
|
|
629
629
|
"""`now_ms`: the clock the timers run on (leases, retries, port_speed), since this boot. `now_ns`: the same clock
|
|
630
630
|
in ns when it has that resolution (virtual_bench_serve: time.monotonic_ns); without it the probe's clock (core §2.6a)
|
|
631
631
|
is `now_ms` in ns. confirm's limits are checked against core §7.1 (max_frame 64 or more, window max_frame or
|
|
632
|
-
more, max_inflight 1 or more
|
|
632
|
+
more, max_inflight 1 or more) and the declared max_op_ms against core §7.5 (1 to max_op_ms_max):
|
|
633
633
|
a probe outside them is not built."""
|
|
634
634
|
if probe.max_frame < reg.MIN_MAX_FRAME or window < probe.max_frame or max_inflight < 1:
|
|
635
635
|
raise ValueError(f"confirm's limits out of core §7.1's bounds: max_frame {probe.max_frame}, window {window}, "
|
|
@@ -639,7 +639,7 @@ class Endpoint(FixtureDrive):
|
|
|
639
639
|
self.now = now_ms
|
|
640
640
|
self._clock_ns = now_ns
|
|
641
641
|
self._origin_ns = 0 # the probe clock's 0: this boot's start (core §2.6a)
|
|
642
|
-
self._last_ns = 0 # the clock never goes back while the boot_id stays
|
|
642
|
+
self._last_ns = 0 # the clock never goes back while the boot_id stays
|
|
643
643
|
self.current_core = None
|
|
644
644
|
self.boot_id = boot_id
|
|
645
645
|
self.lease_default_ms = lease_default_ms
|
|
@@ -1260,7 +1260,7 @@ class Endpoint(FixtureDrive):
|
|
|
1260
1260
|
|
|
1261
1261
|
def now_ns(self) -> int:
|
|
1262
1262
|
"""The probe's one clock (core §2.6a): ns since boot, at the resolution of `now_ns` when the endpoint has one
|
|
1263
|
-
(else ms). It never decreases while the boot_id is the same
|
|
1263
|
+
(else ms). It never decreases while the boot_id is the same, and starts again at a reboot."""
|
|
1264
1264
|
self._last_ns = max(self._last_ns, self._raw_ns() - self._origin_ns)
|
|
1265
1265
|
return self._last_ns
|
|
1266
1266
|
|
|
@@ -1306,7 +1306,7 @@ class Endpoint(FixtureDrive):
|
|
|
1306
1306
|
self.current_core = Core(self)
|
|
1307
1307
|
return self.current_core.handle(data, transport)
|
|
1308
1308
|
if not data or data[0] != m.ROLE_REQUEST or len(data) < REQUEST_HEADER:
|
|
1309
|
-
self.discarded += 1 # not a request, or shorter than its header
|
|
1309
|
+
self.discarded += 1 # not a request, or shorter than its header
|
|
1310
1310
|
return None
|
|
1311
1311
|
if self.restarting:
|
|
1312
1312
|
self.unanswered += 1 # the restart's answer is out: nothing more (oep-if-restart §2)
|
|
@@ -1602,7 +1602,7 @@ class Endpoint(FixtureDrive):
|
|
|
1602
1602
|
"""The probe restarts: lock, last id, the resend table, connections, streams, captures, subscriptions, the
|
|
1603
1603
|
revision in use, the port speed, the unsaved config and the plan are gone; the saved config comes back, and the
|
|
1604
1604
|
clock starts again from 0 (ns since boot, core §2.6a). `boot_id` None
|
|
1605
|
-
draws a new one, as from a hardware random source (core §6.5
|
|
1605
|
+
draws a new one, as from a hardware random source (core §6.5); passing the current one plays a probe
|
|
1606
1606
|
whose only source repeated it - its hosts learn only that their session is gone (no_session, core §6.2)."""
|
|
1607
1607
|
if boot_id is None:
|
|
1608
1608
|
boot_id = self.boot_id
|
|
@@ -3134,7 +3134,7 @@ class Endpoint(FixtureDrive):
|
|
|
3134
3134
|
if address > 0x7F:
|
|
3135
3135
|
raise Reject(m.MALFORMED) # a 7-bit address (excluded for every revision)
|
|
3136
3136
|
if address <= 0x07 or address >= 0x78:
|
|
3137
|
-
raise unsupported_fixed() # reserved by I2C: general call, 10-bit, ...
|
|
3137
|
+
raise unsupported_fixed() # reserved by I2C: general call, 10-bit, ...
|
|
3138
3138
|
if not planned:
|
|
3139
3139
|
raise unavailable("wrong_state") # before the plan (cause 6)
|
|
3140
3140
|
if fn in self.target_fail_configure: # taken, not brought up: state 0, the queue, slots
|
oep_client/frames.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Length-prefixed frames on a reliable byte stream (USB vendor bulk, HID, TCP): u16 length, then the message (oep-spec
|
|
2
2
|
oep-core §3). No CRC: a length that cannot be right, or a frame that stops half way, means the boundaries are lost -
|
|
3
|
-
FramingLost, and the link resyncs. Not on TCP (transports §5
|
|
3
|
+
FramingLost, and the link resyncs. Not on TCP (transports §5): a pause inside a frame is normal there and the
|
|
4
4
|
frame is read on; a stream that keeps its boundaries says so with `keeps_boundaries = True`."""
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
oep_client/host.py
CHANGED
|
@@ -86,7 +86,7 @@ RESTART_WAIT_S = 10.0 # restart_probe: the wait for a p
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
def check_confirm(max_frame: int, window: int, max_inflight: int) -> str:
|
|
89
|
-
"""core §7.1
|
|
89
|
+
"""core §7.1: max_frame >= min_max_frame (64), window >= max_frame, max_inflight >= 1. -> "" or why not."""
|
|
90
90
|
if max_frame < reg.MIN_MAX_FRAME or window < max_frame or max_inflight < 1:
|
|
91
91
|
return (f"confirm answered max_frame {max_frame}, window {window}, max_inflight {max_inflight}: outside core "
|
|
92
92
|
f"§7.1 (max_frame >= {reg.MIN_MAX_FRAME}, window >= max_frame, max_inflight >= 1); the transport is not used")
|
|
@@ -163,7 +163,7 @@ class Unsupported(Rejected):
|
|
|
163
163
|
|
|
164
164
|
@property
|
|
165
165
|
def supported(self) -> tuple[int, int] | None:
|
|
166
|
-
"""confirm's refusal (core §7.1
|
|
166
|
+
"""confirm's refusal (core §7.1): (min, max) of the protocol revisions the probe handles (TLV 0x01
|
|
167
167
|
supported after tag 0x00); None for any other refusal."""
|
|
168
168
|
if self.tag is not None:
|
|
169
169
|
return None
|
|
@@ -294,7 +294,7 @@ class Host:
|
|
|
294
294
|
before_request: Callable[[], None] | None = None
|
|
295
295
|
# Called with confirm's limits after every confirm answer (the link's max_frame for the transfer time, core §4.4)
|
|
296
296
|
on_limits: Callable[[dict], None] | None = None
|
|
297
|
-
unusable: str = "" # why this probe is not used
|
|
297
|
+
unusable: str = "" # why this probe is not used: set, nothing more is sent
|
|
298
298
|
restart_reopened: bool = False # the last restart_probe came back only on its reopen_s (the user's reopen)
|
|
299
299
|
# The session id kept per probe for the next run (kept_session.KeptSession; link.open_host sets one): None = not kept
|
|
300
300
|
kept: object | None = None
|
|
@@ -438,7 +438,7 @@ class Host:
|
|
|
438
438
|
self._boot_id = boot_id
|
|
439
439
|
|
|
440
440
|
def not_usable(self, why: str) -> None:
|
|
441
|
-
"""Stop using this probe
|
|
441
|
+
"""Stop using this probe: every later request raises NotUsable with `why`."""
|
|
442
442
|
self.unusable = why
|
|
443
443
|
raise NotUsable(why)
|
|
444
444
|
|
|
@@ -484,7 +484,7 @@ class Host:
|
|
|
484
484
|
|
|
485
485
|
# ---- confirm (§5, §2) -----------------------------------------------------------------------
|
|
486
486
|
def confirm_range(self) -> tuple[int, int]:
|
|
487
|
-
"""The revisions a confirm asks for (core §7.1
|
|
487
|
+
"""The revisions a confirm asks for (core §7.1): the range this client handles before the first confirm,
|
|
488
488
|
then min_rev = max_rev = the revision in use, in every later confirm (a resync, probing again) - so a later
|
|
489
489
|
probe never switches the revision in the middle of a session."""
|
|
490
490
|
if self.revision:
|
|
@@ -499,7 +499,7 @@ class Host:
|
|
|
499
499
|
"""Ask for a revision in [min_rev, max_rev] (default `confirm_range()`). -> {"revision", "max_frame",
|
|
500
500
|
"window", "max_inflight", "boot_id", "transport", "tail"}. The boot_id is the probe's for this boot (core §6.5,
|
|
501
501
|
§7.1): a host without the lock learns of a restart from it. transport: the index (fn 0's describe) of the
|
|
502
|
-
transport this confirm came on (core §7.1
|
|
502
|
+
transport this confirm came on (core §7.1; 0xFF from a relaying broker; None when the probe sent none).
|
|
503
503
|
The answer's fixed part is the same in every revision (core §7.1): a revision outside the range asked is a
|
|
504
504
|
ProtocolError and nothing of the answer is used. A probe that refuses the ranged confirm as malformed is not v1
|
|
505
505
|
(revision 0 is recorded and the rejection raised). No revision in the range: rejected unsupported (Unsupported:
|
|
@@ -527,7 +527,7 @@ class Host:
|
|
|
527
527
|
tail = rd.tail()
|
|
528
528
|
why = check_confirm(max_frame, window, inflight)
|
|
529
529
|
if why:
|
|
530
|
-
self.not_usable(why) # sends nothing more and reports the values
|
|
530
|
+
self.not_usable(why) # sends nothing more and reports the values
|
|
531
531
|
self.boot_id_seen(boot_id)
|
|
532
532
|
self.revision = revision
|
|
533
533
|
where = tail.get(CONFIRM_TRANSPORT)
|
|
@@ -556,13 +556,15 @@ class Host:
|
|
|
556
556
|
raise NotV1(f"the probe speaks OEP revision {self.revision}; session requests need revision 1 or more")
|
|
557
557
|
|
|
558
558
|
# ---- session --------------------------------------------------------------------------------
|
|
559
|
-
def open(self, lease_ms: int = 0, *, force: bool = False, owner: str | None = None) -> Opened:
|
|
559
|
+
def open(self, lease_ms: int = 0, *, force: bool = False, owner: str | None = None, extensions=()) -> Opened:
|
|
560
560
|
"""Open a new session under a new random id (core §6.4; the id goes in the header, never 0). lease_ms 0 = the
|
|
561
561
|
probe's default; 1000..60000 are taken as asked. owner: who holds the lock (1-32 bytes), shown to other hosts.
|
|
562
562
|
force: take the lock from another session (its resources are released first). A session this host had open
|
|
563
563
|
is left behind: it ended, or the probe refuses this open locked while it holds the lock - end it first. The
|
|
564
564
|
answer's boot_id is watched (core §6.5: a reboot drops the fn mapping). fn 0's describe (its max_op_ms) is read
|
|
565
|
-
first when this host has not read it yet (core §4.4).
|
|
565
|
+
first when this host has not read it yet (core §4.4). extensions: named extensions (core §2.2, §12) to carry, each
|
|
566
|
+
a name or (name, data), sent critical - a probe that does not know one refuses the open (Unsupported, tag the
|
|
567
|
+
named extension's with bit 7) rather than ignore it; a broker's sharing is asked this way (host guide §7)."""
|
|
566
568
|
self.require_v1()
|
|
567
569
|
if self.kept is not None and self.session is None:
|
|
568
570
|
self.kept.before_open(self, owner) # once: the previous run's session ended (host guide §5)
|
|
@@ -571,6 +573,12 @@ class Host:
|
|
|
571
573
|
core.describe(self, m.CORE_FN) # max_op_ms before a request that changes state (core §4.4)
|
|
572
574
|
sid = self.rng.randrange(1, 1 << 32) # random, never 0 (core §6.1)
|
|
573
575
|
tail = m.tlv(OWNER, owner_text(owner)) if owner else b""
|
|
576
|
+
for item in extensions:
|
|
577
|
+
name, data = (item, b"") if isinstance(item, str) else item
|
|
578
|
+
raw = name.encode("ascii")
|
|
579
|
+
if not 1 <= len(raw) <= 255:
|
|
580
|
+
raise ValueError(f"named extension {name!r}: 1 to 255 bytes (core §2.2)")
|
|
581
|
+
tail += m.tlv(m.TAG_NAMED_EXTENSION, bytes([len(raw)]) + raw + bytes(data), critical=True)
|
|
574
582
|
r = self.request(m.CORE_FN, m.OP_OPEN, struct.pack("<IB", lease_ms, int(force)) + tail, session=sid)
|
|
575
583
|
rd = m.Reader(r.payload)
|
|
576
584
|
lease, boot_id = rd.take("II")
|
|
@@ -646,17 +654,17 @@ class Host:
|
|
|
646
654
|
return bool(locked), remaining, owner_of(p[5:])
|
|
647
655
|
|
|
648
656
|
def take(self, lease_ms: int = 3000, *, owner: str | None = None, only_way_in: bool = False,
|
|
649
|
-
wait_s: float = 5.0, force: bool = False) -> Opened:
|
|
657
|
+
wait_s: float = 5.0, force: bool = False, extensions=()) -> Opened:
|
|
650
658
|
"""open() the way host guide §6 takes the lock. only_way_in: this link is the probe's only transport and a
|
|
651
659
|
serial port opened exclusively - whoever held the lock cannot be there any more, so it is taken by force at
|
|
652
660
|
once. Otherwise the holder's lease is waited out (up to wait_s); a holder that keeps it going raises InUse,
|
|
653
|
-
naming it. force: take it anyway (the user said so)."""
|
|
661
|
+
naming it. force: take it anyway (the user said so). extensions: as open's."""
|
|
654
662
|
if force or only_way_in:
|
|
655
|
-
return self.open(lease_ms, force=True, owner=owner)
|
|
663
|
+
return self.open(lease_ms, force=True, owner=owner, extensions=extensions)
|
|
656
664
|
deadline = time.monotonic() + wait_s
|
|
657
665
|
while True:
|
|
658
666
|
try:
|
|
659
|
-
return self.open(lease_ms, owner=owner)
|
|
667
|
+
return self.open(lease_ms, owner=owner, extensions=extensions)
|
|
660
668
|
except Locked as e:
|
|
661
669
|
left = deadline - time.monotonic()
|
|
662
670
|
if left <= 0 or e.remaining_ms / 1000 > left:
|
oep_client/link.py
CHANGED
|
@@ -25,8 +25,8 @@ host as a reboot; read the state before repeating a state-changing request.
|
|
|
25
25
|
Frames the probe sends by itself are routed: data pushes and events are kept (`pushes`, `events`) for the clients
|
|
26
26
|
of the interfaces that sent them (fn 0 sends none, core §10.2). A result shorter than 5
|
|
27
27
|
bytes, or an event or data frame shorter than its header, is a broken frame; a request role from the probe is dropped
|
|
28
|
-
(core §2.4
|
|
29
|
-
transport, then the latest one's max_frame (core §4.4
|
|
28
|
+
(core §2.4). The transfer time of the wait counts min_max_frame (64) until a confirm answer came on the
|
|
29
|
+
transport, then the latest one's max_frame (core §4.4).
|
|
30
30
|
|
|
31
31
|
A serial port is opened exclusively (host guide §6): pyserial's `exclusive=True` (flock, advisory) and, on Linux and
|
|
32
32
|
macOS, TIOCEXCL, so a second open fails at once (EBUSY) instead of sharing the answers. The port opens with pyserial's
|
|
@@ -93,7 +93,7 @@ from . import cobs, host as _host, message as m, registry as reg
|
|
|
93
93
|
from .frames import FramingLost, LengthFrames
|
|
94
94
|
|
|
95
95
|
# the link's own confirms (resync §5.1, confirm_raw) ask for the revisions this client handles before the first
|
|
96
|
-
# confirm, and for the revision in use after it (core §7.1
|
|
96
|
+
# confirm, and for the revision in use after it (core §7.1) - never 0..0xFF
|
|
97
97
|
_OWN_CONFIRM = m.CONFIRM_REQUEST + bytes([_host.MIN_REVISION, _host.MAX_REVISION])
|
|
98
98
|
|
|
99
99
|
RESYNC_QUIET_S = 0.05 # transports §5: read and discard until the input is this quiet
|
|
@@ -121,7 +121,7 @@ def _session_of(message: bytes) -> int:
|
|
|
121
121
|
|
|
122
122
|
|
|
123
123
|
class TransportFailed(TimeoutError):
|
|
124
|
-
"""core §5.2
|
|
124
|
+
"""core §5.2: a request's resend got no answer either. Its outcome is unknown and the requests outstanding
|
|
125
125
|
on the transport failed with it; the link sends nothing else there until it has recovered with the §5.1 confirm
|
|
126
126
|
(done before the next request; ConnectionError when no confirm is answered). After that, read the state before
|
|
127
127
|
repeating a state-changing request."""
|
|
@@ -144,7 +144,7 @@ class PortBusy(OSError):
|
|
|
144
144
|
"""Another program holds the serial port (it was opened exclusively): only one host at a time on a serial port."""
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
BASE_BAUD = reg.TIMING["uart_bridge_boot_baud"] # every UART bridge boots at 115200 8N1 (transports §4
|
|
147
|
+
BASE_BAUD = reg.TIMING["uart_bridge_boot_baud"] # every UART bridge boots at 115200 8N1 (transports §4)
|
|
148
148
|
IDLE_MS = reg.TIMING["port_speed_idle_ms"] # a committed rate goes back after this with no good frame (oep-if-link §3)
|
|
149
149
|
KEEPALIVE_S = 1.0 # raised: a keepalive once the link has been quiet this long (under half of port_speed_idle_ms, §17)
|
|
150
150
|
OPEN_RETRY_S = (IDLE_MS + reg.TIMING["host_wait_add_ms"]) / 1000 # the confirms at the boot speed (transports §4)
|
|
@@ -171,8 +171,7 @@ else:
|
|
|
171
171
|
|
|
172
172
|
def open_serial(port: str, baud: int = BASE_BAUD):
|
|
173
173
|
"""The port opened exclusively (flock and TIOCEXCL): a second open by anyone fails with PortBusy. 8 data bits, no
|
|
174
|
-
parity, 1 stop bit, no flow control, DTR and RTS asserted from the open on and while it stays open (transports §4
|
|
175
|
-
C-09: a UART bridge may wire them to the probe's reset; what a probe does with DTR deasserted is not defined). The
|
|
174
|
+
parity, 1 stop bit, no flow control, DTR and RTS asserted from the open on and while it stays open (transports §4: a UART bridge may wire them to the probe's reset; what a probe does with DTR deasserted is not defined). The
|
|
176
175
|
driver's low-latency mode on where it has one (the FTDI latency timer 16 -> 1 ms; a pty or a driver without it:
|
|
177
176
|
ignored)."""
|
|
178
177
|
try:
|
|
@@ -352,15 +351,15 @@ class SerialLink:
|
|
|
352
351
|
self.keepalive_frame = None # raised: a keepalive request in the session, once bound (attach_host)
|
|
353
352
|
self.last_tx = time.monotonic() # when the link last wrote (raised: quiet for KEEPALIVE_S = keepalive)
|
|
354
353
|
self.last_write: float | None = None # when this host last wrote to the port (None: never; transports §5)
|
|
355
|
-
self.max_frame = reg.MIN_MAX_FRAME # min_max_frame until a confirm answer, then its max_frame (§4.4
|
|
354
|
+
self.max_frame = reg.MIN_MAX_FRAME # min_max_frame until a confirm answer, then its max_frame (§4.4)
|
|
356
355
|
self.on_boot_id = lambda boot_id: None # the boot_id of the link's own confirms (resync, recovery)
|
|
357
356
|
self.on_reopen = lambda: None # the transport closed and opened again (`_reopen`)
|
|
358
|
-
self.failed_transport = "" # why: a resend went unanswered (core §5.2
|
|
357
|
+
self.failed_transport = "" # why: a resend went unanswered (core §5.2); recover first
|
|
359
358
|
self.recoveries = 0 # failed-transport recoveries made (a confirm answered)
|
|
360
359
|
self.wait_add_s = WAIT_ADD_S # the floor's host_wait_add_ms (a test of an in-process virtual bench shortens it)
|
|
361
360
|
self._req_bound_s: float | None = None # the processing bound of the requests whose answers are awaited now
|
|
362
361
|
self.tx_len = 0 # the longest frame on the wire of the last write
|
|
363
|
-
self.confirm_body = lambda: _OWN_CONFIRM # the link's own confirm: the revision in use once bound
|
|
362
|
+
self.confirm_body = lambda: _OWN_CONFIRM # the link's own confirm: the revision in use once bound
|
|
364
363
|
self.held = lambda: False # a session holds the port (its raw transfer stopped): broken = resend
|
|
365
364
|
self._corr_n = 0x8000
|
|
366
365
|
if self.framing == "length":
|
|
@@ -399,7 +398,7 @@ class SerialLink:
|
|
|
399
398
|
return (self.tx_len + self.max_frame * (1 + NOTIFY_PENDING)) * 10 / self.baud
|
|
400
399
|
|
|
401
400
|
def wait_floor_s(self, bound_s: float | None = None) -> float:
|
|
402
|
-
"""The least a request's answer is waited for (core §4.4
|
|
401
|
+
"""The least a request's answer is waited for (core §4.4): the request's processing bound (Host.expecting:
|
|
403
402
|
Host.op_bound_ms - max_op_ms, fn 0's quick ops' shorter one - or longer when its arguments say so; `bound_s`:
|
|
404
403
|
the bound of the requests on the wire, `_bound_s`) + host_wait_add_ms + the transfer time; counted
|
|
405
404
|
from the write, or from the answer before it while several are outstanding (each reply's wait starts when it is
|
|
@@ -472,7 +471,7 @@ class SerialLink:
|
|
|
472
471
|
dropped (core §2.4). True if `frame` was one of them. Only a result (role 0x02) carries a correlation id;
|
|
473
472
|
matching anything else by its bytes 1-2 would take a push for a reply whenever its fn happened to equal the id.
|
|
474
473
|
A result shorter than 5 bytes, or an event / data frame shorter than its header,
|
|
475
|
-
is a broken frame (core §2.4
|
|
474
|
+
is a broken frame (core §2.4): `_broken`."""
|
|
476
475
|
if frame and frame[0] == m.ROLE_RESULT:
|
|
477
476
|
return len(frame) < RESULT_HEADER and self._broken(frame, "a result shorter than its header")
|
|
478
477
|
if frame and frame[0] == m.ROLE_DATA:
|
|
@@ -488,7 +487,7 @@ class SerialLink:
|
|
|
488
487
|
return True
|
|
489
488
|
|
|
490
489
|
def _broken(self, frame: bytes, why: str) -> bool:
|
|
491
|
-
"""A frame that decoded but cannot be read
|
|
490
|
+
"""A frame that decoded but cannot be read: on a length-framed link the boundaries are in doubt (the
|
|
492
491
|
§5.1 resync follows); on a serial port it is a broken frame like a bad CRC - most likely the awaited answer,
|
|
493
492
|
resent at once (core §5.1; transports §4: the port carries OEP frames only)."""
|
|
494
493
|
if self.framing == "length":
|
|
@@ -620,7 +619,7 @@ class SerialLink:
|
|
|
620
619
|
self.on_reopen() # the subscriptions stayed on the closed one (core §10.3)
|
|
621
620
|
|
|
622
621
|
def _fail_transport(self, e: Exception) -> None:
|
|
623
|
-
"""core §5.2
|
|
622
|
+
"""core §5.2: the resend went unanswered too - the transport failed. Its outcome is unknown, so are the
|
|
624
623
|
requests outstanding with it; nothing else goes out before `recover_transport` (the next request runs it)."""
|
|
625
624
|
if getattr(self, "_probing", False):
|
|
626
625
|
raise e # probing: the probe() caller closes the link (transports §3)
|
|
@@ -646,7 +645,7 @@ class SerialLink:
|
|
|
646
645
|
self.failed_transport = f"a request got no answer and was not sent again ({type(e).__name__}: {e})"
|
|
647
646
|
|
|
648
647
|
def recover_transport(self, tries: int = 3) -> None:
|
|
649
|
-
"""core §5.2 / §5.1
|
|
648
|
+
"""core §5.2 / §5.1, on every kind of frame, COBS included: read and discard until the input is quiet,
|
|
650
649
|
wait host_resync_wait_ms since this host's last write, then a confirm until the answer with its own corr comes
|
|
651
650
|
(other frames read past). A changed boot_id in it goes to the host (a reboot, core §6.5). ConnectionError when
|
|
652
651
|
no confirm is answered in `tries`: the transport stays failed (close and open it again)."""
|
|
@@ -713,7 +712,7 @@ class SerialLink:
|
|
|
713
712
|
|
|
714
713
|
def send(self, message: bytes) -> bytes:
|
|
715
714
|
if self.failed_transport:
|
|
716
|
-
self.recover_transport() # nothing else goes out before (core §5.2
|
|
715
|
+
self.recover_transport() # nothing else goes out before (core §5.2)
|
|
717
716
|
self._keep_raised()
|
|
718
717
|
try:
|
|
719
718
|
reply = self._send(message)
|
|
@@ -972,7 +971,7 @@ class SerialLink:
|
|
|
972
971
|
wait = min(wait, max(RAISED_WAIT_MIN_S, lease / 4))
|
|
973
972
|
if self._own and self._req_bound_s is None:
|
|
974
973
|
return wait # a read of the link's own (pump, a repeated confirm)
|
|
975
|
-
return max(wait, self.wait_floor_s(self._req_bound_s)) # never under core §4.4's floor
|
|
974
|
+
return max(wait, self.wait_floor_s(self._req_bound_s)) # never under core §4.4's floor
|
|
976
975
|
|
|
977
976
|
def _strike(self, e: Exception) -> None:
|
|
978
977
|
"""A request's answer did not come (`e`): a lost frame (host guide §17.3.2: counted at the boot speed for the
|
|
@@ -1211,10 +1210,10 @@ class SerialLink:
|
|
|
1211
1210
|
self.lease_s = lambda: hst.lease_ms / 1000 if hst.session is not None and hst.lease_ms else None
|
|
1212
1211
|
hst.before_request = self._keep_raised # the keepalive before the request's corr is taken (core §4.1)
|
|
1213
1212
|
self.expected_s = lambda: hst.expect_ms / 1000
|
|
1214
|
-
self.confirm_body = hst.confirm_body # the revision in use after the first confirm (core §7.1
|
|
1215
|
-
self.on_boot_id = hst.boot_id_seen # the link's own confirms (resync, recovery): a reboot shows
|
|
1213
|
+
self.confirm_body = hst.confirm_body # the revision in use after the first confirm (core §7.1)
|
|
1214
|
+
self.on_boot_id = hst.boot_id_seen # the link's own confirms (resync, recovery): a reboot shows
|
|
1216
1215
|
self.on_reopen = hst.transport_reopened # a subscribe again moves the pushes to the new one (core §10.3)
|
|
1217
|
-
hst.on_limits = self._limits # every confirm answer: max_frame for the transfer time
|
|
1216
|
+
hst.on_limits = self._limits # every confirm answer: max_frame for the transfer time
|
|
1218
1217
|
hst.link = self
|
|
1219
1218
|
|
|
1220
1219
|
def reopen_after_restart(self, hst, wait_s: float) -> dict:
|
|
@@ -1262,7 +1261,7 @@ class SerialLink:
|
|
|
1262
1261
|
|
|
1263
1262
|
def _limits(self, limits: dict) -> None:
|
|
1264
1263
|
"""confirm's answer on this transport: until the first one the transfer time counts min_max_frame (64), then
|
|
1265
|
-
the max_frame of the latest (core §4.4
|
|
1264
|
+
the max_frame of the latest (core §4.4); a length-framed reader bounds frames by it."""
|
|
1266
1265
|
if limits.get("max_frame"):
|
|
1267
1266
|
self.max_frame = limits["max_frame"]
|
|
1268
1267
|
if self.framing == "length":
|
|
@@ -2052,7 +2051,7 @@ class SpeedReport:
|
|
|
2052
2051
|
|
|
2053
2052
|
def _speed_port(hst) -> tuple[int | None, int | None, str]:
|
|
2054
2053
|
"""(the probe's oep.probe.link fn, the port this host's requests come in on - the transport TLV of confirm's answer, core
|
|
2055
|
-
§7.1
|
|
2054
|
+
§7.1) when the probe offers oep.probe.link with port_speed set in its ops (oep-if-link §1) and that transport is a
|
|
2056
2055
|
UART bridge; else Nones and why not."""
|
|
2057
2056
|
from . import core
|
|
2058
2057
|
try:
|
|
@@ -2365,7 +2364,7 @@ def raise_speed(hst, candidates=DEFAULT_CANDIDATES, *, flows=None, verify: bool
|
|
|
2365
2364
|
unit = _unit_id(hst)
|
|
2366
2365
|
if rec.names_a_unit(unit):
|
|
2367
2366
|
lk.record, lk.record_key = rec, (lk.port_path or "<stream>", unit)
|
|
2368
|
-
else: # an x- unit_id names no unit: nothing kept
|
|
2367
|
+
else: # an x- unit_id names no unit: nothing kept
|
|
2369
2368
|
rec = None
|
|
2370
2369
|
report.why = f"unit_id {unit} names no unit (core §7.5): no speed record"
|
|
2371
2370
|
run = _Run(flows, verify, frames, wait, port, rec, max(0, int(probation_bytes)), max(0.0, probation_s),
|
oep_client/pins.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""oep pins: find where a target is wired to a probe whose pins the host chooses - its debug pins and its reset line -
|
|
2
|
-
from what worked and what misled
|
|
2
|
+
from what worked and what misled (ESP32-P4 probe, CH32V003 target).
|
|
3
3
|
|
|
4
4
|
Steps, each optional and each saying what it did:
|
|
5
5
|
|
oep_client/riscv.py
CHANGED
|
@@ -899,7 +899,7 @@ class RiscvDm(Interface, BlockLength):
|
|
|
899
899
|
|
|
900
900
|
def step(self) -> tuple[bool, int, int]:
|
|
901
901
|
"""One instruction (dcsr.step, one resume, privilege kept). -> (moved, dpc before, dpc after). A hart that did
|
|
902
|
-
not come back, and any status but ok, raises StepError (oep-if-debug §4.2
|
|
902
|
+
not come back, and any status but ok, raises StepError (oep-if-debug §4.2) without moved or the dpcs
|
|
903
903
|
(0 unless status ok, not read): `step_left` False - the probe halted it with haltreq and restored it (read its
|
|
904
904
|
dpc with dmi); True (answer TLV step_left) - it could not halt it again: the hart runs and dcsr.step may still
|
|
905
905
|
be set, so the host halts it and clears dcsr.step. DATA0 is written back first (`restore_data`)."""
|
oep_client/speed_record.py
CHANGED
|
@@ -19,7 +19,7 @@ guide §17.4), `passed` true / false / null (the same, for a reader of the older
|
|
|
19
19
|
rec.note("/dev/ttyUSB0", "fafe00000003", 921600, passed=True, phase="verify")
|
|
20
20
|
|
|
21
21
|
`link.raise_speed(..., record=True)` (the `oep speed` CLI's default) reads and writes it; the library default is off.
|
|
22
|
-
A unit_id starting with `x-` names no unit (core §7.5
|
|
22
|
+
A unit_id starting with `x-` names no unit (core §7.5: a probe with neither a unique number nor storage): nothing
|
|
23
23
|
is kept or found under it, so another unit on the same port inherits nothing.
|
|
24
24
|
"""
|
|
25
25
|
|
oep_client/targets.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""What the host knows about target families, in one table: which wire, the reset vector, how to read whether the reset
|
|
2
2
|
line (NRST) exists, the line settings to attach with. The probe knows none of it (oep-if-debug: the host knows the
|
|
3
|
-
target). `oep pins` and anything else that meets a target by its target_id read it from here
|
|
4
|
-
|
|
3
|
+
target). `oep pins` and anything else that meets a target by its target_id read it from here; each family's notes
|
|
4
|
+
say what was measured.
|
|
5
5
|
|
|
6
6
|
A family is matched by the target_id an attach reports (scheme 1 = dmi_7f: the u32 at DMI address 0x7F, which on these
|
|
7
7
|
parts equals the chip's ESIG ID):
|
|
@@ -99,7 +99,7 @@ def gpio_clock_bit(port: str) -> int:
|
|
|
99
99
|
return 1 << (2 + ord(port) - ord("A"))
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
# The families met on the OEP benches
|
|
102
|
+
# The families met on the OEP benches. ids: the top 12 bits of the ESIG ID
|
|
103
103
|
# name the series (CH32V003 0x003..., CH32X035 0x035...).
|
|
104
104
|
FAMILIES: tuple[Family, ...] = (
|
|
105
105
|
Family("ch32v00x", "swio", ids=tuple((0xFFF00000, s << 20) for s in (0x002, 0x003, 0x004, 0x005, 0x006, 0x007)),
|
|
@@ -114,8 +114,9 @@ FAMILIES: tuple[Family, ...] = (
|
|
|
114
114
|
Family("ch32l103", "rvswd", max_speed=1_000_000, idle_clock="low", gpio_ports="ABCD", gpio_pads=16,
|
|
115
115
|
debug_pads=("PA13", "PA14"),
|
|
116
116
|
notes="SWCLK resting high resets the debug link; over 1 MHz fails right after a reset; target_id not recorded"),
|
|
117
|
-
Family("
|
|
118
|
-
|
|
117
|
+
Family("ch32v20x", "rvswd", ids=((0xFFF00000, 0x20800000),), idle_clock="low", gpio_ports="ABCD", gpio_pads=16,
|
|
118
|
+
debug_pads=("PA13", "PA14"),
|
|
119
|
+
notes="the CH32V208 recorded (target_id 0x2080051c, CH32V208WBU6); the CH32V203's not yet"),
|
|
119
120
|
)
|
|
120
121
|
|
|
121
122
|
|
oep_client/virtual_bench.py
CHANGED
|
@@ -82,7 +82,7 @@ class VirtualProbe:
|
|
|
82
82
|
# an interface with plan roles: the probe lists oep.probe.plan too (oep-if-plan), at the next free fn
|
|
83
83
|
offered.append(_plan(max(o.fn for o in offered) + 1))
|
|
84
84
|
if core_contract and any(o.fn != 0 for o in offered):
|
|
85
|
-
raise ValueError('core-v1 profile
|
|
85
|
+
raise ValueError('core-v1 profile exposes fn 0 only')
|
|
86
86
|
self.offered = sorted((o if not fill_ops else self._filled(o) for o in offered), key=lambda o: o.fn)
|
|
87
87
|
self.requests = 0
|
|
88
88
|
for o in self.offered:
|
|
@@ -196,7 +196,7 @@ _CAPD = reg.FIXTURE_LOGIC.tlv["describe"]
|
|
|
196
196
|
_ANAD = reg.FIXTURE_ANALOG.tlv["describe"]
|
|
197
197
|
_GRPD = reg.FIXTURE_CAPTURE_GROUP.tlv["describe"]
|
|
198
198
|
CORE_CHIP = reg.CORE.tlv["describe"]["chip"]
|
|
199
|
-
UNIT_ID = re.compile(r"[a-z0-9-]{1,32}") # an x- unit_id is not unique (core §7.5
|
|
199
|
+
UNIT_ID = re.compile(r"[a-z0-9-]{1,32}") # an x- unit_id is not unique (core §7.5)
|
|
200
200
|
|
|
201
201
|
|
|
202
202
|
def _analog_decl(frontends: list[tuple[int, int, int]], max_samples: int, min_hz: int, max_hz: int) -> tuple[bytes, ...]:
|
|
@@ -269,7 +269,7 @@ def block_max_length(max_frame: int) -> int:
|
|
|
269
269
|
|
|
270
270
|
|
|
271
271
|
def _transports(kinds: list[tuple[int, int]]) -> tuple[bytes, ...]:
|
|
272
|
-
"""(kind, interface) per transport, index = position (core §7.5
|
|
272
|
+
"""(kind, interface) per transport, index = position (core §7.5): a USB CDC names its communication
|
|
273
273
|
interface (the first of the function), built-in USB serial the number the hardware presents or 0xFF, vendor bulk
|
|
274
274
|
and HID their interface; a UART bridge and TCP 0xFF."""
|
|
275
275
|
for k, itf in kinds:
|
|
@@ -712,7 +712,7 @@ def minimal_ops(probe: VirtualProbe) -> VirtualProbe:
|
|
|
712
712
|
|
|
713
713
|
def with_unit_id(probe: VirtualProbe, unit_id: str) -> VirtualProbe:
|
|
714
714
|
"""The profile with another unit_id in fn 0's describe - an `x-` one is a probe with neither a unique number nor
|
|
715
|
-
storage (core §7.5
|
|
715
|
+
storage (core §7.5: hosts key nothing kept across sessions by it). VIRTUAL BENCH ONLY."""
|
|
716
716
|
return _again(probe, [_with(o, (catalog.text(CORE_UNIT_ID, unit_id) if t[0] == CORE_UNIT_ID else t for t in o.tlvs))
|
|
717
717
|
if o.fn == CORE_FN else o for o in probe.offered])
|
|
718
718
|
|
|
@@ -171,7 +171,7 @@ def build(a: argparse.Namespace) -> endpoint.Endpoint:
|
|
|
171
171
|
if getattr(a, "minimal_ops", False):
|
|
172
172
|
probe = virtual_bench.minimal_ops(probe) # console / fixture UART at their smallest
|
|
173
173
|
start = time.monotonic_ns()
|
|
174
|
-
# the clock in ns since this start (core §2.6a), and a boot_id from the OS's random source (core §6.5
|
|
174
|
+
# the clock in ns since this start (core §2.6a), and a boot_id from the OS's random source (core §6.5)
|
|
175
175
|
ep = endpoint.Endpoint(probe, lambda: (time.monotonic_ns() - start) // 1_000_000, boot_id=secrets.randbits(32),
|
|
176
176
|
now_ns=lambda: time.monotonic_ns() - start)
|
|
177
177
|
wire = min(ep.pairs) if ep.pairs else None
|
|
@@ -451,7 +451,7 @@ def serve_tcp(a, ep, console, commands: Commands) -> None:
|
|
|
451
451
|
srv.listen(8)
|
|
452
452
|
port = srv.getsockname()[1]
|
|
453
453
|
if a.framing == "length":
|
|
454
|
-
# the listening socket is a TCP transport of its own (transports §1
|
|
454
|
+
# the listening socket is a TCP transport of its own (transports §1): listed in describe, named by confirm
|
|
455
455
|
ep.tcp_index = ep.add_transport(virtual_bench.TRANSPORT["tcp"])
|
|
456
456
|
if a.announce:
|
|
457
457
|
commands.responder = announce(a, ep, port)
|
oep_client/virtual_core.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""Virtual fn 0 implementation of the
|
|
1
|
+
"""Virtual fn 0 implementation of the core, for the core-v1 profile and the conformance sample models.
|
|
2
2
|
|
|
3
|
-
Independent of the conformance checker and the
|
|
3
|
+
Independent of the conformance checker and of the virtual bench's Endpoint and its interface registry.
|
|
4
4
|
The core-v1 profile has no resources, subscriptions, interfaces or targets.
|
|
5
5
|
Explicit sample models may supply an independently defined extension hook.
|
|
6
6
|
"""
|
|
@@ -62,8 +62,7 @@ def tail(data, owner=False):
|
|
|
62
62
|
class Core:
|
|
63
63
|
def __init__(self, endpoint, extension=None):
|
|
64
64
|
self.ep = endpoint
|
|
65
|
-
# Only explicit
|
|
66
|
-
# and the core-v1 profile continue to expose fn 0 alone.
|
|
65
|
+
# Only explicit sample models supply this hook; the core-v1 profile exposes fn 0 alone.
|
|
67
66
|
self.extension = extension
|
|
68
67
|
self.holder, self.last, self.owner = None, None, b''
|
|
69
68
|
self.lease, self.deadline, self.high = 3000, 0, 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: oep-client-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.44
|
|
4
4
|
Summary: Open Embedded Probe (OEP) v1 host: serial / USB / TCP transports, the session rules, the standard interfaces, and a virtual bench
|
|
5
5
|
Project-URL: Homepage, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
6
6
|
Project-URL: Repository, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
@@ -31,11 +31,10 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
The host side of Open Embedded Probe (OEP). It speaks the v1 protocol of
|
|
33
33
|
[oep-spec](https://github.com/Open-Embedded-Probe/oep-spec) (the core `docs/oep-core.ja.md`, `docs/oep-transports.ja.md`
|
|
34
|
-
and the interfaces `interfaces/*.ja.md`)
|
|
35
|
-
oep-spec's generated `generated/oep-v1/oep_v1_registry.py`.
|
|
36
|
-
no compatible API is promised.
|
|
34
|
+
and the interfaces `interfaces/*.ja.md`). The wire numbers come from `oep_client.registry`, a verbatim copy of
|
|
35
|
+
oep-spec's generated `generated/oep-v1/oep_v1_registry.py`.
|
|
37
36
|
|
|
38
|
-
**The spec this implements: the oep-spec commit in `tests/vectors/SPEC_COMMIT`** (
|
|
37
|
+
**The spec this implements: the oep-spec commit in `tests/vectors/SPEC_COMMIT`** (oep-spec versioning §2 / §6: before the
|
|
39
38
|
freeze, revision 1 alone does not fix the forms, so an implementation names the spec commit it implements). The registry
|
|
40
39
|
and the shared vectors in `tests/vectors/` are copied from that commit; the history is in [CHANGELOG.md](CHANGELOG.md).
|
|
41
40
|
|
|
@@ -66,7 +65,7 @@ sets the version in pyproject.toml, uv.lock and `oep_client.__version__` and tur
|
|
|
66
65
|
the tests and the build it commits, tags, makes the GitHub Release and publishes to PyPI (Trusted Publishing). Record changes
|
|
67
66
|
under Unreleased in CHANGELOG.md, (EN) and (JA).
|
|
68
67
|
|
|
69
|
-
A minimal check: [hardware quickstart](docs/hardware-quickstart.ja.md). See the shared [testing policy](docs/testing-policy.ja.md) and [guide for projects using OEP](docs/testing-consumers.ja.md).
|
|
68
|
+
A minimal check: [hardware quickstart](docs/hardware-quickstart.ja.md). See the shared [testing policy](docs/testing-policy.ja.md) and [guide for projects using OEP](docs/testing-consumers.ja.md). Equipment configuration: [hardware design](docs/hardware-configuration.ja.md) and [configuration examples](docs/hardware-schema.ja.md) (in Japanese).
|
|
70
69
|
|
|
71
70
|
## Modules (`oep_client`)
|
|
72
71
|
|
|
@@ -500,4 +499,3 @@ IP_MULTICAST_IF 127.0.0.1. A different `--unit-id` per job keeps parallel jobs a
|
|
|
500
499
|
name too). A container or VM answers only on its own network. tests/test_virtual_bench_announce.py does this with
|
|
501
500
|
`oep find`, `discovery.find_unit` and `oep://UNIT_ID?via=net` (both engines) - with in-process responders announcing a non-OEP
|
|
502
501
|
TCP service and a wrong TXT unit_id beside it, which the verify drops - skipped where multicast does not loop back.
|
|
503
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
oep_client/__init__.py,sha256=
|
|
1
|
+
oep_client/__init__.py,sha256=BKL5jblIAcotJRxILFXHy_ZQK96TtAGpJBG_e8J0FbQ,164
|
|
2
2
|
oep_client/__main__.py,sha256=fXvhDWRLSEPpHM6eSuBzztNzvDQi-Yqgrvz6MS5iRbg,31431
|
|
3
3
|
oep_client/acquire.py,sha256=uecNDT1onLMeIxFkHFYgqG7chWWvLrAji93KnIisn0A,100072
|
|
4
4
|
oep_client/arm.py,sha256=SszSLae8zErD4T-a1uQ6s6o3Lz7pe2SR8d3Qy-wi5rY,15633
|
|
@@ -8,45 +8,45 @@ oep_client/catalog.py,sha256=WkaksNn9cFvkTRnvtX4I9g1AvkFoldEiNQaKiyPQ7_E,10772
|
|
|
8
8
|
oep_client/ch32_flash.py,sha256=lY_HKxf2foSfhNda91lcWefgRnY9ebTljsg4zJEj-Ks,12885
|
|
9
9
|
oep_client/cobs.py,sha256=RbdPrqgzD3SukxOIXX9m2dZpVTud8xAYYEhm3rcW2nA,2513
|
|
10
10
|
oep_client/console.py,sha256=hDxoaXDCzk6KiOo-Y3RqC4yZuLPkCXOVi886_C4xXaE,12910
|
|
11
|
-
oep_client/core.py,sha256=
|
|
11
|
+
oep_client/core.py,sha256=o7qJjZc68RS9p91TfV3aLsLcTq-jWBO1atB9kGL_tkA,22188
|
|
12
12
|
oep_client/decode.py,sha256=6pTFVBT0MLAKedZLVKyd5yT8hjrRw4fdDjnoDiE9XRs,2380
|
|
13
13
|
oep_client/discovery.py,sha256=oES0_NkR1Hc9E_cgPcfW8qfevQcVuYmayMaHuqufdO8,20180
|
|
14
|
-
oep_client/dump.py,sha256=
|
|
15
|
-
oep_client/endpoint.py,sha256=
|
|
14
|
+
oep_client/dump.py,sha256=9rfO07q1EOY4aLFZkm_9RO8awjxNQa75DIDeKAoF_ac,12995
|
|
15
|
+
oep_client/endpoint.py,sha256=h-B1E8Db8qfRZUmAe1abMy38Xjy4CifF4_WmBInp_6g,238405
|
|
16
16
|
oep_client/fixture.py,sha256=bEbW7JNIh6uqOq1enp6hRiZ0Ipu8YdzEzS37WaM3cd8,33977
|
|
17
|
-
oep_client/frames.py,sha256=
|
|
17
|
+
oep_client/frames.py,sha256=FM1AGn5NY_e-r1yKBsDkEEOqNrk5XTrlKA5Mq6Ycqiw,4984
|
|
18
18
|
oep_client/hardware.py,sha256=RboxkRxkQJ3DbNJEXyyiOFYlLFKGxrlcSwHho8zFC1U,30256
|
|
19
19
|
oep_client/hid_stream.py,sha256=1dG1C5rSqSWs2f0WpzMOKdFd3oY_mzF7z9SxUztjpQA,14093
|
|
20
|
-
oep_client/host.py,sha256=
|
|
20
|
+
oep_client/host.py,sha256=Wv0AKySaRu1ERJKERjnGnzfdKYkmFlQsL-Nh3SF_bbE,47875
|
|
21
21
|
oep_client/interfaces.py,sha256=p-dQ3dtElTIPSmNFEnUzP_Mys6M43_yUqH1cZA0q5LI,11934
|
|
22
22
|
oep_client/kept_session.py,sha256=S45NwNjeZry5kc5HAyk_orqXKAYf26MuyjJ2ojT51FQ,8084
|
|
23
|
-
oep_client/link.py,sha256
|
|
23
|
+
oep_client/link.py,sha256=-6N95ruUInNFJnmVOXCr-7XSgXDK-fpkjYJEK5nUFuE,145428
|
|
24
24
|
oep_client/linktest.py,sha256=zj9Ek28VkjyuIPsiU3iF-UEZmY_xMnNjNL-vK-jOQ3Q,10258
|
|
25
25
|
oep_client/message.py,sha256=Kw-PwkMCwa8VGUBd1HWTXe8Epe4FwNcBx8262mWx2rk,14152
|
|
26
26
|
oep_client/names.py,sha256=ohrRvQNURraYyJ-BtjHRwxGt_GLq-t8IO0aOji9Dhe0,3531
|
|
27
|
-
oep_client/pins.py,sha256=
|
|
27
|
+
oep_client/pins.py,sha256=c5aNQtj7ZWM1bxYBtByLuWtkWM4JOra2QCbjvlHybmg,53690
|
|
28
28
|
oep_client/probe_log.py,sha256=gAamfON2M7lMH3xOn4Wpmes_Wp0H4c2EBNbpXjIvCMM,4927
|
|
29
29
|
oep_client/pytest_conformance.py,sha256=-bcJU2E1UE4xjQ7HZjaIgtp0l65cIibBTNFnaW2tews,2929
|
|
30
30
|
oep_client/registry.py,sha256=GfLdoZYSc37PBQ2rgXHM4DtFdU0mpnroM42jVofRx44,23309
|
|
31
|
-
oep_client/riscv.py,sha256=
|
|
31
|
+
oep_client/riscv.py,sha256=o_YElQuJZoBUzqO7QV9VkWVeD4UE2pUx_ciR_TMHRWI,60419
|
|
32
32
|
oep_client/rp2350.py,sha256=7OMyj270k3AY1e3-ljicPwOjf9jOlokbT6QNkA0b0yY,4227
|
|
33
33
|
oep_client/settings.py,sha256=wVRpvAmcvDygVE9PCbo4-7k-tq6pIzKh7VRlmtLO1Fk,26391
|
|
34
|
-
oep_client/speed_record.py,sha256=
|
|
34
|
+
oep_client/speed_record.py,sha256=hdVx8mCeWqPC3gWrBsOyfzVZcJfN5_CJfpW18WbsBmc,7223
|
|
35
35
|
oep_client/stimulus.py,sha256=pfX4xLjyG3UWIJRWUHnkg9c1ueL1MeozHj3CnlJ7dpA,6072
|
|
36
|
-
oep_client/targets.py,sha256=
|
|
36
|
+
oep_client/targets.py,sha256=ixUxbZUfYqOJjZzcaOudwV2wweVwdk0G85dfRiZzMeA,7183
|
|
37
37
|
oep_client/uiapduino.py,sha256=oafP1SyKPO3cox-2RYbWUZusy4vc6NsEVC7Z-Y1sjWI,6337
|
|
38
38
|
oep_client/usb_stream.py,sha256=zRGGHkvtOme6lIZ-gkrCe2xKn9hC62JGqmc7pKQDAJI,23540
|
|
39
|
-
oep_client/virtual_bench.py,sha256=
|
|
39
|
+
oep_client/virtual_bench.py,sha256=PC6pOoTcm10p0WHUrIO-_xyzPWAyXZimXgQks3IgOYY,52762
|
|
40
40
|
oep_client/virtual_bench_capture.py,sha256=vwlLPkiWopgQxNGNxCFI84FR_cX0y6XWgszkDcoieiY,110696
|
|
41
41
|
oep_client/virtual_bench_mdns.py,sha256=cnJNBKg5zTduXUcH-wqN_ZBDGl7DyL-tL3CBn2jro0E,13305
|
|
42
42
|
oep_client/virtual_bench_serial.py,sha256=EvbdOYeGrM6CRti2b90I0PzpmmherfoJRaAg4v7Iq9Y,13205
|
|
43
|
-
oep_client/virtual_bench_serve.py,sha256=
|
|
43
|
+
oep_client/virtual_bench_serve.py,sha256=oNXrESlch-fXpfCg0Y5w3OmKNdtrOVwXkuhZBTPP6PQ,39015
|
|
44
44
|
oep_client/virtual_bench_trigger.py,sha256=2c3gGOM8wXucTXbc1bTKQ5haX7bAkGJeOryGghrtcRk,23874
|
|
45
45
|
oep_client/virtual_bench_usb.py,sha256=cnewgbsl30b0PgwkfebwSf3xjKyPg9y8vTd5Jew5r9o,4001
|
|
46
|
-
oep_client/virtual_core.py,sha256=
|
|
46
|
+
oep_client/virtual_core.py,sha256=F-UxtQIld4eE04_M8j4ANMlQ12jXANzfTnfn59kUWHk,10716
|
|
47
47
|
oep_client/virtual_fixture_drive.py,sha256=_G3MVqkxpd1SfKPz1XRkYbPzAQvgBlJzVUNhjAhDdz4,21708
|
|
48
48
|
oep_client/virtual_names.py,sha256=p72ME65R9Jy56xmVYnVCtapK3Id8iFpAdI5xLe2tNZ0,1449
|
|
49
|
-
oep_client/conformance/__init__.py,sha256=
|
|
49
|
+
oep_client/conformance/__init__.py,sha256=o17AgcYob-Ahn7qWewjCo_7jK4o6kMnX-xHwG9V4Kv8,61738
|
|
50
50
|
oep_client/conformance/allocator.py,sha256=VE2nzIpXZJe4IBfgbRShp69JIwUFtLpxorQ_mO28SlM,6251
|
|
51
51
|
oep_client/conformance/allocator_model.py,sha256=j6l1E1Hlju1jNeFQ_N0a2mDaegaTeyk6iScqzMpvvTc,1563
|
|
52
52
|
oep_client/conformance/allocator_sample.py,sha256=xtiXuZa3VDTilrZArIWOiRyOkuXJ98_rY2M65sIVvpo,855
|
|
@@ -81,7 +81,7 @@ oep_client/conformance/clock.py,sha256=bDqBjih71zg_mhkgCSryzKI_N8FEoGoGv9jO4PMYD
|
|
|
81
81
|
oep_client/conformance/clock_model.py,sha256=jyB8oTVR5sqbzeJCCWxdQBnzNNl4_uITLLYQ-yx8pRU,2568
|
|
82
82
|
oep_client/conformance/clock_sample.py,sha256=x24mSpQoOamrXrinmnF_Tk4NPk76LwS6ZhfuK2SCwCo,673
|
|
83
83
|
oep_client/conformance/core_fixed_fields.py,sha256=LRFP9yGOjDHQM1diX1HMdwXh9jMgAOY6xIC-woCikSk,6297
|
|
84
|
-
oep_client/conformance/core_smoke.py,sha256=
|
|
84
|
+
oep_client/conformance/core_smoke.py,sha256=4vLm1j2yIuvUH5sxWa7yJzNKzRTAFYzRhby9Ef2eZ6Y,10815
|
|
85
85
|
oep_client/conformance/corr.py,sha256=P6ljdsXpBq-vyEXRWOB1MqACQjT6HRBXgfAxgkyPjl4,6552
|
|
86
86
|
oep_client/conformance/cut.py,sha256=Oj9aj48g2VSrqFHRNlkY22MzXxQbr13Q8QXUsEMETis,7942
|
|
87
87
|
oep_client/conformance/cut_model.py,sha256=bHPnK8vKoInKVslW0KlJXRRgpGK-7ktIVGUEXo60X34,4791
|
|
@@ -115,21 +115,21 @@ oep_client/conformance/gpio_lifecycle_model.py,sha256=K0nzlXf-43kj3jsfaAuzEWOftO
|
|
|
115
115
|
oep_client/conformance/gpio_lifecycle_sample.py,sha256=Yk1WENQ-2tbPCM2yUY4Yk9dgWkjIfL42VtUSpx1qhhM,369
|
|
116
116
|
oep_client/conformance/gpio_model.py,sha256=CXy42K8cdRSTBxLC44h-gTYhjh3HrduSehsgsbvmDpo,5633
|
|
117
117
|
oep_client/conformance/gpio_sample.py,sha256=JHsOrjkYcM738IGSzP5q7v5vANETwKT6-csCTxH55MQ,490
|
|
118
|
-
oep_client/conformance/host_core.py,sha256=
|
|
118
|
+
oep_client/conformance/host_core.py,sha256=o_M0nCTRzGvThC6G2C27DO3QdM1GRSE-eGkbhB7ZIO0,12634
|
|
119
119
|
oep_client/conformance/host_core_sample.py,sha256=egU8Xw-f3eHx_CmUooN1yW1lzltOam6IsJbnD8uOx9I,3803
|
|
120
|
-
oep_client/conformance/host_deadline.py,sha256=
|
|
120
|
+
oep_client/conformance/host_deadline.py,sha256=_Ri9HSAlB27rgAqXdP-evsF-DGBegajXzV9l7NgAh-k,6978
|
|
121
121
|
oep_client/conformance/host_deadline_sample.py,sha256=G471jEc2HxDIu52Acw_aHDSxFAEC72OBCuvn2YQtAdU,5136
|
|
122
122
|
oep_client/conformance/host_notifications.py,sha256=LGRJhOS66_36MYG8fHLcyHroXtoZRQVJgammDieVhQY,9307
|
|
123
123
|
oep_client/conformance/host_notifications_sample.py,sha256=auze2DdWtUOdQMHnfDF8WXLAiETv3OuODR6y77VmsTQ,1788
|
|
124
124
|
oep_client/conformance/host_request_sample.py,sha256=pFlgw_nw_LTPoojI3Igdl1bQzsyMJSEDkuTgGOAV1Gg,1973
|
|
125
|
-
oep_client/conformance/host_requests.py,sha256=
|
|
125
|
+
oep_client/conformance/host_requests.py,sha256=eVgWvAt3PMVVLVfEmpIoySVBiRaG_1G1nGNHwFvKsGg,11197
|
|
126
126
|
oep_client/conformance/host_resend.py,sha256=P8tFWtr6TwQ33ph-JO0l8EXyJGxSjC6fBeyKQ4AhqxU,5519
|
|
127
127
|
oep_client/conformance/host_resend_sample.py,sha256=nwaru9ZVcxpcTi1OEritp8PSF6Qfic_IRGuutH2aBA4,4808
|
|
128
128
|
oep_client/conformance/host_tlv.py,sha256=h5SZT9r3DCsxMLcRacdyKOoZNQHSL8DzRgT1VPu_86A,6140
|
|
129
129
|
oep_client/conformance/host_tlv_sample.py,sha256=V6ORb5E2i76dc8O73Bew0eO2CbjG-ZPc02JLi6w5vmY,743
|
|
130
130
|
oep_client/conformance/host_values.py,sha256=VnjEB5TQbI34fuyAx0OGVbiyfI2ZH_of9I4TLT7Dyyk,4742
|
|
131
131
|
oep_client/conformance/host_values_sample.py,sha256=7C58mSP9jjrkNSgSb3uNK44VrdPXOznof_oe5UCcZro,1532
|
|
132
|
-
oep_client/conformance/host_wait.py,sha256=
|
|
132
|
+
oep_client/conformance/host_wait.py,sha256=2upJp_9wWfssDO-CDnEb2RVp7_cQAnpLIt4MWwAtveE,5591
|
|
133
133
|
oep_client/conformance/host_wait_sample.py,sha256=7SkJ8H_9ScMv8gLnT85xBKj_GXciPvAzmH-KxfMWYCg,3257
|
|
134
134
|
oep_client/conformance/i2c.py,sha256=fNDweyCLlCsv7fCwmmQMC7di5AG1HzDCflW0miWwcjc,18041
|
|
135
135
|
oep_client/conformance/i2c_model.py,sha256=HLYqRKdKMvlCGMcgYxSS3dy50Zf7aPojN_gO5fBvy6U,9759
|
|
@@ -157,7 +157,7 @@ oep_client/conformance/notification_sequence.py,sha256=TGdLjqPkCBtjH6f_oxgPGfl-l
|
|
|
157
157
|
oep_client/conformance/notification_sequence_sample.py,sha256=awSniydBvRoy03PCR5Y6xHAVmLWV1wjKwfA2dwI8XCI,494
|
|
158
158
|
oep_client/conformance/oep_declaration_model.py,sha256=_Zq_eLLkHNGcDfA0tPptWpqT4tZbCsVrdjE2k9lAcco,2947
|
|
159
159
|
oep_client/conformance/oep_declarations.py,sha256=0DrrDdveGEiSMp5fjy-ZJjR0mjAxv2TQLylZl_hnqfo,7492
|
|
160
|
-
oep_client/conformance/oep_smoke.py,sha256=
|
|
160
|
+
oep_client/conformance/oep_smoke.py,sha256=8XjyK4Rx27YLYek_JTJtyS8swGa0x0Pf5dbzb-YlIlg,12488
|
|
161
161
|
oep_client/conformance/outcome_model.py,sha256=jnKDNvkLBsSFQqQQEqCDt3zliKZx0b5LiLCHafrBv4U,3697
|
|
162
162
|
oep_client/conformance/outcome_sample.py,sha256=6dbEwddcY0z4jGZsazp_gGI_Vu0em-0rOrGN2BVUMzI,673
|
|
163
163
|
oep_client/conformance/outcomes.py,sha256=FktfF9aaexnXCgSNDYU6OZeqRuLd3ppFMXE1UrW-H2o,10047
|
|
@@ -250,8 +250,8 @@ oep_client/conformance/wire_options_sample.py,sha256=dog-yvUcu693M8b51tA5TnhAnKr
|
|
|
250
250
|
oep_client/conformance/writer_lease.py,sha256=5VXS7kt1R4j6B26XdJa6UOqJg-a05ZewC2ynVmyxMTM,7331
|
|
251
251
|
oep_client/conformance/writer_lease_model.py,sha256=FxhDqqzdFOa_SSHSvjdQFh49Wn3YuZ98ISSlRRxrq9E,1721
|
|
252
252
|
oep_client/conformance/writer_lease_sample.py,sha256=oowRwYUNAE4f6rEE2HmYlSS9dDWE0TGegkcsqVH8Nak,455
|
|
253
|
-
oep_client_python-0.0.
|
|
254
|
-
oep_client_python-0.0.
|
|
255
|
-
oep_client_python-0.0.
|
|
256
|
-
oep_client_python-0.0.
|
|
257
|
-
oep_client_python-0.0.
|
|
253
|
+
oep_client_python-0.0.44.dist-info/METADATA,sha256=6_CmjBSUWjX21ppK-I0ujv-7oApIOpmgWnRnqEUofA4,55269
|
|
254
|
+
oep_client_python-0.0.44.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
255
|
+
oep_client_python-0.0.44.dist-info/entry_points.txt,sha256=XEt39Jz7oQyU9Hfg_tV6eDGofNxWYrjSfslxKP-fh70,135
|
|
256
|
+
oep_client_python-0.0.44.dist-info/licenses/LICENSE,sha256=ntlalztb3L1OaNAxmQdHlubu5fVhjMc9oRZi3PeQhXg,1076
|
|
257
|
+
oep_client_python-0.0.44.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|