mirror of https://github.com/astral-sh/uv
Add an integration test for publishing to pyx (#16740)
This commit is contained in:
parent
512c0ca5ed
commit
f78ddf05c4
|
|
@ -1991,6 +1991,7 @@ jobs:
|
||||||
UV_TEST_PUBLISH_GITLAB_PAT: ${{ secrets.UV_TEST_PUBLISH_GITLAB_PAT }}
|
UV_TEST_PUBLISH_GITLAB_PAT: ${{ secrets.UV_TEST_PUBLISH_GITLAB_PAT }}
|
||||||
UV_TEST_PUBLISH_CODEBERG_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CODEBERG_TOKEN }}
|
UV_TEST_PUBLISH_CODEBERG_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CODEBERG_TOKEN }}
|
||||||
UV_TEST_PUBLISH_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CLOUDSMITH_TOKEN }}
|
UV_TEST_PUBLISH_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CLOUDSMITH_TOKEN }}
|
||||||
|
UV_TEST_PUBLISH_PYX_TOKEN: ${{ secrets.UV_TEST_PUBLISH_PYX_TOKEN }}
|
||||||
UV_TEST_PUBLISH_PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
|
UV_TEST_PUBLISH_PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
integration-uv-build-backend:
|
integration-uv-build-backend:
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ Web: https://codeberg.org/astral-test-user/-/packages/pypi/astral-test-token/0.1
|
||||||
Docs: https://forgejo.org/docs/latest/user/packages/pypi/
|
Docs: https://forgejo.org/docs/latest/user/packages/pypi/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
|
@ -166,15 +167,30 @@ local_targets: dict[str, TargetConfiguration] = {
|
||||||
"https://python.cloudsmith.io/astral-test/astral-test-1/",
|
"https://python.cloudsmith.io/astral-test/astral-test-1/",
|
||||||
"https://dl.cloudsmith.io/public/astral-test/astral-test-1/python/simple/",
|
"https://dl.cloudsmith.io/public/astral-test/astral-test-1/python/simple/",
|
||||||
),
|
),
|
||||||
|
"pyx-token": TargetConfiguration(
|
||||||
|
"astral-test-token",
|
||||||
|
"https://astral-sh-staging-api.pyx.dev/v1/upload/uv-publish-integration/main",
|
||||||
|
"https://astral-sh-staging-api.pyx.dev/simple/uv-publish-integration/main/",
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
all_targets: dict[str, TargetConfiguration] = local_targets | {
|
all_targets: dict[str, TargetConfiguration] = local_targets | {
|
||||||
"pypi-trusted-publishing": TargetConfiguration(
|
"pypi-trusted-publishing": TargetConfiguration(
|
||||||
"astral-test-trusted-publishing",
|
"astral-test-trusted-publishing",
|
||||||
TEST_PYPI_PUBLISH_URL,
|
TEST_PYPI_PUBLISH_URL,
|
||||||
"https://test.pypi.org/simple/",
|
"https://test.pypi.org/simple/",
|
||||||
)
|
),
|
||||||
|
# TODO: Not enabled until we have a native Trusted Publishing flow for pyx in uv.
|
||||||
|
# "pyx-trusted-publishing": TargetConfiguration(
|
||||||
|
# "astral-test-trusted-publishing",
|
||||||
|
# "https://api.pyx.dev/v1/upload/astral-test/main",
|
||||||
|
# "https://api.pyx.dev/simple/astral-test/main",
|
||||||
|
# ),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Temporarily disable codeberg on CI due to unreliability.
|
||||||
|
all_targets.pop("codeberg", None)
|
||||||
|
|
||||||
|
|
||||||
def get_latest_version(target: str, client: httpx.Client) -> Version | None:
|
def get_latest_version(target: str, client: httpx.Client) -> Version | None:
|
||||||
"""Return the latest version on all indexes of the package."""
|
"""Return the latest version on all indexes of the package."""
|
||||||
|
|
@ -235,7 +251,8 @@ def collect_versions(url: str, client: httpx.Client) -> set[Version]:
|
||||||
|
|
||||||
def get_filenames(url: str, client: httpx.Client) -> list[str]:
|
def get_filenames(url: str, client: httpx.Client) -> list[str]:
|
||||||
"""Get the filenames (source dists and wheels) from an index URL."""
|
"""Get the filenames (source dists and wheels) from an index URL."""
|
||||||
response = client.get(url)
|
response = client.get(url, follow_redirects=True)
|
||||||
|
response.raise_for_status()
|
||||||
data = response.text
|
data = response.text
|
||||||
# Works for the indexes in the list
|
# Works for the indexes in the list
|
||||||
href_text = r"<a(?:\s*[\w-]+=(?:'[^']+'|\"[^\"]+\"))* *>([^<>]+)</a>"
|
href_text = r"<a(?:\s*[\w-]+=(?:'[^']+'|\"[^\"]+\"))* *>([^<>]+)</a>"
|
||||||
|
|
@ -305,6 +322,7 @@ def wait_for_index(
|
||||||
project_name: str,
|
project_name: str,
|
||||||
version: Version,
|
version: Version,
|
||||||
uv: Path,
|
uv: Path,
|
||||||
|
env: dict,
|
||||||
):
|
):
|
||||||
"""Check that the index URL was updated, wait up to 100s if necessary.
|
"""Check that the index URL was updated, wait up to 100s if necessary.
|
||||||
|
|
||||||
|
|
@ -333,6 +351,7 @@ def wait_for_index(
|
||||||
text=True,
|
text=True,
|
||||||
input=f"{project_name}",
|
input=f"{project_name}",
|
||||||
stdout=PIPE,
|
stdout=PIPE,
|
||||||
|
env=env,
|
||||||
)
|
)
|
||||||
# codeberg sometimes times out
|
# codeberg sometimes times out
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
|
|
@ -368,6 +387,13 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
|
||||||
2. If we're using PyPI, uploading the same files again succeeds.
|
2. If we're using PyPI, uploading the same files again succeeds.
|
||||||
3. Check URL works and reports the files as skipped.
|
3. Check URL works and reports the files as skipped.
|
||||||
"""
|
"""
|
||||||
|
# If we're publishing to pyx, we need to give the httpx client
|
||||||
|
# access to an appropriate credential.
|
||||||
|
if target == "pyx-token":
|
||||||
|
client.auth = httpx.BasicAuth(
|
||||||
|
username="__token__", password=os.environ["UV_TEST_PUBLISH_PYX_TOKEN"]
|
||||||
|
)
|
||||||
|
|
||||||
project_name = all_targets[target].project_name
|
project_name = all_targets[target].project_name
|
||||||
|
|
||||||
# If a version was recently uploaded by another run of this script,
|
# If a version was recently uploaded by another run of this script,
|
||||||
|
|
@ -423,7 +449,7 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
|
||||||
f"\n=== 2. Publishing {project_name} {version} again (PyPI) ===",
|
f"\n=== 2. Publishing {project_name} {version} again (PyPI) ===",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
wait_for_index(index_url, project_name, version, uv)
|
wait_for_index(index_url, project_name, version, uv, env)
|
||||||
args = [uv, "publish", "--publish-url", publish_url, *extra_args]
|
args = [uv, "publish", "--publish-url", publish_url, *extra_args]
|
||||||
output = run(
|
output = run(
|
||||||
args, cwd=project_dir, env=env, text=True, check=True, stderr=PIPE
|
args, cwd=project_dir, env=env, text=True, check=True, stderr=PIPE
|
||||||
|
|
@ -444,7 +470,7 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
|
||||||
f"\n=== 3. Publishing {project_name} {version} again with {mode} ===",
|
f"\n=== 3. Publishing {project_name} {version} again with {mode} ===",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
wait_for_index(index_url, project_name, version, uv)
|
wait_for_index(index_url, project_name, version, uv, env)
|
||||||
# Test twine-style and index-style uploads for different packages.
|
# Test twine-style and index-style uploads for different packages.
|
||||||
if index := all_targets[target].index:
|
if index := all_targets[target].index:
|
||||||
args = [
|
args = [
|
||||||
|
|
@ -487,7 +513,7 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
|
||||||
f"again with skip existing (error test) ===",
|
f"again with skip existing (error test) ===",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
wait_for_index(index_url, project_name, version, uv)
|
wait_for_index(index_url, project_name, version, uv, env)
|
||||||
args = [
|
args = [
|
||||||
uv,
|
uv,
|
||||||
"publish",
|
"publish",
|
||||||
|
|
@ -540,12 +566,25 @@ def target_configuration(target: str) -> tuple[dict[str, str], list[str]]:
|
||||||
env = {
|
env = {
|
||||||
"UV_PUBLISH_TOKEN": os.environ["UV_TEST_PUBLISH_CLOUDSMITH_TOKEN"],
|
"UV_PUBLISH_TOKEN": os.environ["UV_TEST_PUBLISH_CLOUDSMITH_TOKEN"],
|
||||||
}
|
}
|
||||||
|
elif target == "pyx-token":
|
||||||
|
extra_args = []
|
||||||
|
env = {
|
||||||
|
"UV_API_KEY": os.environ["UV_TEST_PUBLISH_PYX_TOKEN"],
|
||||||
|
# So that uv accesses the right API for check-url.
|
||||||
|
"PYX_API_URL": "https://astral-sh-staging-api.pyx.dev",
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown target: {target}")
|
raise ValueError(f"Unknown target: {target}")
|
||||||
return env, extra_args
|
return env, extra_args
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
logging.basicConfig(
|
||||||
|
format="%(levelname)s [%(asctime)s] %(name)s - %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
level=logging.DEBUG,
|
||||||
|
)
|
||||||
|
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
target_choices = [*all_targets, "local", "all"]
|
target_choices = [*all_targets, "local", "all"]
|
||||||
parser.add_argument("targets", choices=target_choices, nargs="+")
|
parser.add_argument("targets", choices=target_choices, nargs="+")
|
||||||
|
|
@ -568,8 +607,10 @@ def main():
|
||||||
else:
|
else:
|
||||||
targets = args.targets
|
targets = args.targets
|
||||||
|
|
||||||
with httpx.Client(timeout=120) as client:
|
|
||||||
for project_name in targets:
|
for project_name in targets:
|
||||||
|
# Each publish gets its own client, since we may need to introduce
|
||||||
|
# target-specific authentication.
|
||||||
|
with httpx.Client(timeout=120) as client:
|
||||||
publish_project(project_name, uv, client)
|
publish_project(project_name, uv, client)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
version = 1
|
version = 1
|
||||||
|
revision = 3
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
[manifest]
|
[manifest]
|
||||||
|
|
@ -9,47 +10,47 @@ requirements = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
version = "4.8.0"
|
version = "4.11.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "idna" },
|
{ name = "idna" },
|
||||||
{ name = "sniffio" },
|
{ name = "sniffio" },
|
||||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
|
sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 },
|
{ url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2024.12.14"
|
version = "2025.11.12"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 }
|
sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 },
|
{ url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h11"
|
name = "h11"
|
||||||
version = "0.14.0"
|
version = "0.16.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
|
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
|
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httpcore"
|
name = "httpcore"
|
||||||
version = "1.0.7"
|
version = "1.0.9"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "certifi" },
|
{ name = "certifi" },
|
||||||
{ name = "h11" },
|
{ name = "h11" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
|
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
|
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -62,43 +63,43 @@ dependencies = [
|
||||||
{ name = "httpcore" },
|
{ name = "httpcore" },
|
||||||
{ name = "idna" },
|
{ name = "idna" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
|
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
|
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.10"
|
version = "3.11"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
|
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "24.2"
|
version = "24.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
|
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
|
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
|
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
|
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.12.2"
|
version = "4.15.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
|
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue