fetchurl-sdk 0.1.6__tar.gz → 0.2.1__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,83 @@
1
+ name: Autorelease
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ workflow_dispatch:
9
+ inputs:
10
+ new_version:
11
+ description: "Version bump mode (svu) or explicit x.y.z"
12
+ type: choice
13
+ default: next
14
+ options: [next, patch, minor, major]
15
+
16
+ jobs:
17
+ autorelease:
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ contents: write
21
+ # Trusted publishing (PyPI) — enable if you configure OIDC on PyPI instead of UV_PUBLISH_TOKEN
22
+ id-token: write
23
+ steps:
24
+ - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
25
+ with:
26
+ # svu needs full history + tags
27
+ fetch-depth: 0
28
+
29
+ - name: Setup git config
30
+ run: |
31
+ git config user.name actions-bot
32
+ git config user.email actions-bot@users.noreply.github.com
33
+
34
+ - uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3
35
+
36
+ - name: Sync deps
37
+ run: uv sync --extra test
38
+
39
+ - name: Unit tests
40
+ run: uv run python -m unittest test_fetchurl.py
41
+
42
+ - name: Integration tests (optional)
43
+ if: vars.FETCHURL_TEST_IMAGE != ''
44
+ env:
45
+ FETCHURL_TEST_IMAGE: ${{ vars.FETCHURL_TEST_IMAGE }}
46
+ run: uv run --extra test python -m unittest test_fetchurl_integration.py
47
+ continue-on-error: true
48
+
49
+ - name: Bump version (svu)
50
+ if: github.event_name == 'workflow_dispatch'
51
+ env:
52
+ NEW_VERSION: ${{ github.event.inputs.new_version }}
53
+ run: |
54
+ VERSION="$(NO_TAG=1 ./make_release "$NEW_VERSION")"
55
+ echo "RELEASE_VERSION=$VERSION" >> "$GITHUB_ENV"
56
+ echo "New version: $VERSION (from svu $NEW_VERSION)" >> "$GITHUB_STEP_SUMMARY"
57
+
58
+ - name: Create GitHub release
59
+ if: env.RELEASE_VERSION != ''
60
+ env:
61
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62
+ TAG: ${{ env.RELEASE_VERSION }}
63
+ run: |
64
+ git tag "$TAG"
65
+ git push origin "$TAG"
66
+ git push origin HEAD:main
67
+ gh release create "$TAG" --title "Release $TAG" --generate-notes
68
+
69
+ - name: Build
70
+ if: env.RELEASE_VERSION != ''
71
+ run: uv build
72
+
73
+ - name: Publish to PyPI
74
+ if: env.RELEASE_VERSION != ''
75
+ env:
76
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
77
+ run: |
78
+ if [[ -z "${UV_PUBLISH_TOKEN:-}" ]]; then
79
+ echo "PYPI_TOKEN not set; trying trusted publishing (OIDC)..."
80
+ uv publish
81
+ else
82
+ uv publish --token "$UV_PUBLISH_TOKEN"
83
+ fi
@@ -0,0 +1,2 @@
1
+ tag.prefix: ""
2
+ v0: true
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lucas Eduardo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: fetchurl-sdk
3
+ Version: 0.2.1
4
+ Summary: Protocol-level client SDK for fetchurl content-addressable cache servers
5
+ Project-URL: Homepage, https://pypi.org/project/fetchurl-sdk/
6
+ Project-URL: Repository, https://github.com/fetchurl/sdk-python
7
+ Project-URL: Bug Tracker, https://github.com/fetchurl/sdk-python/issues
8
+ Author: lucasew
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: cache,content-addressable,fetchurl,hash,sha256
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Internet :: WWW/HTTP
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.8
19
+ Provides-Extra: test
20
+ Requires-Dist: httpx>=0.27.0; extra == 'test'
21
+ Requires-Dist: testcontainers>=3.7.0; extra == 'test'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # fetchurl Python SDK
25
+
26
+ Protocol-level client for [fetchurl](https://github.com/fetchurl/spec) content-addressable cache servers.
27
+
28
+ Zero runtime dependencies — uses only the Python standard library. Works with any HTTP library via `Fetcher` / `AsyncFetcher` protocols.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install fetchurl-sdk
34
+ # or
35
+ uv add fetchurl-sdk
36
+ ```
37
+
38
+ ## Protocol
39
+
40
+ Normative behavior: **[fetchurl/spec](https://github.com/fetchurl/spec)** (`SPEC.md`).
41
+
42
+ Reference server: **[fetchurl/fetchurl](https://github.com/fetchurl/fetchurl)**.
43
+
44
+ ## Usage
45
+
46
+ ```python
47
+ from fetchurl import fetch, UrllibFetcher, parse_fetchurl_server
48
+ import os
49
+
50
+ servers = parse_fetchurl_server(os.environ.get("FETCHURL_SERVER", ""))
51
+ # Or drive FetchSession yourself with your HTTP client — see package docstring.
52
+ ```
53
+
54
+ Clients **must** treat the server as untrusted and verify the hash (this SDK does that for you).
55
+
56
+ ## Environment
57
+
58
+ | Variable | Meaning |
59
+ |----------|---------|
60
+ | `FETCHURL_SERVER` | Server base URL(s) per the [spec](https://github.com/fetchurl/spec/blob/main/SPEC.md). Empty/absent disables server use. |
61
+
62
+ ## Development
63
+
64
+ ```bash
65
+ uv sync --dev
66
+ uv run python -m unittest test_fetchurl.py
67
+ # Integration (Docker + image):
68
+ # FETCHURL_TEST_IMAGE=fetchurl:local uv run --extra test python -m unittest test_fetchurl_integration.py
69
+ ```
70
+
71
+ ## Related
72
+
73
+ | Repo | Role |
74
+ |------|------|
75
+ | [fetchurl/spec](https://github.com/fetchurl/spec) | Protocol |
76
+ | [fetchurl/fetchurl](https://github.com/fetchurl/fetchurl) | Go server |
77
+ | [fetchurl/sdk-js](https://github.com/fetchurl/sdk-js) | JavaScript SDK |
78
+ | [fetchurl/sdk-rust](https://github.com/fetchurl/sdk-rust) | Rust SDK |
@@ -0,0 +1,55 @@
1
+ # fetchurl Python SDK
2
+
3
+ Protocol-level client for [fetchurl](https://github.com/fetchurl/spec) content-addressable cache servers.
4
+
5
+ Zero runtime dependencies — uses only the Python standard library. Works with any HTTP library via `Fetcher` / `AsyncFetcher` protocols.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install fetchurl-sdk
11
+ # or
12
+ uv add fetchurl-sdk
13
+ ```
14
+
15
+ ## Protocol
16
+
17
+ Normative behavior: **[fetchurl/spec](https://github.com/fetchurl/spec)** (`SPEC.md`).
18
+
19
+ Reference server: **[fetchurl/fetchurl](https://github.com/fetchurl/fetchurl)**.
20
+
21
+ ## Usage
22
+
23
+ ```python
24
+ from fetchurl import fetch, UrllibFetcher, parse_fetchurl_server
25
+ import os
26
+
27
+ servers = parse_fetchurl_server(os.environ.get("FETCHURL_SERVER", ""))
28
+ # Or drive FetchSession yourself with your HTTP client — see package docstring.
29
+ ```
30
+
31
+ Clients **must** treat the server as untrusted and verify the hash (this SDK does that for you).
32
+
33
+ ## Environment
34
+
35
+ | Variable | Meaning |
36
+ |----------|---------|
37
+ | `FETCHURL_SERVER` | Server base URL(s) per the [spec](https://github.com/fetchurl/spec/blob/main/SPEC.md). Empty/absent disables server use. |
38
+
39
+ ## Development
40
+
41
+ ```bash
42
+ uv sync --dev
43
+ uv run python -m unittest test_fetchurl.py
44
+ # Integration (Docker + image):
45
+ # FETCHURL_TEST_IMAGE=fetchurl:local uv run --extra test python -m unittest test_fetchurl_integration.py
46
+ ```
47
+
48
+ ## Related
49
+
50
+ | Repo | Role |
51
+ |------|------|
52
+ | [fetchurl/spec](https://github.com/fetchurl/spec) | Protocol |
53
+ | [fetchurl/fetchurl](https://github.com/fetchurl/fetchurl) | Go server |
54
+ | [fetchurl/sdk-js](https://github.com/fetchurl/sdk-js) | JavaScript SDK |
55
+ | [fetchurl/sdk-rust](https://github.com/fetchurl/sdk-rust) | Rust SDK |
@@ -242,7 +242,7 @@ class FetchSession:
242
242
 
243
243
  for server in servers:
244
244
  base = server.rstrip("/")
245
- url = f"{base}/api/fetchurl/{algo}/{hash}"
245
+ url = f"{base}/{algo}/{hash}"
246
246
  headers: dict[str, str] = {}
247
247
  if source_header:
248
248
  headers["X-Source-Urls"] = source_header
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Usage: ./make_release [next|major|minor|patch|<x.y.z>]
5
+ # Only the semver is printed on stdout; everything else goes to stderr.
6
+ MODE="${1:-next}"
7
+
8
+ case "$MODE" in
9
+ next|major|minor|patch)
10
+ VERSION="$(svu "$MODE")"
11
+ ;;
12
+ *)
13
+ VERSION="${MODE#v}"
14
+ ;;
15
+ esac
16
+ VERSION="${VERSION#v}"
17
+
18
+ sed -i "s/^version = .*/version = \"$VERSION\"/" pyproject.toml
19
+ git add pyproject.toml
20
+
21
+ git commit -sm "bump to $VERSION" 1>&2 || true
22
+ if [[ ! -v NO_TAG ]]; then
23
+ git tag "$VERSION"
24
+ git push origin "$VERSION" 1>&2
25
+ fi
26
+ git push origin HEAD 1>&2
27
+
28
+ printf '%s\n' "$VERSION"
@@ -1,3 +1,4 @@
1
1
  [tools]
2
2
  ruff = "0.14.13"
3
+ svu = "3.3.0"
3
4
  uv = "0.9.28"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fetchurl-sdk"
7
- version = "0.1.6"
7
+ version = "0.2.1"
8
8
  description = "Protocol-level client SDK for fetchurl content-addressable cache servers"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -22,9 +22,9 @@ classifiers = [
22
22
  ]
23
23
 
24
24
  [project.urls]
25
- Homepage = "https://github.com/lucasew/fetchurl"
26
- Repository = "https://github.com/lucasew/fetchurl"
27
- "Bug Tracker" = "https://github.com/lucasew/fetchurl/issues"
25
+ Homepage = "https://pypi.org/project/fetchurl-sdk/"
26
+ Repository = "https://github.com/fetchurl/sdk-python"
27
+ "Bug Tracker" = "https://github.com/fetchurl/sdk-python/issues"
28
28
 
29
29
  [project.optional-dependencies]
30
30
  test = ["testcontainers>=3.7.0", "httpx>=0.27.0"]
@@ -93,7 +93,7 @@ class TestFetchSession(unittest.TestCase):
93
93
  with self.assertRaises(fetchurl.UnsupportedAlgorithmError):
94
94
  fetchurl.FetchSession("md5", "abc", ["http://src"])
95
95
 
96
- @patch.dict(os.environ, {"FETCHURL_SERVER": '"http://cache1", "http://cache2"'})
96
+ @patch.dict(os.environ, {"FETCHURL_SERVER": '"http://cache1/api/fetchurl", "http://cache2/api/fetchurl"'})
97
97
  def test_attempt_ordering(self):
98
98
  h = sha256hex(b"test")
99
99
  session = fetchurl.FetchSession(
@@ -115,7 +115,7 @@ class TestFetchSession(unittest.TestCase):
115
115
  self.assertIsNone(session.next_attempt())
116
116
  self.assertFalse(session.succeeded())
117
117
 
118
- @patch.dict(os.environ, {"FETCHURL_SERVER": '"http://cache"'})
118
+ @patch.dict(os.environ, {"FETCHURL_SERVER": '"http://cache/api/fetchurl"'})
119
119
  def test_success_stops(self):
120
120
  h = sha256hex(b"test")
121
121
  session = fetchurl.FetchSession("sha256", h, ["http://src"])
@@ -124,7 +124,7 @@ class TestFetchSession(unittest.TestCase):
124
124
  self.assertTrue(session.succeeded())
125
125
  self.assertIsNone(session.next_attempt())
126
126
 
127
- @patch.dict(os.environ, {"FETCHURL_SERVER": '"http://cache"'})
127
+ @patch.dict(os.environ, {"FETCHURL_SERVER": '"http://cache/api/fetchurl"'})
128
128
  def test_partial_stops(self):
129
129
  h = sha256hex(b"test")
130
130
  session = fetchurl.FetchSession("sha256", h, ["http://src"])
@@ -235,7 +235,7 @@ class TestFetch(unittest.TestCase):
235
235
  try:
236
236
  out = io.BytesIO()
237
237
  # Set server via env var
238
- with patch.dict(os.environ, {"FETCHURL_SERVER": f'"{bad_url}"'}):
238
+ with patch.dict(os.environ, {"FETCHURL_SERVER": f'"{bad_url}/api/fetchurl"'}):
239
239
  fetchurl.fetch(
240
240
  fetchurl.UrllibFetcher(), "sha256", h, [good_url], out
241
241
  )
@@ -57,6 +57,7 @@ class TestIntegration(unittest.TestCase):
57
57
  DockerContainer(image_ref)
58
58
  .with_command("server")
59
59
  .with_network(net)
60
+ .with_env("FETCHURL_ALLOW_PRIVATE_IPS", "1")
60
61
  .with_exposed_ports(8080)
61
62
  ).start()
62
63
 
@@ -74,7 +75,7 @@ class TestIntegration(unittest.TestCase):
74
75
  try:
75
76
  host = server.get_container_host_ip()
76
77
  port = server.get_exposed_port(8080)
77
- os.environ["FETCHURL_SERVER"] = f"\"http://{host}:{port}\""
78
+ os.environ["FETCHURL_SERVER"] = f"\"http://{host}:{port}/api/fetchurl\""
78
79
 
79
80
  fetchurl.fetch(
80
81
  TimeoutFetcher(),
@@ -186,16 +186,36 @@ wheels = [
186
186
  { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" },
187
187
  ]
188
188
 
189
+ [[package]]
190
+ name = "docker"
191
+ version = "5.0.3"
192
+ source = { registry = "https://pypi.org/simple" }
193
+ resolution-markers = [
194
+ "python_full_version < '3.9'",
195
+ ]
196
+ dependencies = [
197
+ { name = "pywin32", version = "227", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9' and sys_platform == 'win32'" },
198
+ { name = "requests", version = "2.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
199
+ { name = "websocket-client", marker = "python_full_version < '3.9'" },
200
+ ]
201
+ sdist = { url = "https://files.pythonhosted.org/packages/ab/d2/45ea0ee13c6cffac96c32ac36db0299932447a736660537ec31ec3a6d877/docker-5.0.3.tar.gz", hash = "sha256:d916a26b62970e7c2f554110ed6af04c7ccff8e9f81ad17d0d40c75637e227fb", size = 209485, upload-time = "2021-10-07T22:54:52.22Z" }
202
+ wheels = [
203
+ { url = "https://files.pythonhosted.org/packages/54/f3/7af47ead249fbb798d64a0438bad5c26f17ef6ac5cd324d802038eb10d90/docker-5.0.3-py2.py3-none-any.whl", hash = "sha256:7a79bb439e3df59d0a72621775d600bc8bc8b422d285824cb37103eab91d1ce0", size = 146235, upload-time = "2021-10-07T22:54:50.194Z" },
204
+ ]
205
+
189
206
  [[package]]
190
207
  name = "docker"
191
208
  version = "7.1.0"
192
209
  source = { registry = "https://pypi.org/simple" }
210
+ resolution-markers = [
211
+ "python_full_version >= '3.10'",
212
+ "python_full_version >= '3.9.2' and python_full_version < '3.10'",
213
+ "python_full_version >= '3.9' and python_full_version < '3.9.2'",
214
+ ]
193
215
  dependencies = [
194
- { name = "pywin32", marker = "sys_platform == 'win32'" },
195
- { name = "requests", version = "2.32.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
216
+ { name = "pywin32", version = "311", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and sys_platform == 'win32'" },
196
217
  { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
197
- { name = "urllib3", version = "2.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
198
- { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
218
+ { name = "urllib3", marker = "python_full_version >= '3.9'" },
199
219
  ]
200
220
  sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" }
201
221
  wheels = [
@@ -217,7 +237,7 @@ wheels = [
217
237
 
218
238
  [[package]]
219
239
  name = "fetchurl-sdk"
220
- version = "0.1.0"
240
+ version = "0.2.1"
221
241
  source = { editable = "." }
222
242
 
223
243
  [package.optional-dependencies]
@@ -301,10 +321,29 @@ wheels = [
301
321
  { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" },
302
322
  ]
303
323
 
324
+ [[package]]
325
+ name = "pywin32"
326
+ version = "227"
327
+ source = { registry = "https://pypi.org/simple" }
328
+ resolution-markers = [
329
+ "python_full_version < '3.9'",
330
+ ]
331
+ wheels = [
332
+ { url = "https://files.pythonhosted.org/packages/9f/cb/d693f7cdaed51d83bfec5c77a9fa895c1a5a18cf242ba53b06b98ee366dd/pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc", size = 8355798, upload-time = "2019-11-13T23:34:51.908Z" },
333
+ { url = "https://files.pythonhosted.org/packages/cf/06/0d55292927ada3f8516e437292d85e33d6f763dd2dcc6b95f62a91ad9740/pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e", size = 9065663, upload-time = "2019-11-13T23:35:08.915Z" },
334
+ { url = "https://files.pythonhosted.org/packages/9d/c9/953c3844d046577f5617f927d5c15760ef320e01699e4fd6d214917199c1/pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295", size = 8355746, upload-time = "2019-11-13T23:35:31.998Z" },
335
+ { url = "https://files.pythonhosted.org/packages/56/74/3382d6a06050516f152ac703705794da033b92fc6385fda445631d645612/pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c", size = 9064939, upload-time = "2019-11-13T23:35:42.42Z" },
336
+ ]
337
+
304
338
  [[package]]
305
339
  name = "pywin32"
306
340
  version = "311"
307
341
  source = { registry = "https://pypi.org/simple" }
342
+ resolution-markers = [
343
+ "python_full_version >= '3.10'",
344
+ "python_full_version >= '3.9.2' and python_full_version < '3.10'",
345
+ "python_full_version >= '3.9' and python_full_version < '3.9.2'",
346
+ ]
308
347
  wheels = [
309
348
  { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" },
310
349
  { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" },
@@ -330,20 +369,14 @@ wheels = [
330
369
 
331
370
  [[package]]
332
371
  name = "requests"
333
- version = "2.32.4"
372
+ version = "2.15.1"
334
373
  source = { registry = "https://pypi.org/simple" }
335
374
  resolution-markers = [
336
375
  "python_full_version < '3.9'",
337
376
  ]
338
- dependencies = [
339
- { name = "certifi", marker = "python_full_version < '3.9'" },
340
- { name = "charset-normalizer", marker = "python_full_version < '3.9'" },
341
- { name = "idna", marker = "python_full_version < '3.9'" },
342
- { name = "urllib3", version = "2.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
343
- ]
344
- sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
377
+ sdist = { url = "https://files.pythonhosted.org/packages/6d/ed/3adebdc29ca33f11bca00c38c72125cd4a51091e13685375ba4426fb59dc/requests-2.15.1.tar.gz", hash = "sha256:e5659b9315a0610505e050bb7190bf6fa2ccee1ac295f2b760ef9d8a03ebbb2e", size = 548172, upload-time = "2017-05-27T02:14:22.414Z" }
345
378
  wheels = [
346
- { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
379
+ { url = "https://files.pythonhosted.org/packages/fa/a5/e04c4607dc96e3e6b22dfa13ba8776c64bb65cb97ab90f05a3ee14096a0a/requests-2.15.1-py2.py3-none-any.whl", hash = "sha256:ff753b2196cd18b1bbeddc9dcd5c864056599f7a7d9a4fb5677e723efa2b7fb9", size = 558730, upload-time = "2017-05-27T02:14:19.048Z" },
347
380
  ]
348
381
 
349
382
  [[package]]
@@ -359,7 +392,7 @@ dependencies = [
359
392
  { name = "certifi", marker = "python_full_version >= '3.9'" },
360
393
  { name = "charset-normalizer", marker = "python_full_version >= '3.9'" },
361
394
  { name = "idna", marker = "python_full_version >= '3.9'" },
362
- { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
395
+ { name = "urllib3", marker = "python_full_version >= '3.9'" },
363
396
  ]
364
397
  sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
365
398
  wheels = [
@@ -384,7 +417,7 @@ resolution-markers = [
384
417
  ]
385
418
  dependencies = [
386
419
  { name = "deprecation", marker = "python_full_version < '3.9'" },
387
- { name = "docker", marker = "python_full_version < '3.9'" },
420
+ { name = "docker", version = "5.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
388
421
  { name = "wrapt", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
389
422
  ]
390
423
  wheels = [
@@ -399,10 +432,10 @@ resolution-markers = [
399
432
  "python_full_version >= '3.9' and python_full_version < '3.9.2'",
400
433
  ]
401
434
  dependencies = [
402
- { name = "docker", marker = "python_full_version >= '3.9' and python_full_version < '3.9.2'" },
435
+ { name = "docker", version = "7.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.9.2'" },
403
436
  { name = "python-dotenv", marker = "python_full_version >= '3.9' and python_full_version < '3.9.2'" },
404
437
  { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.9.2'" },
405
- { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.9.2'" },
438
+ { name = "urllib3", marker = "python_full_version >= '3.9' and python_full_version < '3.9.2'" },
406
439
  { name = "wrapt", version = "2.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.9.2'" },
407
440
  ]
408
441
  sdist = { url = "https://files.pythonhosted.org/packages/d7/e5/807161552b8bf7072d63a21d5fd3c7df54e29420e325d50b9001571fcbb6/testcontainers-4.13.0.tar.gz", hash = "sha256:ee2bc39324eeeeb710be779208ae070c8373fa9058861859203f536844b0f412", size = 77824, upload-time = "2025-09-09T13:23:49.976Z" }
@@ -418,10 +451,10 @@ resolution-markers = [
418
451
  "python_full_version >= '3.9.2' and python_full_version < '3.10'",
419
452
  ]
420
453
  dependencies = [
421
- { name = "docker", marker = "python_full_version >= '3.9.2' and python_full_version < '3.10'" },
454
+ { name = "docker", version = "7.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9.2' and python_full_version < '3.10'" },
422
455
  { name = "python-dotenv", marker = "python_full_version >= '3.9.2' and python_full_version < '3.10'" },
423
456
  { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9.2' and python_full_version < '3.10'" },
424
- { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9.2' and python_full_version < '3.10'" },
457
+ { name = "urllib3", marker = "python_full_version >= '3.9.2' and python_full_version < '3.10'" },
425
458
  { name = "wrapt", version = "2.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9.2' and python_full_version < '3.10'" },
426
459
  ]
427
460
  sdist = { url = "https://files.pythonhosted.org/packages/fc/b3/c272537f3ea2f312555efeb86398cc382cd07b740d5f3c730918c36e64e1/testcontainers-4.13.3.tar.gz", hash = "sha256:9d82a7052c9a53c58b69e1dc31da8e7a715e8b3ec1c4df5027561b47e2efe646", size = 79064, upload-time = "2025-11-14T05:08:47.584Z" }
@@ -437,10 +470,10 @@ resolution-markers = [
437
470
  "python_full_version >= '3.10'",
438
471
  ]
439
472
  dependencies = [
440
- { name = "docker", marker = "python_full_version >= '3.10'" },
473
+ { name = "docker", version = "7.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
441
474
  { name = "python-dotenv", marker = "python_full_version >= '3.10'" },
442
475
  { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
443
- { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
476
+ { name = "urllib3", marker = "python_full_version >= '3.10'" },
444
477
  { name = "wrapt", version = "2.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
445
478
  ]
446
479
  sdist = { url = "https://files.pythonhosted.org/packages/8b/02/ef62dec9e4f804189c44df23f0b86897c738d38e9c48282fcd410308632f/testcontainers-4.14.1.tar.gz", hash = "sha256:316f1bb178d829c003acd650233e3ff3c59a833a08d8661c074f58a4fbd42a64", size = 80148, upload-time = "2026-01-31T23:13:46.915Z" }
@@ -476,28 +509,20 @@ wheels = [
476
509
 
477
510
  [[package]]
478
511
  name = "urllib3"
479
- version = "2.2.3"
512
+ version = "2.6.3"
480
513
  source = { registry = "https://pypi.org/simple" }
481
- resolution-markers = [
482
- "python_full_version < '3.9'",
483
- ]
484
- sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677, upload-time = "2024-09-12T10:52:18.401Z" }
514
+ sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
485
515
  wheels = [
486
- { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338, upload-time = "2024-09-12T10:52:16.589Z" },
516
+ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
487
517
  ]
488
518
 
489
519
  [[package]]
490
- name = "urllib3"
491
- version = "2.6.3"
520
+ name = "websocket-client"
521
+ version = "1.8.0"
492
522
  source = { registry = "https://pypi.org/simple" }
493
- resolution-markers = [
494
- "python_full_version >= '3.10'",
495
- "python_full_version >= '3.9.2' and python_full_version < '3.10'",
496
- "python_full_version >= '3.9' and python_full_version < '3.9.2'",
497
- ]
498
- sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
523
+ sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" }
499
524
  wheels = [
500
- { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
525
+ { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" },
501
526
  ]
502
527
 
503
528
  [[package]]
@@ -1,2 +0,0 @@
1
- .venv
2
- __pycache__
@@ -1 +0,0 @@
1
- 3.13
@@ -1,25 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: fetchurl-sdk
3
- Version: 0.1.6
4
- Summary: Protocol-level client SDK for fetchurl content-addressable cache servers
5
- Project-URL: Homepage, https://github.com/lucasew/fetchurl
6
- Project-URL: Repository, https://github.com/lucasew/fetchurl
7
- Project-URL: Bug Tracker, https://github.com/lucasew/fetchurl/issues
8
- Author: lucasew
9
- License: MIT
10
- Keywords: cache,content-addressable,fetchurl,hash,sha256
11
- Classifier: Development Status :: 3 - Alpha
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Topic :: Internet :: WWW/HTTP
16
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
- Requires-Python: >=3.8
18
- Provides-Extra: test
19
- Requires-Dist: httpx>=0.27.0; extra == 'test'
20
- Requires-Dist: testcontainers>=3.7.0; extra == 'test'
21
- Description-Content-Type: text/markdown
22
-
23
- # fetchurl
24
-
25
- Simple caching server for URLs with a hash.
@@ -1,3 +0,0 @@
1
- # fetchurl
2
-
3
- Simple caching server for URLs with a hash.