nexus-raw 0.18.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.
@@ -0,0 +1,9 @@
1
+ /target
2
+ site/
3
+ .cache/
4
+ node_modules/
5
+ *.node
6
+
7
+ # Generated by the examples when they are run from the repository root.
8
+ /demo-dist/
9
+ /demo-vendor/
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.5
2
+ Name: nexus-raw
3
+ Version: 0.18.0
4
+ Summary: The Python reference client for nexus-raw: a minimal Nexus raw-storage client on the Python standard library
5
+ Project-URL: Homepage, https://lipen.github.io/nexus-raw/
6
+ Project-URL: Repository, https://github.com/Lipen/nexus-raw
7
+ Project-URL: Documentation, https://lipen.github.io/nexus-raw/reference/api/
8
+ Author: lipen
9
+ License-Expression: MIT
10
+ Keywords: artifacts,devops,nexus,nexus-raw,sha256
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Build Tools
21
+ Classifier: Topic :: System :: Archiving
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+
25
+ # The Python reference client
26
+
27
+ `nxr.py` is the second client of the protocol: a minimal reference implementation on the Python standard library, python3.10+, nothing to install.
28
+ It proves the claim the project makes about its own wire format: the protocol is implementable from scratch over bare HTTP, with `http.client` for the wire and `hashlib` for the digests.
29
+ The authoritative text it follows lives in `crates/nexus-raw-core/src/protocol.md`.
30
+
31
+ ## What it covers
32
+
33
+ Six verbs over the store: `get`, `put`, `head`, `sha`, `ls`, `channel get/set`.
34
+ Deletion is deliberately absent.
35
+ The interesting rules are all client-side and all present:
36
+
37
+ - `put` writes the bytes, then the `.sha256` marker of the same name, strictly in that order.
38
+ A crash between the two leaves a markerless object, which every reader refuses.
39
+ The marker's name field is the percent-decoded final path segment, refused when it is empty, `.`/`..`, out of grammar, or `.sha256`-suffixed.
40
+ - `get` downloads the bytes and the sibling and refuses unless the digest matches.
41
+ Completion is bytes plus the sibling: anything less is an exception, never a silent return.
42
+ - A 2xx without `Content-Length` is present-but-unverifiable, never absent (`head` reports `length=None`).
43
+ - 401/403 fail fast with no retries.
44
+ 5xx and transport failures retry with backoff (4 attempts, 0.5s x 2^n, capped, jittered).
45
+ A 429 honors its `Retry-After` pause, clamped to 1..=60 seconds, in place of the backoff.
46
+ - Redirects are never followed: a 301 surfaces as `HttpError` with the status.
47
+ - The per-operation socket timeout is the stall watchdog: a frozen server aborts the attempt, a slow-but-moving transfer never trips it.
48
+ - `ls` walks `/service/rest/v1/search/assets` with continuation tokens, capped at 100 pages, and diagnoses a repository-scoped 400 as "missing or not raw".
49
+ - Markers parse strictly (`sha256sum -c` shape: LF only, exactly two spaces, 64 lowercase hex, trailing newline), because a loose parse would bless foreign markers.
50
+ The reference decodes marker bytes as ASCII, one notch stricter than the Rust core's lossy UTF-8: harmless today, since both sides compare digests only, but a porter may loosen it the same way.
51
+
52
+ Every error is an exception carrying `.status` (the HTTP status, `None` for pure transport failures) and `.hint` (the action line a CLI would print).
53
+ Every attempt lands in `.requests` as a `(method, path)` pair, the record the conformance suite asserts on.
54
+
55
+ ## Run the conformance suite
56
+
57
+ ```bash
58
+ cargo build -q -p mock-nexus
59
+ python3 -m unittest discover clients/python
60
+ ```
61
+
62
+ The suite spawns `mock-nexus` itself, one process per test on a free port, and reads the scenario list live from `mock-nexus --print-scenarios`.
63
+ To point it at another binary, set `MOCK_NEXUS_BIN` to the path.
64
+
65
+ ## The scenario contract and the skip table
66
+
67
+ The mock's scenario list is the conformance contract, and the suite refuses to drift from it:
68
+ `test_unit_every_scenario_is_classified` requires every listed scenario to have a test method named `test_<scenario with dashes as underscores>` or a documented row in `SKIP`, in both directions.
69
+ A scenario added to `crates/mock-nexus/src/scenario.rs` fails this suite until it is classified, which is the point.
70
+
71
+ The current skip rows, with the reasons:
72
+
73
+ | Scenario | Reason |
74
+ |:---------|:-------|
75
+ | `doc-drift` | The drift toggle is a library-only knob (`MockNexus::enable_drift`), so the binary serves plain storage. the client-side half, `version.json` being an ordinary name, is pinned under `atomic` |
76
+ | `no-service` | The scenario only breaks `/service/rest/v1/repositories`, and the client has no service-repos surface |
77
+ | `readonly` | The scenario only answers 403 to `DELETE`, and the client implements no delete |
78
+
79
+ ## Porting note
80
+
81
+ What a real integrator must implement, at minimum, beyond the verbs:
82
+
83
+ 1. Basic auth attached to every request, resolved once per invocation.
84
+ 2. The retry classification: retry transport failures and 5xx with bounded backoff, honor `Retry-After` on 429, never retry 401/403, never follow redirects.
85
+ 3. An attempt-level stall bound (a read/write timeout), because a server can hold a connection forever.
86
+ 4. The marker order on upload, the digest check on download, and the strict marker parse.
87
+ 5. The lengthless-answer rule: a 2xx without `Content-Length` is unverifiable, never empty.
88
+ 6. `Accept-Encoding: identity` on every request, unless the client also implements the `zstd` GET decoding, so digests always describe the stored bytes.
89
+
90
+ Deliberate simplifications of this reference, safe to outgrow: bodies are held in memory (no streaming, no Range resume, no part files), one connection per attempt (no keep-alive), and TLS is whatever the runtime verifies by default (the protocol's `--tls-insecure` escape hatch has no equivalent here).
@@ -0,0 +1,66 @@
1
+ # The Python reference client
2
+
3
+ `nxr.py` is the second client of the protocol: a minimal reference implementation on the Python standard library, python3.10+, nothing to install.
4
+ It proves the claim the project makes about its own wire format: the protocol is implementable from scratch over bare HTTP, with `http.client` for the wire and `hashlib` for the digests.
5
+ The authoritative text it follows lives in `crates/nexus-raw-core/src/protocol.md`.
6
+
7
+ ## What it covers
8
+
9
+ Six verbs over the store: `get`, `put`, `head`, `sha`, `ls`, `channel get/set`.
10
+ Deletion is deliberately absent.
11
+ The interesting rules are all client-side and all present:
12
+
13
+ - `put` writes the bytes, then the `.sha256` marker of the same name, strictly in that order.
14
+ A crash between the two leaves a markerless object, which every reader refuses.
15
+ The marker's name field is the percent-decoded final path segment, refused when it is empty, `.`/`..`, out of grammar, or `.sha256`-suffixed.
16
+ - `get` downloads the bytes and the sibling and refuses unless the digest matches.
17
+ Completion is bytes plus the sibling: anything less is an exception, never a silent return.
18
+ - A 2xx without `Content-Length` is present-but-unverifiable, never absent (`head` reports `length=None`).
19
+ - 401/403 fail fast with no retries.
20
+ 5xx and transport failures retry with backoff (4 attempts, 0.5s x 2^n, capped, jittered).
21
+ A 429 honors its `Retry-After` pause, clamped to 1..=60 seconds, in place of the backoff.
22
+ - Redirects are never followed: a 301 surfaces as `HttpError` with the status.
23
+ - The per-operation socket timeout is the stall watchdog: a frozen server aborts the attempt, a slow-but-moving transfer never trips it.
24
+ - `ls` walks `/service/rest/v1/search/assets` with continuation tokens, capped at 100 pages, and diagnoses a repository-scoped 400 as "missing or not raw".
25
+ - Markers parse strictly (`sha256sum -c` shape: LF only, exactly two spaces, 64 lowercase hex, trailing newline), because a loose parse would bless foreign markers.
26
+ The reference decodes marker bytes as ASCII, one notch stricter than the Rust core's lossy UTF-8: harmless today, since both sides compare digests only, but a porter may loosen it the same way.
27
+
28
+ Every error is an exception carrying `.status` (the HTTP status, `None` for pure transport failures) and `.hint` (the action line a CLI would print).
29
+ Every attempt lands in `.requests` as a `(method, path)` pair, the record the conformance suite asserts on.
30
+
31
+ ## Run the conformance suite
32
+
33
+ ```bash
34
+ cargo build -q -p mock-nexus
35
+ python3 -m unittest discover clients/python
36
+ ```
37
+
38
+ The suite spawns `mock-nexus` itself, one process per test on a free port, and reads the scenario list live from `mock-nexus --print-scenarios`.
39
+ To point it at another binary, set `MOCK_NEXUS_BIN` to the path.
40
+
41
+ ## The scenario contract and the skip table
42
+
43
+ The mock's scenario list is the conformance contract, and the suite refuses to drift from it:
44
+ `test_unit_every_scenario_is_classified` requires every listed scenario to have a test method named `test_<scenario with dashes as underscores>` or a documented row in `SKIP`, in both directions.
45
+ A scenario added to `crates/mock-nexus/src/scenario.rs` fails this suite until it is classified, which is the point.
46
+
47
+ The current skip rows, with the reasons:
48
+
49
+ | Scenario | Reason |
50
+ |:---------|:-------|
51
+ | `doc-drift` | The drift toggle is a library-only knob (`MockNexus::enable_drift`), so the binary serves plain storage. the client-side half, `version.json` being an ordinary name, is pinned under `atomic` |
52
+ | `no-service` | The scenario only breaks `/service/rest/v1/repositories`, and the client has no service-repos surface |
53
+ | `readonly` | The scenario only answers 403 to `DELETE`, and the client implements no delete |
54
+
55
+ ## Porting note
56
+
57
+ What a real integrator must implement, at minimum, beyond the verbs:
58
+
59
+ 1. Basic auth attached to every request, resolved once per invocation.
60
+ 2. The retry classification: retry transport failures and 5xx with bounded backoff, honor `Retry-After` on 429, never retry 401/403, never follow redirects.
61
+ 3. An attempt-level stall bound (a read/write timeout), because a server can hold a connection forever.
62
+ 4. The marker order on upload, the digest check on download, and the strict marker parse.
63
+ 5. The lengthless-answer rule: a 2xx without `Content-Length` is unverifiable, never empty.
64
+ 6. `Accept-Encoding: identity` on every request, unless the client also implements the `zstd` GET decoding, so digests always describe the stored bytes.
65
+
66
+ Deliberate simplifications of this reference, safe to outgrow: bodies are held in memory (no streaming, no Range resume, no part files), one connection per attempt (no keep-alive), and TLS is whatever the runtime verifies by default (the protocol's `--tls-insecure` escape hatch has no equivalent here).