diff --git a/pyproject.toml b/pyproject.toml index cacbd4b..378df85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ dev = [ "hatch>=1.14.0", "pytest-cov>=6.0.0", - "pytest-httpx>=0.35.0", + "respx>=0.22.0", "ruff>=0.9.5", ] diff --git a/tests/test_client.py b/tests/test_client.py index f148afd..d80a8f6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,19 +1,23 @@ import json -import re from datetime import UTC, datetime +from http import HTTPMethod from unittest.mock import MagicMock import httpx import pytest -from httpx._config import DEFAULT_TIMEOUT_CONFIG +import respx from pydantic import HttpUrl -from pytest_httpx import HTTPXMock from github_issues_import.client import ApiClient, HttpClient from github_issues_import.models import IssueImportRequest, IssueImportStatus, IssueImportStatusResponse from .utils import get_fixture +RESPONSE_STATUS_MULTIPLE_ISSUES = httpx.Response( + httpx.codes.OK, + text=get_fixture("response-multiple-check-status-of-multiple-issues.json"), +) + GITHUB_TOKEN = "ghp_abc123" @@ -22,74 +26,71 @@ def api_client(): return ApiClient(http_client=HttpClient(token=GITHUB_TOKEN)) -def test_init(api_client: ApiClient): +def test_simple_client_init(api_client: ApiClient): assert api_client -def test_timeout_default(httpx_mock: HTTPXMock): - httpx_mock.add_response( - match_extensions={ - "timeout": { - "connect": DEFAULT_TIMEOUT_CONFIG.connect, - "read": DEFAULT_TIMEOUT_CONFIG.read, - "write": DEFAULT_TIMEOUT_CONFIG.write, - "pool": DEFAULT_TIMEOUT_CONFIG.pool, - } - }, - status_code=httpx.codes.BAD_GATEWAY, - ) - with pytest.raises(httpx.HTTPStatusError): - ApiClient(http_client=HttpClient(token=GITHUB_TOKEN)).get_status_multiple("foo", "bar", datetime.now(tz=UTC)) +def test_base_url_override(respx_mock: respx.mock): + respx_mock.get(url="https://test/repos/foo/bar/import/issues").mock(return_value=RESPONSE_STATUS_MULTIPLE_ISSUES) + api_client = ApiClient(http_client=HttpClient(token=GITHUB_TOKEN, base_url="https://test")) + api_client.get_status_multiple("foo", "bar", datetime.now(tz=UTC)) -def test_timeout_set(httpx_mock: HTTPXMock): - httpx_mock.add_response( - match_extensions={"timeout": {"connect": 123, "read": 123, "write": 123, "pool": 123}}, - status_code=httpx.codes.BAD_GATEWAY, - ) - with pytest.raises(httpx.HTTPStatusError): - ApiClient(http_client=HttpClient(token=GITHUB_TOKEN, timeout=123)).get_status_multiple( - "foo", "bar", datetime.now(tz=UTC) - ) +def test_headers_override(respx_mock: respx.mock): + respx_mock.get(headers={"Foo": "Bar"}).mock(return_value=RESPONSE_STATUS_MULTIPLE_ISSUES) + + api_client = ApiClient(http_client=HttpClient(token=GITHUB_TOKEN, headers={"Foo": "Bar"})) + api_client.get_status_multiple("foo", "bar", datetime.now(tz=UTC)) + + assert "Authorization" not in respx_mock.calls.last.request.headers -def test_base_url(httpx_mock: HTTPXMock): - httpx_mock.add_response( - url=re.compile(r"^https://test/repos/foo/bar/import/issues"), - text=get_fixture("response-multiple-check-status-of-multiple-issues.json"), - ) - ApiClient(http_client=HttpClient(token=GITHUB_TOKEN, base_url="https://test")).get_status_multiple( - "foo", "bar", datetime.now(tz=UTC) - ) +def test_event_hooks_override(monkeypatch, respx_mock: respx.mock): + respx_mock.get().mock(return_value=RESPONSE_STATUS_MULTIPLE_ISSUES) -def test_event_hooks(monkeypatch, httpx_mock: HTTPXMock): - httpx_mock.add_response(text=get_fixture("response-multiple-check-status-of-multiple-issues.json")) + mock_log_request, mock_log_response = MagicMock(), MagicMock() + monkeypatch.setattr(HttpClient, "log_github_api_request", mock_log_request) + monkeypatch.setattr(HttpClient, "log_github_api_response", mock_log_response) - mock = MagicMock() - monkeypatch.setattr(HttpClient, "log_github_api_request", mock) + api_client = ApiClient(http_client=HttpClient(token=GITHUB_TOKEN, base_url="https://test", event_hooks={})) + api_client.get_status_multiple("foo", "bar", datetime.now(tz=UTC)) - ApiClient(http_client=HttpClient(token=GITHUB_TOKEN, base_url="https://test", event_hooks={})).get_status_multiple( - "foo", "bar", datetime.now(tz=UTC) + mock_log_request.assert_not_called() + mock_log_response.assert_not_called() + + +def test_default_event_hooks(monkeypatch, respx_mock: respx.mock): + respx_mock.post("https://api.github.com/repos/foo/bar/import/issues").mock( + return_value=httpx.Response(httpx.codes.BAD_GATEWAY) ) - mock.assert_not_called() + mock_log_request, mock_log_response = MagicMock(), MagicMock() + monkeypatch.setattr(HttpClient, "log_github_api_request", mock_log_request) + monkeypatch.setattr(HttpClient, "log_github_api_response", mock_log_response) + + api_client = ApiClient(http_client=HttpClient(token=GITHUB_TOKEN)) -def test_raise_for_status(api_client: ApiClient, httpx_mock: HTTPXMock): - httpx_mock.add_response(status_code=httpx.codes.BAD_GATEWAY) - with pytest.raises(httpx.HTTPError): + with pytest.raises(httpx.HTTPStatusError): api_client.import_issue( "foo", "bar", IssueImportRequest.model_validate_json(get_fixture("request-issue-and-comment-fields.json")), ) + mock_log_request.assert_called() + mock_log_response.assert_called() + -def test_import_issue(api_client: ApiClient, httpx_mock: HTTPXMock): +def test_import_issue(api_client: ApiClient, respx_mock: respx.mock): import_response = get_fixture("response-single-start-an-issue-import.json") import_request = get_fixture("request-start-issue-import.json") - httpx_mock.add_response(text=import_response) + respx_mock.route( + method=HTTPMethod.POST, + url="https://api.github.com/repos/owner/repository/import/issues", + headers={"Authorization": f"Token {GITHUB_TOKEN}"} | HttpClient.HEADERS, + ).mock(return_value=httpx.Response(httpx.codes.ACCEPTED, text=import_response)) response = api_client.import_issue( "owner", @@ -100,36 +101,28 @@ def test_import_issue(api_client: ApiClient, httpx_mock: HTTPXMock): assert response == IssueImportStatusResponse.model_validate_json(import_response) assert response.status == IssueImportStatus.PENDING - request = httpx_mock.get_request() - assert request.url == "https://api.github.com/repos/owner/repository/import/issues" - assert request.headers["Authorization"] == f"Token {GITHUB_TOKEN}" - assert json.loads(request.content) == json.loads(import_request) - - -def test_get_import_status(api_client: ApiClient, httpx_mock: HTTPXMock): +def test_get_import_status(api_client: ApiClient, respx_mock: respx.mock): import_status_response = get_fixture("response-single-check-status-of-issue-import.json") - httpx_mock.add_response( - url="https://api.github.com/repos/jonmagic/foo/import/issues/3", - text=import_status_response, + respx_mock.get("https://api.github.com/repos/jonmagic/foo/import/issues/3").mock( + return_value=httpx.Response(httpx.codes.OK, text=import_status_response) ) response = api_client.get_status(HttpUrl("https://api.github.com/repos/jonmagic/foo/import/issues/3")) assert response == IssueImportStatusResponse.model_validate_json(import_status_response) -def test_get_import_status_multiple(api_client: ApiClient, httpx_mock: HTTPXMock): +def test_get_import_status_multiple(api_client: ApiClient, respx_mock: respx.mock): multiple_status_response = get_fixture("response-multiple-check-status-of-multiple-issues.json") since = datetime.now(tz=UTC) - httpx_mock.add_response( - url=httpx.URL( + respx_mock.get( + httpx.URL( "https://api.github.com/repos/foo/bar/import/issues", params={"since": since.isoformat()}, - ), - text=multiple_status_response, - ) + ) + ).mock(return_value=RESPONSE_STATUS_MULTIPLE_ISSUES) response = api_client.get_status_multiple("foo", "bar", since) assert response == [IssueImportStatusResponse.model_validate(json.loads(multiple_status_response)[0])] diff --git a/uv.lock b/uv.lock index 2ef3b30..066eb7f 100644 --- a/uv.lock +++ b/uv.lock @@ -221,7 +221,7 @@ dependencies = [ dev = [ { name = "hatch" }, { name = "pytest-cov" }, - { name = "pytest-httpx" }, + { name = "respx" }, { name = "ruff" }, ] @@ -235,7 +235,7 @@ requires-dist = [ dev = [ { name = "hatch", specifier = ">=1.14.0" }, { name = "pytest-cov", specifier = ">=6.0.0" }, - { name = "pytest-httpx", specifier = ">=0.35.0" }, + { name = "respx", specifier = ">=0.22.0" }, { name = "ruff", specifier = ">=0.9.5" }, ] @@ -624,25 +624,24 @@ wheels = [ ] [[package]] -name = "pytest-httpx" -version = "0.35.0" +name = "pywin32-ctypes" +version = "0.2.3" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1f/89/5b12b7b29e3d0af3a4b9c071ee92fa25a9017453731a38f08ba01c280f4c/pytest_httpx-0.35.0.tar.gz", hash = "sha256:d619ad5d2e67734abfbb224c3d9025d64795d4b8711116b1a13f72a251ae511f", size = 54146 } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/ed/026d467c1853dd83102411a78126b4842618e86c895f93528b0528c7a620/pytest_httpx-0.35.0-py3-none-any.whl", hash = "sha256:ee11a00ffcea94a5cbff47af2114d34c5b231c326902458deed73f9c459fd744", size = 19442 }, + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 }, ] [[package]] -name = "pywin32-ctypes" -version = "0.2.3" +name = "respx" +version = "0.22.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 }, + { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127 }, ] [[package]] @@ -773,11 +772,11 @@ wheels = [ [[package]] name = "trove-classifiers" -version = "2025.1.15.22" +version = "2025.2.18.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/cb/8f6a91c74049180e395590901834d68bef5d6a2ce4c9ca9792cfadc1b9b4/trove_classifiers-2025.1.15.22.tar.gz", hash = "sha256:90af74358d3a01b3532bc7b3c88d8c6a094c2fd50a563d13d9576179326d7ed9", size = 16236 } +sdist = { url = "https://files.pythonhosted.org/packages/13/8e/15ba2980e2704edecc53d15506a5bfa6efb3b1cadc5e4df7dc277bc199f8/trove_classifiers-2025.2.18.16.tar.gz", hash = "sha256:b1ee2e1668589217d4edf506743e28b1834da128f8a122bad522c02d837006e1", size = 16271 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/c5/6422dbc59954389b20b2aba85b737ab4a552e357e7ea14b52f40312e7c84/trove_classifiers-2025.1.15.22-py3-none-any.whl", hash = "sha256:5f19c789d4f17f501d36c94dbbf969fb3e8c2784d008e6f5164dd2c3d6a2b07c", size = 13610 }, + { url = "https://files.pythonhosted.org/packages/e1/67/038a8c7f60ffd6037374649826dbaa221e4b17755016b71a581162a15ce1/trove_classifiers-2025.2.18.16-py3-none-any.whl", hash = "sha256:7f6dfae899f23f04b73bc09e0754d9219a6fc4d6cca6acd62f1850a87ea92262", size = 13616 }, ] [[package]] @@ -803,27 +802,27 @@ wheels = [ [[package]] name = "uv" -version = "0.5.31" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/67/04/63265828848c2ca60e322408ed529587f670ee97c5607114df08c389398a/uv-0.5.31.tar.gz", hash = "sha256:59c4c6e3704208a8dd5e8d51b79ec995db18a64bd3ff88fd239ca433fbaf1694", size = 2875508 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/80/58942f09e0a38fdef37d54d553175e3c195da32547711c78dcc70876f2ce/uv-0.5.31-py3-none-linux_armv6l.whl", hash = "sha256:ba5707a6e363284ba1acd29ae9e70e2377ed31e272b953069798c444bae847ef", size = 15475386 }, - { url = "https://files.pythonhosted.org/packages/a2/ed/1605df7bd74eac86975a48e16a76ae04feedc9d27dc841e8d4f3c00a790f/uv-0.5.31-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3169a373d0d41571a7b9d4a442f875f6e26250693ced7779f62461f52ba1da64", size = 15608043 }, - { url = "https://files.pythonhosted.org/packages/1f/5a/1eb42f481a9f9010c8c194d70ab375a6eda96d67ca1fd011bf869d4016c8/uv-0.5.31-py3-none-macosx_11_0_arm64.whl", hash = "sha256:335c16f91b46b4f4a3b31c18cf112a0643d59d4c1708a177103621da0addbaef", size = 14523527 }, - { url = "https://files.pythonhosted.org/packages/9a/05/9817ea1f0d8e7134ed60abeefa7bdc602f4a4a6e2ccdf2760b54fb3dcef3/uv-0.5.31-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:cedceefebf2123b514464671d0544a8db126071c2d56dbc10d408b8222939e6a", size = 14940294 }, - { url = "https://files.pythonhosted.org/packages/e4/2d/aee8e68026057c6db71424e3a312d739af8838ae35321bfa1f5900e93d1c/uv-0.5.31-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7233182a2b8226011562341f05aaee19925b48730fccdb2e7ee20e31a84f12db", size = 15211133 }, - { url = "https://files.pythonhosted.org/packages/58/cf/16c3b71c903e7d8c3aeb0b85efbf2efb4694ffeab72165d7d9166bf2d497/uv-0.5.31-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ce4dc079fd5ddf1946e6085b6ece126ce7c4be23ba27e4010aa68fdec004191", size = 15943734 }, - { url = "https://files.pythonhosted.org/packages/7c/28/8421b94710581c81a9240df95f04b87cfffd5da229eb178733acb6d1a6de/uv-0.5.31-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:007576e1b62268d4a21d4a375d43ff5ae3698313a11f7702c8e7cb5bd29d7f1b", size = 16890117 }, - { url = "https://files.pythonhosted.org/packages/be/9c/a3d4318aebbc68158dc069d3f8de423d56ec3a38017401e92e9e37fe5afc/uv-0.5.31-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51d8287cdb760ea8c44b374cb96a59fae2292f1b3e18e228f7ed817d2bd96243", size = 16623168 }, - { url = "https://files.pythonhosted.org/packages/dd/b1/32a5e1239eca3915bec3825dab8c635f80c64b09ae46cf03d1bef7641892/uv-0.5.31-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27ce8f3eecd281a6ec255644a328b60eb10044e506a46be931db7bbfe8db89ab", size = 20939390 }, - { url = "https://files.pythonhosted.org/packages/ce/2e/0c3ac2f5be92492cbe735de7f66a83b2d3e22bd59554deaa0106562cba45/uv-0.5.31-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d07e9db12a55005a28bb49ecfa444a0221702158fc021f79e26d8e174f1ebdf9", size = 16293460 }, - { url = "https://files.pythonhosted.org/packages/cc/de/59e6665d9f1d4fc93c0b3383eaf31dbf7088cf8fce5c239b5eb8f0bf911b/uv-0.5.31-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:8acf6bcb0c0c27e1a157926f35dc70b1c7620c1a2e1124ffacdbf21c78265761", size = 15234496 }, - { url = "https://files.pythonhosted.org/packages/32/14/e69d04bc77f73a34d2d850d60cf21ded8cf0f3481302ea31533ad5a64733/uv-0.5.31-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:a8f27ea8441ce9de43a6af4825d2b936030a0a6864c608f1015db30e9f5f9cdb", size = 15212989 }, - { url = "https://files.pythonhosted.org/packages/99/29/1afb24345ffa6dd351170adc9b30d8a3855c47a2b85f093f28b7366c2a6d/uv-0.5.31-py3-none-musllinux_1_1_i686.whl", hash = "sha256:e6b5a29c29e774525baf982f570c53e8862f19e3f7e74bd819c7b3749f4cdfa0", size = 15554448 }, - { url = "https://files.pythonhosted.org/packages/5a/5f/784cbe68aa0c291587a3735a61372dc02521780ccd0f0058f159a451df19/uv-0.5.31-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:15109a938c56ee1e1c997b291743812af3ea1d7547b0929569494c359082a993", size = 16405791 }, - { url = "https://files.pythonhosted.org/packages/9f/80/458b8f67e41dddc3c6ca1515ea8136c217a52b92dedd8c53f9eb00287d22/uv-0.5.31-py3-none-win32.whl", hash = "sha256:f2161ef8b9a0308f05dd4a3eb2c1d104301e23c699fab5898e9fc38387690e4b", size = 15602489 }, - { url = "https://files.pythonhosted.org/packages/4c/50/f3f89c6bd27aae15ca3150b839c9d8f5d32a9a19a6eae3daa6d9aae1de4f/uv-0.5.31-py3-none-win_amd64.whl", hash = "sha256:bcc57b75883516233658ff1daee0d17347a8b872f717a1644d36e8ea2b021f45", size = 16895932 }, - { url = "https://files.pythonhosted.org/packages/12/64/af4aa07bc1c525b1fefd1686d31a43a74eac51e74046755ffdca4502784d/uv-0.5.31-py3-none-win_arm64.whl", hash = "sha256:51ceab5a128dd22bcd62489107563e10084e13ed9c15107193c2d7d1139979f4", size = 15776619 }, +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/23/5943c4deef095a3200ba6769a6261d6a1f4f882d4e7acf3bfc3748be0e83/uv-0.6.1.tar.gz", hash = "sha256:7b371891dc6fd174bb3bc3889ff42aa4e07e59fb970b47aa980d8b403e3457a9", size = 2900879 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/a8/30c112d27991b3d84d90c32a2015cebf756f7079917981c6c4694036f310/uv-0.6.1-py3-none-linux_armv6l.whl", hash = "sha256:f9e3a59deb1dfcab1f62ba7585bcd066d780177ea2f7de7f382921d76c580323", size = 15482693 }, + { url = "https://files.pythonhosted.org/packages/65/ae/883cf40b22e043ec861fa0a86c770a27720018b1a1cb4ffbe4b681efe678/uv-0.6.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e50e5209d86255a92bfb453bcd4399f6f47a7cfbb6d1ca6facf83edf97d10805", size = 15650522 }, + { url = "https://files.pythonhosted.org/packages/df/b4/e578a93423f05313fe4be58b5239debc7e4b3675209e5f6380e01cf8a031/uv-0.6.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:be98d181a1a72b1ee7126c3de926eb8d71f5391fd0482371616b46ddb49366e6", size = 14535024 }, + { url = "https://files.pythonhosted.org/packages/c4/bf/661205cb9e2c951c6a2724c1e3a48c17b37e588048188c745a823e78de83/uv-0.6.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:e72c31fe02f2e5856d515b12788ff6d7bfd987bfe4e16599682cb2352a7a2cc0", size = 14995266 }, + { url = "https://files.pythonhosted.org/packages/b3/4e/fb9be5d7b376769f2e30c02e15197e70bab63cf4672ecb628b7f61308ed2/uv-0.6.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:005b2dcca113a9f1537583ada24752287b1ddf831f1add9a7099fac259bf269c", size = 15243176 }, + { url = "https://files.pythonhosted.org/packages/13/04/3e3a2915f9255b121fd5aea1e3ab1e662be966fa08736a3246413084a0c5/uv-0.6.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b52f9e35858c30bc866d07c595d007c6ec713e195174ca9ec2b1806c2c9a8f22", size = 15965307 }, + { url = "https://files.pythonhosted.org/packages/dc/15/3cf0b18b5beb5610740cfdf98d15f41fc527736fff1fc497bcd64fb0cf24/uv-0.6.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:01fa8449ab1d96951711c3073a7d93ae2ccd333cc2141fc30873e720e9aeea34", size = 16949163 }, + { url = "https://files.pythonhosted.org/packages/93/9b/24f50a7d6066d00309c9fc45abba75b8169f0db99fded2437c52cbfad824/uv-0.6.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6cd4ddf03cce1e963120dbb0b1168ba6c6d4d462019947fdecc64b2596aaae6", size = 16660405 }, + { url = "https://files.pythonhosted.org/packages/87/86/c6a13e2bae4ffbd7237cc464e961ef7c99d843d1212c8a14ed8dcc0f0e58/uv-0.6.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e550f08e538260b6d9caae9a4e1999de04f03487f11b6fcdc718d1ae2ba1a5e9", size = 21041049 }, + { url = "https://files.pythonhosted.org/packages/b0/0e/0ce260278b3a311131dc05315745544c4ddf6b1d7ac640aeb4212476312e/uv-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62ef67f173ced2e1beb5e578db08e0e057ee5f21e0d74f09b27f7b43b143fe2a", size = 16300666 }, + { url = "https://files.pythonhosted.org/packages/dc/ae/c17a8c41ccd152161ceb77f3c85c7d8e0c8db8637752803461d5113f74b2/uv-0.6.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c13bb592a1b0f1bcbaf3235f634e7a35bd9840ca497020792fbcc96185174fb4", size = 15268966 }, + { url = "https://files.pythonhosted.org/packages/8f/7e/cc283bd1cd9239bfe94b49db69cf7a0ba310231c2cab8a326885d5bc6684/uv-0.6.1-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:183ee2ec50197e584d24421966acf083cf5e9048489eebcb9ee7532d4ecf65d1", size = 15219571 }, + { url = "https://files.pythonhosted.org/packages/24/f0/952efdc68930f341d419def9dcd5a939fb95d5d76a497693988eba7ceb93/uv-0.6.1-py3-none-musllinux_1_1_i686.whl", hash = "sha256:636c4570e5e39522ba2b8531fbcb5cddcc3fd74c8e733039b9a3ca29693f6e26", size = 15589726 }, + { url = "https://files.pythonhosted.org/packages/e9/74/2b9f9b774c7573689e7f34e4834b1b643caf8aaa47d1a1c2067fcb8da24c/uv-0.6.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d8f21240fbcff0d27a38ee04348a62ee2e25c3ebd4741b18a6bd3dd6891eec5a", size = 16397687 }, + { url = "https://files.pythonhosted.org/packages/c3/64/45d2d100cb66f7750ca555856cddf28d98f8670c619889bef3973e3a46ac/uv-0.6.1-py3-none-win32.whl", hash = "sha256:1876319693e52e15ff72b17cb06a3036cd659d47eeda52e82867017c5a09b6b3", size = 15602366 }, + { url = "https://files.pythonhosted.org/packages/fd/ce/7002f0ca79f440f31c2cc393fcb94109b1d48c714d5ff63bbfedd92b3b50/uv-0.6.1-py3-none-win_amd64.whl", hash = "sha256:e5ba927dcbb90d241acbd8ac6181ef104269875f8e4d4fb69286cb356a173282", size = 16954542 }, + { url = "https://files.pythonhosted.org/packages/ac/7f/2888fa3155789bd7fadbe89b372afeb4e1ebb304905839578d1871dcf6f3/uv-0.6.1-py3-none-win_arm64.whl", hash = "sha256:8bd37eb73cb8b7f6bc862c27b9c79ef3c4f809775f3c62f3e8ba6872bf211c22", size = 15826495 }, ] [[package]]