diff --git a/resolve_cli.python b/resolve_cli.python new file mode 100644 index 00000000..ca085bb3 --- /dev/null +++ b/resolve_cli.python @@ -0,0 +1,403 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# ScanCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/python-inspector for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +from typing import Dict + +import click + +from python_inspector import utils_pypi +from python_inspector.cli_utils import FileOptionType +from python_inspector.utils import write_output_in_file + +TRACE = False + +__version__ = "0.13.0" + +DEFAULT_PYTHON_VERSION = "38" +PYPI_SIMPLE_URL = "https://pypi.org/simple" + + +def print_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + click.echo(f"Python-inspector version: {__version__}") + ctx.exit() + + +@click.command(auto_envvar_prefix="PYTHON_INSPECTOR") +@click.pass_context +@click.option( + "-r", + "--requirement", + "requirement_files", + type=click.Path(exists=True, readable=True, path_type=str, dir_okay=False), + metavar="REQUIREMENT-FILE", + multiple=True, + required=False, + help="Path to pip requirements file listing thirdparty packages. " + "This option can be used multiple times.", +) +@click.option( + "-s", + "--setup-py", + "setup_py_file", + type=click.Path(exists=True, readable=True, path_type=str, dir_okay=False), + metavar="SETUP-PY-FILE", + multiple=False, + required=False, + help="Path to setuptools setup.py file listing dependencies and metadata.", +) +@click.option( + "--spec", + "--specifier", + "specifiers", + type=str, + metavar="SPECIFIER", + multiple=True, + required=False, + help="Package specifier such as django==1.2.3. This option can be used multiple times.", +) +@click.option( + "-p", + "--python-version", + "python_version", + type=click.Choice(utils_pypi.valid_python_versions), + metavar="PYVER", + show_default=True, + required=True, + help="Python version to use for dependency resolution. One of " + + ", ".join(utils_pypi.PYTHON_DOT_VERSIONS_BY_VER.values()), +) +@click.option( + "-o", + "--operating-system", + "operating_system", + type=click.Choice(utils_pypi.PLATFORMS_BY_OS), + metavar="OS", + show_default=True, + required=True, + help="OS to use for dependency resolution. One of " + ", ".join(utils_pypi.PLATFORMS_BY_OS), +) +@click.option( + "--index-url", + "index_urls", + type=str, + metavar="INDEX", + show_default=True, + default=tuple([PYPI_SIMPLE_URL]), + multiple=True, + help="PyPI simple index URL(s) to use in order of preference. " + "This option can be used multiple times.", +) +@click.option( + "--json", + "json_output", + type=FileOptionType(mode="w", encoding="utf-8", lazy=True), + required=False, + metavar="FILE", + help="Write output as pretty-printed JSON to FILE. " + "Use the special '-' file name to print results on screen/stdout.", +) +@click.option( + "--json-pdt", + "pdt_output", + type=FileOptionType(mode="w", encoding="utf-8", lazy=True), + required=False, + metavar="FILE", + help="Write output as pretty-printed JSON to FILE as a tree in the style of pipdeptree. " + "Use the special '-' file name to print results on screen/stdout.", +) +@click.option( + "-n", + "--netrc", + "netrc_file", + type=click.Path(exists=True, readable=True, path_type=str, dir_okay=False), + metavar="NETRC-FILE", + hidden=True, + required=False, + help="Netrc file to use for authentication.", +) +@click.option( + "--max-rounds", + "max_rounds", + hidden=True, + type=int, + default=200000, + help="Increase the maximum number of resolution rounds. " + "Use in the rare cases where the resolution graph is very deep.", +) +@click.option( + "--use-cached-index", + is_flag=True, + hidden=True, + help="Use cached on-disk PyPI simple package indexes " + "and do not refetch package index if cache is present.", +) +@click.option( + "--use-pypi-json-api", + is_flag=True, + help="Use PyPI JSON API to fetch dependency data. Faster but not always correct. " + "--index-url are ignored when this option is active.", +) +@click.option( + "--analyze-setup-py-insecurely", + is_flag=True, + help="Enable collection of requirements in setup.py that compute these " + "dynamically. This is an insecure operation as it can run arbitrary code.", +) +@click.option( + "--prefer-source", + is_flag=True, + help="Prefer source distributions over binary distributions if no source " + "distribution is available then binary distributions are used", +) +@click.option( + "--verbose", + is_flag=True, + help="Enable verbose debug output.", +) +@click.option( + "-V", + "--version", + is_flag=True, + is_eager=True, + expose_value=False, + callback=print_version, + help="Show the version and exit.", +) +@click.option( + "--ignore-errors", is_flag=True, default=False, help="Ignore errors and continue execution." +) +@click.help_option("-h", "--help") +@click.option( + "--generic-paths", + is_flag=True, + hidden=True, + help="Use generic or truncated paths in the JSON output header and files sections. " + "Used only for testing to avoid absolute paths and paths changing at each run.", +) +def resolve_dependencies( + ctx, + requirement_files, + setup_py_file, + specifiers, + python_version, + operating_system, + index_urls, + json_output, + pdt_output, + netrc_file, + max_rounds, + use_cached_index=False, + use_pypi_json_api=False, + analyze_setup_py_insecurely=False, + prefer_source=False, + verbose=TRACE, + generic_paths=False, + ignore_errors=False, +): + """ + Resolve the dependencies for the package requirements listed in one or + more REQUIREMENT-FILE file, one or more SPECIFIER and one setuptools + SETUP-PY-FILE file and save the results as JSON to FILE. + + Resolve the dependencies for the requested ``--python-version`` PYVER and + ``--operating_system`` OS combination. + + Download from the provided PyPI simple --index-url INDEX(s) URLs defaulting + to PyPI.org. + + Provide source distributions over binary distributions with the --prefer-source + option. If no source distribution is available then binary distributions are used. + + Error and progress are printed to stderr. + + For example, display the results of resolving the dependencies for flask==2.1.2 + on screen:: + + python-inspector --spec "flask==2.1.2" --json - + """ + from python_inspector.api import resolve_dependencies as resolver_api + + if not (json_output or pdt_output): + click.secho("No output file specified. Use --json or --json-pdt.", err=True) + ctx.exit(1) + + if json_output and pdt_output: + click.secho("Only one of --json or --json-pdt can be used.", err=True) + ctx.exit(1) + + options = get_pretty_options(ctx, generic_paths=generic_paths) + + notice = ( + "Dependency tree generated with python-inspector.\n" + "python-inspector is a free software tool from nexB Inc. and others.\n" + "Visit https://github.com/aboutcode-org/python-inspector/ for support and download." + ) + + headers = dict( + tool_name="python-inspector", + tool_homepageurl="https://github.com/aboutcode-org/python-inspector", + tool_version=__version__, + options=options, + notice=notice, + warnings=[], + errors=[], + ) + + try: + resolution_result: Dict = resolver_api( + requirement_files=requirement_files, + setup_py_file=setup_py_file, + specifiers=specifiers, + python_version=python_version, + operating_system=operating_system, + index_urls=index_urls, + pdt_output=pdt_output, + netrc_file=netrc_file, + max_rounds=max_rounds, + use_cached_index=use_cached_index, + use_pypi_json_api=use_pypi_json_api, + verbose=verbose, + analyze_setup_py_insecurely=analyze_setup_py_insecurely, + printer=click.secho, + prefer_source=prefer_source, + ignore_errors=ignore_errors, + generic_paths=generic_paths, + ) + + files = resolution_result.files or [] + output = dict( + headers=headers, + files=files, + packages=resolution_result.packages, + resolved_dependencies_graph=resolution_result.resolution, + ) + write_output_in_file( + output=output, + location=json_output or pdt_output, + ) + except Exception: + import traceback + + click.secho(traceback.format_exc(), err=True) + ctx.exit(1) + + +def get_pretty_options(ctx, generic_paths=False): + """ + Return a sorted list of formatted strings for the selected CLI options of + the `ctx` Click.context, putting arguments first then options: + + ["~/some/path", "--license", ...] + + Skip options that are hidden or flags that are not set. + If ``generic_paths`` is True, click.File and click.Path parameters are made + "generic" replacing their value with a placeholder. This is used mostly for + testing. + """ + + args = [] + options = [] + + param_values = ctx.params + for param in ctx.command.params: + name = param.name + value = param_values.get(name) + + if param.is_eager: + continue + + if getattr(param, "hidden", False): + continue + + if value == param.default: + continue + + if value in (None, False): + continue + + if value in (tuple(), []): + # option with multiple values, the value is a emoty tuple + continue + + # opts is a list of CLI options as in "--verbose": the last opt is + # the CLI option long form by convention + cli_opt = param.opts[-1] + + if not isinstance(value, (tuple, list)): + value = [value] + + for val in value: + val = get_pretty_value(param_type=param.type, value=val, generic_paths=generic_paths) + + if isinstance(param, click.Argument): + args.append(val) + else: + # an option + if val is True: + # mere flag... do not add the "true" value + options.append(f"{cli_opt}") + else: + options.append(f"{cli_opt} {val}") + + return sorted(args) + sorted(options) + + +def get_pretty_value(param_type, value, generic_paths=False): + """ + Return pretty formatted string extracted from a parameter ``value``. + Make paths generic (by using a placeholder or truncating the path) if + ``generic_paths`` is True. + """ + if isinstance(param_type, (click.Path, click.File)): + return get_pretty_path(param_type, value, generic_paths) + + elif not (value is None or isinstance(value, (str, bytes, tuple, list, dict, bool))): + # coerce to string for non-basic types + return repr(value) + + else: + return value + + +def get_pretty_path(param_type, value, generic_paths=False): + """ + Return a pretty path value for a Path or File option. Truncate the path or + use a placeholder as needed if ``generic_paths`` is True. Used for testing. + """ + from python_inspector.utils import remove_test_data_dir_variable_prefix + + if value == "-": + return value + + if isinstance(param_type, click.Path): + if generic_paths: + return remove_test_data_dir_variable_prefix(path=value) + return value + + elif isinstance(param_type, click.File): + # the value cannot be displayed as-is as this may be an opened file- + # like object + vname = getattr(value, "name", None) + if not vname: + return "" + else: + value = vname + + if generic_paths: + return remove_test_data_dir_variable_prefix(path=value, placeholder="") + + return value + + +if __name__ == "__main__": + resolve_dependencies() diff --git a/src/python_inspector/api.py b/src/python_inspector/api.py index f820fae3..201338ea 100644 --- a/src/python_inspector/api.py +++ b/src/python_inspector/api.py @@ -5,7 +5,7 @@ # ScanCode is a trademark of nexB Inc. # SPDX-License-Identifier: Apache-2.0 # See http://www.apache.org/licenses/LICENSE-2.0 for the license text. -# See https://aboutcode-orgnexB/python-inspector for support or download. +# See https://aboutcode-org/python-inspector for support or download. # See https://aboutcode.org for more information about nexB OSS projects. # import asyncio @@ -42,6 +42,7 @@ from python_inspector.resolution import get_reqs_insecurely from python_inspector.resolution import get_requirements_from_python_manifest from python_inspector.utils import Candidate +from python_inspector.utils import unique from python_inspector.utils_pypi import PLATFORMS_BY_OS from python_inspector.utils_pypi import Environment from python_inspector.utils_pypi import PypiSimpleRepository @@ -336,7 +337,7 @@ def get_index_urls(index_urls: Tuple, extra_data: Dict) -> Tuple: index_urls = (*index_urls, *tuple(extra_index_urls)) if isinstance(index_url, str): index_urls = (*index_urls, *tuple([index_url])) - return tuple(set(index_urls)) + return tuple(unique(index_urls)) resolver_api = resolve_dependencies diff --git a/src/python_inspector/resolve_cli.py b/src/python_inspector/resolve_cli.py index 0200eb40..29044e2e 100644 --- a/src/python_inspector/resolve_cli.py +++ b/src/python_inspector/resolve_cli.py @@ -91,6 +91,7 @@ def print_version(ctx, param, value): @click.option( "--index-url", "index_urls", + envvar="PYINSP_INDEX_URL", type=str, metavar="INDEX", show_default=True, @@ -122,6 +123,7 @@ def print_version(ctx, param, value): "--netrc", "netrc_file", type=click.Path(exists=True, readable=True, path_type=str, dir_okay=False), + envvar="PYINSP_NETRC_FILE", metavar="NETRC-FILE", hidden=True, required=False, @@ -163,6 +165,7 @@ def print_version(ctx, param, value): ) @click.option( "--verbose", + envvar="PYINSP_VERBOSE", is_flag=True, help="Enable verbose debug output.", ) diff --git a/src/python_inspector/settings.py b/src/python_inspector/settings.py index 5786bb11..ce1f424c 100644 --- a/src/python_inspector/settings.py +++ b/src/python_inspector/settings.py @@ -18,13 +18,13 @@ class Settings(BaseSettings): """ Reference: https://docs.pydantic.dev/latest/concepts/pydantic_settings/ A settings object: use it with an .env file and/or environment variables all prefixed with - PYTHON_INSPECTOR_ + PYINSP_ """ model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", - env_prefix="PYTHON_INSPECTOR_", + env_prefix="PYINSP_", case_sensitive=True, extra="allow", ) diff --git a/src/python_inspector/utils.py b/src/python_inspector/utils.py index a7df84b6..1548ac0b 100644 --- a/src/python_inspector/utils.py +++ b/src/python_inspector/utils.py @@ -101,3 +101,20 @@ def remove_test_data_dir_variable_prefix(path, placeholder=""): return cleaned.replace("\\", "/") else: return placeholder + + +def unique(sequence): + """ + Return a list of unique items found in sequence. Preserve the original sequence order. + Items must be hashable. + For example: + >>> unique([1, 5, 3, 5]) + [1, 5, 3] + """ + seen = set() + deduped = [] + for item in sequence: + if item not in seen: + deduped.append(item) + seen.add(item) + return deduped diff --git a/src/python_inspector/utils_pypi.py b/src/python_inspector/utils_pypi.py index 058fd15c..6a8ea331 100644 --- a/src/python_inspector/utils_pypi.py +++ b/src/python_inspector/utils_pypi.py @@ -252,6 +252,7 @@ async def download_wheel( ) continue for wheel in supported_and_valid_wheels: + wheel.credentials = repo.credentials fetched_wheel_filename = await wheel.download( dest_dir=dest_dir, verbose=verbose, diff --git a/test_resolution2.py.foo b/test_resolution2.py.foo new file mode 100644 index 00000000..f97adc88 --- /dev/null +++ b/test_resolution2.py.foo @@ -0,0 +1,323 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# ScanCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/python-inspector for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# +import os +from unittest.mock import patch + +import packvers +import pytest +from commoncode.system import on_mac +from commoncode.testcase import FileDrivenTesting +from packvers.requirements import Requirement +from test_cli import check_data_results + +from _packagedcode import models +from python_inspector.api import get_resolved_dependencies +from python_inspector.error import NoVersionsFound +from python_inspector.resolution import PythonInputProvider +from python_inspector.resolution import get_requirements_from_dependencies +from python_inspector.resolution import get_requirements_from_python_manifest +from python_inspector.resolution import is_valid_version +from python_inspector.resolution import parse_reqs_from_setup_py_insecurely +from python_inspector.utils_pypi import Environment +from python_inspector.utils_pypi import PypiSimpleRepository +from python_inspector.utils_pypi import get_current_indexes + +# Used for tests to regenerate fixtures with regen=True +REGEN_TEST_FIXTURES = os.getenv("PYINSP_REGEN_TEST_FIXTURES", False) + +setup_test_env = FileDrivenTesting() +setup_test_env.test_data_dir = os.path.join(os.path.dirname(__file__), "data") + + +def check_get_resolved_dependencies( + requirement: Requirement, + expected_file, + python_version, + operating_system, + repos=None, + as_tree=False, + regen=REGEN_TEST_FIXTURES, +): + env = Environment(python_version=python_version, operating_system=operating_system) + + results = list( + get_resolved_dependencies( + requirements=[requirement], + environment=env, + repos=repos or get_current_indexes(), + as_tree=as_tree, + ) + ) + check_data_results(results=results, expected_file=expected_file, regen=regen) + + +@pytest.mark.online +def test_get_resolved_dependencies_with_flask_and_python_310(): + req = Requirement("flask==2.1.2") + req.is_requirement_resolved = True + + expected_file = setup_test_env.get_test_loc( + "resolved_deps/flask-310-expected.json", must_exist=False + ) + + check_get_resolved_dependencies( + req, + expected_file=expected_file, + python_version="310", + operating_system="linux", + as_tree=False, + ) + + +@pytest.mark.online +def test_get_resolved_dependencies_with_flask_and_python_310_windows(): + req = Requirement("flask==2.1.2") + req.is_requirement_resolved = True + + expected_file = setup_test_env.get_test_loc( + "resolved_deps/flask-310-win-expected.json", must_exist=False + ) + + check_get_resolved_dependencies( + req, + expected_file=expected_file, + python_version="310", + operating_system="windows", + as_tree=False, + ) + + +@pytest.mark.online +def test_get_resolved_dependencies_with_flask_and_python_36(): + req = Requirement("flask") + req.is_requirement_resolved = False + + expected_file = setup_test_env.get_test_loc( + "resolved_deps/flask-36-expected.json", must_exist=False + ) + + check_get_resolved_dependencies( + req, + expected_file=expected_file, + python_version="36", + operating_system="linux", + as_tree=False, + ) + + +@pytest.mark.online +def test_get_resolved_dependencies_with_tilde_requirement_using_json_api(): + req = Requirement("flask~=2.1.2") + req.is_requirement_resolved = False + + expected_file = setup_test_env.get_test_loc( + "resolved_deps/flask-39-expected.json", must_exist=False + ) + + check_get_resolved_dependencies( + req, + expected_file=expected_file, + python_version="39", + operating_system="linux", + as_tree=False, + ) + + +@pytest.mark.online +@pytest.mark.skipif(on_mac, reason="torch is only available for linux and windows.") +def test_get_resolved_dependencies_for_version_containing_local_version_identifier(): + req = Requirement("torchcodec==0.2.0+cu124") + req.is_requirement_resolved = True + + repos = [PypiSimpleRepository(index_url="https://download.pytorch.org/whl")] + expected_file = setup_test_env.get_test_loc( + "resolved_deps/torch-312-expected.json", must_exist=False + ) + + check_get_resolved_dependencies( + req, + expected_file=expected_file, + python_version="312", + operating_system="linux", + repos=repos, + as_tree=False, + ) + + +@pytest.mark.online +def test_without_supported_wheels(): + req = Requirement("autobahn==22.3.2") + req.is_requirement_resolved = True + expected_file = setup_test_env.get_test_loc( + "resolved_deps/autobahn-310-expected.json", must_exist=False + ) + + check_get_resolved_dependencies( + req, + expected_file=expected_file, + python_version="39", + operating_system="linux", + as_tree=False, + ) + + +def test_is_valid_version(): + parsed_version = packvers.version.parse("2.1.2") + requirements = {"flask": [Requirement("flask>2.0.0")]} + bad_versions = [] + identifier = "flask" + assert is_valid_version(parsed_version, requirements, identifier, bad_versions) + + +def test_is_valid_version_with_no_specifier(): + parsed_version = packvers.version.parse("2.1.2") + requirements = {"flask": [Requirement("flask")]} + bad_versions = [] + identifier = "flask" + assert is_valid_version(parsed_version, requirements, identifier, bad_versions) + + +def test_is_valid_version_with_no_specifier_and_pre_release(): + parsed_version = packvers.version.parse("1.0.0b4") + requirements = {"flask": [Requirement("flask")]} + bad_versions = [] + identifier = "flask" + assert is_valid_version(parsed_version, requirements, identifier, bad_versions) + + +def test_get_requirements_from_dependencies(): + dependencies = [ + models.DependentPackage( + purl="pkg:pypi/django", + scope="install", + is_runtime=True, + is_optional=False, + is_resolved=False, + extracted_requirement="django>=1.11.11", + extra_data=dict( + is_editable=False, + link=None, + hash_options=[], + is_constraint=False, + is_archive=False, + is_wheel=False, + is_url=False, + is_vcs_url=False, + is_name_at_url=False, + is_local_path=False, + ), + ) + ] + + requirements = [str(r) for r in get_requirements_from_dependencies(dependencies)] + + assert requirements == ["django>=1.11.11"] + + +def test_get_requirements_from_dependencies_with_empty_list(): + assert list(get_requirements_from_dependencies(dependencies=[])) == [] + + +def test_get_requirements_from_dependencies_with_editable_requirements(): + dependencies = [ + models.DependentPackage( + purl="pkg:pypi/django", + scope="install", + is_runtime=True, + is_optional=False, + is_resolved=False, + extracted_requirement="django>=1.11.11", + extra_data=dict( + is_editable=True, + link=None, + hash_options=[], + is_constraint=False, + is_archive=False, + is_wheel=False, + is_url=False, + is_vcs_url=False, + is_name_at_url=False, + is_local_path=False, + ), + ) + ] + + requirements = [str(r) for r in get_requirements_from_dependencies(dependencies)] + + assert requirements == [] + + +def test_get_requirements_from_python_manifest_securely(): + sdist_location = "tests/data/secure-setup" + setup_py_emptyrequires = "setup-emptyrequires.py" + setup_py_norequires = "setup-norequires.py" + setup_py_requires = "setup-requires.py" + analyze_setup_py_insecurely = False + try: + ret = list( + get_requirements_from_python_manifest( + sdist_location, + sdist_location + "/" + setup_py_norequires, + [sdist_location + "/" + setup_py_norequires], + analyze_setup_py_insecurely, + ) + ) + assert ret == [] + except Exception: + pytest.fail("Failure parsing setup.py where requirements are not provided.") + try: + ret = list( + get_requirements_from_python_manifest( + sdist_location, + sdist_location + "/" + setup_py_emptyrequires, + [sdist_location + "/" + setup_py_emptyrequires], + analyze_setup_py_insecurely, + ) + ) + assert ret == [] + except Exception: + pytest.fail("Failure getting empty requirements securely from setup.py.") + with pytest.raises(Exception): + ret = list( + get_requirements_from_python_manifest( + sdist_location, + sdist_location + "/" + setup_py_requires, + [sdist_location + "/" + setup_py_requires], + analyze_setup_py_insecurely, + ).next() + ) + + +def test_setup_py_parsing_insecure(): + setup_py_file = setup_test_env.get_test_loc("insecure-setup/setup.py") + reqs = [str(req) for req in list(parse_reqs_from_setup_py_insecurely(setup_py=setup_py_file))] + assert reqs == ["isodate", "pyparsing", "six"] + + +def test_setup_py_parsing_insecure_testpkh(): + setup_py_file = setup_test_env.get_test_loc("insecure-setup-2/setup.py") + reqs = [str(req) for req in list(parse_reqs_from_setup_py_insecurely(setup_py=setup_py_file))] + assert reqs == [ + "CairoSVG<2.0.0,>=1.0.20", + "click>=5.0.0", + "invenio[auth,base,metadata]>=3.0.0", + "invenio-records==1.0.*,>=1.0.0", + "mock>=1.3.0", + ] + + +@patch("python_inspector.resolution.PythonInputProvider.get_versions_for_package") +def test_iter_matches(mock_versions): + repos = get_current_indexes() + mock_versions.return_value = [] + provider = PythonInputProvider(repos=repos) + with pytest.raises(NoVersionsFound): + list(provider._iter_matches("foo-bar", {"foo-bar": []}, {"foo-bar": []})) diff --git a/tests/data/azure-devops.req-310-expected.json b/tests/data/azure-devops.req-310-expected.json index 157b982a..472c2370 100644 --- a/tests/data/azure-devops.req-310-expected.json +++ b/tests/data/azure-devops.req-310-expected.json @@ -524,12 +524,12 @@ "type": "pypi", "namespace": null, "name": "click", - "version": "8.1.8", + "version": "8.2.1", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate", - "release_date": "2024-12-21T18:38:41", + "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate\n\n## Contributing\n\nSee our [detailed contributing documentation][contrib] for many ways to\ncontribute, including reporting issues, requesting features, asking or answering\nquestions, and making PRs.\n\n[contrib]: https://palletsprojects.com/contributing/", + "release_date": "2025-05-20T23:19:47", "parties": [ { "type": "person", @@ -547,22 +547,18 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", - "size": 98188, + "download_url": "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", + "size": 102215, "sha1": null, - "md5": "7dc0eee374f3bb75bcce4c9dd4222f5f", - "sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "md5": "aeead16d8bed93caa7107ac87b1e5ec8", + "sha256": "61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", "sha512": null, "bug_tracking_url": null, "code_view_url": "https://github.com/pallets/click/", "vcs_url": null, "copyright": null, - "license_expression": null, - "declared_license": { - "classifiers": [ - "License :: OSI Approved :: BSD License" - ] - }, + "license_expression": "BSD-3-Clause", + "declared_license": {}, "notice_text": null, "source_packages": [], "file_references": [], @@ -570,20 +566,20 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/click/8.1.8/json", + "api_data_url": "https://pypi.org/pypi/click/8.2.1/json", "datasource_id": null, - "purl": "pkg:pypi/click@8.1.8" + "purl": "pkg:pypi/click@8.2.1" }, { "type": "pypi", "namespace": null, "name": "cryptography", - "version": "44.0.2", + "version": "45.0.3", "qualifiers": {}, "subpath": null, "primary_language": "Python", "description": "cryptography is a package which provides cryptographic recipes and primitives to Python developers.\npyca/cryptography\n=================\n\n.. image:: https://img.shields.io/pypi/v/cryptography.svg\n :target: https://pypi.org/project/cryptography/\n :alt: Latest Version\n\n.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest\n :target: https://cryptography.io\n :alt: Latest Docs\n\n.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main\n :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain\n\n\n``cryptography`` is a package which provides cryptographic recipes and\nprimitives to Python developers. Our goal is for it to be your \"cryptographic\nstandard library\". It supports Python 3.7+ and PyPy3 7.3.11+.\n\n``cryptography`` includes both high level recipes and low level interfaces to\ncommon cryptographic algorithms such as symmetric ciphers, message digests, and\nkey derivation functions. For example, to encrypt something with\n``cryptography``'s high level symmetric encryption recipe:\n\n.. code-block:: pycon\n\n >>> from cryptography.fernet import Fernet\n >>> # Put this somewhere safe!\n >>> key = Fernet.generate_key()\n >>> f = Fernet(key)\n >>> token = f.encrypt(b\"A really secret message. Not for prying eyes.\")\n >>> token\n b'...'\n >>> f.decrypt(token)\n b'A really secret message. Not for prying eyes.'\n\nYou can find more information in the `documentation`_.\n\nYou can install ``cryptography`` with:\n\n.. code-block:: console\n\n $ pip install cryptography\n\nFor full details see `the installation documentation`_.\n\nDiscussion\n~~~~~~~~~~\n\nIf you run into bugs, you can file them in our `issue tracker`_.\n\nWe maintain a `cryptography-dev`_ mailing list for development discussion.\n\nYou can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get\ninvolved.\n\nSecurity\n~~~~~~~~\n\nNeed to report a security issue? Please consult our `security reporting`_\ndocumentation.\n\n\n.. _`documentation`: https://cryptography.io/\n.. _`the installation documentation`: https://cryptography.io/en/latest/installation/\n.. _`issue tracker`: https://github.com/pyca/cryptography/issues\n.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev\n.. _`security reporting`: https://cryptography.io/en/latest/security/", - "release_date": "2025-03-02T00:00:28", + "release_date": "2025-05-25T14:16:51", "parties": [ { "type": "person", @@ -617,11 +613,11 @@ "Topic :: Security :: Cryptography" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/f3/cd/2558cc08f7b1bb40683f99ff4327f8dcfc7de3affc669e9065e14824511b/cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", - "size": 4298367, + "download_url": "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", + "size": 4560038, "sha1": null, - "md5": "3d99f96dacf683212ee78a468b3ab242", - "sha256": "3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", + "md5": "895f405f8548c6d156a23c615cb8e20f", + "sha256": "232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", "sha512": null, "bug_tracking_url": null, "code_view_url": null, @@ -629,11 +625,7 @@ "copyright": null, "license_expression": null, "declared_license": { - "license": "Apache-2.0 OR BSD-3-Clause", - "classifiers": [ - "License :: OSI Approved :: Apache Software License", - "License :: OSI Approved :: BSD License" - ] + "license": "Apache-2.0 OR BSD-3-Clause" }, "notice_text": null, "source_packages": [], @@ -642,9 +634,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/cryptography/44.0.2/json", + "api_data_url": "https://pypi.org/pypi/cryptography/45.0.3/json", "datasource_id": null, - "purl": "pkg:pypi/cryptography@44.0.2" + "purl": "pkg:pypi/cryptography@45.0.3" }, { "type": "pypi", @@ -1174,12 +1166,12 @@ "type": "pypi", "namespace": null, "name": "typing-extensions", - "version": "4.13.2", + "version": "4.14.0", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Backported and Experimental Type Hints for Python 3.8+\n# Typing Extensions\n\n[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)\n\n[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) \u2013\n[PyPI](https://pypi.org/project/typing-extensions/)\n\n## Overview\n\nThe `typing_extensions` module serves two related purposes:\n\n- Enable use of new type system features on older Python versions. For example,\n `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows\n users on previous Python versions to use it too.\n- Enable experimentation with new type system PEPs before they are accepted and\n added to the `typing` module.\n\n`typing_extensions` is treated specially by static type checkers such as\nmypy and pyright. Objects defined in `typing_extensions` are treated the same\nway as equivalent forms in `typing`.\n\n`typing_extensions` uses\n[Semantic Versioning](https://semver.org/). The\nmajor version will be incremented only for backwards-incompatible changes.\nTherefore, it's safe to depend\non `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,\nwhere `x.y` is the first version that includes all features you need.\n\n## Included items\n\nSee [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a\ncomplete listing of module contents.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)\nfor how to contribute to `typing_extensions`.", - "release_date": "2025-04-10T14:19:03", + "description": "Backported and Experimental Type Hints for Python 3.9+\n# Typing Extensions\n\n[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)\n\n[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) \u2013\n[PyPI](https://pypi.org/project/typing-extensions/)\n\n## Overview\n\nThe `typing_extensions` module serves two related purposes:\n\n- Enable use of new type system features on older Python versions. For example,\n `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows\n users on previous Python versions to use it too.\n- Enable experimentation with new type system PEPs before they are accepted and\n added to the `typing` module.\n\n`typing_extensions` is treated specially by static type checkers such as\nmypy and pyright. Objects defined in `typing_extensions` are treated the same\nway as equivalent forms in `typing`.\n\n`typing_extensions` uses\n[Semantic Versioning](https://semver.org/). The\nmajor version will be incremented only for backwards-incompatible changes.\nTherefore, it's safe to depend\non `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,\nwhere `x.y` is the first version that includes all features you need.\n\n## Included items\n\nSee [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a\ncomplete listing of module contents.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)\nfor how to contribute to `typing_extensions`.", + "release_date": "2025-06-02T14:52:10", "parties": [ { "type": "person", @@ -1212,16 +1204,16 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.9", "Topic :: Software Development" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", - "size": 45806, + "download_url": "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", + "size": 43839, "sha1": null, - "md5": "0d9ada689b5a7c88163dd4c3417b8cc8", - "sha256": "a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", + "md5": "d27a9ceff49d98145c50076842fca792", + "sha256": "a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", "sha512": null, "bug_tracking_url": "https://github.com/python/typing_extensions/issues", "code_view_url": null, @@ -1236,9 +1228,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/typing-extensions/4.13.2/json", + "api_data_url": "https://pypi.org/pypi/typing-extensions/4.14.0/json", "datasource_id": null, - "purl": "pkg:pypi/typing-extensions@4.13.2" + "purl": "pkg:pypi/typing-extensions@4.14.0" }, { "type": "pypi", @@ -1322,7 +1314,7 @@ "dependencies": [ "pkg:pypi/requests@2.32.3", "pkg:pypi/six@1.17.0", - "pkg:pypi/typing-extensions@4.13.2" + "pkg:pypi/typing-extensions@4.14.0" ] }, { @@ -1335,9 +1327,9 @@ "package": "pkg:pypi/azure-storage-blob@12.25.1", "dependencies": [ "pkg:pypi/azure-core@1.34.0", - "pkg:pypi/cryptography@44.0.2", + "pkg:pypi/cryptography@45.0.3", "pkg:pypi/isodate@0.7.2", - "pkg:pypi/typing-extensions@4.13.2" + "pkg:pypi/typing-extensions@4.14.0" ] }, { @@ -1355,11 +1347,11 @@ "dependencies": [] }, { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [] }, { - "package": "pkg:pypi/cryptography@44.0.2", + "package": "pkg:pypi/cryptography@45.0.3", "dependencies": [ "pkg:pypi/cffi@1.17.1" ] @@ -1411,7 +1403,7 @@ "dependencies": [] }, { - "package": "pkg:pypi/typing-extensions@4.13.2", + "package": "pkg:pypi/typing-extensions@4.14.0", "dependencies": [] }, { diff --git a/tests/data/azure-devops.req-312-expected.json b/tests/data/azure-devops.req-312-expected.json index 90640eca..f9c2dbea 100644 --- a/tests/data/azure-devops.req-312-expected.json +++ b/tests/data/azure-devops.req-312-expected.json @@ -524,12 +524,12 @@ "type": "pypi", "namespace": null, "name": "click", - "version": "8.1.8", + "version": "8.2.1", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate", - "release_date": "2024-12-21T18:38:41", + "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate\n\n## Contributing\n\nSee our [detailed contributing documentation][contrib] for many ways to\ncontribute, including reporting issues, requesting features, asking or answering\nquestions, and making PRs.\n\n[contrib]: https://palletsprojects.com/contributing/", + "release_date": "2025-05-20T23:19:47", "parties": [ { "type": "person", @@ -547,22 +547,18 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", - "size": 98188, + "download_url": "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", + "size": 102215, "sha1": null, - "md5": "7dc0eee374f3bb75bcce4c9dd4222f5f", - "sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "md5": "aeead16d8bed93caa7107ac87b1e5ec8", + "sha256": "61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", "sha512": null, "bug_tracking_url": null, "code_view_url": "https://github.com/pallets/click/", "vcs_url": null, "copyright": null, - "license_expression": null, - "declared_license": { - "classifiers": [ - "License :: OSI Approved :: BSD License" - ] - }, + "license_expression": "BSD-3-Clause", + "declared_license": {}, "notice_text": null, "source_packages": [], "file_references": [], @@ -570,20 +566,20 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/click/8.1.8/json", + "api_data_url": "https://pypi.org/pypi/click/8.2.1/json", "datasource_id": null, - "purl": "pkg:pypi/click@8.1.8" + "purl": "pkg:pypi/click@8.2.1" }, { "type": "pypi", "namespace": null, "name": "cryptography", - "version": "44.0.2", + "version": "45.0.3", "qualifiers": {}, "subpath": null, "primary_language": "Python", "description": "cryptography is a package which provides cryptographic recipes and primitives to Python developers.\npyca/cryptography\n=================\n\n.. image:: https://img.shields.io/pypi/v/cryptography.svg\n :target: https://pypi.org/project/cryptography/\n :alt: Latest Version\n\n.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest\n :target: https://cryptography.io\n :alt: Latest Docs\n\n.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main\n :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain\n\n\n``cryptography`` is a package which provides cryptographic recipes and\nprimitives to Python developers. Our goal is for it to be your \"cryptographic\nstandard library\". It supports Python 3.7+ and PyPy3 7.3.11+.\n\n``cryptography`` includes both high level recipes and low level interfaces to\ncommon cryptographic algorithms such as symmetric ciphers, message digests, and\nkey derivation functions. For example, to encrypt something with\n``cryptography``'s high level symmetric encryption recipe:\n\n.. code-block:: pycon\n\n >>> from cryptography.fernet import Fernet\n >>> # Put this somewhere safe!\n >>> key = Fernet.generate_key()\n >>> f = Fernet(key)\n >>> token = f.encrypt(b\"A really secret message. Not for prying eyes.\")\n >>> token\n b'...'\n >>> f.decrypt(token)\n b'A really secret message. Not for prying eyes.'\n\nYou can find more information in the `documentation`_.\n\nYou can install ``cryptography`` with:\n\n.. code-block:: console\n\n $ pip install cryptography\n\nFor full details see `the installation documentation`_.\n\nDiscussion\n~~~~~~~~~~\n\nIf you run into bugs, you can file them in our `issue tracker`_.\n\nWe maintain a `cryptography-dev`_ mailing list for development discussion.\n\nYou can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get\ninvolved.\n\nSecurity\n~~~~~~~~\n\nNeed to report a security issue? Please consult our `security reporting`_\ndocumentation.\n\n\n.. _`documentation`: https://cryptography.io/\n.. _`the installation documentation`: https://cryptography.io/en/latest/installation/\n.. _`issue tracker`: https://github.com/pyca/cryptography/issues\n.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev\n.. _`security reporting`: https://cryptography.io/en/latest/security/", - "release_date": "2025-03-02T00:00:28", + "release_date": "2025-05-25T14:16:51", "parties": [ { "type": "person", @@ -617,11 +613,11 @@ "Topic :: Security :: Cryptography" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/f3/cd/2558cc08f7b1bb40683f99ff4327f8dcfc7de3affc669e9065e14824511b/cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", - "size": 4298367, + "download_url": "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", + "size": 4560038, "sha1": null, - "md5": "3d99f96dacf683212ee78a468b3ab242", - "sha256": "3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", + "md5": "895f405f8548c6d156a23c615cb8e20f", + "sha256": "232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", "sha512": null, "bug_tracking_url": null, "code_view_url": null, @@ -629,11 +625,7 @@ "copyright": null, "license_expression": null, "declared_license": { - "license": "Apache-2.0 OR BSD-3-Clause", - "classifiers": [ - "License :: OSI Approved :: Apache Software License", - "License :: OSI Approved :: BSD License" - ] + "license": "Apache-2.0 OR BSD-3-Clause" }, "notice_text": null, "source_packages": [], @@ -642,9 +634,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/cryptography/44.0.2/json", + "api_data_url": "https://pypi.org/pypi/cryptography/45.0.3/json", "datasource_id": null, - "purl": "pkg:pypi/cryptography@44.0.2" + "purl": "pkg:pypi/cryptography@45.0.3" }, { "type": "pypi", @@ -1174,12 +1166,12 @@ "type": "pypi", "namespace": null, "name": "typing-extensions", - "version": "4.13.2", + "version": "4.14.0", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Backported and Experimental Type Hints for Python 3.8+\n# Typing Extensions\n\n[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)\n\n[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) \u2013\n[PyPI](https://pypi.org/project/typing-extensions/)\n\n## Overview\n\nThe `typing_extensions` module serves two related purposes:\n\n- Enable use of new type system features on older Python versions. For example,\n `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows\n users on previous Python versions to use it too.\n- Enable experimentation with new type system PEPs before they are accepted and\n added to the `typing` module.\n\n`typing_extensions` is treated specially by static type checkers such as\nmypy and pyright. Objects defined in `typing_extensions` are treated the same\nway as equivalent forms in `typing`.\n\n`typing_extensions` uses\n[Semantic Versioning](https://semver.org/). The\nmajor version will be incremented only for backwards-incompatible changes.\nTherefore, it's safe to depend\non `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,\nwhere `x.y` is the first version that includes all features you need.\n\n## Included items\n\nSee [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a\ncomplete listing of module contents.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)\nfor how to contribute to `typing_extensions`.", - "release_date": "2025-04-10T14:19:03", + "description": "Backported and Experimental Type Hints for Python 3.9+\n# Typing Extensions\n\n[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)\n\n[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) \u2013\n[PyPI](https://pypi.org/project/typing-extensions/)\n\n## Overview\n\nThe `typing_extensions` module serves two related purposes:\n\n- Enable use of new type system features on older Python versions. For example,\n `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows\n users on previous Python versions to use it too.\n- Enable experimentation with new type system PEPs before they are accepted and\n added to the `typing` module.\n\n`typing_extensions` is treated specially by static type checkers such as\nmypy and pyright. Objects defined in `typing_extensions` are treated the same\nway as equivalent forms in `typing`.\n\n`typing_extensions` uses\n[Semantic Versioning](https://semver.org/). The\nmajor version will be incremented only for backwards-incompatible changes.\nTherefore, it's safe to depend\non `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,\nwhere `x.y` is the first version that includes all features you need.\n\n## Included items\n\nSee [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a\ncomplete listing of module contents.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)\nfor how to contribute to `typing_extensions`.", + "release_date": "2025-06-02T14:52:10", "parties": [ { "type": "person", @@ -1212,16 +1204,16 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.9", "Topic :: Software Development" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", - "size": 45806, + "download_url": "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", + "size": 43839, "sha1": null, - "md5": "0d9ada689b5a7c88163dd4c3417b8cc8", - "sha256": "a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", + "md5": "d27a9ceff49d98145c50076842fca792", + "sha256": "a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", "sha512": null, "bug_tracking_url": "https://github.com/python/typing_extensions/issues", "code_view_url": null, @@ -1236,9 +1228,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/typing-extensions/4.13.2/json", + "api_data_url": "https://pypi.org/pypi/typing-extensions/4.14.0/json", "datasource_id": null, - "purl": "pkg:pypi/typing-extensions@4.13.2" + "purl": "pkg:pypi/typing-extensions@4.14.0" }, { "type": "pypi", @@ -1322,7 +1314,7 @@ "dependencies": [ "pkg:pypi/requests@2.32.3", "pkg:pypi/six@1.17.0", - "pkg:pypi/typing-extensions@4.13.2" + "pkg:pypi/typing-extensions@4.14.0" ] }, { @@ -1335,9 +1327,9 @@ "package": "pkg:pypi/azure-storage-blob@12.25.1", "dependencies": [ "pkg:pypi/azure-core@1.34.0", - "pkg:pypi/cryptography@44.0.2", + "pkg:pypi/cryptography@45.0.3", "pkg:pypi/isodate@0.7.2", - "pkg:pypi/typing-extensions@4.13.2" + "pkg:pypi/typing-extensions@4.14.0" ] }, { @@ -1355,11 +1347,11 @@ "dependencies": [] }, { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [] }, { - "package": "pkg:pypi/cryptography@44.0.2", + "package": "pkg:pypi/cryptography@45.0.3", "dependencies": [ "pkg:pypi/cffi@1.17.1" ] @@ -1411,7 +1403,7 @@ "dependencies": [] }, { - "package": "pkg:pypi/typing-extensions@4.13.2", + "package": "pkg:pypi/typing-extensions@4.14.0", "dependencies": [] }, { diff --git a/tests/data/azure-devops.req-313-expected.json b/tests/data/azure-devops.req-313-expected.json index 6d71dbc0..65dc9e11 100644 --- a/tests/data/azure-devops.req-313-expected.json +++ b/tests/data/azure-devops.req-313-expected.json @@ -524,12 +524,12 @@ "type": "pypi", "namespace": null, "name": "click", - "version": "8.1.8", + "version": "8.2.1", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate", - "release_date": "2024-12-21T18:38:41", + "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate\n\n## Contributing\n\nSee our [detailed contributing documentation][contrib] for many ways to\ncontribute, including reporting issues, requesting features, asking or answering\nquestions, and making PRs.\n\n[contrib]: https://palletsprojects.com/contributing/", + "release_date": "2025-05-20T23:19:47", "parties": [ { "type": "person", @@ -547,22 +547,18 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", - "size": 98188, + "download_url": "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", + "size": 102215, "sha1": null, - "md5": "7dc0eee374f3bb75bcce4c9dd4222f5f", - "sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "md5": "aeead16d8bed93caa7107ac87b1e5ec8", + "sha256": "61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", "sha512": null, "bug_tracking_url": null, "code_view_url": "https://github.com/pallets/click/", "vcs_url": null, "copyright": null, - "license_expression": null, - "declared_license": { - "classifiers": [ - "License :: OSI Approved :: BSD License" - ] - }, + "license_expression": "BSD-3-Clause", + "declared_license": {}, "notice_text": null, "source_packages": [], "file_references": [], @@ -570,20 +566,20 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/click/8.1.8/json", + "api_data_url": "https://pypi.org/pypi/click/8.2.1/json", "datasource_id": null, - "purl": "pkg:pypi/click@8.1.8" + "purl": "pkg:pypi/click@8.2.1" }, { "type": "pypi", "namespace": null, "name": "cryptography", - "version": "44.0.2", + "version": "45.0.3", "qualifiers": {}, "subpath": null, "primary_language": "Python", "description": "cryptography is a package which provides cryptographic recipes and primitives to Python developers.\npyca/cryptography\n=================\n\n.. image:: https://img.shields.io/pypi/v/cryptography.svg\n :target: https://pypi.org/project/cryptography/\n :alt: Latest Version\n\n.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest\n :target: https://cryptography.io\n :alt: Latest Docs\n\n.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main\n :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain\n\n\n``cryptography`` is a package which provides cryptographic recipes and\nprimitives to Python developers. Our goal is for it to be your \"cryptographic\nstandard library\". It supports Python 3.7+ and PyPy3 7.3.11+.\n\n``cryptography`` includes both high level recipes and low level interfaces to\ncommon cryptographic algorithms such as symmetric ciphers, message digests, and\nkey derivation functions. For example, to encrypt something with\n``cryptography``'s high level symmetric encryption recipe:\n\n.. code-block:: pycon\n\n >>> from cryptography.fernet import Fernet\n >>> # Put this somewhere safe!\n >>> key = Fernet.generate_key()\n >>> f = Fernet(key)\n >>> token = f.encrypt(b\"A really secret message. Not for prying eyes.\")\n >>> token\n b'...'\n >>> f.decrypt(token)\n b'A really secret message. Not for prying eyes.'\n\nYou can find more information in the `documentation`_.\n\nYou can install ``cryptography`` with:\n\n.. code-block:: console\n\n $ pip install cryptography\n\nFor full details see `the installation documentation`_.\n\nDiscussion\n~~~~~~~~~~\n\nIf you run into bugs, you can file them in our `issue tracker`_.\n\nWe maintain a `cryptography-dev`_ mailing list for development discussion.\n\nYou can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get\ninvolved.\n\nSecurity\n~~~~~~~~\n\nNeed to report a security issue? Please consult our `security reporting`_\ndocumentation.\n\n\n.. _`documentation`: https://cryptography.io/\n.. _`the installation documentation`: https://cryptography.io/en/latest/installation/\n.. _`issue tracker`: https://github.com/pyca/cryptography/issues\n.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev\n.. _`security reporting`: https://cryptography.io/en/latest/security/", - "release_date": "2025-03-02T00:00:28", + "release_date": "2025-05-25T14:16:51", "parties": [ { "type": "person", @@ -617,11 +613,11 @@ "Topic :: Security :: Cryptography" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/f3/cd/2558cc08f7b1bb40683f99ff4327f8dcfc7de3affc669e9065e14824511b/cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", - "size": 4298367, + "download_url": "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", + "size": 4560038, "sha1": null, - "md5": "3d99f96dacf683212ee78a468b3ab242", - "sha256": "3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", + "md5": "895f405f8548c6d156a23c615cb8e20f", + "sha256": "232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", "sha512": null, "bug_tracking_url": null, "code_view_url": null, @@ -629,11 +625,7 @@ "copyright": null, "license_expression": null, "declared_license": { - "license": "Apache-2.0 OR BSD-3-Clause", - "classifiers": [ - "License :: OSI Approved :: Apache Software License", - "License :: OSI Approved :: BSD License" - ] + "license": "Apache-2.0 OR BSD-3-Clause" }, "notice_text": null, "source_packages": [], @@ -642,9 +634,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/cryptography/44.0.2/json", + "api_data_url": "https://pypi.org/pypi/cryptography/45.0.3/json", "datasource_id": null, - "purl": "pkg:pypi/cryptography@44.0.2" + "purl": "pkg:pypi/cryptography@45.0.3" }, { "type": "pypi", @@ -1174,12 +1166,12 @@ "type": "pypi", "namespace": null, "name": "typing-extensions", - "version": "4.13.2", + "version": "4.14.0", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Backported and Experimental Type Hints for Python 3.8+\n# Typing Extensions\n\n[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)\n\n[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) \u2013\n[PyPI](https://pypi.org/project/typing-extensions/)\n\n## Overview\n\nThe `typing_extensions` module serves two related purposes:\n\n- Enable use of new type system features on older Python versions. For example,\n `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows\n users on previous Python versions to use it too.\n- Enable experimentation with new type system PEPs before they are accepted and\n added to the `typing` module.\n\n`typing_extensions` is treated specially by static type checkers such as\nmypy and pyright. Objects defined in `typing_extensions` are treated the same\nway as equivalent forms in `typing`.\n\n`typing_extensions` uses\n[Semantic Versioning](https://semver.org/). The\nmajor version will be incremented only for backwards-incompatible changes.\nTherefore, it's safe to depend\non `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,\nwhere `x.y` is the first version that includes all features you need.\n\n## Included items\n\nSee [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a\ncomplete listing of module contents.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)\nfor how to contribute to `typing_extensions`.", - "release_date": "2025-04-10T14:19:03", + "description": "Backported and Experimental Type Hints for Python 3.9+\n# Typing Extensions\n\n[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)\n\n[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) \u2013\n[PyPI](https://pypi.org/project/typing-extensions/)\n\n## Overview\n\nThe `typing_extensions` module serves two related purposes:\n\n- Enable use of new type system features on older Python versions. For example,\n `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows\n users on previous Python versions to use it too.\n- Enable experimentation with new type system PEPs before they are accepted and\n added to the `typing` module.\n\n`typing_extensions` is treated specially by static type checkers such as\nmypy and pyright. Objects defined in `typing_extensions` are treated the same\nway as equivalent forms in `typing`.\n\n`typing_extensions` uses\n[Semantic Versioning](https://semver.org/). The\nmajor version will be incremented only for backwards-incompatible changes.\nTherefore, it's safe to depend\non `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,\nwhere `x.y` is the first version that includes all features you need.\n\n## Included items\n\nSee [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a\ncomplete listing of module contents.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)\nfor how to contribute to `typing_extensions`.", + "release_date": "2025-06-02T14:52:10", "parties": [ { "type": "person", @@ -1212,16 +1204,16 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.9", "Topic :: Software Development" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", - "size": 45806, + "download_url": "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", + "size": 43839, "sha1": null, - "md5": "0d9ada689b5a7c88163dd4c3417b8cc8", - "sha256": "a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", + "md5": "d27a9ceff49d98145c50076842fca792", + "sha256": "a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", "sha512": null, "bug_tracking_url": "https://github.com/python/typing_extensions/issues", "code_view_url": null, @@ -1236,9 +1228,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/typing-extensions/4.13.2/json", + "api_data_url": "https://pypi.org/pypi/typing-extensions/4.14.0/json", "datasource_id": null, - "purl": "pkg:pypi/typing-extensions@4.13.2" + "purl": "pkg:pypi/typing-extensions@4.14.0" }, { "type": "pypi", @@ -1322,7 +1314,7 @@ "dependencies": [ "pkg:pypi/requests@2.32.3", "pkg:pypi/six@1.17.0", - "pkg:pypi/typing-extensions@4.13.2" + "pkg:pypi/typing-extensions@4.14.0" ] }, { @@ -1335,9 +1327,9 @@ "package": "pkg:pypi/azure-storage-blob@12.25.1", "dependencies": [ "pkg:pypi/azure-core@1.34.0", - "pkg:pypi/cryptography@44.0.2", + "pkg:pypi/cryptography@45.0.3", "pkg:pypi/isodate@0.7.2", - "pkg:pypi/typing-extensions@4.13.2" + "pkg:pypi/typing-extensions@4.14.0" ] }, { @@ -1355,11 +1347,11 @@ "dependencies": [] }, { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [] }, { - "package": "pkg:pypi/cryptography@44.0.2", + "package": "pkg:pypi/cryptography@45.0.3", "dependencies": [ "pkg:pypi/cffi@1.17.1" ] @@ -1411,7 +1403,7 @@ "dependencies": [] }, { - "package": "pkg:pypi/typing-extensions@4.13.2", + "package": "pkg:pypi/typing-extensions@4.14.0", "dependencies": [] }, { diff --git a/tests/data/azure-devops.req-38-expected.json b/tests/data/azure-devops.req-38-expected.json index a3b820a8..5dd355c8 100644 --- a/tests/data/azure-devops.req-38-expected.json +++ b/tests/data/azure-devops.req-38-expected.json @@ -579,12 +579,12 @@ "type": "pypi", "namespace": null, "name": "cryptography", - "version": "44.0.2", + "version": "45.0.3", "qualifiers": {}, "subpath": null, "primary_language": "Python", "description": "cryptography is a package which provides cryptographic recipes and primitives to Python developers.\npyca/cryptography\n=================\n\n.. image:: https://img.shields.io/pypi/v/cryptography.svg\n :target: https://pypi.org/project/cryptography/\n :alt: Latest Version\n\n.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest\n :target: https://cryptography.io\n :alt: Latest Docs\n\n.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main\n :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain\n\n\n``cryptography`` is a package which provides cryptographic recipes and\nprimitives to Python developers. Our goal is for it to be your \"cryptographic\nstandard library\". It supports Python 3.7+ and PyPy3 7.3.11+.\n\n``cryptography`` includes both high level recipes and low level interfaces to\ncommon cryptographic algorithms such as symmetric ciphers, message digests, and\nkey derivation functions. For example, to encrypt something with\n``cryptography``'s high level symmetric encryption recipe:\n\n.. code-block:: pycon\n\n >>> from cryptography.fernet import Fernet\n >>> # Put this somewhere safe!\n >>> key = Fernet.generate_key()\n >>> f = Fernet(key)\n >>> token = f.encrypt(b\"A really secret message. Not for prying eyes.\")\n >>> token\n b'...'\n >>> f.decrypt(token)\n b'A really secret message. Not for prying eyes.'\n\nYou can find more information in the `documentation`_.\n\nYou can install ``cryptography`` with:\n\n.. code-block:: console\n\n $ pip install cryptography\n\nFor full details see `the installation documentation`_.\n\nDiscussion\n~~~~~~~~~~\n\nIf you run into bugs, you can file them in our `issue tracker`_.\n\nWe maintain a `cryptography-dev`_ mailing list for development discussion.\n\nYou can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get\ninvolved.\n\nSecurity\n~~~~~~~~\n\nNeed to report a security issue? Please consult our `security reporting`_\ndocumentation.\n\n\n.. _`documentation`: https://cryptography.io/\n.. _`the installation documentation`: https://cryptography.io/en/latest/installation/\n.. _`issue tracker`: https://github.com/pyca/cryptography/issues\n.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev\n.. _`security reporting`: https://cryptography.io/en/latest/security/", - "release_date": "2025-03-02T00:00:28", + "release_date": "2025-05-25T14:16:51", "parties": [ { "type": "person", @@ -618,11 +618,11 @@ "Topic :: Security :: Cryptography" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/f3/cd/2558cc08f7b1bb40683f99ff4327f8dcfc7de3affc669e9065e14824511b/cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", - "size": 4298367, + "download_url": "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", + "size": 4560038, "sha1": null, - "md5": "3d99f96dacf683212ee78a468b3ab242", - "sha256": "3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", + "md5": "895f405f8548c6d156a23c615cb8e20f", + "sha256": "232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", "sha512": null, "bug_tracking_url": null, "code_view_url": null, @@ -630,11 +630,7 @@ "copyright": null, "license_expression": null, "declared_license": { - "license": "Apache-2.0 OR BSD-3-Clause", - "classifiers": [ - "License :: OSI Approved :: Apache Software License", - "License :: OSI Approved :: BSD License" - ] + "license": "Apache-2.0 OR BSD-3-Clause" }, "notice_text": null, "source_packages": [], @@ -643,9 +639,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/cryptography/44.0.2/json", + "api_data_url": "https://pypi.org/pypi/cryptography/45.0.3/json", "datasource_id": null, - "purl": "pkg:pypi/cryptography@44.0.2" + "purl": "pkg:pypi/cryptography@45.0.3" }, { "type": "pypi", @@ -1341,7 +1337,7 @@ "package": "pkg:pypi/azure-storage-blob@12.25.1", "dependencies": [ "pkg:pypi/azure-core@1.33.0", - "pkg:pypi/cryptography@44.0.2", + "pkg:pypi/cryptography@45.0.3", "pkg:pypi/isodate@0.7.2", "pkg:pypi/typing-extensions@4.13.2" ] @@ -1365,7 +1361,7 @@ "dependencies": [] }, { - "package": "pkg:pypi/cryptography@44.0.2", + "package": "pkg:pypi/cryptography@45.0.3", "dependencies": [ "pkg:pypi/cffi@1.17.1" ] diff --git a/tests/data/example-requirements-ignore-errors-expected.json b/tests/data/example-requirements-ignore-errors-expected.json index 9a797f66..36f0b426 100644 --- a/tests/data/example-requirements-ignore-errors-expected.json +++ b/tests/data/example-requirements-ignore-errors-expected.json @@ -107,12 +107,12 @@ "type": "pypi", "namespace": null, "name": "exceptiongroup", - "version": "1.2.2", + "version": "1.3.0", "qualifiers": {}, "subpath": null, "primary_language": "Python", "description": "Backport of PEP 654 (exception groups)\n.. image:: https://github.com/agronholm/exceptiongroup/actions/workflows/test.yml/badge.svg\n :target: https://github.com/agronholm/exceptiongroup/actions/workflows/test.yml\n :alt: Build Status\n.. image:: https://coveralls.io/repos/github/agronholm/exceptiongroup/badge.svg?branch=main\n :target: https://coveralls.io/github/agronholm/exceptiongroup?branch=main\n :alt: Code Coverage\n\nThis is a backport of the ``BaseExceptionGroup`` and ``ExceptionGroup`` classes from\nPython 3.11.\n\nIt contains the following:\n\n* The ``exceptiongroup.BaseExceptionGroup`` and ``exceptiongroup.ExceptionGroup``\n classes\n* A utility function (``exceptiongroup.catch()``) for catching exceptions possibly\n nested in an exception group\n* Patches to the ``TracebackException`` class that properly formats exception groups\n (installed on import)\n* An exception hook that handles formatting of exception groups through\n ``TracebackException`` (installed on import)\n* Special versions of some of the functions from the ``traceback`` module, modified to\n correctly handle exception groups even when monkey patching is disabled, or blocked by\n another custom exception hook:\n\n * ``traceback.format_exception()``\n * ``traceback.format_exception_only()``\n * ``traceback.print_exception()``\n * ``traceback.print_exc()``\n* A backported version of ``contextlib.suppress()`` from Python 3.12.1 which also\n handles suppressing exceptions inside exception groups\n\nIf this package is imported on Python 3.11 or later, the built-in implementations of the\nexception group classes are used instead, ``TracebackException`` is not monkey patched\nand the exception hook won't be installed.\n\nSee the `standard library documentation`_ for more information on exception groups.\n\n.. _standard library documentation: https://docs.python.org/3/library/exceptions.html\n\nCatching exceptions\n===================\n\nDue to the lack of the ``except*`` syntax introduced by `PEP 654`_ in earlier Python\nversions, you need to use ``exceptiongroup.catch()`` to catch exceptions that are\npotentially nested inside an exception group. This function returns a context manager\nthat calls the given handler for any exceptions matching the sole argument.\n\nThe argument to ``catch()`` must be a dict (or any ``Mapping``) where each key is either\nan exception class or an iterable of exception classes. Each value must be a callable\nthat takes a single positional argument. The handler will be called at most once, with\nan exception group as an argument which will contain all the exceptions that are any\nof the given types, or their subclasses. The exception group may contain nested groups\ncontaining more matching exceptions.\n\nThus, the following Python 3.11+ code:\n\n.. code-block:: python\n\n try:\n ...\n except* (ValueError, KeyError) as excgroup:\n for exc in excgroup.exceptions:\n print('Caught exception:', type(exc))\n except* RuntimeError:\n print('Caught runtime error')\n\nwould be written with this backport like this:\n\n.. code-block:: python\n\n from exceptiongroup import BaseExceptionGroup, catch\n\n def value_key_err_handler(excgroup: BaseExceptionGroup) -> None:\n for exc in excgroup.exceptions:\n print('Caught exception:', type(exc))\n\n def runtime_err_handler(exc: BaseExceptionGroup) -> None:\n print('Caught runtime error')\n\n with catch({\n (ValueError, KeyError): value_key_err_handler,\n RuntimeError: runtime_err_handler\n }):\n ...\n\n**NOTE**: Just like with ``except*``, you cannot handle ``BaseExceptionGroup`` or\n``ExceptionGroup`` with ``catch()``.\n\nSuppressing exceptions\n======================\n\nThis library contains a backport of the ``contextlib.suppress()`` context manager from\nPython 3.12.1. It allows you to selectively ignore certain exceptions, even when they're\ninside exception groups:\n\n.. code-block:: python\n\n from exceptiongroup import suppress\n\n with suppress(RuntimeError):\n raise ExceptionGroup(\"\", [RuntimeError(\"boo\")])\n\nNotes on monkey patching\n========================\n\nTo make exception groups render properly when an unhandled exception group is being\nprinted out, this package does two things when it is imported on any Python version\nearlier than 3.11:\n\n#. The ``traceback.TracebackException`` class is monkey patched to store extra\n information about exception groups (in ``__init__()``) and properly format them (in\n ``format()``)\n#. An exception hook is installed at ``sys.excepthook``, provided that no other hook is\n already present. This hook causes the exception to be formatted using\n ``traceback.TracebackException`` rather than the built-in rendered.\n\nIf ``sys.exceptionhook`` is found to be set to something else than the default when\n``exceptiongroup`` is imported, no monkeypatching is done at all.\n\nTo prevent the exception hook and patches from being installed, set the environment\nvariable ``EXCEPTIONGROUP_NO_PATCH`` to ``1``.\n\nFormatting exception groups\n---------------------------\n\nNormally, the monkey patching applied by this library on import will cause exception\ngroups to be printed properly in tracebacks. But in cases when the monkey patching is\nblocked by a third party exception hook, or monkey patching is explicitly disabled,\nyou can still manually format exceptions using the special versions of the ``traceback``\nfunctions, like ``format_exception()``, listed at the top of this page. They work just\nlike their counterparts in the ``traceback`` module, except that they use a separately\npatched subclass of ``TracebackException`` to perform the rendering.\n\nParticularly in cases where a library installs its own exception hook, it is recommended\nto use these special versions to do the actual formatting of exceptions/tracebacks.\n\n.. _PEP 654: https://www.python.org/dev/peps/pep-0654/", - "release_date": "2024-07-12T22:25:58", + "release_date": "2025-05-10T17:42:49", "parties": [ { "type": "person", @@ -130,11 +130,11 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", - "size": 16453, + "download_url": "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", + "size": 16674, "sha1": null, - "md5": "59d2b950145f615193c5ccb02795fdef", - "sha256": "3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "md5": "23ce45ace6329e74ea58448cb6fc4a5b", + "sha256": "4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", "sha512": null, "bug_tracking_url": "https://github.com/agronholm/exceptiongroup/issues", "code_view_url": null, @@ -153,9 +153,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/exceptiongroup/1.2.2/json", + "api_data_url": "https://pypi.org/pypi/exceptiongroup/1.3.0/json", "datasource_id": null, - "purl": "pkg:pypi/exceptiongroup@1.2.2" + "purl": "pkg:pypi/exceptiongroup@1.3.0" }, { "type": "pypi", @@ -289,18 +289,18 @@ "type": "pypi", "namespace": null, "name": "pluggy", - "version": "1.5.0", + "version": "1.6.0", "qualifiers": {}, "subpath": null, "primary_language": "Python", "description": "plugin and hook calling mechanisms for python\n====================================================\npluggy - A minimalist production ready plugin system\n====================================================\n\n|pypi| |conda-forge| |versions| |github-actions| |gitter| |black| |codecov|\n\nThis is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.\n\nPlease `read the docs`_ to learn more!\n\nA definitive example\n====================\n.. code-block:: python\n\n import pluggy\n\n hookspec = pluggy.HookspecMarker(\"myproject\")\n hookimpl = pluggy.HookimplMarker(\"myproject\")\n\n\n class MySpec:\n \"\"\"A hook specification namespace.\"\"\"\n\n @hookspec\n def myhook(self, arg1, arg2):\n \"\"\"My special little hook that you can customize.\"\"\"\n\n\n class Plugin_1:\n \"\"\"A hook implementation namespace.\"\"\"\n\n @hookimpl\n def myhook(self, arg1, arg2):\n print(\"inside Plugin_1.myhook()\")\n return arg1 + arg2\n\n\n class Plugin_2:\n \"\"\"A 2nd hook implementation namespace.\"\"\"\n\n @hookimpl\n def myhook(self, arg1, arg2):\n print(\"inside Plugin_2.myhook()\")\n return arg1 - arg2\n\n\n # create a manager and add the spec\n pm = pluggy.PluginManager(\"myproject\")\n pm.add_hookspecs(MySpec)\n\n # register plugins\n pm.register(Plugin_1())\n pm.register(Plugin_2())\n\n # call our ``myhook`` hook\n results = pm.hook.myhook(arg1=1, arg2=2)\n print(results)\n\n\nRunning this directly gets us::\n\n $ python docs/examples/toy-example.py\n inside Plugin_2.myhook()\n inside Plugin_1.myhook()\n [-1, 3]\n\n\n.. badges\n\n.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg\n :target: https://pypi.org/pypi/pluggy\n\n.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg\n :target: https://pypi.org/pypi/pluggy\n\n.. |github-actions| image:: https://github.com/pytest-dev/pluggy/workflows/main/badge.svg\n :target: https://github.com/pytest-dev/pluggy/actions\n\n.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg\n :target: https://anaconda.org/conda-forge/pytest\n\n.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg\n :alt: Join the chat at https://gitter.im/pytest-dev/pluggy\n :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge\n\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n\n.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/pytest-dev/pluggy\n :alt: Code coverage Status\n\n.. links\n.. _pytest:\n http://pytest.org\n.. _tox:\n https://tox.readthedocs.org\n.. _devpi:\n http://doc.devpi.net\n.. _read the docs:\n https://pluggy.readthedocs.io/en/latest/\n\n\nSupport pluggy\n--------------\n\n`Open Collective`_ is an online funding platform for open and transparent communities.\nIt provides tools to raise money and share your finances in full transparency.\n\nIt is the platform of choice for individuals and companies that want to make one-time or\nmonthly donations directly to the project.\n\n``pluggy`` is part of the ``pytest-dev`` project, see more details in the `pytest collective`_.\n\n.. _Open Collective: https://opencollective.com\n.. _pytest collective: https://opencollective.com/pytest", - "release_date": "2024-04-20T21:34:40", + "release_date": "2025-05-15T12:30:06", "parties": [ { "type": "person", "role": "author", - "name": "Holger Krekel", - "email": "holger@merlinux.eu", + "name": null, + "email": "Holger Krekel ", "url": null } ], @@ -314,7 +314,8 @@ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", @@ -322,12 +323,12 @@ "Topic :: Software Development :: Testing", "Topic :: Utilities" ], - "homepage_url": "https://github.com/pytest-dev/pluggy", - "download_url": "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", - "size": 20556, + "homepage_url": null, + "download_url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", + "size": 20538, "sha1": null, - "md5": "07349a98333a31cbb6ef8f7a17905c77", - "sha256": "44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", + "md5": "e107bd9fd0c26746617d74bac26fa0c5", + "sha256": "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", "sha512": null, "bug_tracking_url": null, "code_view_url": null, @@ -347,20 +348,95 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/pluggy/1.5.0/json", + "api_data_url": "https://pypi.org/pypi/pluggy/1.6.0/json", + "datasource_id": null, + "purl": "pkg:pypi/pluggy@1.6.0" + }, + { + "type": "pypi", + "namespace": null, + "name": "pygments", + "version": "2.19.1", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Pygments\n~~~~~~~~\n\nPygments is a syntax highlighting package written in Python.\n\nIt is a generic syntax highlighter suitable for use in code hosting, forums,\nwikis or other applications that need to prettify source code. Highlights\nare:\n\n* a wide range of over 500 languages and other text formats is supported\n* special attention is paid to details, increasing quality by a fair amount\n* support for new languages and formats are added easily\n* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image\n formats that PIL supports and ANSI sequences\n* it is usable as a command-line tool and as a library\n\nCopyright 2006-2025 by the Pygments team, see ``AUTHORS``.\nLicensed under the BSD, see ``LICENSE`` for details.", + "release_date": "2025-01-06T17:26:25", + "parties": [ + { + "type": "person", + "role": "author", + "name": null, + "email": "Georg Brandl ", + "url": null + }, + { + "type": "person", + "role": "maintainer", + "name": "Matth\u00e4us G. Chajdas", + "email": "Georg Brandl , Jean Abou Samra ", + "url": null + } + ], + "keywords": [ + "syntax highlighting", + "Development Status :: 6 - Mature", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: System Administrators", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Text Processing :: Filters", + "Topic :: Utilities" + ], + "homepage_url": null, + "download_url": "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", + "size": 1225293, + "sha1": null, + "md5": "794747e68f6a2c85e86a8a49e4abb285", + "sha256": "9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", + "sha512": null, + "bug_tracking_url": "https://github.com/pygments/pygments/issues", + "code_view_url": "https://github.com/pygments/pygments", + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": { + "license": "BSD-2-Clause", + "classifiers": [ + "License :: OSI Approved :: BSD License" + ] + }, + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/pygments/2.19.1/json", "datasource_id": null, - "purl": "pkg:pypi/pluggy@1.5.0" + "purl": "pkg:pypi/pygments@2.19.1" }, { "type": "pypi", "namespace": null, "name": "pytest", - "version": "8.3.5", + "version": "8.4.0", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "pytest: simple powerful testing with Python\n.. image:: https://github.com/pytest-dev/pytest/raw/main/doc/en/img/pytest_logo_curves.svg\n :target: https://docs.pytest.org/en/stable/\n :align: center\n :height: 200\n :alt: pytest\n\n\n------\n\n.. image:: https://img.shields.io/pypi/v/pytest.svg\n :target: https://pypi.org/project/pytest/\n\n.. image:: https://img.shields.io/conda/vn/conda-forge/pytest.svg\n :target: https://anaconda.org/conda-forge/pytest\n\n.. image:: https://img.shields.io/pypi/pyversions/pytest.svg\n :target: https://pypi.org/project/pytest/\n\n.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/main/graph/badge.svg\n :target: https://codecov.io/gh/pytest-dev/pytest\n :alt: Code coverage Status\n\n.. image:: https://github.com/pytest-dev/pytest/actions/workflows/test.yml/badge.svg\n :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Atest\n\n.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg\n :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main\n :alt: pre-commit.ci status\n\n.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg\n :target: https://www.codetriage.com/pytest-dev/pytest\n\n.. image:: https://readthedocs.org/projects/pytest/badge/?version=latest\n :target: https://pytest.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n.. image:: https://img.shields.io/badge/Discord-pytest--dev-blue\n :target: https://discord.com/invite/pytest-dev\n :alt: Discord\n\n.. image:: https://img.shields.io/badge/Libera%20chat-%23pytest-orange\n :target: https://web.libera.chat/#pytest\n :alt: Libera chat\n\n\nThe ``pytest`` framework makes it easy to write small tests, yet\nscales to support complex functional testing for applications and libraries.\n\nAn example of a simple test:\n\n.. code-block:: python\n\n # content of test_sample.py\n def inc(x):\n return x + 1\n\n\n def test_answer():\n assert inc(3) == 5\n\n\nTo execute it::\n\n $ pytest\n ============================= test session starts =============================\n collected 1 items\n\n test_sample.py F\n\n ================================== FAILURES ===================================\n _________________________________ test_answer _________________________________\n\n def test_answer():\n > assert inc(3) == 5\n E assert 4 == 5\n E + where 4 = inc(3)\n\n test_sample.py:5: AssertionError\n ========================== 1 failed in 0.04 seconds ===========================\n\n\nDue to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started `_ for more examples.\n\n\nFeatures\n--------\n\n- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names)\n\n- `Auto-discovery\n `_\n of test modules and functions\n\n- `Modular fixtures `_ for\n managing small or parametrized long-lived test resources\n\n- Can run `unittest `_ (or trial)\n test suites out of the box\n\n- Python 3.8+ or PyPy3\n\n- Rich plugin architecture, with over 1300+ `external plugins `_ and thriving community\n\n\nDocumentation\n-------------\n\nFor full documentation, including installation, tutorials and PDF documents, please see https://docs.pytest.org/en/stable/.\n\n\nBugs/Requests\n-------------\n\nPlease use the `GitHub issue tracker `_ to submit bugs or request features.\n\n\nChangelog\n---------\n\nConsult the `Changelog `__ page for fixes and enhancements of each version.\n\n\nSupport pytest\n--------------\n\n`Open Collective`_ is an online funding platform for open and transparent communities.\nIt provides tools to raise money and share your finances in full transparency.\n\nIt is the platform of choice for individuals and companies that want to make one-time or\nmonthly donations directly to the project.\n\nSee more details in the `pytest collective`_.\n\n.. _Open Collective: https://opencollective.com\n.. _pytest collective: https://opencollective.com/pytest\n\n\npytest for enterprise\n---------------------\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of pytest and thousands of other packages are working with Tidelift to deliver commercial support and\nmaintenance for the open source dependencies you use to build your applications.\nSave time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.\n\n`Learn more. `_\n\nSecurity\n^^^^^^^^\n\npytest has never been associated with a security vulnerability, but in any case, to report a\nsecurity vulnerability please use the `Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\n\nLicense\n-------\n\nCopyright Holger Krekel and others, 2004.\n\nDistributed under the terms of the `MIT`_ license, pytest is free and open source software.\n\n.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE", - "release_date": "2025-03-02T12:54:52", + "description": "pytest: simple powerful testing with Python\n.. image:: https://github.com/pytest-dev/pytest/raw/main/doc/en/img/pytest_logo_curves.svg\n :target: https://docs.pytest.org/en/stable/\n :align: center\n :height: 200\n :alt: pytest\n\n\n------\n\n.. image:: https://img.shields.io/pypi/v/pytest.svg\n :target: https://pypi.org/project/pytest/\n\n.. image:: https://img.shields.io/conda/vn/conda-forge/pytest.svg\n :target: https://anaconda.org/conda-forge/pytest\n\n.. image:: https://img.shields.io/pypi/pyversions/pytest.svg\n :target: https://pypi.org/project/pytest/\n\n.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/main/graph/badge.svg\n :target: https://codecov.io/gh/pytest-dev/pytest\n :alt: Code coverage Status\n\n.. image:: https://github.com/pytest-dev/pytest/actions/workflows/test.yml/badge.svg\n :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Atest\n\n.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg\n :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main\n :alt: pre-commit.ci status\n\n.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg\n :target: https://www.codetriage.com/pytest-dev/pytest\n\n.. image:: https://readthedocs.org/projects/pytest/badge/?version=latest\n :target: https://pytest.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n.. image:: https://img.shields.io/badge/Discord-pytest--dev-blue\n :target: https://discord.com/invite/pytest-dev\n :alt: Discord\n\n.. image:: https://img.shields.io/badge/Libera%20chat-%23pytest-orange\n :target: https://web.libera.chat/#pytest\n :alt: Libera chat\n\n\nThe ``pytest`` framework makes it easy to write small tests, yet\nscales to support complex functional testing for applications and libraries.\n\nAn example of a simple test:\n\n.. code-block:: python\n\n # content of test_sample.py\n def inc(x):\n return x + 1\n\n\n def test_answer():\n assert inc(3) == 5\n\n\nTo execute it::\n\n $ pytest\n ============================= test session starts =============================\n collected 1 items\n\n test_sample.py F\n\n ================================== FAILURES ===================================\n _________________________________ test_answer _________________________________\n\n def test_answer():\n > assert inc(3) == 5\n E assert 4 == 5\n E + where 4 = inc(3)\n\n test_sample.py:5: AssertionError\n ========================== 1 failed in 0.04 seconds ===========================\n\n\nDue to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started `_ for more examples.\n\n\nFeatures\n--------\n\n- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names)\n\n- `Auto-discovery\n `_\n of test modules and functions\n\n- `Modular fixtures `_ for\n managing small or parametrized long-lived test resources\n\n- Can run `unittest `_ (or trial)\n test suites out of the box\n\n- Python 3.9+ or PyPy3\n\n- Rich plugin architecture, with over 1300+ `external plugins `_ and thriving community\n\n\nDocumentation\n-------------\n\nFor full documentation, including installation, tutorials and PDF documents, please see https://docs.pytest.org/en/stable/.\n\n\nBugs/Requests\n-------------\n\nPlease use the `GitHub issue tracker `_ to submit bugs or request features.\n\n\nChangelog\n---------\n\nConsult the `Changelog `__ page for fixes and enhancements of each version.\n\n\nSupport pytest\n--------------\n\n`Open Collective`_ is an online funding platform for open and transparent communities.\nIt provides tools to raise money and share your finances in full transparency.\n\nIt is the platform of choice for individuals and companies that want to make one-time or\nmonthly donations directly to the project.\n\nSee more details in the `pytest collective`_.\n\n.. _Open Collective: https://opencollective.com\n.. _pytest collective: https://opencollective.com/pytest\n\n\npytest for enterprise\n---------------------\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of pytest and thousands of other packages are working with Tidelift to deliver commercial support and\nmaintenance for the open source dependencies you use to build your applications.\nSave time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.\n\n`Learn more. `_\n\nSecurity\n^^^^^^^^\n\npytest has never been associated with a security vulnerability, but in any case, to report a\nsecurity vulnerability please use the `Tidelift security contact `_.\nTidelift will coordinate the fix and disclosure.\n\n\nLicense\n-------\n\nCopyright Holger Krekel and others, 2004.\n\nDistributed under the terms of the `MIT`_ license, pytest is free and open source software.\n\n.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE", + "release_date": "2025-06-02T17:36:27", "parties": [ { "type": "person", @@ -383,18 +459,19 @@ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Testing", "Topic :: Utilities" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", - "size": 343634, + "download_url": "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", + "size": 363797, "sha1": null, - "md5": "7ce75916908b7b41f087b13afbe5614c", - "sha256": "c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", + "md5": "e3ae70dc3edd90a392d9061ba2c8d6e6", + "sha256": "f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", "sha512": null, "bug_tracking_url": "https://github.com/pytest-dev/pytest/issues", "code_view_url": "https://github.com/pytest-dev/pytest", @@ -414,9 +491,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/pytest/8.3.5/json", + "api_data_url": "https://pypi.org/pypi/pytest/8.4.0/json", "datasource_id": null, - "purl": "pkg:pypi/pytest@8.3.5" + "purl": "pkg:pypi/pytest@8.4.0" }, { "type": "pypi", @@ -476,12 +553,84 @@ "api_data_url": "https://pypi.org/pypi/tomli/2.2.1/json", "datasource_id": null, "purl": "pkg:pypi/tomli@2.2.1" + }, + { + "type": "pypi", + "namespace": null, + "name": "typing-extensions", + "version": "4.14.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Backported and Experimental Type Hints for Python 3.9+\n# Typing Extensions\n\n[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)\n\n[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) \u2013\n[PyPI](https://pypi.org/project/typing-extensions/)\n\n## Overview\n\nThe `typing_extensions` module serves two related purposes:\n\n- Enable use of new type system features on older Python versions. For example,\n `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows\n users on previous Python versions to use it too.\n- Enable experimentation with new type system PEPs before they are accepted and\n added to the `typing` module.\n\n`typing_extensions` is treated specially by static type checkers such as\nmypy and pyright. Objects defined in `typing_extensions` are treated the same\nway as equivalent forms in `typing`.\n\n`typing_extensions` uses\n[Semantic Versioning](https://semver.org/). The\nmajor version will be incremented only for backwards-incompatible changes.\nTherefore, it's safe to depend\non `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,\nwhere `x.y` is the first version that includes all features you need.\n\n## Included items\n\nSee [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a\ncomplete listing of module contents.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)\nfor how to contribute to `typing_extensions`.", + "release_date": "2025-06-02T14:52:10", + "parties": [ + { + "type": "person", + "role": "author", + "name": null, + "email": "\"Guido van Rossum, Jukka Lehtosalo, \u0141ukasz Langa, Michael Lee\" ", + "url": null + } + ], + "keywords": [ + "annotations", + "backport", + "checker", + "checking", + "function", + "hinting", + "hints", + "type", + "typechecking", + "typehinting", + "typehints", + "typing", + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.9", + "Topic :: Software Development" + ], + "homepage_url": null, + "download_url": "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", + "size": 43839, + "sha1": null, + "md5": "d27a9ceff49d98145c50076842fca792", + "sha256": "a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", + "sha512": null, + "bug_tracking_url": "https://github.com/python/typing_extensions/issues", + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": "PSF-2.0", + "declared_license": {}, + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/typing-extensions/4.14.0/json", + "datasource_id": null, + "purl": "pkg:pypi/typing-extensions@4.14.0" } ], "resolved_dependencies_graph": [ { - "package": "pkg:pypi/exceptiongroup@1.2.2", - "dependencies": [] + "package": "pkg:pypi/exceptiongroup@1.3.0", + "dependencies": [ + "pkg:pypi/typing-extensions@4.14.0" + ] }, { "package": "pkg:pypi/iniconfig@2.1.0", @@ -496,22 +645,31 @@ "dependencies": [] }, { - "package": "pkg:pypi/pluggy@1.5.0", + "package": "pkg:pypi/pluggy@1.6.0", "dependencies": [] }, { - "package": "pkg:pypi/pytest@8.3.5", + "package": "pkg:pypi/pygments@2.19.1", + "dependencies": [] + }, + { + "package": "pkg:pypi/pytest@8.4.0", "dependencies": [ - "pkg:pypi/exceptiongroup@1.2.2", + "pkg:pypi/exceptiongroup@1.3.0", "pkg:pypi/iniconfig@2.1.0", "pkg:pypi/packaging@25.0", - "pkg:pypi/pluggy@1.5.0", + "pkg:pypi/pluggy@1.6.0", + "pkg:pypi/pygments@2.19.1", "pkg:pypi/tomli@2.2.1" ] }, { "package": "pkg:pypi/tomli@2.2.1", "dependencies": [] + }, + { + "package": "pkg:pypi/typing-extensions@4.14.0", + "dependencies": [] } ] } \ No newline at end of file diff --git a/tests/data/resolved_deps/autobahn-310-expected.json b/tests/data/resolved_deps/autobahn-310-expected.json index 0e4bbc24..088bac40 100644 --- a/tests/data/resolved_deps/autobahn-310-expected.json +++ b/tests/data/resolved_deps/autobahn-310-expected.json @@ -5,7 +5,7 @@ "dependencies": [ "pkg:pypi/cryptography@43.0.3", "pkg:pypi/hyperlink@21.0.0", - "pkg:pypi/setuptools@80.1.0", + "pkg:pypi/setuptools@80.9.0", "pkg:pypi/txaio@23.1.1" ] }, @@ -36,7 +36,7 @@ "dependencies": [] }, { - "package": "pkg:pypi/setuptools@80.1.0", + "package": "pkg:pypi/setuptools@80.9.0", "dependencies": [] }, { @@ -51,7 +51,7 @@ "pkg:pypi/hyperlink@21.0.0", "pkg:pypi/idna@3.10", "pkg:pypi/pycparser@2.22", - "pkg:pypi/setuptools@80.1.0", + "pkg:pypi/setuptools@80.9.0", "pkg:pypi/txaio@23.1.1" ] ] \ No newline at end of file diff --git a/tests/data/resolved_deps/flask-310-expected.json b/tests/data/resolved_deps/flask-310-expected.json index 7216782a..cc88a552 100644 --- a/tests/data/resolved_deps/flask-310-expected.json +++ b/tests/data/resolved_deps/flask-310-expected.json @@ -1,13 +1,13 @@ [ [ { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [] }, { "package": "pkg:pypi/flask@2.1.2", "dependencies": [ - "pkg:pypi/click@8.1.8", + "pkg:pypi/click@8.2.1", "pkg:pypi/itsdangerous@2.2.0", "pkg:pypi/jinja2@3.1.6", "pkg:pypi/werkzeug@3.1.3" @@ -35,7 +35,7 @@ } ], [ - "pkg:pypi/click@8.1.8", + "pkg:pypi/click@8.2.1", "pkg:pypi/flask@2.1.2", "pkg:pypi/itsdangerous@2.2.0", "pkg:pypi/jinja2@3.1.6", diff --git a/tests/data/resolved_deps/flask-310-win-expected.json b/tests/data/resolved_deps/flask-310-win-expected.json index af954aa0..a8f45499 100644 --- a/tests/data/resolved_deps/flask-310-win-expected.json +++ b/tests/data/resolved_deps/flask-310-win-expected.json @@ -1,7 +1,7 @@ [ [ { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [ "pkg:pypi/colorama@0.4.6" ] @@ -13,7 +13,7 @@ { "package": "pkg:pypi/flask@2.1.2", "dependencies": [ - "pkg:pypi/click@8.1.8", + "pkg:pypi/click@8.2.1", "pkg:pypi/itsdangerous@2.2.0", "pkg:pypi/jinja2@3.1.6", "pkg:pypi/werkzeug@3.1.3" @@ -41,7 +41,7 @@ } ], [ - "pkg:pypi/click@8.1.8", + "pkg:pypi/click@8.2.1", "pkg:pypi/colorama@0.4.6", "pkg:pypi/flask@2.1.2", "pkg:pypi/itsdangerous@2.2.0", diff --git a/tests/data/resolved_deps/flask-39-expected.json b/tests/data/resolved_deps/flask-39-expected.json index 96d5bf35..d3345b2d 100644 --- a/tests/data/resolved_deps/flask-39-expected.json +++ b/tests/data/resolved_deps/flask-39-expected.json @@ -17,7 +17,7 @@ { "package": "pkg:pypi/importlib-metadata@8.7.0", "dependencies": [ - "pkg:pypi/zipp@3.21.0" + "pkg:pypi/zipp@3.22.0" ] }, { @@ -41,7 +41,7 @@ ] }, { - "package": "pkg:pypi/zipp@3.21.0", + "package": "pkg:pypi/zipp@3.22.0", "dependencies": [] } ], @@ -53,6 +53,6 @@ "pkg:pypi/jinja2@3.1.6", "pkg:pypi/markupsafe@3.0.2", "pkg:pypi/werkzeug@3.1.3", - "pkg:pypi/zipp@3.21.0" + "pkg:pypi/zipp@3.22.0" ] ] \ No newline at end of file diff --git a/tests/data/single-url-env-var-expected.json b/tests/data/single-url-env-var-expected.json new file mode 100644 index 00000000..48db55af --- /dev/null +++ b/tests/data/single-url-env-var-expected.json @@ -0,0 +1,79 @@ +{ + "headers": { + "tool_name": "python-inspector", + "tool_homepageurl": "https://github.com/aboutcode-org/python-inspector", + "tool_version": "0.13.0", + "options": [ + "--index-url https://pypi.org/simple", + "--json ", + "--operating-system linux", + "--python-version 38", + "--specifier zipp==3.8.0" + ], + "notice": "Dependency tree generated with python-inspector.\npython-inspector is a free software tool from nexB Inc. and others.\nVisit https://github.com/aboutcode-org/python-inspector/ for support and download.", + "warnings": [], + "errors": [] + }, + "files": [], + "packages": [ + { + "type": "pypi", + "namespace": null, + "name": "zipp", + "version": "3.8.0", + "qualifiers": {}, + "subpath": null, + "primary_language": "Python", + "description": "Backport of pathlib-compatible object wrapper for zip files\n.. image:: https://img.shields.io/pypi/v/zipp.svg\n :target: `PyPI link`_\n\n.. image:: https://img.shields.io/pypi/pyversions/zipp.svg\n :target: `PyPI link`_\n\n.. _PyPI link: https://pypi.org/project/zipp\n\n.. image:: https://github.com/jaraco/zipp/workflows/tests/badge.svg\n :target: https://github.com/jaraco/zipp/actions?query=workflow%3A%22tests%22\n :alt: tests\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/psf/black\n :alt: Code style: Black\n\n.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest\n.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest\n\n.. image:: https://img.shields.io/badge/skeleton-2022-informational\n :target: https://blog.jaraco.com/skeleton\n\n\nA pathlib-compatible Zipfile object wrapper. Official backport of the standard library\n`Path object `_.\n\n\nCompatibility\n=============\n\nNew features are introduced in this third-party library and later merged\ninto CPython. The following table indicates which versions of this library\nwere contributed to different versions in the standard library:\n\n.. list-table::\n :header-rows: 1\n\n * - zipp\n - stdlib\n * - 3.5\n - 3.11\n * - 3.3\n - 3.9\n * - 1.0\n - 3.8\n\n\nUsage\n=====\n\nUse ``zipp.Path`` in place of ``zipfile.Path`` on any Python.", + "release_date": "2022-04-03T15:07:27", + "parties": [ + { + "type": "person", + "role": "author", + "name": "Jason R. Coombs", + "email": "jaraco@jaraco.com", + "url": null + } + ], + "keywords": [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only" + ], + "homepage_url": "https://github.com/jaraco/zipp", + "download_url": "https://files.pythonhosted.org/packages/80/0e/16a7ee38617aab6a624e95948d314097cc2669edae9b02ded53309941cfc/zipp-3.8.0-py3-none-any.whl", + "size": 5369, + "sha1": null, + "md5": "da531f1b3a5c5e65470cb74729242bfc", + "sha256": "c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099", + "sha512": null, + "bug_tracking_url": null, + "code_view_url": null, + "vcs_url": null, + "copyright": null, + "license_expression": null, + "declared_license": { + "classifiers": [ + "License :: OSI Approved :: MIT License" + ] + }, + "notice_text": null, + "source_packages": [], + "file_references": [], + "extra_data": {}, + "dependencies": [], + "repository_homepage_url": null, + "repository_download_url": null, + "api_data_url": "https://pypi.org/pypi/zipp/3.8.0/json", + "datasource_id": null, + "purl": "pkg:pypi/zipp@3.8.0" + } + ], + "resolved_dependencies_graph": [ + { + "package": "pkg:pypi/zipp@3.8.0", + "dependencies": [] + } + ] +} \ No newline at end of file diff --git a/tests/data/test-api-expected.json b/tests/data/test-api-expected.json index f105ce7a..9236c0c8 100644 --- a/tests/data/test-api-expected.json +++ b/tests/data/test-api-expected.json @@ -5,12 +5,12 @@ "type": "pypi", "namespace": null, "name": "click", - "version": "8.1.8", + "version": "8.2.1", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate", - "release_date": "2024-12-21T18:38:41", + "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate\n\n## Contributing\n\nSee our [detailed contributing documentation][contrib] for many ways to\ncontribute, including reporting issues, requesting features, asking or answering\nquestions, and making PRs.\n\n[contrib]: https://palletsprojects.com/contributing/", + "release_date": "2025-05-20T23:19:47", "parties": [ { "type": "person", @@ -28,22 +28,18 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", - "size": 98188, + "download_url": "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", + "size": 102215, "sha1": null, - "md5": "7dc0eee374f3bb75bcce4c9dd4222f5f", - "sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "md5": "aeead16d8bed93caa7107ac87b1e5ec8", + "sha256": "61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", "sha512": null, "bug_tracking_url": null, "code_view_url": "https://github.com/pallets/click/", "vcs_url": null, "copyright": null, - "license_expression": null, - "declared_license": { - "classifiers": [ - "License :: OSI Approved :: BSD License" - ] - }, + "license_expression": "BSD-3-Clause", + "declared_license": {}, "notice_text": null, "source_packages": [], "file_references": [], @@ -51,9 +47,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/click/8.1.8/json", + "api_data_url": "https://pypi.org/pypi/click/8.2.1/json", "datasource_id": null, - "purl": "pkg:pypi/click@8.1.8" + "purl": "pkg:pypi/click@8.2.1" }, { "type": "pypi", @@ -354,13 +350,13 @@ ], "resolution": [ { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [] }, { "package": "pkg:pypi/flask@2.1.2", "dependencies": [ - "pkg:pypi/click@8.1.8", + "pkg:pypi/click@8.2.1", "pkg:pypi/itsdangerous@2.2.0", "pkg:pypi/jinja2@3.1.6", "pkg:pypi/werkzeug@3.1.3" diff --git a/tests/data/test-api-pdt-expected.json b/tests/data/test-api-pdt-expected.json index 7ad8a8c8..12f53983 100644 --- a/tests/data/test-api-pdt-expected.json +++ b/tests/data/test-api-pdt-expected.json @@ -5,12 +5,12 @@ "type": "pypi", "namespace": null, "name": "click", - "version": "8.1.8", + "version": "8.2.1", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate", - "release_date": "2024-12-21T18:38:41", + "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate\n\n## Contributing\n\nSee our [detailed contributing documentation][contrib] for many ways to\ncontribute, including reporting issues, requesting features, asking or answering\nquestions, and making PRs.\n\n[contrib]: https://palletsprojects.com/contributing/", + "release_date": "2025-05-20T23:19:47", "parties": [ { "type": "person", @@ -28,22 +28,18 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", - "size": 98188, + "download_url": "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", + "size": 102215, "sha1": null, - "md5": "7dc0eee374f3bb75bcce4c9dd4222f5f", - "sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "md5": "aeead16d8bed93caa7107ac87b1e5ec8", + "sha256": "61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", "sha512": null, "bug_tracking_url": null, "code_view_url": "https://github.com/pallets/click/", "vcs_url": null, "copyright": null, - "license_expression": null, - "declared_license": { - "classifiers": [ - "License :: OSI Approved :: BSD License" - ] - }, + "license_expression": "BSD-3-Clause", + "declared_license": {}, "notice_text": null, "source_packages": [], "file_references": [], @@ -51,9 +47,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/click/8.1.8/json", + "api_data_url": "https://pypi.org/pypi/click/8.2.1/json", "datasource_id": null, - "purl": "pkg:pypi/click@8.1.8" + "purl": "pkg:pypi/click@8.2.1" }, { "type": "pypi", @@ -361,7 +357,7 @@ { "key": "click", "package_name": "click", - "installed_version": "8.1.8", + "installed_version": "8.2.1", "dependencies": [] }, { diff --git a/tests/data/test-api-with-prefer-source.json b/tests/data/test-api-with-prefer-source.json index 957b9ea5..80b32323 100644 --- a/tests/data/test-api-with-prefer-source.json +++ b/tests/data/test-api-with-prefer-source.json @@ -5,12 +5,12 @@ "type": "pypi", "namespace": null, "name": "click", - "version": "8.1.8", + "version": "8.2.1", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate", - "release_date": "2024-12-21T18:38:44", + "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate\n\n## Contributing\n\nSee our [detailed contributing documentation][contrib] for many ways to\ncontribute, including reporting issues, requesting features, asking or answering\nquestions, and making PRs.\n\n[contrib]: https://palletsprojects.com/contributing/", + "release_date": "2025-05-20T23:19:49", "parties": [ { "type": "person", @@ -28,22 +28,18 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", - "size": 226593, + "download_url": "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", + "size": 286342, "sha1": null, - "md5": "b52ee8e6c33d88a2b4626e6a6002245d", - "sha256": "ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", + "md5": "438cfd4974584ae4f960ffeab32e8991", + "sha256": "27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", "sha512": null, "bug_tracking_url": null, "code_view_url": "https://github.com/pallets/click/", "vcs_url": null, "copyright": null, - "license_expression": null, - "declared_license": { - "classifiers": [ - "License :: OSI Approved :: BSD License" - ] - }, + "license_expression": "BSD-3-Clause", + "declared_license": {}, "notice_text": null, "source_packages": [], "file_references": [], @@ -51,9 +47,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/click/8.1.8/json", + "api_data_url": "https://pypi.org/pypi/click/8.2.1/json", "datasource_id": null, - "purl": "pkg:pypi/click@8.1.8" + "purl": "pkg:pypi/click@8.2.1" }, { "type": "pypi", @@ -354,13 +350,13 @@ ], "resolution": [ { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [] }, { "package": "pkg:pypi/flask@2.1.2", "dependencies": [ - "pkg:pypi/click@8.1.8", + "pkg:pypi/click@8.2.1", "pkg:pypi/itsdangerous@2.2.0", "pkg:pypi/jinja2@3.1.6", "pkg:pypi/werkzeug@3.1.3" diff --git a/tests/data/test-api-with-python-311.json b/tests/data/test-api-with-python-311.json index 957b9ea5..80b32323 100644 --- a/tests/data/test-api-with-python-311.json +++ b/tests/data/test-api-with-python-311.json @@ -5,12 +5,12 @@ "type": "pypi", "namespace": null, "name": "click", - "version": "8.1.8", + "version": "8.2.1", "qualifiers": {}, "subpath": null, "primary_language": "Python", - "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate", - "release_date": "2024-12-21T18:38:44", + "description": "Composable command line interface toolkit\n# $ click_\n\nClick is a Python package for creating beautiful command line interfaces\nin a composable way with as little code as necessary. It's the \"Command\nLine Interface Creation Kit\". It's highly configurable but comes with\nsensible defaults out of the box.\n\nIt aims to make the process of writing command line tools quick and fun\nwhile also preventing any frustration caused by the inability to\nimplement an intended CLI API.\n\nClick in three points:\n\n- Arbitrary nesting of commands\n- Automatic help page generation\n- Supports lazy loading of subcommands at runtime\n\n\n## A Simple Example\n\n```python\nimport click\n\n@click.command()\n@click.option(\"--count\", default=1, help=\"Number of greetings.\")\n@click.option(\"--name\", prompt=\"Your name\", help=\"The person to greet.\")\ndef hello(count, name):\n \"\"\"Simple program that greets NAME for a total of COUNT times.\"\"\"\n for _ in range(count):\n click.echo(f\"Hello, {name}!\")\n\nif __name__ == '__main__':\n hello()\n```\n\n```\n$ python hello.py --count=3\nYour name: Click\nHello, Click!\nHello, Click!\nHello, Click!\n```\n\n\n## Donate\n\nThe Pallets organization develops and supports Click and other popular\npackages. In order to grow the community of contributors and users, and\nallow the maintainers to devote more time to the projects, [please\ndonate today][].\n\n[please donate today]: https://palletsprojects.com/donate\n\n## Contributing\n\nSee our [detailed contributing documentation][contrib] for many ways to\ncontribute, including reporting issues, requesting features, asking or answering\nquestions, and making PRs.\n\n[contrib]: https://palletsprojects.com/contributing/", + "release_date": "2025-05-20T23:19:49", "parties": [ { "type": "person", @@ -28,22 +28,18 @@ "Typing :: Typed" ], "homepage_url": null, - "download_url": "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", - "size": 226593, + "download_url": "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", + "size": 286342, "sha1": null, - "md5": "b52ee8e6c33d88a2b4626e6a6002245d", - "sha256": "ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", + "md5": "438cfd4974584ae4f960ffeab32e8991", + "sha256": "27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", "sha512": null, "bug_tracking_url": null, "code_view_url": "https://github.com/pallets/click/", "vcs_url": null, "copyright": null, - "license_expression": null, - "declared_license": { - "classifiers": [ - "License :: OSI Approved :: BSD License" - ] - }, + "license_expression": "BSD-3-Clause", + "declared_license": {}, "notice_text": null, "source_packages": [], "file_references": [], @@ -51,9 +47,9 @@ "dependencies": [], "repository_homepage_url": null, "repository_download_url": null, - "api_data_url": "https://pypi.org/pypi/click/8.1.8/json", + "api_data_url": "https://pypi.org/pypi/click/8.2.1/json", "datasource_id": null, - "purl": "pkg:pypi/click@8.1.8" + "purl": "pkg:pypi/click@8.2.1" }, { "type": "pypi", @@ -354,13 +350,13 @@ ], "resolution": [ { - "package": "pkg:pypi/click@8.1.8", + "package": "pkg:pypi/click@8.2.1", "dependencies": [] }, { "package": "pkg:pypi/flask@2.1.2", "dependencies": [ - "pkg:pypi/click@8.1.8", + "pkg:pypi/click@8.2.1", "pkg:pypi/itsdangerous@2.2.0", "pkg:pypi/jinja2@3.1.6", "pkg:pypi/werkzeug@3.1.3" diff --git a/tests/test_cli.py b/tests/test_cli.py index f4b70f99..b7dcc507 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -28,6 +28,15 @@ setup_test_env.test_data_dir = os.path.join(os.path.dirname(__file__), "data", "setup") +def clear_environ(): + for k, v in os.environ.items(): + if k == "PYINSP_REGEN_TEST_FIXTURES": + continue + if "PYINSP" in k: + print(f"deleting: {k}: {v}") + os.unsetenv(k) + + @pytest.mark.online def test_cli_with_default_urls(): expected_file = test_env.get_test_loc("default-url-expected.json", must_exist=False) @@ -174,6 +183,77 @@ def test_cli_with_multiple_index_url_and_tilde_req(): ) +@pytest.mark.online +def test_cli_with_single_env_var_index_url_flag_override(): + # Click default is to override env vars via flag as shown here + expected_file = test_env.get_test_loc("single-url-env-var-expected.json", must_exist=False) + specifier = "zipp==3.8.0" + os.environ["PYINSP_INDEX_URL"] = "https://thirdparty.aboutcode.org/pypi/simple/" + extra_options = [ + "--index-url", + "https://pypi.org/simple", + ] + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=extra_options, + regen=REGEN_TEST_FIXTURES, + ) + os.unsetenv("PYINSP_INDEX_URL") + + +@pytest.mark.online +def test_cli_with_single_env_var_index_url_except_pypi_simple(): + expected_file = test_env.get_test_loc( + "single-url-env-var-except-simple-expected.json", must_exist=False + ) + # using flask since it's not present in thirdparty + specifier = "flask" + os.environ["PYINSP_INDEX_URL"] = "https://thirdparty.aboutcode.org/pypi/simple/" + try: + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=[], + regen=REGEN_TEST_FIXTURES, + ) + except Exception as e: + assert "python_inspector.error.NoVersionsFound: This package does not exist: flask" in str( + e + ) + os.unsetenv("PYINSP_INDEX_URL") + + +@pytest.mark.online +def test_cli_with_multiple_env_var_index_url_and_tilde_req(): + expected_file = test_env.get_test_loc("tilde_req-expected.json", must_exist=False) + specifier = "zipp~=3.8.0" + os.environ[ + "PYINSP_INDEX_URL" + ] = "https://pypi.org/simple https://thirdparty.aboutcode.org/pypi/simple/" + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=[], + regen=REGEN_TEST_FIXTURES, + ) + os.unsetenv("PYINSP_INDEX_URL") + + +@pytest.mark.online +def test_cli_with_single_env_var_index_url(): + expected_file = test_env.get_test_loc("single-url-env-var-expected.json", must_exist=False) + specifier = "zipp==3.8.0" + os.environ["PYINSP_INDEX_URL"] = "https://pypi.org/simple" + check_specs_resolution( + specifier=specifier, + expected_file=expected_file, + extra_options=[], + regen=REGEN_TEST_FIXTURES, + ) + os.unsetenv("PYINSP_INDEX_URL") + + @pytest.mark.online def test_cli_with_environment_marker_and_complex_ranges(): requirements_file = test_env.get_test_loc("environment-marker-test-requirements.txt")