diff --git a/docs/features.md b/docs/features.md index 61067d4..1f13339 100644 --- a/docs/features.md +++ b/docs/features.md @@ -78,7 +78,7 @@ discussion and subject to change. ::: If you maintain a conda channel, you can now serve Python wheels directly -alongside regular conda packages. Add your wheels to a `packages.whl` section +alongside regular conda packages. Add your wheels to a `v3.whl` section in `repodata.json` and point each entry at the wheel URL — `conda install` will pick them up, resolve their dependencies, and extract them correctly, with no pre-conversion step required. diff --git a/pyproject.toml b/pyproject.toml index 7ff76fa..5ff0b15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ python = ">=3.10" conda = ">=26.1" conda-index = ">=0.7.0" conda-package-streaming = ">=0.11" -conda-rattler-solver = ">=0.0.5" +conda-rattler-solver = ">=0.0.6" packaging = "*" pip = "*" unearth = "*" diff --git a/tests/conda_local_channel/generate_noarch_wheel_repodata.py b/tests/conda_local_channel/generate_noarch_wheel_repodata.py index d531a44..b7c1fbd 100644 --- a/tests/conda_local_channel/generate_noarch_wheel_repodata.py +++ b/tests/conda_local_channel/generate_noarch_wheel_repodata.py @@ -1,15 +1,60 @@ -# This is a utility for generating test specific data in conda-pypi -# only. It is not appropriate to use this to generate production level -# repodata. +""" +Utility for generating test-specific local channel repodata. + +This is test data generation logic for conda-pypi only; it is not intended for +production repodata generation. + +Marker conversion policy for this test channel: +- Convert Python markers to `python...` matchspec fragments, including + `python_version not in "x, y"` -> `(python!=x and python!=y)`. +- Convert platform/os markers to virtual packages when feasible + (`__win`, `__linux`, `__osx`, `__unix`). +- Keep extras in `extra_depends`, with remaining non-extra marker logic + encoded via `[when="..."]`. +- Drop unsupported marker dimensions (for example interpreter/machine-specific + variants) for these noarch channel tests. +""" import json -import re import requests from concurrent.futures import ThreadPoolExecutor, as_completed +from enum import StrEnum +from packaging.markers import Marker from packaging.requirements import Requirement from typing import Any -EXTRA_MARKER_RE = re.compile(r'extra\s*==\s*["\']([^"\']+)["\']') + +class MarkerVar(StrEnum): + PYTHON_VERSION = "python_version" + PYTHON_FULL_VERSION = "python_full_version" + EXTRA = "extra" + SYS_PLATFORM = "sys_platform" + PLATFORM_SYSTEM = "platform_system" + OS_NAME = "os_name" + IMPLEMENTATION_NAME = "implementation_name" + PLATFORM_PYTHON_IMPLEMENTATION = "platform_python_implementation" + PLATFORM_MACHINE = "platform_machine" + + +class MarkerOp(StrEnum): + EQ = "==" + NE = "!=" + NOT_IN = "not in" + + +SYSTEM_TO_VIRTUAL_PACKAGE = { + "windows": "__win", + "win32": "__win", + "linux": "__linux", + "darwin": "__osx", + "cygwin": "__unix", +} + +OS_NAME_TO_VIRTUAL_PACKAGE = { + "nt": "__win", + "windows": "__win", + "posix": "__unix", +} def normalize_name(name: str) -> str: @@ -17,18 +62,124 @@ def normalize_name(name: str) -> str: return name.lower().replace("_", "-") +def _marker_value(token: Any) -> str: + """Extract the textual value from packaging marker tokens.""" + return getattr(token, "value", str(token)) + + +def _normalize_marker_atom(lhs: str, op: str, rhs: str) -> str | None: + """Map a single PEP 508 marker atom to a MatchSpec-like fragment.""" + lhs_l = lhs.lower() + rhs_l = rhs.lower() + + if lhs_l in {MarkerVar.PYTHON_VERSION, MarkerVar.PYTHON_FULL_VERSION}: + if op == MarkerOp.NOT_IN: + excluded_versions = [version.strip() for version in rhs.split(",") if version.strip()] + if not excluded_versions: + return None + clauses = [f"python!={version}" for version in excluded_versions] + if len(clauses) == 1: + return clauses[0] + return f"({' and '.join(clauses)})" + return f"python{op}{rhs}" + + if lhs_l == MarkerVar.EXTRA and op == MarkerOp.EQ: + return None + + if lhs_l in {MarkerVar.SYS_PLATFORM, MarkerVar.PLATFORM_SYSTEM}: + mapped = SYSTEM_TO_VIRTUAL_PACKAGE.get(rhs_l) + if op == MarkerOp.EQ and mapped: + return mapped + if op == MarkerOp.NE and rhs_l in {"win32", "windows", "cygwin"}: + return "__unix" + if op == MarkerOp.NE and rhs_l == "emscripten": + return None + return None + + if lhs_l == MarkerVar.OS_NAME: + mapped = OS_NAME_TO_VIRTUAL_PACKAGE.get(rhs_l) + if not mapped: + return None + if op == MarkerOp.EQ: + return mapped + if op == MarkerOp.NE: + return "__unix" if mapped == "__win" else "__win" + return None + + if lhs_l in {MarkerVar.IMPLEMENTATION_NAME, MarkerVar.PLATFORM_PYTHON_IMPLEMENTATION}: + if rhs_l in {"cpython", "pypy", "jython"}: + return None + return None + + if lhs_l == MarkerVar.PLATFORM_MACHINE: + return None + + return None + + +def _combine_expr(left: str | None, op: str, right: str | None) -> str | None: + """Combine optional left/right expressions with a boolean operator.""" + if left is None: + return right + if right is None: + return left + if left == right: + return left + return f"({left} {op} {right})" + + +def extract_marker_condition_and_extras(marker: Marker) -> tuple[str | None, list[str]]: + """Split a Marker into optional non-extra condition and extra group names.""" + extras: list[str] = [] + seen_extras: set[str] = set() + + def visit(node: Any) -> str | None: + if isinstance(node, tuple) and len(node) == 3: + lhs = _marker_value(node[0]) + op = _marker_value(node[1]) + rhs = _marker_value(node[2]) + + if lhs.lower() == MarkerVar.EXTRA and op == MarkerOp.EQ: + extra_name = rhs.lower() + if extra_name not in seen_extras: + seen_extras.add(extra_name) + extras.append(extra_name) + return None + + return _normalize_marker_atom(lhs, op, rhs) + + if isinstance(node, list): + if not node: + return None + + expr = visit(node[0]) + i = 1 + while i + 1 < len(node): + op = str(node[i]).lower() + rhs_expr = visit(node[i + 1]) + expr = _combine_expr(expr, op, rhs_expr) + i += 2 + return expr + + return None + + # Marker._markers is a private packaging attribute; keep access isolated here. + condition = visit(getattr(marker, "_markers", [])) + return condition, extras + + def pypi_to_repodata_noarch_whl_entry( pypi_data: dict[str, Any], ) -> dict[str, Any] | None: """ - Convert PyPI JSON endpoint data to a repodata.json packages.whl entry for a + Convert PyPI JSON endpoint data to a repodata.json v3.whl entry for a pure Python (noarch) wheel. Args: pypi_data: Dictionary containing the complete info from PyPI JSON endpoint Returns: - Dictionary representing the entry for packages.whl, or None if no pure + Dictionary representing the entry for v3.whl, or None if no pure Python wheel (platform tag "none-any") is found """ # Find a pure Python wheel (platform tag "none-any") @@ -48,17 +199,26 @@ def pypi_to_repodata_noarch_whl_entry( pypi_info = pypi_data.get("info") depends_list: list[str] = [] - extras_dict: dict[str, list[str]] = {} + extra_depends_dict: dict[str, list[str]] = {} for dep in pypi_info.get("requires_dist") or []: req = Requirement(dep) conda_dep = normalize_name(req.name) + str(req.specifier) if req.marker: - extra_match = EXTRA_MARKER_RE.search(str(req.marker)) - if extra_match: - extras_dict.setdefault(extra_match.group(1), []).append(conda_dep) + non_extra_condition, extra_names = extract_marker_condition_and_extras(req.marker) + if extra_names: + for extra_name in extra_names: + extra_dep = conda_dep + if non_extra_condition: + marker_condition = json.dumps(non_extra_condition) + extra_dep = f"{extra_dep}[when={marker_condition}]" + extra_depends_dict.setdefault(extra_name, []).append(extra_dep) else: - depends_list.append(conda_dep) + if non_extra_condition: + marker_condition = json.dumps(non_extra_condition) + depends_list.append(f"{conda_dep}[when={marker_condition}]") + else: + depends_list.append(conda_dep) else: depends_list.append(conda_dep) @@ -78,7 +238,7 @@ def pypi_to_repodata_noarch_whl_entry( "build": "py3_none_any_0", "build_number": 0, "depends": depends_list, - "extras": extras_dict, + "extra_depends": extra_depends_dict, "fn": f"{pypi_info.get('name')}-{pypi_info.get('version')}-py3-none-any.whl", "sha256": wheel_url.get("digests", {}).get("sha256", ""), "size": wheel_url.get("size", 0), @@ -137,9 +297,9 @@ def get_repodata_entry(name: str, version: str) -> dict[str, Any] | None: "packages": {}, "packages.conda": {}, "removed": [], - "repodata_version": 1, + "repodata_version": 3, "signatures": {}, - "packages.whl": {key: value for key, value in sorted(pkg_whls.items())}, + "v3": {"whl": {key: value for key, value in sorted(pkg_whls.items())}}, } with open(wheel_repodata, "w") as f: diff --git a/tests/conda_local_channel/noarch/repodata.json b/tests/conda_local_channel/noarch/repodata.json index 6ead4a2..6890ce5 100644 --- a/tests/conda_local_channel/noarch/repodata.json +++ b/tests/conda_local_channel/noarch/repodata.json @@ -5,542 +5,1038 @@ "packages": {}, "packages.conda": {}, "removed": [], - "repodata_version": 1, + "repodata_version": 3, "signatures": {}, - "packages.whl": { - "annotated-types-0.7.0-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", - "record_version": 3, - "name": "annotated-types", - "version": "0.7.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "typing-extensions>=4.0.0", - "python >=3.8" - ], - "extras": {}, - "fn": "annotated-types-0.7.0-py3-none-any.whl", - "sha256": "1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", - "size": 13643, - "subdir": "noarch", - "noarch": "python" - }, - "anyio-4.9.0-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", - "record_version": 3, - "name": "anyio", - "version": "4.9.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "exceptiongroup>=1.0.2", - "idna>=2.8", - "sniffio>=1.1", - "typing-extensions>=4.5", - "python >=3.9" - ], - "extras": { - "trio": [ - "trio>=0.26.1" + "v3": { + "whl": { + "0x-web3-4.8.2.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/f4/ee/c063d8f583a5773b6463b73c9ea61e9f6b5c45287424e5600f756d0fadfc/0x_web3-4.8.2.1-py3-none-any.whl", + "record_version": 3, + "name": "0x-web3", + "version": "4.8.2.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "eth-abi<2.0.0,>=1.2.0", + "eth-account<0.4.0,>=0.2.1", + "eth-utils<2.0.0,>=1.2.0", + "hexbytes<1.0.0,>=0.1.0", + "lru-dict<2.0.0,>=1.1.6", + "eth-hash<1.0.0,>=0.2.0", + "requests<3.0.0,>=2.16.0", + "websockets<7.0.0,>=6.0.0", + "web3==4.8.2", + "cytoolz<1.0.0,>=0.9.0", + "toolz<1.0.0,>=0.9.0", + "pypiwin32>=223[when=\"__win\"]", + "python >=3.5.3,<4" ], - "test": [ - "anyio", - "blockbuster>=1.5.23", - "coverage>=7", - "exceptiongroup>=1.2.0", - "hypothesis>=4.0", - "psutil>=5.9", - "pytest>=7.0", - "trustme", - "truststore>=0.9.1", - "uvloop>=0.21" + "extra_depends": { + "dev": [ + "eth-tester==0.1.0-beta.33", + "py-geth<3.0.0,>=2.0.1", + "flake8==3.4.1", + "isort<5,>=4.2.15", + "mock", + "sphinx-better-theme>=0.1.4", + "click>=5.1", + "configparser==3.5.0", + "contextlib2>=0.5.4", + "ethtoken", + "py-geth>=1.4.0", + "py-solc>=0.4.0", + "pytest>=2.7.2", + "sphinx", + "sphinx-rtd-theme>=0.1.9", + "toposort>=1.4", + "urllib3", + "web3>=2.1.0", + "wheel", + "bumpversion", + "flaky>=3.3.0", + "hypothesis>=3.31.2", + "pytest<4,>=3.5.0", + "pytest-mock==1.*", + "pytest-pythonpath>=0.3", + "pytest-watch==4.*", + "pytest-xdist==1.*", + "tox>=1.8.0", + "tqdm", + "when-changed" + ], + "docs": [ + "mock", + "sphinx-better-theme>=0.1.4", + "click>=5.1", + "configparser==3.5.0", + "contextlib2>=0.5.4", + "ethtoken", + "py-geth>=1.4.0", + "py-solc>=0.4.0", + "pytest>=2.7.2", + "sphinx", + "sphinx-rtd-theme>=0.1.9", + "toposort>=1.4", + "urllib3", + "web3>=2.1.0", + "wheel" + ], + "linter": [ + "flake8==3.4.1", + "isort<5,>=4.2.15" + ], + "tester": [ + "eth-tester==0.1.0-beta.33", + "py-geth<3.0.0,>=2.0.1" + ], + "testrpc": [ + "eth-testrpc<2.0.0,>=1.3.3" + ] + }, + "fn": "0x-web3-4.8.2.1-py3-none-any.whl", + "sha256": "f78c95809ead54b67c1bfb76886b364f174b93536ebac7a7756cc6d28c7d6a10", + "size": 125839, + "subdir": "noarch", + "noarch": "python" + }, + "aba-cli-scrapper-0.7.6-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/0c/cd/3517b4f8649d87204300f0b6e9efca0188b0eac06ad04464930937b0f57b/aba_cli_scrapper-0.7.6-py3-none-any.whl", + "record_version": 3, + "name": "aba-cli-scrapper", + "version": "0.7.6", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "aiosignal==1.3.1[when=\"python>=3.7\"]", + "annotated-types==0.7.0[when=\"python>=3.8\"]", + "attrs==23.2.0[when=\"python>=3.7\"]", + "certifi==2024.7.4[when=\"python>=3.6\"]", + "cffi==1.16.0", + "charset-normalizer==3.3.2[when=\"python>=3.7.0\"]", + "click==8.1.7[when=\"python>=3.7\"]", + "colorama==0.4.6[when=\"__win\"]", + "cryptography==43.0.1[when=\"python>=3.7\"]", + "datahorse<0.0.4,>=0.0.3", + "frozenlist==1.4.1[when=\"python>=3.8\"]", + "furo<2025.0.0,>=2024.8.6", + "greenlet==3.0.3[when=\"python>=3.7\"]", + "idna==3.7[when=\"python>=3.5\"]", + "loguru<0.8.0,>=0.7.2", + "markdown-it-py==3.0.0[when=\"python>=3.8\"]", + "mdurl==0.1.2[when=\"python>=3.7\"]", + "multidict==6.0.5[when=\"python>=3.7\"]", + "mypy-extensions==1.0.0[when=\"python>=3.5\"]", + "nodeenv==1.9.1[when=\"(python>=2.7 and (python!=3.0 and python!=3.1 and python!=3.2 and python!=3.3 and python!=3.4 and python!=3.5 and python!=3.6))\"]", + "packaging==24.1[when=\"python>=3.8\"]", + "pandas<3.0.0,>=2.2.2", + "pathspec==0.12.1[when=\"python>=3.8\"]", + "platformdirs==4.2.2[when=\"python>=3.8\"]", + "playwright==1.45.0", + "plotnine<0.14.0,>=0.13.6", + "prettytable<4.0.0,>=3.11.0", + "pycparser==2.22[when=\"python>=3.8\"]", + "pydantic==2.8.2[when=\"python>=3.8\"]", + "pydantic-core==2.20.1[when=\"python>=3.8\"]", + "pyee==11.1.0[when=\"python>=3.8\"]", + "pygments==2.18.0[when=\"python>=3.8\"]", + "pymysql<2.0.0,>=1.1.1", + "python-dotenv<2.0.0,>=1.0.1", + "requests==2.32.3[when=\"python>=3.8\"]", + "rich==13.7.1[when=\"python>=3.7.0\"]", + "selectolax<0.4.0,>=0.3.21", + "shellingham==1.5.4[when=\"python>=3.7\"]", + "sqlalchemy<3.0.0,>=2.0.32", + "sqlmodel<0.0.22,>=0.0.21", + "tabulate<0.10.0,>=0.9.0", + "trogon<0.6.0,>=0.5.0", + "typer<0.13.0,>=0.12.5", + "typing-extensions==4.12.2[when=\"python>=3.8\"]", + "urllib3==2.2.2[when=\"python>=3.8\"]", + "win32-setctime==1.1.0[when=\"__win\"]", + "yarl==1.9.4[when=\"python>=3.7\"]", + "python <4.0.0,>=3.11.0" + ], + "extra_depends": {}, + "fn": "aba-cli-scrapper-0.7.6-py3-none-any.whl", + "sha256": "25ac0fe60c51cd3617c4c679f2dbb0657f3c731673f7fb5daf148535b3ee2c4c", + "size": 18846611, + "subdir": "noarch", + "noarch": "python" + }, + "adup-0.1.0-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/72/a5/7cf520145d7f58a4dd4088fb004ac8793fc3a1ad328109a4fc5ab0d184f7/adup-0.1.0-py2.py3-none-any.whl", + "record_version": 3, + "name": "adup", + "version": "0.1.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "alive-progress", + "click", + "configparser", + "sqlalchemy", + "tabulate", + "tabulate", + "python >=3.8" + ], + "extra_depends": { + "docs": [ + "pygments-github-lexers>=0.0.5", + "sphinx>=2.0.0", + "sphinxcontrib-autoprogram>=0.1.5", + "towncrier>=18.5.0" + ], + "testing": [ + "flaky>=3.4.0", + "pytest>=4.0.0", + "pytest-cov>=2.5.1", + "psutil>=5.6.1", + "pathlib2>=2.3.3[when=\"python<3.4\"]" + ] + }, + "fn": "adup-0.1.0-py3-none-any.whl", + "sha256": "e34fb4f227e7014b82725d08bb83bd469ff61c605b2e5b5fa7eab11e6b2907c3", + "size": 41355, + "subdir": "noarch", + "noarch": "python" + }, + "advancedselector-3.1.0-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/fd/fb/7322f62a2067c173d098631ee752d93724e11f5966b82a95211eb43c2ddc/advancedselector-3.1.0-py3-none-any.whl", + "record_version": 3, + "name": "advancedselector", + "version": "3.1.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "getch[when=\"__unix\"]", + "python >=3.8" + ], + "extra_depends": {}, + "fn": "advancedselector-3.1.0-py3-none-any.whl", + "sha256": "2d59fe09056aae41e6e079b4663d33195696d5473fe3177f0ec3b3823f9cf608", + "size": 5505, + "subdir": "noarch", + "noarch": "python" + }, + "ali2b-cli-scrapper-1.0.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/f1/f8/4ffe27e7e0824d505c3bb506c3ac56a94b236cf47a93c973353718624b68/ali2b_cli_scrapper-1.0.1-py3-none-any.whl", + "record_version": 3, + "name": "ali2b-cli-scrapper", + "version": "1.0.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "aiohttp==3.9.5[when=\"python>=3.8\"]", + "aiosignal==1.3.1[when=\"python>=3.7\"]", + "annotated-types==0.7.0[when=\"python>=3.8\"]", + "attrs==23.2.0[when=\"python>=3.7\"]", + "black==24.4.2[when=\"python>=3.8\"]", + "cffi==1.16.0", + "click==8.1.7[when=\"python>=3.7\"]", + "colorama==0.4.6[when=\"__win\"]", + "cryptography==42.0.7[when=\"python>=3.7\"]", + "frozenlist==1.4.1[when=\"python>=3.8\"]", + "greenlet==3.0.3", + "idna==3.7[when=\"python>=3.5\"]", + "loguru==0.7.2[when=\"python>=3.5\"]", + "markdown-it-py==3.0.0[when=\"python>=3.8\"]", + "mdurl==0.1.2[when=\"python>=3.7\"]", + "multidict==6.0.5[when=\"python>=3.7\"]", + "mypy-extensions==1.0.0[when=\"python>=3.5\"]", + "mysqlclient==2.2.4[when=\"python>=3.8\"]", + "nodeenv==1.9.1[when=\"(python>=2.7 and (python!=3.0 and python!=3.1 and python!=3.2 and python!=3.3 and python!=3.4 and python!=3.5 and python!=3.6))\"]", + "packaging==24.0[when=\"python>=3.7\"]", + "pathspec==0.12.1[when=\"python>=3.8\"]", + "platformdirs==4.2.2[when=\"python>=3.8\"]", + "playwright==1.44.0[when=\"python>=3.8\"]", + "pycparser==2.22[when=\"python>=3.8\"]", + "pydantic==2.7.3[when=\"python>=3.8\"]", + "pydantic-core==2.18.4[when=\"python>=3.8\"]", + "pyee==11.1.0[when=\"python>=3.8\"]", + "pygments==2.18.0[when=\"python>=3.8\"]", + "pyright==1.1.365[when=\"python>=3.7\"]", + "python-decouple==3.8", + "python-dotenv==1.0.1[when=\"python>=3.8\"]", + "rich==13.7.1[when=\"python>=3.7.0\"]", + "selectolax==0.3.21", + "shellingham==1.5.4[when=\"python>=3.7\"]", + "sqlalchemy==2.0.30[when=\"python>=3.7\"]", + "sqlmodel==0.0.19[when=\"python>=3.7\"]", + "typer==0.12.3[when=\"python>=3.7\"]", + "typing-extensions==4.12.1[when=\"python>=3.8\"]", + "win32-setctime==1.1.0[when=\"__win\"]", + "yarl==1.9.4[when=\"python>=3.7\"]", + "python <4.0,>=3.7" + ], + "extra_depends": {}, + "fn": "ali2b-cli-scrapper-1.0.1-py3-none-any.whl", + "sha256": "5adccca1bd655fbe251c84cf045f2e73d5f60aeb474c94e7384f09bc0ebf7906", + "size": 46938, + "subdir": "noarch", + "noarch": "python" + }, + "annotated-types-0.7.0-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", + "record_version": 3, + "name": "annotated-types", + "version": "0.7.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "typing-extensions>=4.0.0[when=\"python<3.9\"]", + "python >=3.8" + ], + "extra_depends": {}, + "fn": "annotated-types-0.7.0-py3-none-any.whl", + "sha256": "1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "size": 13643, + "subdir": "noarch", + "noarch": "python" + }, + "anyio-4.9.0-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", + "record_version": 3, + "name": "anyio", + "version": "4.9.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "exceptiongroup>=1.0.2[when=\"python<3.11\"]", + "idna>=2.8", + "sniffio>=1.1", + "typing-extensions>=4.5[when=\"python<3.13\"]", + "python >=3.9" + ], + "extra_depends": { + "trio": [ + "trio>=0.26.1" + ], + "test": [ + "anyio", + "blockbuster>=1.5.23", + "coverage>=7", + "exceptiongroup>=1.2.0", + "hypothesis>=4.0", + "psutil>=5.9", + "pytest>=7.0", + "trustme", + "truststore>=0.9.1[when=\"python>=3.10\"]", + "uvloop>=0.21[when=\"(__unix and python<3.14)\"]" + ], + "doc": [ + "packaging", + "sphinx~=8.2", + "sphinx-rtd-theme", + "sphinx-autodoc-typehints>=1.2.0" + ] + }, + "fn": "anyio-4.9.0-py3-none-any.whl", + "sha256": "9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", + "size": 100916, + "subdir": "noarch", + "noarch": "python" + }, + "auto-all-1.4.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/cf/ae/1317a5362e0016be1efb445b77b98cfddc41f87ae95492da5b43e6537a07/auto_all-1.4.1-py2.py3-none-any.whl", + "record_version": 3, + "name": "auto-all", + "version": "1.4.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python" + ], + "extra_depends": {}, + "fn": "auto-all-1.4.1-py3-none-any.whl", + "sha256": "48dae02a670e94c87e2da2985e20e33ec231dffd3ae29078777002680f9eee13", + "size": 5562, + "subdir": "noarch", + "noarch": "python" + }, + "certifi-2024.12.14-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", + "record_version": 3, + "name": "certifi", + "version": "2024.12.14", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.6" + ], + "extra_depends": {}, + "fn": "certifi-2024.12.14-py3-none-any.whl", + "sha256": "1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "size": 164927, + "subdir": "noarch", + "noarch": "python" + }, + "charset-normalizer-3.3.2-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", + "record_version": 3, + "name": "charset-normalizer", + "version": "3.3.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.7.0" ], - "doc": [ - "packaging", - "sphinx~=8.2", - "sphinx-rtd-theme", - "sphinx-autodoc-typehints>=1.2.0" - ] + "extra_depends": {}, + "fn": "charset-normalizer-3.3.2-py3-none-any.whl", + "sha256": "3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "size": 48543, + "subdir": "noarch", + "noarch": "python" }, - "fn": "anyio-4.9.0-py3-none-any.whl", - "sha256": "9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", - "size": 100916, - "subdir": "noarch", - "noarch": "python" - }, - "auto-all-1.4.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/cf/ae/1317a5362e0016be1efb445b77b98cfddc41f87ae95492da5b43e6537a07/auto_all-1.4.1-py2.py3-none-any.whl", - "record_version": 3, - "name": "auto-all", - "version": "1.4.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python" - ], - "extras": {}, - "fn": "auto-all-1.4.1-py3-none-any.whl", - "sha256": "48dae02a670e94c87e2da2985e20e33ec231dffd3ae29078777002680f9eee13", - "size": 5562, - "subdir": "noarch", - "noarch": "python" - }, - "certifi-2024.12.14-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", - "record_version": 3, - "name": "certifi", - "version": "2024.12.14", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.6" - ], - "extras": {}, - "fn": "certifi-2024.12.14-py3-none-any.whl", - "sha256": "1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", - "size": 164927, - "subdir": "noarch", - "noarch": "python" - }, - "charset-normalizer-3.3.2-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", - "record_version": 3, - "name": "charset-normalizer", - "version": "3.3.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.7.0" - ], - "extras": {}, - "fn": "charset-normalizer-3.3.2-py3-none-any.whl", - "sha256": "3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "size": 48543, - "subdir": "noarch", - "noarch": "python" - }, - "exceptiongroup-1.2.2-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", - "record_version": 3, - "name": "exceptiongroup", - "version": "1.2.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.7" - ], - "extras": { - "test": [ - "pytest>=6" - ] + "click-8.1.8-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", + "record_version": 3, + "name": "click", + "version": "8.1.8", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "colorama[when=\"__win\"]", + "importlib-metadata[when=\"python<3.8\"]", + "python >=3.7" + ], + "extra_depends": {}, + "fn": "click-8.1.8-py3-none-any.whl", + "sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "size": 98188, + "subdir": "noarch", + "noarch": "python" }, - "fn": "exceptiongroup-1.2.2-py3-none-any.whl", - "sha256": "3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", - "size": 16453, - "subdir": "noarch", - "noarch": "python" - }, - "fastapi-0.116.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", - "record_version": 3, - "name": "fastapi", - "version": "0.116.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "starlette<0.48.0,>=0.40.0", - "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4", - "typing-extensions>=4.8.0", - "python >=3.8" - ], - "extras": { - "standard": [ - "fastapi-cli>=0.0.8", - "httpx>=0.23.0", - "jinja2>=3.1.5", - "python-multipart>=0.0.18", - "email-validator>=2.0.0", - "uvicorn>=0.12.0" + "exceptiongroup-1.2.2-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", + "record_version": 3, + "name": "exceptiongroup", + "version": "1.2.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.7" ], - "standard-no-fastapi-cloud-cli": [ - "fastapi-cli>=0.0.8", - "httpx>=0.23.0", - "jinja2>=3.1.5", - "python-multipart>=0.0.18", - "email-validator>=2.0.0", - "uvicorn>=0.12.0" + "extra_depends": { + "test": [ + "pytest>=6" + ] + }, + "fn": "exceptiongroup-1.2.2-py3-none-any.whl", + "sha256": "3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "size": 16453, + "subdir": "noarch", + "noarch": "python" + }, + "fastapi-0.116.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", + "record_version": 3, + "name": "fastapi", + "version": "0.116.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "starlette<0.48.0,>=0.40.0", + "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4", + "typing-extensions>=4.8.0", + "python >=3.8" ], - "all": [ - "fastapi-cli>=0.0.8", - "httpx>=0.23.0", - "jinja2>=3.1.5", - "python-multipart>=0.0.18", - "itsdangerous>=1.1.0", - "pyyaml>=5.3.1", - "ujson!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,>=4.0.1", - "orjson>=3.2.1", - "email-validator>=2.0.0", - "uvicorn>=0.12.0", - "pydantic-settings>=2.0.0", - "pydantic-extra-types>=2.0.0" - ] + "extra_depends": { + "standard": [ + "fastapi-cli>=0.0.8", + "httpx>=0.23.0", + "jinja2>=3.1.5", + "python-multipart>=0.0.18", + "email-validator>=2.0.0", + "uvicorn>=0.12.0" + ], + "standard-no-fastapi-cloud-cli": [ + "fastapi-cli>=0.0.8", + "httpx>=0.23.0", + "jinja2>=3.1.5", + "python-multipart>=0.0.18", + "email-validator>=2.0.0", + "uvicorn>=0.12.0" + ], + "all": [ + "fastapi-cli>=0.0.8", + "httpx>=0.23.0", + "jinja2>=3.1.5", + "python-multipart>=0.0.18", + "itsdangerous>=1.1.0", + "pyyaml>=5.3.1", + "ujson!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,>=4.0.1", + "orjson>=3.2.1", + "email-validator>=2.0.0", + "uvicorn>=0.12.0", + "pydantic-settings>=2.0.0", + "pydantic-extra-types>=2.0.0" + ] + }, + "fn": "fastapi-0.116.1-py3-none-any.whl", + "sha256": "c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", + "size": 95631, + "subdir": "noarch", + "noarch": "python" }, - "fn": "fastapi-0.116.1-py3-none-any.whl", - "sha256": "c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", - "size": 95631, - "subdir": "noarch", - "noarch": "python" - }, - "fire-0.7.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", - "record_version": 3, - "name": "fire", - "version": "0.7.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "termcolor", - "python >=3.7" - ], - "extras": { - "test": [ - "setuptools<=80.9.0", - "pip", - "pylint<3.3.8", - "pytest<=8.4.1", - "pytest-pylint<=1.1.2", - "pytest-runner<7.0.0", - "termcolor<3.2.0", - "hypothesis<6.136.0", - "levenshtein<=0.27.1" - ] + "fire-0.7.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", + "record_version": 3, + "name": "fire", + "version": "0.7.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "termcolor", + "python >=3.7" + ], + "extra_depends": { + "test": [ + "setuptools<=80.9.0", + "pip", + "pylint<3.3.8", + "pytest<=8.4.1", + "pytest-pylint<=1.1.2", + "pytest-runner<7.0.0", + "termcolor<3.2.0", + "hypothesis<6.136.0", + "levenshtein<=0.27.1" + ] + }, + "fn": "fire-0.7.1-py3-none-any.whl", + "sha256": "e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", + "size": 115945, + "subdir": "noarch", + "noarch": "python" }, - "fn": "fire-0.7.1-py3-none-any.whl", - "sha256": "e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", - "size": 115945, - "subdir": "noarch", - "noarch": "python" - }, - "idna-3.10-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", - "record_version": 3, - "name": "idna", - "version": "3.10", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.6" - ], - "extras": { - "all": [ - "ruff>=0.6.2", - "mypy>=1.11.2", - "pytest>=8.3.2", - "flake8>=7.1.1" - ] + "httpx-0.28.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", + "record_version": 3, + "name": "httpx", + "version": "0.28.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "anyio", + "certifi", + "httpcore==1.*", + "idna", + "python >=3.8" + ], + "extra_depends": { + "brotli": [ + "brotli", + "brotlicffi" + ], + "cli": [ + "click==8.*", + "pygments==2.*", + "rich<14,>=10" + ], + "http2": [ + "h2<5,>=3" + ], + "socks": [ + "socksio==1.*" + ], + "zstd": [ + "zstandard>=0.18.0" + ] + }, + "fn": "httpx-0.28.1-py3-none-any.whl", + "sha256": "d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", + "size": 73517, + "subdir": "noarch", + "noarch": "python" }, - "fn": "idna-3.10-py3-none-any.whl", - "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", - "size": 70442, - "subdir": "noarch", - "noarch": "python" - }, - "requests-2.32.5-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", - "record_version": 3, - "name": "requests", - "version": "2.32.5", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "charset-normalizer<4,>=2", - "idna<4,>=2.5", - "urllib3<3,>=1.21.1", - "certifi>=2017.4.17", - "python >=3.9" - ], - "extras": { - "socks": [ - "pysocks!=1.5.7,>=1.5.6" + "idna-3.10-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", + "record_version": 3, + "name": "idna", + "version": "3.10", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.6" ], - "use-chardet-on-py3": [ - "chardet<6,>=3.0.2" - ] + "extra_depends": { + "all": [ + "ruff>=0.6.2", + "mypy>=1.11.2", + "pytest>=8.3.2", + "flake8>=7.1.1" + ] + }, + "fn": "idna-3.10-py3-none-any.whl", + "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", + "size": 70442, + "subdir": "noarch", + "noarch": "python" }, - "fn": "requests-2.32.5-py3-none-any.whl", - "sha256": "2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", - "size": 64738, - "subdir": "noarch", - "noarch": "python" - }, - "setuptools-78.1.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl", - "record_version": 3, - "name": "setuptools", - "version": "78.1.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.9" - ], - "extras": { - "test": [ - "pytest!=8.1.*,>=6", - "virtualenv>=13.0.0", - "wheel>=0.44.0", - "pip>=19.1", - "packaging>=24.2", - "jaraco.envs>=2.2", - "pytest-xdist>=3", - "jaraco.path>=3.7.2", - "build>=1.0.3", - "filelock>=3.4.0", - "ini2toml>=0.14", - "tomli-w>=1.0.0", - "pytest-timeout", - "pytest-perf", - "jaraco.develop>=7.21", - "pytest-home>=0.5", - "pytest-subprocess", - "pyproject-hooks!=1.1", - "jaraco.test>=5.5" + "ipython-8.30.0-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/1d/f3/1332ba2f682b07b304ad34cad2f003adcfeb349486103f4b632335074a7c/ipython-8.30.0-py3-none-any.whl", + "record_version": 3, + "name": "ipython", + "version": "8.30.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "colorama[when=\"__win\"]", + "decorator", + "exceptiongroup[when=\"python<3.11\"]", + "jedi>=0.16", + "matplotlib-inline", + "pexpect>4.3[when=\"__unix\"]", + "prompt-toolkit<3.1.0,>=3.0.41", + "pygments>=2.4.0", + "stack-data", + "traitlets>=5.13.0", + "typing-extensions>=4.6[when=\"python<3.12\"]", + "python >=3.10" ], - "doc": [ - "sphinx>=3.5", - "jaraco.packaging>=9.3", - "rst.linker>=1.9", - "furo", - "sphinx-lint", - "jaraco.tidelift>=1.4", - "pygments-github-lexers==0.0.5", - "sphinx-favicon", - "sphinx-inline-tabs", - "sphinx-reredirects", - "sphinxcontrib-towncrier", - "sphinx-notfound-page<2,>=1", - "pyproject-hooks!=1.1", - "towncrier<24.7" + "extra_depends": { + "black": [ + "black" + ], + "doc": [ + "docrepr", + "exceptiongroup", + "intersphinx-registry", + "ipykernel", + "ipython", + "matplotlib", + "setuptools>=18.5", + "sphinx-rtd-theme", + "sphinx>=1.3", + "sphinxcontrib-jquery", + "tomli[when=\"python<3.11\"]", + "typing-extensions" + ], + "kernel": [ + "ipykernel" + ], + "nbconvert": [ + "nbconvert" + ], + "nbformat": [ + "nbformat" + ], + "notebook": [ + "ipywidgets", + "notebook" + ], + "parallel": [ + "ipyparallel" + ], + "qtconsole": [ + "qtconsole" + ], + "test": [ + "pytest", + "pytest-asyncio<0.22", + "testpath", + "pickleshare", + "packaging" + ], + "test-extra": [ + "ipython", + "curio", + "matplotlib!=3.2.0", + "nbformat", + "numpy>=1.23", + "pandas", + "trio" + ], + "matplotlib": [ + "matplotlib" + ], + "all": [ + "ipython", + "ipython" + ] + }, + "fn": "ipython-8.30.0-py3-none-any.whl", + "sha256": "85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321", + "size": 820765, + "subdir": "noarch", + "noarch": "python" + }, + "pydantic-2.10.6-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", + "record_version": 3, + "name": "pydantic", + "version": "2.10.6", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "annotated-types>=0.6.0", + "pydantic-core==2.27.2", + "typing-extensions>=4.12.2", + "python >=3.8" ], - "core": [ - "packaging>=24.2", - "more-itertools>=8.8", - "jaraco.text>=3.7", - "importlib-metadata>=6", - "tomli>=2.0.1", - "wheel>=0.43.0", - "platformdirs>=4.2.2", - "jaraco.functools>=4", - "more-itertools" + "extra_depends": { + "email": [ + "email-validator>=2.0.0" + ], + "timezone": [ + "tzdata[when=\"(python>=3.9 and __win)\"]" + ] + }, + "fn": "pydantic-2.10.6-py3-none-any.whl", + "sha256": "427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", + "size": 431696, + "subdir": "noarch", + "noarch": "python" + }, + "requests-2.32.5-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", + "record_version": 3, + "name": "requests", + "version": "2.32.5", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", + "certifi>=2017.4.17", + "python >=3.9" ], - "check": [ - "pytest-checkdocs>=2.4", - "pytest-ruff>=0.2.1", - "ruff>=0.8.0" + "extra_depends": { + "socks": [ + "pysocks!=1.5.7,>=1.5.6" + ], + "use-chardet-on-py3": [ + "chardet<6,>=3.0.2" + ] + }, + "fn": "requests-2.32.5-py3-none-any.whl", + "sha256": "2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", + "size": 64738, + "subdir": "noarch", + "noarch": "python" + }, + "rich-13.9.4-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", + "record_version": 3, + "name": "rich", + "version": "13.9.4", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "typing-extensions<5.0,>=4.0.0[when=\"python<3.11\"]", + "pygments<3.0.0,>=2.13.0", + "markdown-it-py>=2.2.0", + "python >=3.8.0" ], - "cover": [ - "pytest-cov" + "extra_depends": { + "jupyter": [ + "ipywidgets<9,>=7.5.1" + ] + }, + "fn": "rich-13.9.4-py3-none-any.whl", + "sha256": "6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", + "size": 242424, + "subdir": "noarch", + "noarch": "python" + }, + "setuptools-78.1.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl", + "record_version": 3, + "name": "setuptools", + "version": "78.1.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.9" ], - "enabler": [ - "pytest-enabler>=2.2" + "extra_depends": { + "test": [ + "pytest!=8.1.*,>=6", + "virtualenv>=13.0.0", + "wheel>=0.44.0", + "pip>=19.1", + "packaging>=24.2", + "jaraco.envs>=2.2", + "pytest-xdist>=3", + "jaraco.path>=3.7.2", + "build>=1.0.3", + "filelock>=3.4.0", + "ini2toml>=0.14", + "tomli-w>=1.0.0", + "pytest-timeout", + "pytest-perf[when=\"__unix\"]", + "jaraco.develop>=7.21[when=\"(python>=3.9 and __unix)\"]", + "pytest-home>=0.5", + "pytest-subprocess", + "pyproject-hooks!=1.1", + "jaraco.test>=5.5" + ], + "doc": [ + "sphinx>=3.5", + "jaraco.packaging>=9.3", + "rst.linker>=1.9", + "furo", + "sphinx-lint", + "jaraco.tidelift>=1.4", + "pygments-github-lexers==0.0.5", + "sphinx-favicon", + "sphinx-inline-tabs", + "sphinx-reredirects", + "sphinxcontrib-towncrier", + "sphinx-notfound-page<2,>=1", + "pyproject-hooks!=1.1", + "towncrier<24.7" + ], + "core": [ + "packaging>=24.2", + "more-itertools>=8.8", + "jaraco.text>=3.7", + "importlib-metadata>=6[when=\"python<3.10\"]", + "tomli>=2.0.1[when=\"python<3.11\"]", + "wheel>=0.43.0", + "platformdirs>=4.2.2", + "jaraco.functools>=4", + "more-itertools" + ], + "check": [ + "pytest-checkdocs>=2.4", + "pytest-ruff>=0.2.1[when=\"__unix\"]", + "ruff>=0.8.0[when=\"__unix\"]" + ], + "cover": [ + "pytest-cov" + ], + "enabler": [ + "pytest-enabler>=2.2" + ], + "type": [ + "pytest-mypy", + "mypy==1.14.*", + "importlib-metadata>=7.0.2[when=\"python<3.10\"]", + "jaraco.develop>=7.21[when=\"__unix\"]" + ] + }, + "fn": "setuptools-78.1.1-py3-none-any.whl", + "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561", + "size": 1256462, + "subdir": "noarch", + "noarch": "python" + }, + "sniffio-1.3.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", + "record_version": 3, + "name": "sniffio", + "version": "1.3.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.7" ], - "type": [ - "pytest-mypy", - "mypy==1.14.*", - "importlib-metadata>=7.0.2", - "jaraco.develop>=7.21" - ] + "extra_depends": {}, + "fn": "sniffio-1.3.1-py3-none-any.whl", + "sha256": "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "size": 10235, + "subdir": "noarch", + "noarch": "python" }, - "fn": "setuptools-78.1.1-py3-none-any.whl", - "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561", - "size": 1256462, - "subdir": "noarch", - "noarch": "python" - }, - "sniffio-1.3.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", - "record_version": 3, - "name": "sniffio", - "version": "1.3.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.7" - ], - "extras": {}, - "fn": "sniffio-1.3.1-py3-none-any.whl", - "sha256": "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", - "size": 10235, - "subdir": "noarch", - "noarch": "python" - }, - "starlette-0.47.2-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", - "record_version": 3, - "name": "starlette", - "version": "0.47.2", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "anyio<5,>=3.6.2", - "typing-extensions>=4.10.0", - "python >=3.9" - ], - "extras": { - "full": [ - "httpx<0.29.0,>=0.27.0", - "itsdangerous", - "jinja2", - "python-multipart>=0.0.18", - "pyyaml" - ] + "starlette-0.47.2-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", + "record_version": 3, + "name": "starlette", + "version": "0.47.2", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "anyio<5,>=3.6.2", + "typing-extensions>=4.10.0[when=\"python<3.13\"]", + "python >=3.9" + ], + "extra_depends": { + "full": [ + "httpx<0.29.0,>=0.27.0", + "itsdangerous", + "jinja2", + "python-multipart>=0.0.18", + "pyyaml" + ] + }, + "fn": "starlette-0.47.2-py3-none-any.whl", + "sha256": "c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", + "size": 72984, + "subdir": "noarch", + "noarch": "python" }, - "fn": "starlette-0.47.2-py3-none-any.whl", - "sha256": "c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", - "size": 72984, - "subdir": "noarch", - "noarch": "python" - }, - "termcolor-2.5.0-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", - "record_version": 3, - "name": "termcolor", - "version": "2.5.0", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.9" - ], - "extras": { - "tests": [ - "pytest", - "pytest-cov" - ] + "termcolor-2.5.0-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", + "record_version": 3, + "name": "termcolor", + "version": "2.5.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.9" + ], + "extra_depends": { + "tests": [ + "pytest", + "pytest-cov" + ] + }, + "fn": "termcolor-2.5.0-py3-none-any.whl", + "sha256": "37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", + "size": 7755, + "subdir": "noarch", + "noarch": "python" }, - "fn": "termcolor-2.5.0-py3-none-any.whl", - "sha256": "37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", - "size": 7755, - "subdir": "noarch", - "noarch": "python" - }, - "typeguard-4.4.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/f2/53/9465dedf2d69fe26008e7732cf6e0a385e387c240869e7d54eed49782a3c/typeguard-4.4.1-py3-none-any.whl", - "record_version": 3, - "name": "typeguard", - "version": "4.4.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "typing-extensions>=4.10.0", - "importlib-metadata>=3.6", - "python >=3.9" - ], - "extras": { - "doc": [ - "packaging", - "sphinx>=7", - "sphinx-autodoc-typehints>=1.2.0", - "sphinx-rtd-theme>=1.3.0" + "typeguard-4.4.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/f2/53/9465dedf2d69fe26008e7732cf6e0a385e387c240869e7d54eed49782a3c/typeguard-4.4.1-py3-none-any.whl", + "record_version": 3, + "name": "typeguard", + "version": "4.4.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "typing-extensions>=4.10.0", + "importlib-metadata>=3.6[when=\"python<3.10\"]", + "python >=3.9" ], - "test": [ - "coverage>=7", - "pytest>=7", - "mypy>=1.2.0" - ] + "extra_depends": { + "doc": [ + "packaging", + "sphinx>=7", + "sphinx-autodoc-typehints>=1.2.0", + "sphinx-rtd-theme>=1.3.0" + ], + "test": [ + "coverage>=7", + "pytest>=7", + "mypy>=1.2.0" + ] + }, + "fn": "typeguard-4.4.1-py3-none-any.whl", + "sha256": "9324ec07a27ec67fc54a9c063020ca4c0ae6abad5e9f0f9804ca59aee68c6e21", + "size": 35635, + "subdir": "noarch", + "noarch": "python" }, - "fn": "typeguard-4.4.1-py3-none-any.whl", - "sha256": "9324ec07a27ec67fc54a9c063020ca4c0ae6abad5e9f0f9804ca59aee68c6e21", - "size": 35635, - "subdir": "noarch", - "noarch": "python" - }, - "typing-extensions-4.14.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", - "record_version": 3, - "name": "typing-extensions", - "version": "4.14.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.9" - ], - "extras": {}, - "fn": "typing-extensions-4.14.1-py3-none-any.whl", - "sha256": "d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", - "size": 43906, - "subdir": "noarch", - "noarch": "python" - }, - "typing-inspection-0.4.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", - "record_version": 3, - "name": "typing-inspection", - "version": "0.4.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "typing-extensions>=4.12.0", - "python >=3.9" - ], - "extras": {}, - "fn": "typing-inspection-0.4.1-py3-none-any.whl", - "sha256": "389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", - "size": 14552, - "subdir": "noarch", - "noarch": "python" - }, - "urllib3-2.2.3-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", - "record_version": 3, - "name": "urllib3", - "version": "2.2.3", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.8" - ], - "extras": { - "brotli": [ - "brotli>=1.0.9", - "brotlicffi>=0.8.0" + "typing-extensions-4.14.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", + "record_version": 3, + "name": "typing-extensions", + "version": "4.14.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.9" ], - "h2": [ - "h2<5,>=4" + "extra_depends": {}, + "fn": "typing-extensions-4.14.1-py3-none-any.whl", + "sha256": "d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", + "size": 43906, + "subdir": "noarch", + "noarch": "python" + }, + "typing-inspection-0.4.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", + "record_version": 3, + "name": "typing-inspection", + "version": "0.4.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "typing-extensions>=4.12.0", + "python >=3.9" ], - "socks": [ - "pysocks!=1.5.7,<2.0,>=1.5.6" + "extra_depends": {}, + "fn": "typing-inspection-0.4.1-py3-none-any.whl", + "sha256": "389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", + "size": 14552, + "subdir": "noarch", + "noarch": "python" + }, + "urllib3-2.2.3-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", + "record_version": 3, + "name": "urllib3", + "version": "2.2.3", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.8" ], - "zstd": [ - "zstandard>=0.18.0" - ] + "extra_depends": { + "brotli": [ + "brotli>=1.0.9", + "brotlicffi>=0.8.0" + ], + "h2": [ + "h2<5,>=4" + ], + "socks": [ + "pysocks!=1.5.7,<2.0,>=1.5.6" + ], + "zstd": [ + "zstandard>=0.18.0" + ] + }, + "fn": "urllib3-2.2.3-py3-none-any.whl", + "sha256": "ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "size": 126338, + "subdir": "noarch", + "noarch": "python" }, - "fn": "urllib3-2.2.3-py3-none-any.whl", - "sha256": "ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", - "size": 126338, - "subdir": "noarch", - "noarch": "python" - }, - "wheel-0.45.1-py3_none_any_0": { - "url": "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", - "record_version": 3, - "name": "wheel", - "version": "0.45.1", - "build": "py3_none_any_0", - "build_number": 0, - "depends": [ - "python >=3.8" - ], - "extras": { - "test": [ - "pytest>=6.0.0", - "setuptools>=65" - ] + "uvicorn-0.34.0-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", + "record_version": 3, + "name": "uvicorn", + "version": "0.34.0", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "click>=7.0", + "h11>=0.8", + "typing-extensions>=4.0[when=\"python<3.11\"]", + "python >=3.9" + ], + "extra_depends": { + "standard": [ + "colorama>=0.4[when=\"__win\"]", + "httptools>=0.6.3", + "python-dotenv>=0.13", + "pyyaml>=5.1", + "uvloop!=0.15.0,!=0.15.1,>=0.14.0[when=\"__unix\"]", + "watchfiles>=0.13", + "websockets>=10.4" + ] + }, + "fn": "uvicorn-0.34.0-py3-none-any.whl", + "sha256": "023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", + "size": 62315, + "subdir": "noarch", + "noarch": "python" }, - "fn": "wheel-0.45.1-py3-none-any.whl", - "sha256": "708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", - "size": 72494, - "subdir": "noarch", - "noarch": "python" + "wheel-0.45.1-py3_none_any_0": { + "url": "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", + "record_version": 3, + "name": "wheel", + "version": "0.45.1", + "build": "py3_none_any_0", + "build_number": 0, + "depends": [ + "python >=3.8" + ], + "extra_depends": { + "test": [ + "pytest>=6.0.0", + "setuptools>=65" + ] + }, + "fn": "wheel-0.45.1-py3-none-any.whl", + "sha256": "708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", + "size": 72494, + "subdir": "noarch", + "noarch": "python" + } } } } diff --git a/tests/conda_local_channel/wheel_packages.txt b/tests/conda_local_channel/wheel_packages.txt index 2004168..696c930 100644 --- a/tests/conda_local_channel/wheel_packages.txt +++ b/tests/conda_local_channel/wheel_packages.txt @@ -17,3 +17,14 @@ fire==0.7.1 typeguard==4.4.1 exceptiongroup==1.2.2 termcolor==2.5.0 +httpx==0.28.1 +click==8.1.8 +rich==13.9.4 +pydantic==2.10.6 +ipython==8.30.0 +uvicorn==0.34.0 +0x-web3==4.8.2.1 +advancedselector==3.1.0 +adup==0.1.0 +aba-cli-scrapper==0.7.6 +ali2b-cli-scrapper==1.0.1 diff --git a/tests/test_conda_local_channel.py b/tests/test_conda_local_channel.py index 5379d2f..6ff1968 100644 --- a/tests/test_conda_local_channel.py +++ b/tests/test_conda_local_channel.py @@ -1,8 +1,13 @@ """ Tests for making sure the conda_local_channel fixture functions as we expect + +This module intentionally validates marker-conversion edge cases using +selected pure-Python wheels in `wheel_packages.txt` (for example `ipython`, +`uvicorn`, `0x-web3`, `adup`, and `advancedselector`). """ import json +import re import urllib.request from pathlib import Path @@ -19,18 +24,99 @@ def test_conda_channel(conda_local_channel): with urllib.request.urlopen(url) as response: repodata = json.loads(response.read()) - assert "packages.whl" in repodata - assert len(repodata["packages.whl"]) > 0 + assert "v3" in repodata + assert "whl" in repodata["v3"] + assert len(repodata["v3"]["whl"]) > 0 def test_conda_channel_extras_in_repodata(): - """Verify that wheel entries with extras are present in the repodata.""" + """Verify that wheel entries keep extras as a dedicated repodata field.""" + repodata = json.loads((HERE / "conda_local_channel" / "noarch" / "repodata.json").read_text()) + + record = repodata["v3"]["whl"]["requests-2.32.5-py3_none_any_0"] + assert "extra_depends" in record + socks_dep = record["extra_depends"]["socks"][0] + base_req = Requirement(socks_dep) + assert base_req.name == "pysocks" + assert base_req.specifier == SpecifierSet(">=1.5.6,!=1.5.7") + + +def test_conditional_dependencies_stay_in_depends(): + """Verify non-extra markers are encoded as `when` conditions in `depends`.""" + repodata = json.loads((HERE / "conda_local_channel" / "noarch" / "repodata.json").read_text()) + + record = repodata["v3"]["whl"]["annotated-types-0.7.0-py3_none_any_0"] + assert any( + dep.startswith("typing-extensions>=4.0.0") and '[when="python<3.9"]' in dep + for dep in record["depends"] + ) + + +def test_sys_platform_markers_are_normalized_for_ipython(): + """IPython marker dependencies should use virtual-package conditions.""" + repodata = json.loads((HERE / "conda_local_channel" / "noarch" / "repodata.json").read_text()) + + record = repodata["v3"]["whl"]["ipython-8.30.0-py3_none_any_0"] + colorama_dep = next(dep for dep in record["depends"] if dep.startswith("colorama")) + assert '[when="__win"]' in colorama_dep + + pexpect_dep = next(dep for dep in record["depends"] if dep.startswith("pexpect>4.3")) + assert '[when="__unix"]' in pexpect_dep + assert "sys_platform" not in pexpect_dep + + +def test_uvicorn_standard_extra_has_clean_when_condition(): + """Uvicorn extra deps should not emit malformed logical expressions.""" repodata = json.loads((HERE / "conda_local_channel" / "noarch" / "repodata.json").read_text()) - record = repodata["packages.whl"]["requests-2.32.5-py3_none_any_0"] - assert "extras" in record - socks_deps = record["extras"]["socks"] - assert len(socks_deps) == 1 - req = Requirement(socks_deps[0]) - assert req.name == "pysocks" - assert req.specifier == SpecifierSet(">=1.5.6,!=1.5.7") + record = repodata["v3"]["whl"]["uvicorn-0.34.0-py3_none_any_0"] + uvloop_dep = next( + dep for dep in record["extra_depends"]["standard"] if dep.startswith("uvloop") + ) + assert '[when="__unix"]' in uvloop_dep + assert "and and" not in uvloop_dep + assert "or or" not in uvloop_dep + assert "sys_platform" not in uvloop_dep + assert re.search(r"\b(and|or)\s*\)", uvloop_dep) is None + + +def test_extended_marker_families_are_normalized_in_examples(): + """Known edge-case marker families should normalize in selected records.""" + repodata = json.loads((HERE / "conda_local_channel" / "noarch" / "repodata.json").read_text()) + records = repodata["v3"]["whl"] + + web3_record = records["0x-web3-4.8.2.1-py3_none_any_0"] + cytoolz_dep = next(dep for dep in web3_record["depends"] if dep.startswith("cytoolz")) + toolz_dep = next(dep for dep in web3_record["depends"] if dep.startswith("toolz<1.0.0")) + assert "[when=" not in cytoolz_dep + assert "[when=" not in toolz_dep + + aba_record = records["aba-cli-scrapper-0.7.6-py3_none_any_0"] + nodeenv_dep = next(dep for dep in aba_record["depends"] if dep.startswith("nodeenv==1.9.1")) + charset_dep = next( + dep for dep in aba_record["depends"] if dep.startswith("charset-normalizer==3.3.2") + ) + assert '[when="' in nodeenv_dep + assert "python>=2.7" in nodeenv_dep + assert "python!=3.0" in nodeenv_dep + assert "python!=3.6" in nodeenv_dep + assert "python_version" not in nodeenv_dep + assert '[when="python>=3.7.0"]' in charset_dep + assert "python_full_version" not in charset_dep + + adup_record = records["adup-0.1.0-py3_none_any_0"] + psutil_dep = next( + dep for dep in adup_record["extra_depends"]["testing"] if dep.startswith("psutil") + ) + assert "[when=" not in psutil_dep + + advancedselector_record = records["advancedselector-3.1.0-py3_none_any_0"] + getch_dep = next(dep for dep in advancedselector_record["depends"] if dep.startswith("getch")) + assert '[when="__unix"]' in getch_dep + + ali2b_record = records["ali2b-cli-scrapper-1.0.1-py3_none_any_0"] + greenlet_dep = next( + dep for dep in ali2b_record["depends"] if dep.startswith("greenlet==3.0.3") + ) + assert "[when=" not in greenlet_dep + assert "or" not in greenlet_dep