pytail 0.1.0__py3-none-win_amd64.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.
|
Binary file
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytail
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Programming Language :: Rust
|
|
8
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
9
|
+
Classifier: Topic :: System :: Archiving :: Mirroring
|
|
10
|
+
Summary: Incremental PyPI Simple API caching mirror
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
13
|
+
|
|
14
|
+
# PyTail
|
|
15
|
+
|
|
16
|
+
`pytail` is a minimal incremental PyPI caching mirror.
|
|
17
|
+
|
|
18
|
+
It is no longer a `devpi` clone. The new server only does four things:
|
|
19
|
+
|
|
20
|
+
- expose a valid Python Simple Repository API root at `/simple/`
|
|
21
|
+
- lazily fetch and cache `/simple/<project>/` from an upstream index
|
|
22
|
+
- rewrite file links to a devpi-style `root/pypi/+f/...` path
|
|
23
|
+
- serve cached project pages and cached files on later requests
|
|
24
|
+
|
|
25
|
+
## Plan
|
|
26
|
+
|
|
27
|
+
1. Implement the smallest Simple API surface that `pip` and `uv` actually need:
|
|
28
|
+
root index, project index, normalized names, file links, and content negotiation
|
|
29
|
+
for HTML vs JSON responses.
|
|
30
|
+
2. Keep the cache incremental:
|
|
31
|
+
do not mirror the full upstream project list, only remember projects that have
|
|
32
|
+
already been requested.
|
|
33
|
+
3. Use conditional project refresh:
|
|
34
|
+
cache upstream project pages, preserve `ETag`, and refresh a project after a TTL
|
|
35
|
+
with `If-None-Match`.
|
|
36
|
+
4. Keep package files immutable:
|
|
37
|
+
once a file URL is requested, cache it under `+files/` and serve it from a
|
|
38
|
+
stable devpi-style file route on later requests.
|
|
39
|
+
5. Store the index in SQLite:
|
|
40
|
+
project pages, file metadata, and known-project state live in SQLite, while
|
|
41
|
+
package bytes live on disk.
|
|
42
|
+
6. Handle concurrent requests safely inside one process:
|
|
43
|
+
per-project and per-file locks avoid duplicate upstream fetches and duplicate
|
|
44
|
+
downloads.
|
|
45
|
+
7. Explicitly drop old `devpi` goals:
|
|
46
|
+
no users, no ACLs, no upload API, no index inheritance, no replication, no
|
|
47
|
+
snapshot format, no mirror whitelist, no web UI.
|
|
48
|
+
|
|
49
|
+
## Spec Coverage
|
|
50
|
+
|
|
51
|
+
The implementation is intentionally narrow:
|
|
52
|
+
|
|
53
|
+
- PEP 503 HTML Simple API for `/simple/` and `/simple/<project>/`
|
|
54
|
+
- PEP 691 JSON Simple API responses when `Accept` asks for
|
|
55
|
+
`application/vnd.pypi.simple.v1+json`
|
|
56
|
+
- preservation of `requires-python`, `yanked`, `gpg-sig`,
|
|
57
|
+
`dist-info-metadata`, and `core-metadata` markers when they are present on the
|
|
58
|
+
upstream project page
|
|
59
|
+
- lazy local root index containing only already-cached projects
|
|
60
|
+
|
|
61
|
+
The implementation does not currently fetch or synthesize a full global upstream
|
|
62
|
+
project list. That is deliberate: the root index is only a local catalogue of
|
|
63
|
+
known projects, while project pages are fetched on demand.
|
|
64
|
+
|
|
65
|
+
## Why This Is Enough
|
|
66
|
+
|
|
67
|
+
Package resolution for `pip` and `uv` depends primarily on per-project Simple API
|
|
68
|
+
pages. A full pre-fetched mirror root is not required for normal dependency
|
|
69
|
+
resolution as long as:
|
|
70
|
+
|
|
71
|
+
- `/simple/<normalized-project>/` is available and correct
|
|
72
|
+
- file links are reachable
|
|
73
|
+
- project metadata such as hashes and `requires-python` are preserved
|
|
74
|
+
|
|
75
|
+
## Run
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
cargo run -- \
|
|
79
|
+
--bind 127.0.0.1:3141 \
|
|
80
|
+
--upstream-base-url https://pypi.org \
|
|
81
|
+
--cache-dir .cache/pytail
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then point tools at it:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
uv pip install --index-url http://127.0.0.1:3141/simple/ requests
|
|
88
|
+
pip install --index-url http://127.0.0.1:3141/simple/ requests
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
- `--bind`: listen address, default `127.0.0.1:3141`
|
|
94
|
+
- `--upstream-base-url`: upstream index origin, default `https://pypi.org`
|
|
95
|
+
- `--cache-dir`: local cache directory, default `.cache/pytail`
|
|
96
|
+
- `--project-cache-ttl-secs`: refresh age for cached project pages, default `900`
|
|
97
|
+
- `--request-timeout-secs`: upstream HTTP timeout, default `15`
|
|
98
|
+
|
|
99
|
+
## Package
|
|
100
|
+
|
|
101
|
+
Build a Python wheel with `maturin`:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
maturin build --release
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Publish to PyPI:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
maturin publish --release
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The wheel installs the `pytail` command as a native Rust binary.
|
|
114
|
+
|
|
115
|
+
## Cache Layout
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
<cache-dir>/
|
|
119
|
+
index.sqlite3
|
|
120
|
+
+files/
|
|
121
|
+
root/
|
|
122
|
+
pypi/
|
|
123
|
+
+f/
|
|
124
|
+
f0a/
|
|
125
|
+
f3fc39e7459b0/
|
|
126
|
+
gradio_client-1.0.2-py3-none-any.whl
|
|
127
|
+
_url/
|
|
128
|
+
<url-digest>/
|
|
129
|
+
<filename>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- SQLite stores project cache entries, parsed file rows, upstream `ETag`, and
|
|
133
|
+
known project names
|
|
134
|
+
- `+files/root/pypi/+f/<first-3-sha256>/<next-13-sha256>/<filename>` stores
|
|
135
|
+
hashed files in the same shape as devpi's filesystem layout
|
|
136
|
+
- `+files/root/pypi/+f/_url/<url-digest>/<filename>` is used only when an
|
|
137
|
+
upstream file link does not provide a usable `sha256` hash
|
|
138
|
+
|
|
139
|
+
## Non-Goals
|
|
140
|
+
|
|
141
|
+
- full PyPI mirroring
|
|
142
|
+
- authentication or private indexes
|
|
143
|
+
- package upload
|
|
144
|
+
- merge of local and upstream package sources
|
|
145
|
+
- replica mode or background synchronization
|
|
146
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
pytail-0.1.0.data/scripts/pytail.exe,sha256=_4DQie062xMUxCYPQf0uQ1YSFKlBnzvBi6QCz5i0IdY,3971072
|
|
2
|
+
pytail-0.1.0.dist-info/METADATA,sha256=3qmRQPfVGlV1nwvjmoPzCYKF_1mZeRJlRDJhOJSp7GM,4825
|
|
3
|
+
pytail-0.1.0.dist-info/WHEEL,sha256=fxUURV-tpz4EGoGaAcyzIZ0CMCngNhGnetS-bU4AaSg,94
|
|
4
|
+
pytail-0.1.0.dist-info/sboms/pytail.cyclonedx.json,sha256=IoHbmC6ApW8uhD0VNXhd4eGmIWk0sf5BeBUTCwD4ZVM,230304
|
|
5
|
+
pytail-0.1.0.dist-info/RECORD,,
|