From e5711bad70dcb7ba1a57bdabbbea11c619870b2a Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Fri, 25 Apr 2025 04:26:29 -0400 Subject: [PATCH 1/4] Start of pylock.toml PEP 751 support. --- docs/index.md | 1 + docs/pylock.md | 70 +++++++++ examples/pylock.toml | 69 ++++++++ news/7751.feature.rst | 1 + pipenv/project.py | 21 +++ pipenv/utils/pylock.py | 259 +++++++++++++++++++++++++++++++ tests/integration/test_pylock.py | 95 ++++++++++++ tests/unit/test_pylock.py | 166 ++++++++++++++++++++ 8 files changed, 682 insertions(+) create mode 100644 docs/pylock.md create mode 100644 examples/pylock.toml create mode 100644 news/7751.feature.rst create mode 100644 pipenv/utils/pylock.py create mode 100644 tests/integration/test_pylock.py create mode 100644 tests/unit/test_pylock.py diff --git a/docs/index.md b/docs/index.md index c11edaa358..1574d5186d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -61,6 +61,7 @@ credentials shell docker scripts +pylock advanced diagnose changelog diff --git a/docs/pylock.md b/docs/pylock.md new file mode 100644 index 0000000000..71e287feef --- /dev/null +++ b/docs/pylock.md @@ -0,0 +1,70 @@ +# PEP 751 pylock.toml Support + +Pipenv supports [PEP 751](https://peps.python.org/pep-0751/) pylock.toml files, which provide a standardized format for recording Python dependencies to enable installation reproducibility. + +## What is pylock.toml? + +The pylock.toml file is a standardized lock file format introduced in PEP 751. It is designed to be: + +- Human-readable and machine-generated +- Secure by default (includes file hashes) +- Able to support both single-use and multi-use lock files +- Compatible across different Python packaging tools + +## Using pylock.toml with Pipenv + +Pipenv can automatically detect and use pylock.toml files in your project. When both a Pipfile.lock and a pylock.toml file exist, Pipenv will prioritize the pylock.toml file. + +### Reading pylock.toml Files + +When you run commands like `pipenv install` or `pipenv sync`, Pipenv will check for a pylock.toml file in your project directory. If found, it will use the dependencies specified in the pylock.toml file instead of Pipfile.lock. + +Pipenv looks for pylock.toml files in the following order: +1. A file named `pylock.toml` in the project directory +2. A file matching the pattern `pylock.*.toml` in the project directory + +### Example pylock.toml File + +Here's a simplified example of a pylock.toml file: + +```toml +lock-version = '1.0' +environments = ["sys_platform == 'win32'", "sys_platform == 'linux'", "sys_platform == 'darwin'"] +requires-python = '>=3.8' +extras = [] +dependency-groups = [] +default-groups = [] +created-by = 'pipenv' + +[[packages]] +name = 'requests' +version = '2.28.1' +requires-python = '>=3.7' + +[[packages.wheels]] +name = 'requests-2.28.1-py3-none-any.whl' +upload-time = '2022-07-13T14:00:00Z' +url = 'https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c61441f4AE7b7338a82051330d70/requests-2.28.1-py3-none-any.whl' +size = 61805 +hashes = {sha256 = 'b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7'} +``` + +## Benefits of Using pylock.toml + +- **Standardization**: pylock.toml is a standardized format that can be used by multiple Python packaging tools. +- **Security**: pylock.toml includes file hashes by default, making it more secure against supply chain attacks. +- **Flexibility**: pylock.toml can support both single-use and multi-use lock files, allowing for more complex dependency scenarios. +- **Interoperability**: pylock.toml can be used by different tools, reducing vendor lock-in. + +## Limitations + +- Currently, Pipenv only supports reading pylock.toml files, not writing them. +- Some advanced features of pylock.toml, such as environment markers for extras and dependency groups, are not fully supported yet. + +## Future Plans + +In future releases, Pipenv plans to add support for: + +- Writing pylock.toml files +- Full support for environment markers for extras and dependency groups +- Converting between Pipfile.lock and pylock.toml formats diff --git a/examples/pylock.toml b/examples/pylock.toml new file mode 100644 index 0000000000..ea2c11edcd --- /dev/null +++ b/examples/pylock.toml @@ -0,0 +1,69 @@ +lock-version = '1.0' +environments = ["sys_platform == 'win32'", "sys_platform == 'linux'", "sys_platform == 'darwin'"] +requires-python = '>=3.8' +extras = [] +dependency-groups = [] +default-groups = [] +created-by = 'pipenv' + +[[packages]] +name = 'requests' +version = '2.28.1' +requires-python = '>=3.7' + +[[packages.wheels]] +name = 'requests-2.28.1-py3-none-any.whl' +upload-time = '2022-07-13T14:00:00Z' +url = 'https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c61441f4AE7b7338a82051330d70/requests-2.28.1-py3-none-any.whl' +size = 61805 +hashes = {sha256 = 'b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7'} + +[[packages]] +name = 'urllib3' +version = '1.26.12' +requires-python = '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4' + +[[packages.wheels]] +name = 'urllib3-1.26.12-py2.py3-none-any.whl' +upload-time = '2022-09-08T14:30:00Z' +url = 'https://files.pythonhosted.org/packages/6f/de/5be2e3eed8426f871b170663333a0f627fc2924cc386cd41a2d2d3f1699/urllib3-1.26.12-py2.py3-none-any.whl' +size = 141862 +hashes = {sha256 = 'b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997'} + +[[packages]] +name = 'certifi' +version = '2022.9.24' + +[[packages.wheels]] +name = 'certifi-2022.9.24-py3-none-any.whl' +upload-time = '2022-09-24T18:00:00Z' +url = 'https://files.pythonhosted.org/packages/1d/38/fa96a426e0c0e68aabc68e896584b83ad1eec779265a028e156ce509630/certifi-2022.9.24-py3-none-any.whl' +size = 161915 +hashes = {sha256 = '0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14'} + +[[packages]] +name = 'charset-normalizer' +version = '2.1.1' +requires-python = '>=3.6.0' + +[[packages.wheels]] +name = 'charset_normalizer-2.1.1-py3-none-any.whl' +upload-time = '2022-08-05T12:00:00Z' +url = 'https://files.pythonhosted.org/packages/a1/34/44964211e5410b051e4b8d2869c470ae8a68ae274953b1c7de6d98bbcf94/charset_normalizer-2.1.1-py3-none-any.whl' +size = 39673 +hashes = {sha256 = '83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f'} + +[[packages]] +name = 'idna' +version = '3.4' + +[[packages.wheels]] +name = 'idna-3.4-py3-none-any.whl' +upload-time = '2022-08-12T15:00:00Z' +url = 'https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl' +size = 61545 +hashes = {sha256 = '90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2'} + +[tool.pipenv] +generated_from = "Pipfile.lock" +generation_date = "2025-04-25T04:20:00Z" diff --git a/news/7751.feature.rst b/news/7751.feature.rst new file mode 100644 index 0000000000..950e7dbb0c --- /dev/null +++ b/news/7751.feature.rst @@ -0,0 +1 @@ +Added support for reading PEP 751 pylock.toml files. When both a Pipfile.lock and a pylock.toml file exist, Pipenv will prioritize the pylock.toml file. diff --git a/pipenv/project.py b/pipenv/project.py index 8f51e4d25d..d02e2bb926 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -57,6 +57,7 @@ proper_case, ) from pipenv.utils.locking import atomic_open_for_write +from pipenv.utils.pylock import PylockFile, find_pylock_file from pipenv.utils.project import get_default_pyproject_backend from pipenv.utils.requirements import normalize_name from pipenv.utils.shell import ( @@ -793,6 +794,19 @@ def _pipfile(self): pf = ReqLibPipfile.load(self.pipfile_location) return pf + @property + def pylock_location(self): + """Returns the location of the pylock.toml file, if it exists.""" + pylock_path = find_pylock_file(self.project_directory) + if pylock_path: + return str(pylock_path) + return None + + @property + def pylock_exists(self): + """Returns True if a pylock.toml file exists.""" + return self.pylock_location is not None + @property def lockfile_location(self): return f"{self.pipfile_location}.lock" @@ -803,6 +817,13 @@ def lockfile_exists(self): @property def lockfile_content(self): + """Returns the content of the lockfile, checking for pylock.toml first.""" + if self.pylock_exists: + try: + pylock = PylockFile.from_path(self.pylock_location) + return pylock.convert_to_pipenv_lockfile() + except Exception as e: + err.print(f"[bold yellow]Error loading pylock.toml: {e}[/bold yellow]") return self.load_lockfile() def get_editable_packages(self, category): diff --git a/pipenv/utils/pylock.py b/pipenv/utils/pylock.py new file mode 100644 index 0000000000..a82004fab9 --- /dev/null +++ b/pipenv/utils/pylock.py @@ -0,0 +1,259 @@ +""" +PEP 751 pylock.toml file handling utilities. + +This module provides functionality for reading and parsing pylock.toml files +as specified in PEP 751 (A file format to record Python dependencies for +installation reproducibility). +""" + +import os +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Dict, List, Optional, Set, Union + +from pipenv.utils.toml import tomlkit_value_to_python +from pipenv.vendor import tomlkit + + +class PylockError(Exception): + """Base exception for pylock.toml related errors.""" + + pass + + +class PylockVersionError(PylockError): + """Raised when the lock-version is not supported.""" + + pass + + +class PylockFormatError(PylockError): + """Raised when the pylock.toml file format is invalid.""" + + pass + + +@dataclass +class PylockFile: + """Represents a pylock.toml file as specified in PEP 751.""" + + path: Path + data: Dict[str, Any] = field(default_factory=dict) + + @classmethod + def from_path(cls, path: Union[str, Path]) -> "PylockFile": + """Load a pylock.toml file from the given path. + + Args: + path: Path to the pylock.toml file + + Returns: + A PylockFile instance + + Raises: + FileNotFoundError: If the file doesn't exist + PylockFormatError: If the file is not a valid pylock.toml file + PylockVersionError: If the lock-version is not supported + """ + if isinstance(path, str): + path = Path(path) + + if not path.exists(): + raise FileNotFoundError(f"Pylock file not found: {path}") + + try: + with open(path, encoding="utf-8") as f: + data = tomlkit.parse(f.read()) + except Exception as e: + raise PylockFormatError(f"Invalid pylock.toml file: {e}") + + # Validate lock-version + lock_version = data.get("lock-version") + if not lock_version: + raise PylockFormatError("Missing required field: lock-version") + + # Currently, we only support version 1.0 + if lock_version != "1.0": + raise PylockVersionError( + f"Unsupported lock-version: {lock_version}. Only version 1.0 is supported." + ) + + return cls(path=path, data=tomlkit_value_to_python(data)) + + @property + def lock_version(self) -> str: + """Get the lock-version.""" + return self.data.get("lock-version", "") + + @property + def environments(self) -> List[str]: + """Get the environments list.""" + return self.data.get("environments", []) + + @property + def requires_python(self) -> Optional[str]: + """Get the requires-python value.""" + return self.data.get("requires-python") + + @property + def extras(self) -> List[str]: + """Get the extras list.""" + return self.data.get("extras", []) + + @property + def dependency_groups(self) -> List[str]: + """Get the dependency-groups list.""" + return self.data.get("dependency-groups", []) + + @property + def default_groups(self) -> List[str]: + """Get the default-groups list.""" + return self.data.get("default-groups", []) + + @property + def created_by(self) -> str: + """Get the created-by value.""" + return self.data.get("created-by", "") + + @property + def packages(self) -> List[Dict[str, Any]]: + """Get the packages list.""" + return self.data.get("packages", []) + + @property + def tool(self) -> Dict[str, Any]: + """Get the tool table.""" + return self.data.get("tool", {}) + + def get_packages_for_environment( + self, + extras: Optional[Set[str]] = None, + dependency_groups: Optional[Set[str]] = None, + ) -> List[Dict[str, Any]]: + """Get packages that should be installed for the given environment. + + Args: + extras: Set of extras to include + dependency_groups: Set of dependency groups to include + + Returns: + List of package dictionaries that should be installed + """ + if extras is None: + extras = set() + if dependency_groups is None: + dependency_groups = set(self.default_groups) + + result = [] + + for package in self.packages: + # Check if the package has a marker + marker = package.get("marker") + if marker: + # TODO: Implement proper marker evaluation with extras and dependency_groups + # For now, we'll just include packages without markers or with simple markers + if "extras" in marker or "dependency_groups" in marker: + # Skip packages with extras or dependency_groups markers for now + continue + + result.append(package) + + return result + + def convert_to_pipenv_lockfile(self) -> Dict[str, Any]: + """Convert the pylock.toml file to a Pipfile.lock format. + + Returns: + A dictionary in Pipfile.lock format + """ + # Create the basic structure + lockfile = { + "_meta": { + "hash": {"sha256": ""}, # We don't have a hash in pylock.toml + "pipfile-spec": 6, + "requires": {}, + "sources": [], + }, + "default": {}, + "develop": {}, + } + + # Add Python version requirement if present + if self.requires_python: + lockfile["_meta"]["requires"]["python_version"] = self.requires_python + + # Process packages + for package in self.packages: + name = package.get("name") + if not name: + continue + + # Determine if this is a dev package based on markers + # This is a simplification - in reality we'd need to parse the markers + is_dev = False + marker = package.get("marker", "") + if marker and "dependency_groups" in marker: + # Simple heuristic - if it mentions dev or test, it's probably a dev package + if "dev" in marker.lower() or "test" in marker.lower(): + is_dev = True + + # Create the package entry + package_entry = {} + + # Add version if present + if "version" in package: + package_entry["version"] = f"=={package['version']}" + + # Add hashes if present + hashes = [] + if "wheels" in package: + hashes.extend( + f"sha256:{wheel['hashes']['sha256']}" + for wheel in package["wheels"] + if "hashes" in wheel and "sha256" in wheel["hashes"] + ) + if ( + "sdist" in package + and "hashes" in package["sdist"] + and "sha256" in package["sdist"]["hashes"] + ): + hashes.append(f"sha256:{package['sdist']['hashes']['sha256']}") + if hashes: + package_entry["hashes"] = hashes + + # Add marker if present + if marker: + package_entry["markers"] = marker + + # Add to the appropriate section + section = "develop" if is_dev else "default" + lockfile[section][name] = package_entry + + return lockfile + + +def find_pylock_file(directory: Union[str, Path] = None) -> Optional[Path]: + """Find a pylock.toml file in the given directory. + + Args: + directory: Directory to search in, defaults to current directory + + Returns: + Path to the pylock.toml file if found, None otherwise + """ + if directory is None: + directory = os.getcwd() + + if isinstance(directory, str): + directory = Path(directory) + + # First, look for pylock.toml + pylock_path = directory / "pylock.toml" + if pylock_path.exists(): + return pylock_path + + # Then, look for named pylock files (pylock.*.toml) + for file in directory.glob("pylock.*.toml"): + return file + + return None diff --git a/tests/integration/test_pylock.py b/tests/integration/test_pylock.py new file mode 100644 index 0000000000..88ee83ba3b --- /dev/null +++ b/tests/integration/test_pylock.py @@ -0,0 +1,95 @@ +import os +import shutil +from pathlib import Path + +import pytest + +from pipenv.project import Project +from pipenv.utils.pylock import PylockFile, find_pylock_file + + +@pytest.fixture +def pylock_project(tmp_path): + """Create a temporary project with a pylock.toml file.""" + # Copy the example pylock.toml to the temporary directory + example_pylock = Path(__file__).parent.parent.parent / "examples" / "pylock.toml" + tmp_pylock = tmp_path / "pylock.toml" + + # Create a simple Pipfile + pipfile_content = """ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +requests = "*" + +[dev-packages] + +[requires] +python_version = "3.8" +""" + + with open(tmp_path / "Pipfile", "w") as f: + f.write(pipfile_content) + + shutil.copy(example_pylock, tmp_pylock) + + # Change to the temporary directory + old_cwd = os.getcwd() + os.chdir(tmp_path) + + try: + yield tmp_path + finally: + os.chdir(old_cwd) + + +def test_find_pylock_file(pylock_project): + """Test that find_pylock_file correctly finds the pylock.toml file.""" + pylock_path = find_pylock_file(pylock_project) + assert pylock_path is not None + assert pylock_path.name == "pylock.toml" + assert pylock_path.exists() + + +def test_pylock_file_loading(pylock_project): + """Test loading a pylock.toml file.""" + pylock_path = pylock_project / "pylock.toml" + pylock = PylockFile.from_path(pylock_path) + + assert pylock.lock_version == "1.0" + assert pylock.created_by == "pipenv" + assert pylock.requires_python == ">=3.8" + assert len(pylock.packages) == 5 + assert pylock.packages[0]["name"] == "requests" + assert pylock.packages[0]["version"] == "2.28.1" + + +def test_project_pylock_integration(pylock_project): + """Test that Project class correctly detects and uses pylock.toml.""" + # Create a project instance + project = Project(chdir=False) + + # Check that pylock.toml is detected + assert project.pylock_exists + assert project.pylock_location is not None + assert Path(project.pylock_location).name == "pylock.toml" + + # Check that lockfile_content returns the converted pylock content + lockfile_content = project.lockfile_content + assert "_meta" in lockfile_content + assert "default" in lockfile_content + assert "requests" in lockfile_content["default"] + assert "urllib3" in lockfile_content["default"] + assert "certifi" in lockfile_content["default"] + assert "charset-normalizer" in lockfile_content["default"] + assert "idna" in lockfile_content["default"] + + # Check that the converted content has the correct format + requests_entry = lockfile_content["default"]["requests"] + assert requests_entry["version"] == "==2.28.1" + assert "hashes" in requests_entry + assert len(requests_entry["hashes"]) == 1 + assert requests_entry["hashes"][0].startswith("sha256:") diff --git a/tests/unit/test_pylock.py b/tests/unit/test_pylock.py new file mode 100644 index 0000000000..6f995d0f0a --- /dev/null +++ b/tests/unit/test_pylock.py @@ -0,0 +1,166 @@ +import os +import tempfile +from pathlib import Path + +import pytest + +from pipenv.utils.pylock import PylockFile, PylockFormatError, PylockVersionError, find_pylock_file + + +@pytest.fixture +def valid_pylock_content(): + return """ +lock-version = '1.0' +environments = ["sys_platform == 'win32'", "sys_platform == 'linux'"] +requires-python = '==3.12' +created-by = 'test-tool' + +[[packages]] +name = 'requests' +version = '2.28.1' +requires-python = '>=3.7' + +[[packages.wheels]] +name = 'requests-2.28.1-py3-none-any.whl' +upload-time = '2022-07-13T14:00:00Z' +url = 'https://files.pythonhosted.org/packages/example/requests-2.28.1-py3-none-any.whl' +size = 61000 +hashes = {sha256 = 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'} + +[[packages]] +name = 'pytest' +version = '7.0.0' +marker = "dependency_groups in ('dev', 'test')" +requires-python = '>=3.6' + +[[packages.wheels]] +name = 'pytest-7.0.0-py3-none-any.whl' +upload-time = '2022-02-03T12:00:00Z' +url = 'https://files.pythonhosted.org/packages/example/pytest-7.0.0-py3-none-any.whl' +size = 45000 +hashes = {sha256 = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'} +""" + + +@pytest.fixture +def invalid_version_pylock_content(): + return """ +lock-version = '2.0' +created-by = 'test-tool' + +[[packages]] +name = 'requests' +version = '2.28.1' +""" + + +@pytest.fixture +def missing_version_pylock_content(): + return """ +created-by = 'test-tool' + +[[packages]] +name = 'requests' +version = '2.28.1' +""" + + +@pytest.fixture +def pylock_file(valid_pylock_content): + with tempfile.NamedTemporaryFile(mode='w+', suffix='.toml', delete=False) as f: + f.write(valid_pylock_content) + f.flush() + path = f.name + + yield path + + # Clean up + if os.path.exists(path): + os.unlink(path) + + +def test_pylock_file_from_path(pylock_file): + """Test loading a pylock file from a path.""" + pylock = PylockFile.from_path(pylock_file) + + assert pylock.lock_version == "1.0" + assert pylock.created_by == "test-tool" + assert pylock.requires_python == "==3.12" + assert len(pylock.packages) == 2 + assert pylock.packages[0]["name"] == "requests" + assert pylock.packages[0]["version"] == "2.28.1" + assert pylock.packages[1]["name"] == "pytest" + assert pylock.packages[1]["marker"] == "dependency_groups in ('dev', 'test')" + + +def test_pylock_file_invalid_version(invalid_version_pylock_content): + """Test loading a pylock file with an invalid version.""" + with tempfile.NamedTemporaryFile(mode='w+', suffix='.toml', delete=False) as f: + f.write(invalid_version_pylock_content) + f.flush() + path = f.name + + try: + with pytest.raises(PylockVersionError): + PylockFile.from_path(path) + finally: + if os.path.exists(path): + os.unlink(path) + + +def test_pylock_file_missing_version(missing_version_pylock_content): + """Test loading a pylock file with a missing version.""" + with tempfile.NamedTemporaryFile(mode='w+', suffix='.toml', delete=False) as f: + f.write(missing_version_pylock_content) + f.flush() + path = f.name + + try: + with pytest.raises(PylockFormatError): + PylockFile.from_path(path) + finally: + if os.path.exists(path): + os.unlink(path) + + +def test_find_pylock_file(): + """Test finding a pylock file.""" + with tempfile.TemporaryDirectory() as tmpdir: + # No pylock file + assert find_pylock_file(tmpdir) is None + + # Create pylock.toml + pylock_path = os.path.join(tmpdir, "pylock.toml") + with open(pylock_path, "w") as f: + f.write("lock-version = '1.0'") + + assert find_pylock_file(tmpdir) == Path(pylock_path) + + # Create pylock.dev.toml + os.unlink(pylock_path) # Remove pylock.toml + pylock_dev_path = os.path.join(tmpdir, "pylock.dev.toml") + with open(pylock_dev_path, "w") as f: + f.write("lock-version = '1.0'") + + assert find_pylock_file(tmpdir) == Path(pylock_dev_path) + + +def test_convert_to_pipenv_lockfile(pylock_file): + """Test converting a pylock file to a Pipfile.lock format.""" + pylock = PylockFile.from_path(pylock_file) + lockfile = pylock.convert_to_pipenv_lockfile() + + # Check structure + assert "_meta" in lockfile + assert "default" in lockfile + assert "develop" in lockfile + + # Check packages + assert "requests" in lockfile["default"] + assert "pytest" in lockfile["develop"] + + # Check package details + assert lockfile["default"]["requests"]["version"] == "==2.28.1" + assert "hashes" in lockfile["default"]["requests"] + assert lockfile["develop"]["pytest"]["version"] == "==7.0.0" + assert lockfile["develop"]["pytest"]["markers"] == "dependency_groups in ('dev', 'test')" From fdcc04a71d7026a56952e72ec88cb70e723a896f Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Fri, 25 Apr 2025 04:35:44 -0400 Subject: [PATCH 2/4] Ability to write the pylock.yaml --- docs/pylock.md | 25 ++- examples/Pipfile.with_pylock | 21 +++ news/7751.feature.rst | 6 +- pipenv/project.py | 30 ++++ pipenv/utils/pylock.py | 188 +++++++++++++++++++++ tests/integration/test_pylock.py | 271 +++++++++++++++++++++++++++++++ tests/unit/test_pylock.py | 122 ++++++++++++++ 7 files changed, 659 insertions(+), 4 deletions(-) create mode 100644 examples/Pipfile.with_pylock diff --git a/docs/pylock.md b/docs/pylock.md index 71e287feef..fdb7061b74 100644 --- a/docs/pylock.md +++ b/docs/pylock.md @@ -56,15 +56,34 @@ hashes = {sha256 = 'b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b - **Flexibility**: pylock.toml can support both single-use and multi-use lock files, allowing for more complex dependency scenarios. - **Interoperability**: pylock.toml can be used by different tools, reducing vendor lock-in. +## Writing pylock.toml Files + +Pipenv can generate pylock.toml files alongside Pipfile.lock files. To enable this feature, add the following to your Pipfile: + +```toml +[pipenv] +use_pylock = true +``` + +With this setting, whenever Pipenv updates the Pipfile.lock file (e.g., when running `pipenv lock`), it will also generate a pylock.toml file in the same directory. + +You can also specify a custom name for the pylock.toml file: + +```toml +[pipenv] +use_pylock = true +pylock_name = "dev" # This will generate pylock.dev.toml +``` + ## Limitations -- Currently, Pipenv only supports reading pylock.toml files, not writing them. - Some advanced features of pylock.toml, such as environment markers for extras and dependency groups, are not fully supported yet. +- The generated pylock.toml files are simplified versions of what a full PEP 751 implementation might produce. ## Future Plans In future releases, Pipenv plans to add support for: -- Writing pylock.toml files - Full support for environment markers for extras and dependency groups -- Converting between Pipfile.lock and pylock.toml formats +- More comprehensive conversion between Pipfile.lock and pylock.toml formats +- Command-line options for generating pylock.toml files diff --git a/examples/Pipfile.with_pylock b/examples/Pipfile.with_pylock new file mode 100644 index 0000000000..62cec7a062 --- /dev/null +++ b/examples/Pipfile.with_pylock @@ -0,0 +1,21 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +requests = "*" + +[dev-packages] +pytest = "*" + +[requires] +python_version = "3.8" + +[pipenv] +# Enable pylock.toml generation +use_pylock = true + +# Optional: Specify a custom name for the pylock file +# This will generate pylock.dev.toml instead of pylock.toml +# pylock_name = "dev" diff --git a/news/7751.feature.rst b/news/7751.feature.rst index 950e7dbb0c..af3bc2bbd4 100644 --- a/news/7751.feature.rst +++ b/news/7751.feature.rst @@ -1 +1,5 @@ -Added support for reading PEP 751 pylock.toml files. When both a Pipfile.lock and a pylock.toml file exist, Pipenv will prioritize the pylock.toml file. +Added support for PEP 751 pylock.toml files: + +- Reading: When both a Pipfile.lock and a pylock.toml file exist, Pipenv will prioritize the pylock.toml file. +- Writing: Add ``use_pylock = true`` to the ``[pipenv]`` section of your Pipfile to generate pylock.toml files alongside Pipfile.lock. +- Customization: Use ``pylock_name = "name"`` in the ``[pipenv]`` section to generate named pylock files (e.g., pylock.name.toml). diff --git a/pipenv/project.py b/pipenv/project.py index d02e2bb926..e952bc9d9f 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -1007,8 +1007,22 @@ def write_toml(self, data, path=None): with open(path, "w", newline=newlines) as f: f.write(formatted_data) + @property + def use_pylock(self) -> bool: + """Returns True if pylock.toml should be generated.""" + return self.settings.get("use_pylock", False) + + @property + def pylock_output_path(self) -> str: + """Returns the path where pylock.toml should be written.""" + pylock_name = self.settings.get("pylock_name") + if pylock_name: + return str(Path(self.project_directory) / f"pylock.{pylock_name}.toml") + return str(Path(self.project_directory) / "pylock.toml") + def write_lockfile(self, content): """Write out the lockfile.""" + # Always write the Pipfile.lock s = self._lockfile_encoder.encode(content) open_kwargs = {"newline": self._lockfile_newlines, "encoding": "utf-8"} with atomic_open_for_write(self.lockfile_location, **open_kwargs) as f: @@ -1018,6 +1032,22 @@ def write_lockfile(self, content): if not s.endswith("\n"): f.write("\n") + # If use_pylock is enabled, also write a pylock.toml file + if self.use_pylock: + try: + from pipenv.utils.pylock import PylockFile + + pylock = PylockFile.from_lockfile( + lockfile_path=self.lockfile_location, + pylock_path=self.pylock_output_path, + ) + pylock.write() + err.print( + f"[bold green]Generated pylock.toml at {self.pylock_output_path}[/bold green]" + ) + except Exception as e: + err.print(f"[bold red]Error generating pylock.toml: {e}[/bold red]") + def pipfile_sources(self, expand_vars=True): if self.pipfile_is_empty or "source" not in self.parsed_pipfile: sources = [self.default_source] diff --git a/pipenv/utils/pylock.py b/pipenv/utils/pylock.py index a82004fab9..124a3de9cb 100644 --- a/pipenv/utils/pylock.py +++ b/pipenv/utils/pylock.py @@ -6,11 +6,15 @@ installation reproducibility). """ +import datetime +import json import os from dataclasses import dataclass, field from pathlib import Path from typing import Any, Dict, List, Optional, Set, Union +from pipenv.utils import err +from pipenv.utils.locking import atomic_open_for_write from pipenv.utils.toml import tomlkit_value_to_python from pipenv.vendor import tomlkit @@ -40,6 +44,122 @@ class PylockFile: path: Path data: Dict[str, Any] = field(default_factory=dict) + @classmethod + def from_lockfile( + cls, lockfile_path: Union[str, Path], pylock_path: Union[str, Path] = None + ) -> "PylockFile": + """Create a PylockFile from a Pipfile.lock file. + + Args: + lockfile_path: Path to the Pipfile.lock file + pylock_path: Path to save the pylock.toml file, defaults to pylock.toml in the same directory + + Returns: + A PylockFile instance + + Raises: + FileNotFoundError: If the Pipfile.lock file doesn't exist + ValueError: If the Pipfile.lock file is invalid + """ + if isinstance(lockfile_path, str): + lockfile_path = Path(lockfile_path) + + if not lockfile_path.exists(): + raise FileNotFoundError(f"Pipfile.lock not found: {lockfile_path}") + + if pylock_path is None: + pylock_path = lockfile_path.parent / "pylock.toml" + elif isinstance(pylock_path, str): + pylock_path = Path(pylock_path) + + try: + with open(lockfile_path, encoding="utf-8") as f: + lockfile_data = json.load(f) + except Exception as e: + raise ValueError(f"Invalid Pipfile.lock file: {e}") + + # Create the basic pylock.toml structure + pylock_data = { + "lock-version": "1.0", + "environments": [], + "extras": [], + "dependency-groups": [], + "default-groups": [], + "created-by": "pipenv", + "packages": [], + } + + # Add Python version requirement if present + meta = lockfile_data.get("_meta", {}) + requires = meta.get("requires", {}) + if "python_version" in requires: + pylock_data["requires-python"] = f">={requires['python_version']}" + elif "python_full_version" in requires: + pylock_data["requires-python"] = f"=={requires['python_full_version']}" + + # Add sources + sources = meta.get("sources", []) + if sources: + pylock_data["sources"] = sources + + # Process packages + for section in ["default", "develop"]: + packages = lockfile_data.get(section, {}) + for name, package_data in packages.items(): + package = {"name": name} + + # Add version if present + if "version" in package_data: + version = package_data["version"] + if version.startswith("=="): + package["version"] = version[2:] + else: + package["version"] = version + + # Add markers if present + if "markers" in package_data: + # For develop packages, add dependency_groups marker + if section == "develop": + if "markers" in package_data: + package["marker"] = ( + f"dependency_groups in ('dev', 'test') and ({package_data['markers']})" + ) + else: + package["marker"] = "dependency_groups in ('dev', 'test')" + else: + package["marker"] = package_data["markers"] + elif section == "develop": + package["marker"] = "dependency_groups in ('dev', 'test')" + + # Add hashes if present + if "hashes" in package_data: + wheels = [] + for hash_value in package_data["hashes"]: + if hash_value.startswith("sha256:"): + hash_value = hash_value[7:] # Remove "sha256:" prefix + wheel = { + "name": f"{name}-{package.get('version', '0.0.0')}-py3-none-any.whl", + "hashes": {"sha256": hash_value}, + } + wheels.append(wheel) + if wheels: + package["wheels"] = wheels + + pylock_data["packages"].append(package) + + # Add tool.pipenv section with metadata + pylock_data["tool"] = { + "pipenv": { + "generated_from": "Pipfile.lock", + "generation_date": datetime.datetime.now( + datetime.timezone.utc + ).isoformat(), + } + } + + instance = cls(path=pylock_path, data=pylock_data) + return instance + @classmethod def from_path(cls, path: Union[str, Path]) -> "PylockFile": """Load a pylock.toml file from the given path. @@ -80,6 +200,74 @@ def from_path(cls, path: Union[str, Path]) -> "PylockFile": return cls(path=path, data=tomlkit_value_to_python(data)) + def write(self) -> None: + """Write the pylock.toml file to disk. + + Raises: + OSError: If there is an error writing the file + """ + try: + # Convert the data to a TOML document + doc = tomlkit.document() + + # Add top-level keys in a specific order for readability + for key in [ + "lock-version", + "environments", + "requires-python", + "extras", + "dependency-groups", + "default-groups", + "created-by", + ]: + if key in self.data: + doc[key] = self.data[key] + + # Add packages + if "packages" in self.data: + doc["packages"] = [] + for package in self.data["packages"]: + pkg_table = tomlkit.table() + for k, v in package.items(): + if k not in {"wheels", "sdist"}: + pkg_table[k] = v + + # Add wheels as an array of tables + if "wheels" in package: + pkg_table["wheels"] = [] + for wheel in package["wheels"]: + wheel_table = tomlkit.inline_table() + for k, v in wheel.items(): + wheel_table[k] = v + pkg_table["wheels"].append(wheel_table) + + # Add sdist as a table + if "sdist" in package: + sdist_table = tomlkit.table() + for k, v in package["sdist"].items(): + sdist_table[k] = v + pkg_table["sdist"] = sdist_table + + doc["packages"].append(pkg_table) + + # Add tool section + if "tool" in self.data: + tool_table = tomlkit.table() + for tool_name, tool_data in self.data["tool"].items(): + tool_section = tomlkit.table() + for k, v in tool_data.items(): + tool_section[k] = v + tool_table[tool_name] = tool_section + doc["tool"] = tool_table + + # Write the document to the file + with atomic_open_for_write(self.path, encoding="utf-8") as f: + f.write(tomlkit.dumps(doc)) + + except Exception as e: + err.print(f"[bold red]Error writing pylock.toml: {e}[/bold red]") + raise OSError(f"Error writing pylock.toml: {e}") + @property def lock_version(self) -> str: """Get the lock-version.""" diff --git a/tests/integration/test_pylock.py b/tests/integration/test_pylock.py index 88ee83ba3b..dc4aa15581 100644 --- a/tests/integration/test_pylock.py +++ b/tests/integration/test_pylock.py @@ -93,3 +93,274 @@ def test_project_pylock_integration(pylock_project): assert "hashes" in requests_entry assert len(requests_entry["hashes"]) == 1 assert requests_entry["hashes"][0].startswith("sha256:") + + +@pytest.fixture +def pylock_write_project(tmp_path): + """Create a temporary project with a Pipfile that has use_pylock enabled.""" + # Create a simple Pipfile with use_pylock enabled + pipfile_content = """ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +requests = "*" + +[dev-packages] + +[requires] +python_version = "3.8" + +[pipenv] +use_pylock = true +""" + + # Create a simple Pipfile.lock + lockfile_content = """ +{ + "_meta": { + "hash": { + "sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984c674dc07bcce19a8b5b28c6b2" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14" + ], + "version": "==2022.9.24" + }, + "charset-normalizer": { + "hashes": [ + "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" + ], + "version": "==2.1.1" + }, + "idna": { + "hashes": [ + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "version": "==3.4" + }, + "requests": { + "hashes": [ + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + ], + "index": "pypi", + "version": "==2.28.1" + }, + "urllib3": { + "hashes": [ + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" + ], + "version": "==1.26.12" + } + }, + "develop": {} +} +""" + + with open(tmp_path / "Pipfile", "w") as f: + f.write(pipfile_content) + + with open(tmp_path / "Pipfile.lock", "w") as f: + f.write(lockfile_content) + + # Change to the temporary directory + old_cwd = os.getcwd() + os.chdir(tmp_path) + + try: + yield tmp_path + finally: + os.chdir(old_cwd) + + +def test_write_pylock_file(pylock_write_project): + """Test that Project class correctly writes pylock.toml files.""" + # Create a project instance + project = Project(chdir=False) + + # Check that use_pylock is enabled + assert project.use_pylock is True + + # Check that pylock_output_path is correct + assert project.pylock_output_path == str(pylock_write_project / "pylock.toml") + + # Load the lockfile content + lockfile_content = project.lockfile_content + + # Write the lockfile (which should also write pylock.toml) + project.write_lockfile(lockfile_content) + + # Check that pylock.toml was created + pylock_path = pylock_write_project / "pylock.toml" + assert pylock_path.exists() + + # Load the pylock.toml file and verify its contents + pylock = PylockFile.from_path(pylock_path) + + # Check basic properties + assert pylock.lock_version == "1.0" + assert pylock.created_by == "pipenv" + + # Check that all packages are included + package_names = [p["name"] for p in pylock.packages] + assert "requests" in package_names + assert "urllib3" in package_names + assert "certifi" in package_names + assert "charset-normalizer" in package_names + assert "idna" in package_names + + # Check that the tool.pipenv section exists + assert "pipenv" in pylock.tool + assert "generated_from" in pylock.tool["pipenv"] + assert pylock.tool["pipenv"]["generated_from"] == "Pipfile.lock" + + +@pytest.fixture +def pylock_write_named_project(tmp_path): + """Create a temporary project with a Pipfile that has use_pylock and pylock_name enabled.""" + # Create a simple Pipfile with use_pylock and pylock_name enabled + pipfile_content = """ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +requests = "*" + +[dev-packages] + +[requires] +python_version = "3.8" + +[pipenv] +use_pylock = true +pylock_name = "dev" +""" + + # Create a simple Pipfile.lock (same as in pylock_write_project) + lockfile_content = """ +{ + "_meta": { + "hash": { + "sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984c674dc07bcce19a8b5b28c6b2" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14" + ], + "version": "==2022.9.24" + }, + "charset-normalizer": { + "hashes": [ + "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" + ], + "version": "==2.1.1" + }, + "idna": { + "hashes": [ + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "version": "==3.4" + }, + "requests": { + "hashes": [ + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + ], + "index": "pypi", + "version": "==2.28.1" + }, + "urllib3": { + "hashes": [ + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" + ], + "version": "==1.26.12" + } + }, + "develop": {} +} +""" + + with open(tmp_path / "Pipfile", "w") as f: + f.write(pipfile_content) + + with open(tmp_path / "Pipfile.lock", "w") as f: + f.write(lockfile_content) + + # Change to the temporary directory + old_cwd = os.getcwd() + os.chdir(tmp_path) + + try: + yield tmp_path + finally: + os.chdir(old_cwd) + + +def test_write_named_pylock_file(pylock_write_named_project): + """Test that Project class correctly writes named pylock.toml files.""" + # Create a project instance + project = Project(chdir=False) + + # Check that use_pylock is enabled + assert project.use_pylock is True + + # Check that pylock_name is set + assert project.settings.get("pylock_name") == "dev" + + # Check that pylock_output_path is correct + assert project.pylock_output_path == str(pylock_write_named_project / "pylock.dev.toml") + + # Load the lockfile content + lockfile_content = project.lockfile_content + + # Write the lockfile (which should also write pylock.dev.toml) + project.write_lockfile(lockfile_content) + + # Check that pylock.dev.toml was created + pylock_path = pylock_write_named_project / "pylock.dev.toml" + assert pylock_path.exists() + + # Load the pylock.dev.toml file and verify its contents + pylock = PylockFile.from_path(pylock_path) + + # Check basic properties + assert pylock.lock_version == "1.0" + assert pylock.created_by == "pipenv" + + # Check that all packages are included + package_names = [p["name"] for p in pylock.packages] + assert "requests" in package_names + assert "urllib3" in package_names + assert "certifi" in package_names + assert "charset-normalizer" in package_names + assert "idna" in package_names diff --git a/tests/unit/test_pylock.py b/tests/unit/test_pylock.py index 6f995d0f0a..cb62ec115f 100644 --- a/tests/unit/test_pylock.py +++ b/tests/unit/test_pylock.py @@ -164,3 +164,125 @@ def test_convert_to_pipenv_lockfile(pylock_file): assert "hashes" in lockfile["default"]["requests"] assert lockfile["develop"]["pytest"]["version"] == "==7.0.0" assert lockfile["develop"]["pytest"]["markers"] == "dependency_groups in ('dev', 'test')" + + +def test_from_lockfile(tmp_path): + """Test creating a PylockFile from a Pipfile.lock file.""" + # Create a simple Pipfile.lock + lockfile_content = """ +{ + "_meta": { + "hash": { + "sha256": "b8c2e1580c53e383cfe4254c1f16560b855d984c674dc07bcce19a8b5b28c6b2" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "requests": { + "hashes": [ + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + ], + "index": "pypi", + "version": "==2.28.1" + } + }, + "develop": { + "pytest": { + "hashes": [ + "sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + ], + "version": "==7.0.0" + } + } +} +""" + lockfile_path = tmp_path / "Pipfile.lock" + pylock_path = tmp_path / "pylock.toml" + + with open(lockfile_path, "w") as f: + f.write(lockfile_content) + + # Create a PylockFile from the Pipfile.lock + pylock = PylockFile.from_lockfile(lockfile_path, pylock_path) + + # Check basic properties + assert pylock.lock_version == "1.0" + assert pylock.created_by == "pipenv" + assert pylock.requires_python == ">=3.8" + + # Check that packages were correctly converted + package_names = [p["name"] for p in pylock.packages] + assert "requests" in package_names + assert "pytest" in package_names + + # Check that the tool.pipenv section exists + assert "pipenv" in pylock.tool + assert "generated_from" in pylock.tool["pipenv"] + assert pylock.tool["pipenv"]["generated_from"] == "Pipfile.lock" + + +def test_write_method(tmp_path): + """Test writing a PylockFile to disk.""" + # Create a simple PylockFile + pylock_data = { + "lock-version": "1.0", + "environments": ["sys_platform == 'linux'"], + "requires-python": ">=3.8", + "extras": [], + "dependency-groups": [], + "default-groups": [], + "created-by": "test", + "packages": [ + { + "name": "requests", + "version": "2.28.1", + "wheels": [ + { + "name": "requests-2.28.1-py3-none-any.whl", + "hashes": {"sha256": "test-hash"} + } + ] + } + ], + "tool": { + "pipenv": { + "generated_from": "test" + } + } + } + + pylock_path = tmp_path / "pylock.toml" + pylock = PylockFile(path=pylock_path, data=pylock_data) + + # Write the file + pylock.write() + + # Check that the file was created + assert pylock_path.exists() + + # Load the file and check its contents + loaded_pylock = PylockFile.from_path(pylock_path) + + # Check basic properties + assert loaded_pylock.lock_version == "1.0" + assert loaded_pylock.created_by == "test" + assert loaded_pylock.requires_python == ">=3.8" + + # Check that packages were correctly written + assert len(loaded_pylock.packages) == 1 + assert loaded_pylock.packages[0]["name"] == "requests" + assert loaded_pylock.packages[0]["version"] == "2.28.1" + + # Check that the tool.pipenv section was correctly written + assert "pipenv" in loaded_pylock.tool + assert loaded_pylock.tool["pipenv"]["generated_from"] == "test" From 40310240d933a79fa3c738240de5929c6e9b978f Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Fri, 25 Apr 2025 04:48:39 -0400 Subject: [PATCH 3/4] Ability to read/write the pylock.yaml --- Pipfile | 1 + Pipfile.lock | 258 +++---- pipenv/utils/pylock.py | 135 +++- pylock.toml | 1442 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1691 insertions(+), 145 deletions(-) create mode 100644 pylock.toml diff --git a/Pipfile b/Pipfile index 6c9c811d80..0a6aaae6ce 100644 --- a/Pipfile +++ b/Pipfile @@ -39,3 +39,4 @@ test = "pytest -vvs" [pipenv] allow_prereleases = true +use_pylock = true diff --git a/Pipfile.lock b/Pipfile.lock index 0805ac2750..39de9fd00c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -16,11 +16,11 @@ "default": { "pytz": { "hashes": [ - "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", - "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e" + "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", + "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" ], "index": "pypi", - "version": "==2025.1" + "version": "==2025.2" } }, "develop": { @@ -43,15 +43,16 @@ "hashes": [ "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11" ], + "markers": "sys_platform == 'win32'", "version": "==1.4.1" }, "attrs": { "hashes": [ - "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", - "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a" + "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", + "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b" ], "markers": "python_version >= '3.8'", - "version": "==25.1.0" + "version": "==25.3.0" }, "babel": { "hashes": [ @@ -63,10 +64,11 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", - "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16" + "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", + "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195" ], - "version": "==4.13.3" + "markers": "python_full_version >= '3.7.0'", + "version": "==4.13.4" }, "black": { "hashes": [ @@ -93,6 +95,7 @@ "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd", "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7" ], + "markers": "python_version >= '3.8'", "version": "==24.3.0" }, "build": { @@ -182,7 +185,7 @@ "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" ], - "markers": "platform_python_implementation != 'PyPy'", + "markers": "python_version >= '3.8'", "version": "==1.17.1" }, "cfgv": { @@ -305,7 +308,7 @@ "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6' and sys_platform == 'win32'", + "markers": "sys_platform == 'win32'", "version": "==0.4.6" }, "coverage": { @@ -313,72 +316,72 @@ "toml" ], "hashes": [ - "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", - "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9", - "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe", - "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0", - "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924", - "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", - "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702", - "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", - "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b", - "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2", - "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", - "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f", - "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3", - "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674", - "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9", - "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0", - "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e", - "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef", - "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb", - "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87", - "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1", - "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", - "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703", - "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e", - "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd", - "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", - "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4", - "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45", - "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa", - "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31", - "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8", - "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86", - "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6", - "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", - "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf", - "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929", - "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc", - "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", - "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3", - "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd", - "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e", - "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879", - "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57", - "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", - "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", - "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba", - "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d", - "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", - "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c", - "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c", - "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f", - "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015", - "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558", - "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f", - "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d", - "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d", - "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425", - "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3", - "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", - "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827", - "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c", - "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f", - "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73" + "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f", + "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3", + "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", + "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25", + "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe", + "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257", + "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", + "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada", + "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64", + "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", + "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28", + "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067", + "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733", + "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676", + "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23", + "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008", + "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd", + "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3", + "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82", + "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", + "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00", + "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", + "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", + "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d", + "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814", + "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd", + "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", + "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", + "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3", + "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c", + "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42", + "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a", + "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", + "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a", + "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", + "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487", + "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4", + "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", + "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", + "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", + "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73", + "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", + "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f", + "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", + "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899", + "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a", + "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", + "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1", + "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", + "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d", + "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502", + "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", + "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", + "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", + "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", + "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c", + "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", + "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4", + "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", + "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323", + "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883", + "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f", + "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f" ], "markers": "python_version >= '3.9'", - "version": "==7.6.12" + "version": "==7.8.0" }, "cryptography": { "hashes": [ @@ -455,17 +458,18 @@ }, "filelock": { "hashes": [ - "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", - "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e" + "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", + "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de" ], "markers": "python_version >= '3.9'", - "version": "==3.17.0" + "version": "==3.18.0" }, "flake8": { "hashes": [ "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b", "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.9.2" }, "flaky": { @@ -473,6 +477,7 @@ "sha256:194ccf4f0d3a22b2de7130f4b62e45e977ac1b5ccad74d4d48f3005dcc38815e", "sha256:47204a81ec905f3d5acfbd61daeabcada8f9d4031616d9bcb0618461729699f5" ], + "markers": "python_version >= '3.5'", "version": "==3.8.1" }, "gunicorn": { @@ -480,7 +485,7 @@ "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec" ], - "markers": "python_version >= '3.7' and sys_platform == 'linux'", + "markers": "sys_platform == 'linux'", "version": "==23.0.0" }, "id": { @@ -493,11 +498,11 @@ }, "identify": { "hashes": [ - "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc", - "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255" + "sha256:45e92fd704f3da71cc3880036633f48b4b7265fd4de2b57627cb157216eb7eb8", + "sha256:5f34248f54136beed1a7ba6a6b5c4b6cf21ff495aac7c359e1ef831ae3b8ab25" ], "markers": "python_version >= '3.9'", - "version": "==2.6.8" + "version": "==2.6.10" }, "idna": { "hashes": [ @@ -526,11 +531,11 @@ }, "iniconfig": { "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", + "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" }, "invoke": { "hashes": [ @@ -570,7 +575,7 @@ "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732" ], - "markers": "sys_platform == 'linux'", + "markers": "python_version >= '3.7'", "version": "==0.9.0" }, "jinja2": { @@ -586,7 +591,7 @@ "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd" ], - "markers": "platform_machine != 'ppc64le' and platform_machine != 's390x'", + "markers": "python_version >= '3.9'", "version": "==25.6.0" }, "linkify-it-py": { @@ -594,6 +599,7 @@ "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048", "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79" ], + "markers": "python_version >= '3.7'", "version": "==2.0.3" }, "markdown-it-py": { @@ -699,23 +705,24 @@ "sha256:4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0", "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f" ], + "markers": "python_version >= '3.6'", "version": "==5.2.0" }, "more-itertools": { "hashes": [ - "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b", - "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89" + "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3", + "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e" ], "markers": "python_version >= '3.9'", - "version": "==10.6.0" + "version": "==10.7.0" }, "mypy-extensions": { "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", + "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558" ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" + "markers": "python_version >= '3.8'", + "version": "==1.1.0" }, "myst-parser": { "extras": [ @@ -768,11 +775,11 @@ }, "packaging": { "hashes": [ - "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", - "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" ], "markers": "python_version >= '3.8'", - "version": "==24.2" + "version": "==25.0" }, "parse": { "hashes": [ @@ -787,6 +794,7 @@ "sha256:2281b187276c8e8e3c15634f62287b2fb6fe0efe3010f739a6bd1e45fa2bf2b2", "sha256:b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777" ], + "markers": "python_version >= '3.8'", "version": "==0.5" }, "pathspec": { @@ -811,16 +819,16 @@ "dev", "tests" ], - "markers": "python_version >= '3.8'", + "markers": "python_version >= '3.9'", "path": "." }, "platformdirs": { "hashes": [ - "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", - "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" + "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", + "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351" ], - "markers": "python_version >= '3.8'", - "version": "==4.3.6" + "markers": "python_version >= '3.9'", + "version": "==4.3.7" }, "pluggy": { "hashes": [ @@ -920,6 +928,7 @@ "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9", "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e" ], + "markers": "python_version >= '3.7'", "version": "==2.3.1" }, "pytest-xdist": { @@ -927,6 +936,7 @@ "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d" ], + "markers": "python_version >= '3.8'", "version": "==3.6.1" }, "pyyaml": { @@ -1021,11 +1031,11 @@ }, "rich": { "hashes": [ - "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", - "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90" + "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", + "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725" ], "markers": "python_full_version >= '3.8.0'", - "version": "==13.9.4" + "version": "==14.0.0" }, "roman-numerals-py": { "hashes": [ @@ -1040,7 +1050,7 @@ "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99" ], - "markers": "sys_platform == 'linux'", + "markers": "python_version >= '3.6'", "version": "==3.3.3" }, "semver": { @@ -1054,11 +1064,11 @@ }, "setuptools": { "hashes": [ - "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2", - "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f" + "sha256:128ce7b8f33c3079fd1b067ecbb4051a66e8526e7b65f6cec075dfc650ddfa88", + "sha256:e147c0549f27767ba362f9da434eab9c5dc0045d5304feb602a0af001089fc51" ], "markers": "python_version >= '3.9'", - "version": "==75.8.2" + "version": "==79.0.1" }, "snowballstemmer": { "hashes": [ @@ -1069,11 +1079,11 @@ }, "soupsieve": { "hashes": [ - "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", - "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9" + "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", + "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a" ], "markers": "python_version >= '3.8'", - "version": "==2.6" + "version": "==2.7" }, "sphinx": { "hashes": [ @@ -1154,6 +1164,7 @@ "hashes": [ "sha256:08c22c9c03b28a140fe3ec5064b53a5288279f22e596ca06b0be698d50c93cf2" ], + "markers": "sys_platform == 'linux'", "version": "==0.10.0" }, "tomli": { @@ -1200,6 +1211,7 @@ "sha256:013423ee7eed102b2f393c287d22d95f66f1a3ea10a4baa82d298001a7f18af3", "sha256:9343209592b839209cdf28c339ba45792fbfe9775b5f9c177462fd693e127d8d" ], + "markers": "python_version >= '3.8'", "version": "==24.8.0" }, "twine": { @@ -1213,12 +1225,12 @@ }, "typing-extensions": { "hashes": [ - "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", - "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", + "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.12.2" + "version": "==4.13.2" }, "uc-micro-py": { "hashes": [ @@ -1230,26 +1242,26 @@ }, "urllib3": { "hashes": [ - "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", - "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" + "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", + "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813" ], "markers": "python_version >= '3.9'", - "version": "==2.3.0" + "version": "==2.4.0" }, "virtualenv": { "hashes": [ - "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728", - "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a" + "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8", + "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6" ], "markers": "python_version >= '3.8'", - "version": "==20.29.2" + "version": "==20.30.0" }, "waitress": { "hashes": [ "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e" ], - "markers": "python_full_version >= '3.9.0' and sys_platform == 'win32'", + "markers": "sys_platform == 'win32'", "version": "==3.0.2" }, "zipp": { diff --git a/pipenv/utils/pylock.py b/pipenv/utils/pylock.py index 124a3de9cb..f9ebccdbb0 100644 --- a/pipenv/utils/pylock.py +++ b/pipenv/utils/pylock.py @@ -15,7 +15,6 @@ from pipenv.utils import err from pipenv.utils.locking import atomic_open_for_write -from pipenv.utils.toml import tomlkit_value_to_python from pipenv.vendor import tomlkit @@ -97,6 +96,12 @@ def from_lockfile( elif "python_full_version" in requires: pylock_data["requires-python"] = f"=={requires['python_full_version']}" + # Ensure all values are properly formatted for TOML + # Convert None values to empty strings or arrays + for key in ["environments", "extras", "dependency-groups", "default-groups"]: + if key in pylock_data and pylock_data[key] is None: + pylock_data[key] = [] + # Add sources sources = meta.get("sources", []) if sources: @@ -183,12 +188,23 @@ def from_path(cls, path: Union[str, Path]) -> "PylockFile": try: with open(path, encoding="utf-8") as f: - data = tomlkit.parse(f.read()) + content = f.read() + data = tomlkit.parse(content) + # Convert tomlkit objects to Python native types + data_dict = {} + for key, value in data.items(): + if isinstance( + value, + (tomlkit.items.Table, tomlkit.items.AoT, tomlkit.items.Array), + ): + data_dict[key] = value.unwrap() + else: + data_dict[key] = value except Exception as e: raise PylockFormatError(f"Invalid pylock.toml file: {e}") # Validate lock-version - lock_version = data.get("lock-version") + lock_version = data_dict.get("lock-version") if not lock_version: raise PylockFormatError("Missing required field: lock-version") @@ -198,7 +214,7 @@ def from_path(cls, path: Union[str, Path]) -> "PylockFile": f"Unsupported lock-version: {lock_version}. Only version 1.0 is supported." ) - return cls(path=path, data=tomlkit_value_to_python(data)) + return cls(path=path, data=data_dict) def write(self) -> None: """Write the pylock.toml file to disk. @@ -207,6 +223,23 @@ def write(self) -> None: OSError: If there is an error writing the file """ try: + # Ensure all values are properly formatted for TOML + # Create a deep copy of the data to avoid modifying the original + data_copy = {} + for key, value in self.data.items(): + if isinstance(value, dict): + data_copy[key] = value.copy() + elif isinstance(value, list): + data_copy[key] = value.copy() + else: + data_copy[key] = value + + # Convert None values to empty strings or arrays + for key in ["environments", "extras", "dependency-groups", "default-groups"]: + if key in data_copy: + if data_copy[key] is None: + data_copy[key] = [] + # Convert the data to a TOML document doc = tomlkit.document() @@ -220,49 +253,94 @@ def write(self) -> None: "default-groups", "created-by", ]: - if key in self.data: - doc[key] = self.data[key] + if key in data_copy: + doc[key] = data_copy[key] # Add packages - if "packages" in self.data: - doc["packages"] = [] - for package in self.data["packages"]: + if "packages" in data_copy: + doc["packages"] = tomlkit.aot() + for package in data_copy["packages"]: pkg_table = tomlkit.table() + + # Add basic package info first for better readability + for key in ["name", "version", "marker", "requires-python"]: + if key in package: + pkg_table[key] = package[key] + + # Add remaining keys except wheels and sdist for k, v in package.items(): - if k not in {"wheels", "sdist"}: + if k not in { + "name", + "version", + "marker", + "requires-python", + "wheels", + "sdist", + }: pkg_table[k] = v - # Add wheels as an array of tables + # Add wheels as an array of tables with better formatting if "wheels" in package: - pkg_table["wheels"] = [] + wheels_array = tomlkit.array() + wheels_array.multiline(True) + for wheel in package["wheels"]: wheel_table = tomlkit.inline_table() - for k, v in wheel.items(): - wheel_table[k] = v - pkg_table["wheels"].append(wheel_table) + + # Add wheel properties in a specific order + for key in ["name", "upload-time", "url", "size"]: + if key in wheel: + wheel_table[key] = wheel[key] + + # Add hashes as a table + if "hashes" in wheel: + hashes_table = tomlkit.inline_table() + for hash_algo, hash_value in wheel["hashes"].items(): + hashes_table[hash_algo] = hash_value + wheel_table["hashes"] = hashes_table + + wheels_array.append(wheel_table) + + pkg_table["wheels"] = wheels_array # Add sdist as a table if "sdist" in package: - sdist_table = tomlkit.table() - for k, v in package["sdist"].items(): - sdist_table[k] = v + sdist_table = tomlkit.inline_table() + + # Add sdist properties in a specific order + for key in ["name", "upload-time", "url", "size"]: + if key in package["sdist"]: + sdist_table[key] = package["sdist"][key] + + # Add hashes as a table + if "hashes" in package["sdist"]: + hashes_table = tomlkit.inline_table() + for hash_algo, hash_value in package["sdist"][ + "hashes" + ].items(): + hashes_table[hash_algo] = hash_value + sdist_table["hashes"] = hashes_table + pkg_table["sdist"] = sdist_table doc["packages"].append(pkg_table) # Add tool section - if "tool" in self.data: + if "tool" in data_copy: tool_table = tomlkit.table() - for tool_name, tool_data in self.data["tool"].items(): + for tool_name, tool_data in data_copy["tool"].items(): tool_section = tomlkit.table() for k, v in tool_data.items(): tool_section[k] = v tool_table[tool_name] = tool_section doc["tool"] = tool_table - # Write the document to the file + # Write the document to the file with proper formatting with atomic_open_for_write(self.path, encoding="utf-8") as f: - f.write(tomlkit.dumps(doc)) + content = tomlkit.dumps(doc) + # Ensure there's a blank line between package entries for readability + content = content.replace("[[packages]]\n", "\n[[packages]]\n") + f.write(content) except Exception as e: err.print(f"[bold red]Error writing pylock.toml: {e}[/bold red]") @@ -370,6 +448,19 @@ def convert_to_pipenv_lockfile(self) -> Dict[str, Any]: if self.requires_python: lockfile["_meta"]["requires"]["python_version"] = self.requires_python + # Add sources if present + if "sources" in self.data: + lockfile["_meta"]["sources"] = self.data["sources"] + # If no sources in pylock.toml, add a default source + else: + lockfile["_meta"]["sources"] = [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": True, + } + ] + # Process packages for package in self.packages: name = package.get("name") diff --git a/pylock.toml b/pylock.toml new file mode 100644 index 0000000000..f398f5b660 --- /dev/null +++ b/pylock.toml @@ -0,0 +1,1442 @@ +lock-version = "1.0" +environments = [] +extras = [] +dependency-groups = [] +default-groups = [] +created-by = "pipenv" + +[[packages]] +name = "pytz" +version = "2025.2" +wheels = [ + {name = "pytz-2025.2-py3-none-any.whl", hashes = {sha256 = "360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}}, + {name = "pytz-2025.2-py3-none-any.whl", hashes = {sha256 = "5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}}, +] + + +[[packages]] +name = "alabaster" +version = "1.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.10')" +wheels = [ + {name = "alabaster-1.0.0-py3-none-any.whl", hashes = {sha256 = "c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}}, + {name = "alabaster-1.0.0-py3-none-any.whl", hashes = {sha256 = "fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}}, +] + + +[[packages]] +name = "arpeggio" +version = "2.0.2" +marker = "dependency_groups in ('dev', 'test')" +wheels = [ + {name = "arpeggio-2.0.2-py3-none-any.whl", hashes = {sha256 = "c790b2b06e226d2dd468e4fbfb5b7f506cec66416031fde1441cf1de2a0ba700"}}, + {name = "arpeggio-2.0.2-py3-none-any.whl", hashes = {sha256 = "f7c8ae4f4056a89e020c24c7202ac8df3e2bc84e416746f20b0da35bb1de0250"}}, +] + + +[[packages]] +name = "atomicwrites" +version = "1.4.1" +marker = "dependency_groups in ('dev', 'test') and (sys_platform == 'win32')" +wheels = [ + {name = "atomicwrites-1.4.1-py3-none-any.whl", hashes = {sha256 = "81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}}, +] + + +[[packages]] +name = "attrs" +version = "25.3.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "attrs-25.3.0-py3-none-any.whl", hashes = {sha256 = "427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}}, + {name = "attrs-25.3.0-py3-none-any.whl", hashes = {sha256 = "75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}}, +] + + +[[packages]] +name = "babel" +version = "2.17.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "babel-2.17.0-py3-none-any.whl", hashes = {sha256 = "0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}}, + {name = "babel-2.17.0-py3-none-any.whl", hashes = {sha256 = "4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}}, +] + + +[[packages]] +name = "beautifulsoup4" +version = "4.13.4" +marker = "dependency_groups in ('dev', 'test') and (python_full_version >= '3.7.0')" +wheels = [ + {name = "beautifulsoup4-4.13.4-py3-none-any.whl", hashes = {sha256 = "9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}}, + {name = "beautifulsoup4-4.13.4-py3-none-any.whl", hashes = {sha256 = "dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}}, +] + + +[[packages]] +name = "black" +version = "24.3.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}}, + {name = "black-24.3.0-py3-none-any.whl", hashes = {sha256 = "e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}}, +] + + +[[packages]] +name = "build" +version = "1.2.2.post1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "build-1.2.2.post1-py3-none-any.whl", hashes = {sha256 = "1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5"}}, + {name = "build-1.2.2.post1-py3-none-any.whl", hashes = {sha256 = "b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7"}}, +] + + +[[packages]] +name = "certifi" +version = "2025.1.31" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "certifi-2025.1.31-py3-none-any.whl", hashes = {sha256 = "3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}}, + {name = "certifi-2025.1.31-py3-none-any.whl", hashes = {sha256 = "ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}}, +] + + +[[packages]] +name = "cffi" +version = "1.17.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}}, + {name = "cffi-1.17.1-py3-none-any.whl", hashes = {sha256 = "fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}}, +] + + +[[packages]] +name = "cfgv" +version = "3.4.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "cfgv-3.4.0-py3-none-any.whl", hashes = {sha256 = "b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}}, + {name = "cfgv-3.4.0-py3-none-any.whl", hashes = {sha256 = "e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}}, +] + + +[[packages]] +name = "charset-normalizer" +version = "3.4.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}}, + {name = "charset-normalizer-3.4.1-py3-none-any.whl", hashes = {sha256 = "ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}}, +] + + +[[packages]] +name = "click" +version = "8.0.3" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "click-8.0.3-py3-none-any.whl", hashes = {sha256 = "353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}}, + {name = "click-8.0.3-py3-none-any.whl", hashes = {sha256 = "410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}}, +] + + +[[packages]] +name = "colorama" +version = "0.4.6" +marker = "dependency_groups in ('dev', 'test') and (sys_platform == 'win32')" +wheels = [ + {name = "colorama-0.4.6-py3-none-any.whl", hashes = {sha256 = "08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}}, + {name = "colorama-0.4.6-py3-none-any.whl", hashes = {sha256 = "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}}, +] + + +[[packages]] +name = "coverage" +version = "7.8.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f"}}, + {name = "coverage-7.8.0-py3-none-any.whl", hashes = {sha256 = "fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f"}}, +] + + +[[packages]] +name = "cryptography" +version = "44.0.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1')" +wheels = [ + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}}, + {name = "cryptography-44.0.2-py3-none-any.whl", hashes = {sha256 = "f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"}}, +] + + +[[packages]] +name = "distlib" +version = "0.3.9" +marker = "dependency_groups in ('dev', 'test')" +wheels = [ + {name = "distlib-0.3.9-py3-none-any.whl", hashes = {sha256 = "47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}}, + {name = "distlib-0.3.9-py3-none-any.whl", hashes = {sha256 = "a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}}, +] + + +[[packages]] +name = "docutils" +version = "0.21.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "docutils-0.21.2-py3-none-any.whl", hashes = {sha256 = "3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}}, + {name = "docutils-0.21.2-py3-none-any.whl", hashes = {sha256 = "dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}}, +] + + +[[packages]] +name = "exceptiongroup" +version = "1.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "exceptiongroup-1.1.0-py3-none-any.whl", hashes = {sha256 = "327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}}, + {name = "exceptiongroup-1.1.0-py3-none-any.whl", hashes = {sha256 = "bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}}, +] + + +[[packages]] +name = "execnet" +version = "2.1.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "execnet-2.1.1-py3-none-any.whl", hashes = {sha256 = "26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}}, + {name = "execnet-2.1.1-py3-none-any.whl", hashes = {sha256 = "5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}}, +] + + +[[packages]] +name = "filelock" +version = "3.18.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "filelock-3.18.0-py3-none-any.whl", hashes = {sha256 = "adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}}, + {name = "filelock-3.18.0-py3-none-any.whl", hashes = {sha256 = "c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}}, +] + + +[[packages]] +name = "flake8" +version = "3.9.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4')" +wheels = [ + {name = "flake8-3.9.2-py3-none-any.whl", hashes = {sha256 = "07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}}, + {name = "flake8-3.9.2-py3-none-any.whl", hashes = {sha256 = "bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}}, +] + + +[[packages]] +name = "flaky" +version = "3.8.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.5')" +wheels = [ + {name = "flaky-3.8.1-py3-none-any.whl", hashes = {sha256 = "194ccf4f0d3a22b2de7130f4b62e45e977ac1b5ccad74d4d48f3005dcc38815e"}}, + {name = "flaky-3.8.1-py3-none-any.whl", hashes = {sha256 = "47204a81ec905f3d5acfbd61daeabcada8f9d4031616d9bcb0618461729699f5"}}, +] + + +[[packages]] +name = "gunicorn" +version = "23.0.0" +marker = "dependency_groups in ('dev', 'test') and (sys_platform == 'linux')" +wheels = [ + {name = "gunicorn-23.0.0-py3-none-any.whl", hashes = {sha256 = "ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}}, + {name = "gunicorn-23.0.0-py3-none-any.whl", hashes = {sha256 = "f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}}, +] + + +[[packages]] +name = "id" +version = "1.5.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "id-1.5.0-py3-none-any.whl", hashes = {sha256 = "292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}}, + {name = "id-1.5.0-py3-none-any.whl", hashes = {sha256 = "f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}}, +] + + +[[packages]] +name = "identify" +version = "2.6.10" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "identify-2.6.10-py3-none-any.whl", hashes = {sha256 = "45e92fd704f3da71cc3880036633f48b4b7265fd4de2b57627cb157216eb7eb8"}}, + {name = "identify-2.6.10-py3-none-any.whl", hashes = {sha256 = "5f34248f54136beed1a7ba6a6b5c4b6cf21ff495aac7c359e1ef831ae3b8ab25"}}, +] + + +[[packages]] +name = "idna" +version = "3.10" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "idna-3.10-py3-none-any.whl", hashes = {sha256 = "12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}}, + {name = "idna-3.10-py3-none-any.whl", hashes = {sha256 = "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}}, +] + + +[[packages]] +name = "imagesize" +version = "1.4.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3')" +wheels = [ + {name = "imagesize-1.4.1-py3-none-any.whl", hashes = {sha256 = "0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}}, + {name = "imagesize-1.4.1-py3-none-any.whl", hashes = {sha256 = "69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}}, +] + + +[[packages]] +name = "importlib-metadata" +version = "8.6.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "importlib-metadata-8.6.1-py3-none-any.whl", hashes = {sha256 = "02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}}, + {name = "importlib-metadata-8.6.1-py3-none-any.whl", hashes = {sha256 = "310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}}, +] + + +[[packages]] +name = "iniconfig" +version = "2.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "iniconfig-2.1.0-py3-none-any.whl", hashes = {sha256 = "3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}}, + {name = "iniconfig-2.1.0-py3-none-any.whl", hashes = {sha256 = "9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}}, +] + + +[[packages]] +name = "invoke" +version = "2.2.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "invoke-2.2.0-py3-none-any.whl", hashes = {sha256 = "6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"}}, + {name = "invoke-2.2.0-py3-none-any.whl", hashes = {sha256 = "ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}}, +] + + +[[packages]] +name = "jaraco.classes" +version = "3.4.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "jaraco.classes-3.4.0-py3-none-any.whl", hashes = {sha256 = "47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}}, + {name = "jaraco.classes-3.4.0-py3-none-any.whl", hashes = {sha256 = "f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}}, +] + + +[[packages]] +name = "jaraco.context" +version = "6.0.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "jaraco.context-6.0.1-py3-none-any.whl", hashes = {sha256 = "9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}}, + {name = "jaraco.context-6.0.1-py3-none-any.whl", hashes = {sha256 = "f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}}, +] + + +[[packages]] +name = "jaraco.functools" +version = "4.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "jaraco.functools-4.1.0-py3-none-any.whl", hashes = {sha256 = "70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"}}, + {name = "jaraco.functools-4.1.0-py3-none-any.whl", hashes = {sha256 = "ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"}}, +] + + +[[packages]] +name = "jeepney" +version = "0.9.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "jeepney-0.9.0-py3-none-any.whl", hashes = {sha256 = "97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}}, + {name = "jeepney-0.9.0-py3-none-any.whl", hashes = {sha256 = "cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}}, +] + + +[[packages]] +name = "jinja2" +version = "3.1.6" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "jinja2-3.1.6-py3-none-any.whl", hashes = {sha256 = "0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}}, + {name = "jinja2-3.1.6-py3-none-any.whl", hashes = {sha256 = "85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}}, +] + + +[[packages]] +name = "keyring" +version = "25.6.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "keyring-25.6.0-py3-none-any.whl", hashes = {sha256 = "0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}}, + {name = "keyring-25.6.0-py3-none-any.whl", hashes = {sha256 = "552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}}, +] + + +[[packages]] +name = "linkify-it-py" +version = "2.0.3" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "linkify-it-py-2.0.3-py3-none-any.whl", hashes = {sha256 = "68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}}, + {name = "linkify-it-py-2.0.3-py3-none-any.whl", hashes = {sha256 = "6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}}, +] + + +[[packages]] +name = "markdown-it-py" +version = "3.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "markdown-it-py-3.0.0-py3-none-any.whl", hashes = {sha256 = "355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}}, + {name = "markdown-it-py-3.0.0-py3-none-any.whl", hashes = {sha256 = "e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}}, +] + + +[[packages]] +name = "markupsafe" +version = "3.0.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}}, + {name = "markupsafe-3.0.2-py3-none-any.whl", hashes = {sha256 = "fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}}, +] + + +[[packages]] +name = "mccabe" +version = "0.6.1" +marker = "dependency_groups in ('dev', 'test')" +wheels = [ + {name = "mccabe-0.6.1-py3-none-any.whl", hashes = {sha256 = "ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}}, + {name = "mccabe-0.6.1-py3-none-any.whl", hashes = {sha256 = "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}}, +] + + +[[packages]] +name = "mdit-py-plugins" +version = "0.4.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "mdit-py-plugins-0.4.2-py3-none-any.whl", hashes = {sha256 = "0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}}, + {name = "mdit-py-plugins-0.4.2-py3-none-any.whl", hashes = {sha256 = "5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}}, +] + + +[[packages]] +name = "mdurl" +version = "0.1.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "mdurl-0.1.2-py3-none-any.whl", hashes = {sha256 = "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}}, + {name = "mdurl-0.1.2-py3-none-any.whl", hashes = {sha256 = "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}}, +] + + +[[packages]] +name = "mock" +version = "5.2.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "mock-5.2.0-py3-none-any.whl", hashes = {sha256 = "4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0"}}, + {name = "mock-5.2.0-py3-none-any.whl", hashes = {sha256 = "7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f"}}, +] + + +[[packages]] +name = "more-itertools" +version = "10.7.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "more-itertools-10.7.0-py3-none-any.whl", hashes = {sha256 = "9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3"}}, + {name = "more-itertools-10.7.0-py3-none-any.whl", hashes = {sha256 = "d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e"}}, +] + + +[[packages]] +name = "mypy-extensions" +version = "1.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "mypy-extensions-1.1.0-py3-none-any.whl", hashes = {sha256 = "1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}}, + {name = "mypy-extensions-1.1.0-py3-none-any.whl", hashes = {sha256 = "52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}}, +] + + +[[packages]] +name = "myst-parser" +version = "4.0.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.10')" +wheels = [ + {name = "myst-parser-4.0.1-py3-none-any.whl", hashes = {sha256 = "5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4"}}, + {name = "myst-parser-4.0.1-py3-none-any.whl", hashes = {sha256 = "9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d"}}, +] + + +[[packages]] +name = "nh3" +version = "0.2.21" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "087ffadfdcd497658c3adc797258ce0f06be8a537786a7217649fc1c0c60c293"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "20979783526641c81d2f5bfa6ca5ccca3d1e4472474b162c6256745fbfe31cd1"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "2a5174551f95f2836f2ad6a8074560f261cf9740a48437d6151fd2d4d7d617ab"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "31eedcd7d08b0eae28ba47f43fd33a653b4cdb271d64f1aeda47001618348fde"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "4990e7ee6a55490dbf00d61a6f476c9a3258e31e711e13713b2ea7d6616f670e"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "55823c5ea1f6b267a4fad5de39bc0524d49a47783e1fe094bcf9c537a37df251"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "6141caabe00bbddc869665b35fc56a478eb774a8c1dfd6fba9fe1dfdf29e6efa"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "637d4a10c834e1b7d9548592c7aad760611415fcd5bd346f77fd8a064309ae6d"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "63ca02ac6f27fc80f9894409eb61de2cb20ef0a23740c7e29f9ec827139fa578"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "6ae319f17cd8960d0612f0f0ddff5a90700fa71926ca800e9028e7851ce44a6f"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "6c9c30b8b0d291a7c5ab0967ab200598ba33208f754f2f4920e9343bdd88f79a"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "713d16686596e556b65e7f8c58328c2df63f1a7abe1277d87625dcbbc012ef82"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "818f2b6df3763e058efa9e69677b5a92f9bc0acff3295af5ed013da544250d5b"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "9d67709bc0d7d1f5797b21db26e7a8b3d15d21c9c5f58ccfe48b5328483b685b"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "a5f77e62aed5c4acad635239ac1290404c7e940c81abe561fd2af011ff59f585"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "a772dec5b7b7325780922dd904709f0f5f3a79fbf756de5291c01370f6df0967"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "a7ea28cd49293749d67e4fcf326c554c83ec912cd09cd94aa7ec3ab1921c8283"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "ac7006c3abd097790e611fe4646ecb19a8d7f2184b882f6093293b8d9b887431"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "b3b5c58161e08549904ac4abd450dacd94ff648916f7c376ae4b2c0652b98ff9"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "b8d55ea1fc7ae3633d758a92aafa3505cd3cc5a6e40470c9164d54dff6f96d42"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "bb0014948f04d7976aabae43fcd4cb7f551f9f8ce785a4c9ef66e6c2590f8629"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "d002b648592bf3033adfd875a48f09b8ecc000abd7f6a8769ed86b6ccc70c759"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "d426d7be1a2f3d896950fe263332ed1662f6c78525b4520c8e9861f8d7f0d243"}}, + {name = "nh3-0.2.21-py3-none-any.whl", hashes = {sha256 = "fcff321bd60c6c5c9cb4ddf2554e22772bb41ebd93ad88171bbbb6f271255286"}}, +] + + +[[packages]] +name = "nodeenv" +version = "1.9.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6')" +wheels = [ + {name = "nodeenv-1.9.1-py3-none-any.whl", hashes = {sha256 = "6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}}, + {name = "nodeenv-1.9.1-py3-none-any.whl", hashes = {sha256 = "ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}}, +] + + +[[packages]] +name = "packaging" +version = "25.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "packaging-25.0-py3-none-any.whl", hashes = {sha256 = "29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}}, + {name = "packaging-25.0-py3-none-any.whl", hashes = {sha256 = "d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}}, +] + + +[[packages]] +name = "parse" +version = "1.20.2" +marker = "dependency_groups in ('dev', 'test')" +wheels = [ + {name = "parse-1.20.2-py3-none-any.whl", hashes = {sha256 = "967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558"}}, + {name = "parse-1.20.2-py3-none-any.whl", hashes = {sha256 = "b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce"}}, +] + + +[[packages]] +name = "parver" +version = "0.5" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "parver-0.5-py3-none-any.whl", hashes = {sha256 = "2281b187276c8e8e3c15634f62287b2fb6fe0efe3010f739a6bd1e45fa2bf2b2"}}, + {name = "parver-0.5-py3-none-any.whl", hashes = {sha256 = "b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777"}}, +] + + +[[packages]] +name = "pathspec" +version = "0.12.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "pathspec-0.12.1-py3-none-any.whl", hashes = {sha256 = "a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}}, + {name = "pathspec-0.12.1-py3-none-any.whl", hashes = {sha256 = "a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}}, +] + + +[[packages]] +name = "pip" +version = "25.0.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "pip-25.0.1-py3-none-any.whl", hashes = {sha256 = "88f96547ea48b940a3a385494e181e29fb8637898f88d88737c5049780f196ea"}}, + {name = "pip-25.0.1-py3-none-any.whl", hashes = {sha256 = "c46efd13b6aa8279f33f2864459c8ce587ea6a1a59ee20de055868d8f7688f7f"}}, +] + + +[[packages]] +name = "pipenv" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" + + +[[packages]] +name = "platformdirs" +version = "4.3.7" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "platformdirs-4.3.7-py3-none-any.whl", hashes = {sha256 = "a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}}, + {name = "platformdirs-4.3.7-py3-none-any.whl", hashes = {sha256 = "eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}}, +] + + +[[packages]] +name = "pluggy" +version = "1.5.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "pluggy-1.5.0-py3-none-any.whl", hashes = {sha256 = "2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}}, + {name = "pluggy-1.5.0-py3-none-any.whl", hashes = {sha256 = "44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}}, +] + + +[[packages]] +name = "pre-commit" +version = "2.21.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "pre-commit-2.21.0-py3-none-any.whl", hashes = {sha256 = "31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}}, + {name = "pre-commit-2.21.0-py3-none-any.whl", hashes = {sha256 = "e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}}, +] + + +[[packages]] +name = "pycodestyle" +version = "2.7.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3')" +wheels = [ + {name = "pycodestyle-2.7.0-py3-none-any.whl", hashes = {sha256 = "514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}}, + {name = "pycodestyle-2.7.0-py3-none-any.whl", hashes = {sha256 = "c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}}, +] + + +[[packages]] +name = "pycparser" +version = "2.22" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "pycparser-2.22-py3-none-any.whl", hashes = {sha256 = "491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}}, + {name = "pycparser-2.22-py3-none-any.whl", hashes = {sha256 = "c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}}, +] + + +[[packages]] +name = "pyenchant" +version = "3.3.0rc1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "pyenchant-3.3.0rc1-py3-none-any.whl", hashes = {sha256 = "0314d162b7af83adc500f5aff850c91466129363ca8c4d79a8b8d99253346204"}}, + {name = "pyenchant-3.3.0rc1-py3-none-any.whl", hashes = {sha256 = "377a2aaafcb41f871c573c5b74e502dcc6ddba72a62deae7d36dc601a9fcad3d"}}, + {name = "pyenchant-3.3.0rc1-py3-none-any.whl", hashes = {sha256 = "960cbbf4ac99cf9c662308aa9a017ef23017dbea4c8c1e8329978ee4600b3f55"}}, + {name = "pyenchant-3.3.0rc1-py3-none-any.whl", hashes = {sha256 = "ee514d7adf8d0fe39d3a14088f5915953e7fb3bda35092e696fc38f78aabb8b8"}}, +] + + +[[packages]] +name = "pyflakes" +version = "2.3.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3')" +wheels = [ + {name = "pyflakes-2.3.1-py3-none-any.whl", hashes = {sha256 = "7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}}, + {name = "pyflakes-2.3.1-py3-none-any.whl", hashes = {sha256 = "f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}}, +] + + +[[packages]] +name = "pygments" +version = "2.19.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "pygments-2.19.1-py3-none-any.whl", hashes = {sha256 = "61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}}, + {name = "pygments-2.19.1-py3-none-any.whl", hashes = {sha256 = "9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}}, +] + + +[[packages]] +name = "pypiserver" +version = "2.3.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "pypiserver-2.3.2-py3-none-any.whl", hashes = {sha256 = "67c30d2a1a2cea1c4cf6785ddfbce958dc8ee7978e56a54b5f4ebc0c5cb89079"}}, + {name = "pypiserver-2.3.2-py3-none-any.whl", hashes = {sha256 = "d2b16b12a667a9f356f1b022e039d5ad2c33a76986bb2fdb3f18633dbb1afca3"}}, +] + + +[[packages]] +name = "pyproject-hooks" +version = "1.2.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "pyproject-hooks-1.2.0-py3-none-any.whl", hashes = {sha256 = "1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}}, + {name = "pyproject-hooks-1.2.0-py3-none-any.whl", hashes = {sha256 = "9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}}, +] + + +[[packages]] +name = "pytest" +version = "8.3.5" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "pytest-8.3.5-py3-none-any.whl", hashes = {sha256 = "c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}}, + {name = "pytest-8.3.5-py3-none-any.whl", hashes = {sha256 = "f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}}, +] + + +[[packages]] +name = "pytest-cov" +version = "4.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "pytest-cov-4.1.0-py3-none-any.whl", hashes = {sha256 = "3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}}, + {name = "pytest-cov-4.1.0-py3-none-any.whl", hashes = {sha256 = "6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}}, +] + + +[[packages]] +name = "pytest-timeout" +version = "2.3.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "pytest-timeout-2.3.1-py3-none-any.whl", hashes = {sha256 = "12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9"}}, + {name = "pytest-timeout-2.3.1-py3-none-any.whl", hashes = {sha256 = "68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e"}}, +] + + +[[packages]] +name = "pytest-xdist" +version = "3.6.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "pytest-xdist-3.6.1-py3-none-any.whl", hashes = {sha256 = "9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}}, + {name = "pytest-xdist-3.6.1-py3-none-any.whl", hashes = {sha256 = "ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}}, +] + + +[[packages]] +name = "pyyaml" +version = "6.0.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}}, + {name = "pyyaml-6.0.1-py3-none-any.whl", hashes = {sha256 = "fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}}, +] + + +[[packages]] +name = "readme-renderer" +version = "44.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "readme-renderer-44.0-py3-none-any.whl", hashes = {sha256 = "2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}}, + {name = "readme-renderer-44.0-py3-none-any.whl", hashes = {sha256 = "8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}}, +] + + +[[packages]] +name = "requests" +version = "2.32.3" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "requests-2.32.3-py3-none-any.whl", hashes = {sha256 = "55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}}, + {name = "requests-2.32.3-py3-none-any.whl", hashes = {sha256 = "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}}, +] + + +[[packages]] +name = "requests-toolbelt" +version = "1.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3')" +wheels = [ + {name = "requests-toolbelt-1.0.0-py3-none-any.whl", hashes = {sha256 = "7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}}, + {name = "requests-toolbelt-1.0.0-py3-none-any.whl", hashes = {sha256 = "cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}}, +] + + +[[packages]] +name = "rfc3986" +version = "2.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "rfc3986-2.0.0-py3-none-any.whl", hashes = {sha256 = "50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}}, + {name = "rfc3986-2.0.0-py3-none-any.whl", hashes = {sha256 = "97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}}, +] + + +[[packages]] +name = "rich" +version = "14.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_full_version >= '3.8.0')" +wheels = [ + {name = "rich-14.0.0-py3-none-any.whl", hashes = {sha256 = "1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}}, + {name = "rich-14.0.0-py3-none-any.whl", hashes = {sha256 = "82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}}, +] + + +[[packages]] +name = "roman-numerals-py" +version = "3.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "roman-numerals-py-3.1.0-py3-none-any.whl", hashes = {sha256 = "9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c"}}, + {name = "roman-numerals-py-3.1.0-py3-none-any.whl", hashes = {sha256 = "be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d"}}, +] + + +[[packages]] +name = "secretstorage" +version = "3.3.3" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "secretstorage-3.3.3-py3-none-any.whl", hashes = {sha256 = "2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}}, + {name = "secretstorage-3.3.3-py3-none-any.whl", hashes = {sha256 = "f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}}, +] + + +[[packages]] +name = "semver" +version = "3.0.4" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "semver-3.0.4-py3-none-any.whl", hashes = {sha256 = "9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}}, + {name = "semver-3.0.4-py3-none-any.whl", hashes = {sha256 = "afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}}, +] + + +[[packages]] +name = "setuptools" +version = "79.0.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "setuptools-79.0.1-py3-none-any.whl", hashes = {sha256 = "128ce7b8f33c3079fd1b067ecbb4051a66e8526e7b65f6cec075dfc650ddfa88"}}, + {name = "setuptools-79.0.1-py3-none-any.whl", hashes = {sha256 = "e147c0549f27767ba362f9da434eab9c5dc0045d5304feb602a0af001089fc51"}}, +] + + +[[packages]] +name = "snowballstemmer" +version = "2.2.0" +marker = "dependency_groups in ('dev', 'test')" +wheels = [ + {name = "snowballstemmer-2.2.0-py3-none-any.whl", hashes = {sha256 = "09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}}, + {name = "snowballstemmer-2.2.0-py3-none-any.whl", hashes = {sha256 = "c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}}, +] + + +[[packages]] +name = "soupsieve" +version = "2.7" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "soupsieve-2.7-py3-none-any.whl", hashes = {sha256 = "6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}}, + {name = "soupsieve-2.7-py3-none-any.whl", hashes = {sha256 = "ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}}, +] + + +[[packages]] +name = "sphinx" +version = "8.2.3" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.11')" +wheels = [ + {name = "sphinx-8.2.3-py3-none-any.whl", hashes = {sha256 = "398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348"}}, + {name = "sphinx-8.2.3-py3-none-any.whl", hashes = {sha256 = "4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3"}}, +] + + +[[packages]] +name = "sphinx-click" +version = "4.4.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "sphinx-click-4.4.0-py3-none-any.whl", hashes = {sha256 = "2821c10a68fc9ee6ce7c92fad26540d8d8c8f45e6d7258f0e4fb7529ae8fab49"}}, + {name = "sphinx-click-4.4.0-py3-none-any.whl", hashes = {sha256 = "cc67692bd28f482c7f01531c61b64e9d2f069bfcf3d24cbbb51d4a84a749fa48"}}, +] + + +[[packages]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "sphinxcontrib-applehelp-2.0.0-py3-none-any.whl", hashes = {sha256 = "2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}}, + {name = "sphinxcontrib-applehelp-2.0.0-py3-none-any.whl", hashes = {sha256 = "4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}}, +] + + +[[packages]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "sphinxcontrib-devhelp-2.0.0-py3-none-any.whl", hashes = {sha256 = "411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}}, + {name = "sphinxcontrib-devhelp-2.0.0-py3-none-any.whl", hashes = {sha256 = "aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}}, +] + + +[[packages]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "sphinxcontrib-htmlhelp-2.1.0-py3-none-any.whl", hashes = {sha256 = "166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}}, + {name = "sphinxcontrib-htmlhelp-2.1.0-py3-none-any.whl", hashes = {sha256 = "c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}}, +] + + +[[packages]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.5')" +wheels = [ + {name = "sphinxcontrib-jsmath-1.0.1-py3-none-any.whl", hashes = {sha256 = "2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}}, + {name = "sphinxcontrib-jsmath-1.0.1-py3-none-any.whl", hashes = {sha256 = "a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}}, +] + + +[[packages]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "sphinxcontrib-qthelp-2.0.0-py3-none-any.whl", hashes = {sha256 = "4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}}, + {name = "sphinxcontrib-qthelp-2.0.0-py3-none-any.whl", hashes = {sha256 = "b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}}, +] + + +[[packages]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "sphinxcontrib-serializinghtml-2.0.0-py3-none-any.whl", hashes = {sha256 = "6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}}, + {name = "sphinxcontrib-serializinghtml-2.0.0-py3-none-any.whl", hashes = {sha256 = "e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}}, +] + + +[[packages]] +name = "sphinxcontrib-spelling" +version = "7.7.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.6')" +wheels = [ + {name = "sphinxcontrib-spelling-7.7.0-py3-none-any.whl", hashes = {sha256 = "56561c3f6a155b0946914e4de988729859315729dc181b5e4dc8a68fe78de35a"}}, + {name = "sphinxcontrib-spelling-7.7.0-py3-none-any.whl", hashes = {sha256 = "95a0defef8ffec6526f9e83b20cc24b08c9179298729d87976891840e3aa3064"}}, +] + + +[[packages]] +name = "stdeb" +version = "0.10.0" +marker = "dependency_groups in ('dev', 'test') and (sys_platform == 'linux')" +wheels = [ + {name = "stdeb-0.10.0-py3-none-any.whl", hashes = {sha256 = "08c22c9c03b28a140fe3ec5064b53a5288279f22e596ca06b0be698d50c93cf2"}}, +] + + +[[packages]] +name = "tomli" +version = "2.2.1" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}}, + {name = "tomli-2.2.1-py3-none-any.whl", hashes = {sha256 = "f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}}, +] + + +[[packages]] +name = "towncrier" +version = "24.8.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "towncrier-24.8.0-py3-none-any.whl", hashes = {sha256 = "013423ee7eed102b2f393c287d22d95f66f1a3ea10a4baa82d298001a7f18af3"}}, + {name = "towncrier-24.8.0-py3-none-any.whl", hashes = {sha256 = "9343209592b839209cdf28c339ba45792fbfe9775b5f9c177462fd693e127d8d"}}, +] + + +[[packages]] +name = "twine" +version = "6.1.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "twine-6.1.0-py3-none-any.whl", hashes = {sha256 = "a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"}}, + {name = "twine-6.1.0-py3-none-any.whl", hashes = {sha256 = "be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"}}, +] + + +[[packages]] +name = "typing-extensions" +version = "4.13.2" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "typing-extensions-4.13.2-py3-none-any.whl", hashes = {sha256 = "a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}}, + {name = "typing-extensions-4.13.2-py3-none-any.whl", hashes = {sha256 = "e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}}, +] + + +[[packages]] +name = "uc-micro-py" +version = "1.0.3" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.7')" +wheels = [ + {name = "uc-micro-py-1.0.3-py3-none-any.whl", hashes = {sha256 = "d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}}, + {name = "uc-micro-py-1.0.3-py3-none-any.whl", hashes = {sha256 = "db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}}, +] + + +[[packages]] +name = "urllib3" +version = "2.4.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "urllib3-2.4.0-py3-none-any.whl", hashes = {sha256 = "414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}}, + {name = "urllib3-2.4.0-py3-none-any.whl", hashes = {sha256 = "4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}}, +] + + +[[packages]] +name = "virtualenv" +version = "20.30.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.8')" +wheels = [ + {name = "virtualenv-20.30.0-py3-none-any.whl", hashes = {sha256 = "800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8"}}, + {name = "virtualenv-20.30.0-py3-none-any.whl", hashes = {sha256 = "e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6"}}, +] + + +[[packages]] +name = "waitress" +version = "3.0.2" +marker = "dependency_groups in ('dev', 'test') and (sys_platform == 'win32')" +wheels = [ + {name = "waitress-3.0.2-py3-none-any.whl", hashes = {sha256 = "682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f"}}, + {name = "waitress-3.0.2-py3-none-any.whl", hashes = {sha256 = "c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e"}}, +] + + +[[packages]] +name = "zipp" +version = "3.21.0" +marker = "dependency_groups in ('dev', 'test') and (python_version >= '3.9')" +wheels = [ + {name = "zipp-3.21.0-py3-none-any.whl", hashes = {sha256 = "2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}}, + {name = "zipp-3.21.0-py3-none-any.whl", hashes = {sha256 = "ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}}, +] + +[tool.pipenv] +generated_from = "Pipfile.lock" +generation_date = "2025-04-25T08:48:26.759736+00:00" From 4a3440c96f12b99cdac8ac47dc8ae4a13478389e Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Fri, 25 Apr 2025 04:56:06 -0400 Subject: [PATCH 4/4] Ability to read/write the pylock.yaml --- pipenv/project.py | 8 +++++--- pipenv/routines/install.py | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pipenv/project.py b/pipenv/project.py index e952bc9d9f..b4717a5c47 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -818,10 +818,12 @@ def lockfile_exists(self): @property def lockfile_content(self): """Returns the content of the lockfile, checking for pylock.toml first.""" - if self.pylock_exists: + if self.pylock_exists or self.use_pylock: try: - pylock = PylockFile.from_path(self.pylock_location) - return pylock.convert_to_pipenv_lockfile() + if self.pylock_exists: + pylock = PylockFile.from_path(self.pylock_location) + lockfile_data = pylock.convert_to_pipenv_lockfile() + return lockfile_data except Exception as e: err.print(f"[bold yellow]Error loading pylock.toml: {e}[/bold yellow]") return self.load_lockfile() diff --git a/pipenv/routines/install.py b/pipenv/routines/install.py index 0094e25e89..2b63ee9663 100644 --- a/pipenv/routines/install.py +++ b/pipenv/routines/install.py @@ -478,8 +478,13 @@ def do_install_dependencies( lockfile_category = get_lockfile_section_using_pipfile_category( pipfile_category ) + lockfile_type = ( + "pylock.toml" + if project.use_pylock and project.pylock_location + else "Pipfile.lock" + ) console.print( - f"Installing dependencies from Pipfile.lock [{lockfile_category}]" + f"Installing dependencies from {lockfile_type} [{lockfile_category}]" f"({lockfile['_meta'].get('hash', {}).get('sha256')[-6:]})...", style="bold", )