├── setup.cfg ├── datargs ├── py.typed ├── meta.py ├── __init__.py ├── compat │ ├── attrs.py │ └── __init__.py ├── convert.py └── make.py ├── tests ├── __init__.py ├── test_arg_options.py ├── test_subcommands.py └── test_arg_type.py ├── .gitattributes ├── .gitignore ├── make-toc.sh ├── tox.ini ├── LICENSE ├── pyproject.toml ├── README.md └── uv.lock /setup.cfg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /datargs/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=input 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .venv 3 | *.egg-info 4 | __pycache__ 5 | dist 6 | .python-version 7 | .tox 8 | -------------------------------------------------------------------------------- /make-toc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | markdown-toc -i README.md 3 | sed -E -i 's/\* \[Use \[(.*?)\]\(.*\)\?\](.*)/* [Use \1]\2?/' README.md 4 | -------------------------------------------------------------------------------- /datargs/meta.py: -------------------------------------------------------------------------------- 1 | class AbstractClassProperty: 2 | """ 3 | Abstract property that should be overridden by a class-level constant. 4 | """ 5 | 6 | __isabstractmethod__ = True 7 | -------------------------------------------------------------------------------- /datargs/__init__.py: -------------------------------------------------------------------------------- 1 | from .make import parse, arg, make_parser, argsclass 2 | from .convert import convert, convert_str 3 | 4 | __all__ = ["parse", "arg", "make_parser", "argsclass", "convert"] 5 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # content of: tox.ini , put in same dir as setup.py 2 | [tox] 3 | envlist = py37,py38,py39,py310,py311,py312 4 | 5 | [testenv] 6 | # install pytest in the virtualenv where commands will be executed 7 | deps = pytest 8 | extras = attrs 9 | commands = 10 | # NOTE: you can run any command line tool here - not just tests 11 | pytest 12 | -------------------------------------------------------------------------------- /datargs/compat/attrs.py: -------------------------------------------------------------------------------- 1 | from . import RecordField, RecordClass 2 | 3 | try: 4 | import attr 5 | except ImportError: 6 | pass 7 | else: 8 | 9 | class AttrField(RecordField[attr.Attribute]): 10 | def is_required(self) -> bool: 11 | return self.default is attr.NOTHING 12 | 13 | class AttrClass(RecordClass[attr.Attribute]): 14 | fields_attribute = "__attrs_attrs__" 15 | field_wrapper_type = AttrField 16 | 17 | def fields_dict(self): 18 | return { 19 | name: self.get_field(field, self.cls) 20 | for name, field in attr.fields_dict(self.cls).items() 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Roee Nizan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "datargs" 3 | requires-python = '>=3.13' 4 | version = "1.1.0" 5 | description = "Declarative, type-safe command line argument parsers from dataclasses and attrs classes" 6 | authors = [{name="Roee Nizan", email="roeen30@gmail.com"}] 7 | license-files = ['LICENSE'] 8 | keywords = ["argparse", "dataclass", "attrs"] 9 | readme = "README.md" 10 | classifiers = [ 11 | "Operating System :: OS Independent", 12 | "Environment :: Console", 13 | "License :: OSI Approved :: MIT License", 14 | "Programming Language :: Python :: 3.13", 15 | "Typing :: Typed", 16 | ] 17 | dependencies = [ 18 | 'boltons>=20.2.1', 19 | 'typing-extensions ; python_full_version < "3.8"', 20 | ] 21 | 22 | [project.urls] 23 | homepage = "https://github.com/roee30/datargs" 24 | repository = "https://github.com/roee30/datargs" 25 | 26 | [project.optional-dependencies] 27 | attrs = ["attrs>=20.2.0"] 28 | 29 | [dependency-groups] 30 | dev = [ 31 | "black>=20.8b1", 32 | "pytest>=6.0.2", 33 | "attrs>=20.2.0", 34 | "ipdb>=0.13.3", 35 | "pdbpp>=0.10.2", 36 | "tox>=3.24.4", 37 | "tox-pyenv>=1.1.0", 38 | ] 39 | 40 | [build-system] 41 | requires = ["setuptools"] 42 | build-backend = "setuptools.build_meta" 43 | -------------------------------------------------------------------------------- /tests/test_arg_options.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | 3 | from datargs.make import arg, parse, make_parser 4 | from tests.test_arg_type import ParserTest 5 | 6 | 7 | def test_help(): 8 | parser_help = "Program documentation" 9 | program = "My prog" 10 | parser = ParserTest(description=parser_help, prog=program) 11 | help_string = parser.format_help() 12 | assert parser_help in help_string 13 | assert program in help_string 14 | 15 | @dataclass 16 | class Args: 17 | flag: bool = arg(help="helpful message") 18 | 19 | args = parse(Args, []) 20 | assert not args.flag 21 | parser = make_parser(Args, parser) 22 | help_string = parser.format_help() 23 | assert "helpful message" in help_string 24 | assert parser_help in help_string 25 | assert program in help_string 26 | 27 | 28 | def test_decorator_no_args(): 29 | @dataclass 30 | class Args: 31 | flag: bool = arg(help="helpful message") 32 | 33 | assert not parse(Args, []).flag 34 | 35 | 36 | def test_decorator_with_args(): 37 | @dataclass(repr=True) 38 | class Args: 39 | flag: bool = arg(help="helpful message") 40 | 41 | assert not parse(Args, []).flag 42 | 43 | 44 | def test_dataclass_with_args(): 45 | @dataclass 46 | class Args: 47 | x: int = arg(default=0) 48 | 49 | assert Args().x == 0 50 | 51 | 52 | def test_default(): 53 | @dataclass 54 | class Args: 55 | x: int = arg(default=0) 56 | 57 | assert Args().x == 0 58 | 59 | 60 | def test_alias(): 61 | @dataclass 62 | class Args: 63 | num: int = arg("-n") 64 | 65 | args = parse(Args, ["-n", "0"]) 66 | assert args.num == 0 67 | 68 | 69 | def test_count(): 70 | @dataclass 71 | class Args: 72 | verbosity: int = field( 73 | default=0, 74 | metadata=dict( 75 | aliases=["-v"], 76 | help="Increase logging verbosity", 77 | action="count", 78 | ), 79 | ) 80 | args = parse(Args, ["-vv"]) 81 | assert args.verbosity == 2 -------------------------------------------------------------------------------- /datargs/convert.py: -------------------------------------------------------------------------------- 1 | from operator import attrgetter 2 | import dataclasses 3 | from argparse import ArgumentParser 4 | from textwrap import indent, dedent 5 | from typing import Tuple 6 | 7 | 8 | def convert(parser: ArgumentParser): 9 | print(convert_str(parser)) 10 | 11 | 12 | def convert_str(parser: ArgumentParser) -> str: 13 | attrs, is_argsclass = get_attrs(parser) 14 | if is_argsclass: 15 | header = dedent( 16 | """ 17 | from datargs import argsclass, arg 18 | 19 | 20 | @argsclass 21 | class Args: 22 | """ 23 | ) 24 | else: 25 | header = dedent( 26 | f""" 27 | from dataclasses import dataclass 28 | 29 | 30 | @dataclass 31 | class Args: 32 | """ 33 | ) 34 | 35 | return header + indent(attrs, " " * 4) 36 | 37 | 38 | def get_attrs(parser: ArgumentParser) -> Tuple[str, bool]: 39 | def to_var(x): 40 | return x.strip("-").replace("-", "_") 41 | 42 | seen = set() 43 | is_argsclass = False 44 | result = "" 45 | for action in sorted(parser._actions, key=lambda a: a.default is not None): 46 | first, *aliases = action.option_strings 47 | if first in seen or first == "-h": 48 | continue 49 | seen.add(first) 50 | name = to_var(first) 51 | aliases = list(map(repr, aliases)) 52 | try: 53 | typ = action.type.__name__ 54 | except AttributeError: 55 | if action.type is not None: 56 | raise 57 | if "StoreTrue" in str(action): 58 | typ = "bool" 59 | else: 60 | typ = action.type or "str" 61 | if action.nargs: 62 | typ = f"list[{typ}]" 63 | relevant_pairs = { 64 | key: value 65 | for key, value in vars(action).items() 66 | if value is not None 67 | and not isinstance(value, dataclasses._MISSING_TYPE) 68 | and key 69 | not in [ 70 | "required", 71 | "dest", 72 | "const", 73 | "option_strings", 74 | "container", 75 | "nargs", 76 | "type", 77 | ] 78 | } 79 | if relevant_pairs.get("deprecated", None) is False: 80 | relevant_pairs.pop("deprecated") 81 | rest = [f"{key}={value!r}" for key, value in relevant_pairs.items()] 82 | result += f"{name}: {typ}" 83 | if set(relevant_pairs) - {"default"}: 84 | is_argsclass = True 85 | result += f" = arg({', '.join(aliases + rest)})" 86 | elif not action.required: 87 | default = vars(action).get("default", None) 88 | result += f" = {default!r}" 89 | result += "\n" 90 | return result, is_argsclass 91 | 92 | 93 | def random_test(): 94 | parser = ArgumentParser() 95 | parser.add_argument("-a", required=True) 96 | parser.add_argument("-b", default=3, type=int) 97 | print(convert(parser)) 98 | parser = ArgumentParser() 99 | parser.add_argument("-a", required=True, help="help") 100 | parser.add_argument("-b", default=3, type=int) 101 | print(convert(parser)) 102 | parser = ArgumentParser() 103 | parser.add_argument("-a", type=str, required=True, help="help") 104 | parser.add_argument("-b", default=3, type=int, help="help") 105 | print(convert(parser)) 106 | 107 | 108 | if __name__ == "__main__": 109 | main() 110 | -------------------------------------------------------------------------------- /tests/test_subcommands.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # noinspection PyUnresolvedReferences,PyProtectedMember 4 | from argparse import _SubParsersAction 5 | from typing import Union 6 | 7 | from dataclasses import dataclass 8 | 9 | import pytest 10 | from pytest import raises 11 | 12 | from datargs import make_parser, argsclass, arg, parse 13 | from tests.test_arg_type import ParserTest, ParserError 14 | 15 | 16 | def test_subcommands(): 17 | install_help = "installing command" 18 | package_help = "package help" 19 | 20 | @argsclass(description=install_help) 21 | class Install: 22 | package: str = arg(positional=True, help=package_help) 23 | 24 | help_help = "helping command" 25 | 26 | @argsclass(description=help_help) 27 | class Help: 28 | command: str = arg(positional=True) 29 | verbose: bool 30 | 31 | pip_help = "Pip install packages!" 32 | 33 | @argsclass(description=pip_help) 34 | class Pip: 35 | action: Union[Install, Help] 36 | verbose: bool 37 | 38 | # passing your own parser ignores `description` 39 | assert pip_help in make_parser(Pip).format_help() 40 | 41 | parser = make_parser(Pip, ParserTest()) 42 | subparsers = next( 43 | action for action in parser._actions if isinstance(action, _SubParsersAction) 44 | ) 45 | args = [("install", "package", install_help), ("help", "command", help_help)] 46 | for sub_command, arg_name, help_str in args: 47 | assert sub_command in parser.format_help() 48 | # noinspection PyUnresolvedReferences 49 | sub_help = subparsers.choices[sub_command].format_help() 50 | assert arg_name in sub_help 51 | assert help_str in sub_help 52 | 53 | result = parse(Pip, ["--verbose", "install", "foo"]) 54 | assert isinstance(result, Pip) 55 | assert isinstance(result.action, Install) 56 | assert result.action.package == "foo" 57 | assert result.verbose 58 | 59 | result = parse(Pip, ["help", "foo", "--verbose"]) 60 | assert isinstance(result, Pip) 61 | assert isinstance(result.action, Help) 62 | assert result.action.command == "foo" 63 | assert result.action.verbose 64 | assert not result.verbose 65 | 66 | # When a required argument is missing, argparse should print an error and trigger a 67 | # system exit 68 | with raises(SystemExit): 69 | parse(Pip, []) 70 | 71 | 72 | def test_union_no_dataclass(): 73 | @dataclass 74 | class Args: 75 | action: Union[int, str] 76 | 77 | with raises(Exception) as exc_info: 78 | parse(Args, []) 79 | assert "Union" in exc_info.value.args[0] 80 | 81 | 82 | def test_name(): 83 | @argsclass(name="foo") 84 | class Install: 85 | package: str = arg(positional=True) 86 | 87 | @argsclass(name="bar") 88 | class Help: 89 | command: str = arg(positional=True) 90 | 91 | @argsclass 92 | class Pip: 93 | action: Union[Install, Help] 94 | 95 | result = parse(Pip, ["foo", "x"], parser=ParserTest()) 96 | assert result.action.package == "x" 97 | with raises(ParserError): 98 | parse(Pip, ["install", "x"], parser=ParserTest()) 99 | 100 | result = parse(Pip, ["bar", "x"], parser=ParserTest()) 101 | assert result.action.command == "x" 102 | with raises(ParserError): 103 | parse(Pip, ["help", "x"], parser=ParserTest()) 104 | 105 | 106 | def test_aliases(): 107 | @argsclass(parser_params=dict(aliases=["foo"])) 108 | class Install: 109 | package: str = arg(positional=True) 110 | 111 | @argsclass(parser_params=dict(aliases=["bar"])) 112 | class Help: 113 | command: str = arg(positional=True) 114 | 115 | @argsclass 116 | class Pip: 117 | action: Union[Install, Help] 118 | 119 | for alias in ["install", "foo"]: 120 | result = parse(Pip, [alias, "x"], parser=ParserTest()) 121 | assert result.action.package == "x" 122 | 123 | for alias in ["help", "bar"]: 124 | result = parse(Pip, [alias, "x"], parser=ParserTest()) 125 | assert result.action.command == "x" 126 | 127 | 128 | def test_hyphen(): 129 | @dataclass 130 | class FooBar: 131 | baz: str = arg(positional=True) 132 | 133 | @dataclass 134 | class BarFoo: 135 | spam: str = arg(positional=True) 136 | 137 | @argsclass 138 | class Prog: 139 | action: Union[FooBar, BarFoo] 140 | 141 | result = parse(Prog, ["foo-bar", "x"], parser=ParserTest()) 142 | assert result.action.baz == "x" 143 | result = parse(Prog, ["bar-foo", "x"], parser=ParserTest()) 144 | assert result.action.spam == "x" 145 | 146 | 147 | @pytest.mark.skipif(sys.version_info < (3, 10), reason="Test requires Python 3.10+") 148 | def test_union_operator(): 149 | @argsclass(name="foo") 150 | class Install: 151 | package: str = arg(positional=True) 152 | 153 | @argsclass(name="bar") 154 | class Help: 155 | command: str = arg(positional=True) 156 | 157 | @argsclass 158 | class Pip: 159 | action: Install | Help 160 | 161 | result = parse(Pip, ["foo", "x"], parser=ParserTest()) 162 | assert result.action.package == "x" 163 | with raises(ParserError): 164 | parse(Pip, ["install", "x"], parser=ParserTest()) 165 | 166 | result = parse(Pip, ["bar", "x"], parser=ParserTest()) 167 | assert result.action.command == "x" 168 | with raises(ParserError): 169 | parse(Pip, ["help", "x"], parser=ParserTest()) 170 | -------------------------------------------------------------------------------- /datargs/compat/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module for uniform treatment of dataclasses and attrs classes. 3 | """ 4 | 5 | import dataclasses 6 | from abc import abstractmethod 7 | from dataclasses import dataclass 8 | from types import UnionType 9 | from typing import ( 10 | Callable, 11 | Mapping, 12 | Type, 13 | Union, 14 | get_type_hints, 15 | Optional, 16 | cast, TYPE_CHECKING, 17 | ) 18 | 19 | from datargs.meta import AbstractClassProperty 20 | 21 | if TYPE_CHECKING: 22 | from _typeshed import DataclassInstance 23 | 24 | 25 | @dataclass 26 | class DatargsParams: 27 | parser: dict = dataclasses.field(default_factory=dict) 28 | sub_commands: dict = dataclasses.field(default_factory=dict) 29 | name: Optional[str] = None 30 | 31 | def __post_init__(self, *_, **__): 32 | for key, value in ( 33 | ("required", True), 34 | ("dest", "__datargs_dest__"), 35 | ): 36 | self.sub_commands.setdefault(key, value) 37 | 38 | 39 | class RecordField[FieldType: dataclasses.Field]: 40 | """ 41 | Abstract base class for fields of dataclasses or attrs classes. 42 | """ 43 | 44 | _field: FieldType 45 | _cls: type | None 46 | NONE = object() 47 | 48 | def __init__(self, field, cls=None): 49 | self._field = field 50 | self._cls = cls 51 | 52 | @abstractmethod 53 | def is_required(self) -> bool: 54 | """ 55 | Return whether field is required. 56 | """ 57 | pass 58 | 59 | def has_default(self) -> bool: 60 | """ 61 | Helper method to indicate whether a field has a default value. 62 | Used to make intention clearer in call sites. 63 | """ 64 | return not self.is_required() 65 | 66 | @property 67 | def default(self): 68 | if (f := self._field.default_factory) is not dataclasses.MISSING: 69 | return cast(Callable, f)() 70 | return self._field.default 71 | 72 | @property 73 | def name(self): 74 | return self._field.name 75 | 76 | @property 77 | def type(self): 78 | typ = self._field.type 79 | if isinstance(typ, str): 80 | return get_type_hints(self._cls)[self.name] 81 | return typ 82 | 83 | @property 84 | def metadata(self): 85 | return self._field.metadata 86 | 87 | @property 88 | def is_positional(self) -> bool: 89 | return self.metadata.get("positional", False) 90 | 91 | 92 | class DataField(RecordField[dataclasses.Field]): 93 | """ 94 | Represents a dataclass field. 95 | """ 96 | 97 | def is_required(self) -> bool: 98 | return self.default is dataclasses.MISSING 99 | 100 | 101 | class NotARecordClass(Exception): 102 | pass 103 | 104 | 105 | class RecordClass[FieldType]: 106 | """ 107 | Abstract base class for dataclasses or attrs classes. 108 | """ 109 | 110 | # The name of the attribute that holds field definitions 111 | fields_attribute: str = AbstractClassProperty() 112 | 113 | # The type to wrap fields with 114 | field_wrapper_type: Type[RecordField] = AbstractClassProperty() 115 | _implementors = [] 116 | 117 | def __init_subclass__(cls, **_): 118 | super().__init_subclass__() 119 | if not getattr(cls, "__abstractmethods__", None): 120 | cls._implementors.append(cls) 121 | 122 | def __init__(self, cls): 123 | self.cls: type[DataclassInstance] = cls 124 | 125 | @property 126 | def datargs_params(self) -> DatargsParams: 127 | return getattr(self.cls, "__datargs_params__", DatargsParams()) 128 | 129 | @property 130 | def parser_params(self): 131 | return self.datargs_params.parser 132 | 133 | @property 134 | def sub_commands_params(self): 135 | return self.datargs_params.sub_commands 136 | 137 | @property 138 | def name(self): 139 | return self.cls.__name__ 140 | 141 | @abstractmethod 142 | def fields_dict(self) -> Mapping[str, FieldType]: 143 | """ 144 | Returns a mapping of field names to field wrapper classes. 145 | """ 146 | pass 147 | 148 | @classmethod 149 | def can_wrap_class(cls, potential_record_class) -> bool: 150 | """ 151 | Returns whether this class is the appropriate implementation for wrapping `potential_record_class`. 152 | """ 153 | return getattr(potential_record_class, cls.fields_attribute, None) is not None 154 | 155 | @classmethod 156 | def wrap_class(cls, record_class) -> "RecordClass": 157 | """ 158 | Wrap `record_class` with the appropriate wrapper. 159 | """ 160 | for candidate in cls._implementors: 161 | if candidate.can_wrap_class(record_class): 162 | return candidate(record_class) 163 | if getattr(record_class, "__attrs_attrs__", None) is not None: 164 | raise NotARecordClass( 165 | f"can't accept '{record_class.__name__}' because it is an attrs class and attrs is not installed" 166 | ) 167 | raise NotARecordClass( 168 | f"class '{record_class.__name__}' is not a dataclass nor an attrs class" 169 | ) 170 | 171 | @classmethod 172 | def get_field(cls, field: FieldType, record_class): 173 | """ 174 | Wrap field with field classes with a uniform interface. 175 | """ 176 | return cast(FieldType, cls.field_wrapper_type)(field, record_class) 177 | 178 | 179 | class DataClass(RecordClass[dataclasses.Field]): 180 | """ 181 | Represents a dataclass. 182 | """ 183 | 184 | fields_attribute = "__dataclass_fields__" 185 | field_wrapper_type = DataField 186 | 187 | def fields_dict(self) -> Mapping[str, dataclasses.Field]: 188 | fields = dataclasses.fields(self.cls) 189 | return {field.name: self.get_field(field, self.cls) for field in fields} 190 | 191 | 192 | def is_optional(typ): 193 | assert typ 194 | return ( 195 | (isinstance(typ, UnionType) or getattr(typ, "__origin__", None) is Union) 196 | and len(typ.__args__) == 2 197 | and type(None) in typ.__args__ 198 | ) 199 | 200 | 201 | try: 202 | import attr 203 | except ImportError: 204 | pass 205 | else: 206 | # import class to register it in RecordClass._implementors 207 | import datargs.compat.attrs 208 | -------------------------------------------------------------------------------- /tests/test_arg_type.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | import sys 3 | from argparse import ArgumentParser 4 | from dataclasses import dataclass 5 | from enum import Enum 6 | from pathlib import Path 7 | from typing import Type, Sequence, Text, NoReturn, TypeVar, Optional 8 | 9 | import attr 10 | import pytest 11 | from pytest import raises 12 | 13 | from datargs.compat import DataClass, RecordClass 14 | from datargs.make import argsclass, parse, arg, make_parser 15 | 16 | 17 | @pytest.fixture( 18 | scope="module", 19 | params=[attr.dataclass, dataclass, argsclass], 20 | ids=["attrs", None, None], 21 | ) 22 | def factory(request): 23 | return request.param 24 | 25 | 26 | def test_attrs_imported(): 27 | try: 28 | import attr 29 | except ImportError: 30 | pass 31 | else: 32 | import datargs.compat.attrs 33 | 34 | 35 | def test_attrs_error(): 36 | class NoInitSubclass: 37 | def __init_subclass__(cls, **kwargs): 38 | pass 39 | 40 | class MockResolver(NoInitSubclass, RecordClass, ABC): 41 | _implementors = [DataClass] 42 | 43 | @attr.s 44 | class Args: 45 | pass 46 | 47 | with raises(Exception) as exc_info: 48 | MockResolver.wrap_class(Args) 49 | assert "not installed" in exc_info.value.args[0] 50 | 51 | 52 | def test_invalid_class(): 53 | class NoDataclass: 54 | pass 55 | 56 | with pytest.raises(Exception) as exc_info: 57 | parse_test(NoDataclass, []) 58 | assert "not a dataclass" in exc_info.value.args[0] 59 | 60 | 61 | def test_bool(factory): 62 | @factory 63 | class TestStoreTrue: 64 | store_true: bool = False 65 | 66 | args = parse_test(TestStoreTrue, []) 67 | assert not args.store_true 68 | args = parse_test(TestStoreTrue, ["--store-true"]) 69 | assert args.store_true 70 | 71 | @factory 72 | class TestStoreTrueNoDefault: 73 | store_true: bool 74 | 75 | args = parse_test(TestStoreTrueNoDefault, []) 76 | assert not args.store_true 77 | args = parse_test(TestStoreTrueNoDefault, ["--store-true"]) 78 | assert args.store_true 79 | 80 | @factory 81 | class TestStoreFalse: 82 | store_false: bool = True 83 | 84 | args = parse_test(TestStoreFalse, []) 85 | assert args.store_false 86 | args = parse_test(TestStoreFalse, ["--store-false"]) 87 | assert not args.store_false 88 | 89 | 90 | def test_str(factory): 91 | @factory 92 | class TestStringRequired: 93 | arg: str 94 | 95 | _test_required(TestStringRequired) 96 | 97 | args = parse_test(TestStringRequired, ["--arg", "test"]) 98 | assert args.arg == "test" 99 | 100 | @factory 101 | class TestStringOptional: 102 | arg: str = "default" 103 | 104 | args = parse_test(TestStringOptional, []) 105 | assert args.arg == "default" 106 | args = parse_test(TestStringOptional, ["--arg", "test"]) 107 | assert args.arg == "test" 108 | 109 | 110 | def test_int(factory): 111 | @factory 112 | class TestIntRequired: 113 | arg: int 114 | 115 | _test_required(TestIntRequired) 116 | 117 | args = parse_test(TestIntRequired, ["--arg", "1"]) 118 | assert args.arg == 1 119 | 120 | @factory 121 | class TestIntOptional: 122 | arg: int = 0 123 | 124 | args = parse_test(TestIntOptional, []) 125 | assert args.arg == 0 126 | args = parse_test(TestIntOptional, ["--arg", "1"]) 127 | assert args.arg == 1 128 | 129 | 130 | def test_float(factory): 131 | @factory 132 | class TestIntRequired: 133 | arg: int 134 | 135 | _test_required(TestIntRequired) 136 | 137 | args = parse_test(TestIntRequired, ["--arg", "1"]) 138 | assert args.arg == 1 139 | 140 | @factory 141 | class TestIntOptional: 142 | arg: int = 0 143 | 144 | args = parse_test(TestIntOptional, []) 145 | assert args.arg == 0 146 | args = parse_test(TestIntOptional, ["--arg", "1"]) 147 | assert args.arg == 1 148 | 149 | 150 | def test_enum(factory): 151 | class TestEnum(Enum): 152 | a = 0 153 | b = 1 154 | 155 | @factory 156 | class TestEnumRequired: 157 | arg: TestEnum 158 | 159 | _test_required(TestEnumRequired) 160 | args = parse_test(TestEnumRequired, ["--arg", "a"]) 161 | assert args.arg == TestEnum.a 162 | 163 | @factory 164 | class TestEnumOptional: 165 | arg: TestEnum = TestEnum.b 166 | 167 | args = parse_test(TestEnumOptional, ["--arg", "a"]) 168 | assert args.arg == TestEnum.a 169 | args = parse_test(TestEnumOptional, []) 170 | assert args.arg == TestEnum.b 171 | 172 | 173 | def test_optional_enum(factory): 174 | class TestEnum(Enum): 175 | foo = 0 176 | bar = 0 177 | 178 | @factory 179 | class Args: 180 | arg: Optional[TestEnum] = None 181 | 182 | args = parse_test(Args, ["--arg", "foo"]) 183 | assert args.arg == TestEnum.foo 184 | args = parse_test(Args, []) 185 | assert args.arg is None 186 | 187 | 188 | def test_sequence(factory): 189 | @factory 190 | class TestSequenceRequired: 191 | arg: Sequence[int] 192 | 193 | _test_required(TestSequenceRequired) 194 | 195 | args = parse_test(TestSequenceRequired, ["--arg", "1", "2"]) 196 | assert args.arg == [1, 2] 197 | 198 | @factory 199 | class TestSequenceOptional: 200 | arg: Sequence[int] = () 201 | 202 | args = parse_test(TestSequenceOptional, []) 203 | assert args.arg == () 204 | args = parse_test(TestSequenceOptional, ["--arg", "1", "2"]) 205 | assert args.arg == [1, 2] 206 | 207 | @argsclass 208 | class TestSequencePositional: 209 | arg: Sequence[int] = arg(default=(), positional=True) 210 | 211 | args = parse_test(TestSequencePositional, []) 212 | assert args.arg == () 213 | args = parse_test(TestSequencePositional, ["1", "2"]) 214 | assert args.arg == [1, 2] 215 | 216 | @argsclass 217 | class TestSequencePositionalPath: 218 | arg: Sequence[Path] = arg(default=(), positional=True) 219 | 220 | args = parse_test(TestSequencePositionalPath, []) 221 | assert args.arg == () 222 | args = parse_test(TestSequencePositionalPath, ["1", "2"]) 223 | assert args.arg == [Path("1"), Path("2")] 224 | 225 | 226 | def test_sequence_optional(factory): 227 | @factory 228 | class TestSequenceRequired: 229 | arg: Optional[Sequence[int]] 230 | 231 | args = parse_test(TestSequenceRequired, []) 232 | assert args.arg is None 233 | args = parse_test(TestSequenceRequired, ["--arg", "1", "2"]) 234 | assert args.arg == [1, 2] 235 | 236 | 237 | def test_optional(factory): 238 | @factory 239 | class TestOptional: 240 | arg: Optional[int] = None 241 | 242 | args = parse_test(TestOptional, []) 243 | assert args.arg is None 244 | args = parse_test(TestOptional, ["--arg", "1"]) 245 | assert args.arg == 1 246 | 247 | 248 | @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher") 249 | def test_optional_with_union_operator(factory): 250 | @factory 251 | class TestOptional: 252 | arg: int | None = None 253 | 254 | args = parse_test(TestOptional, []) 255 | assert args.arg is None 256 | args = parse_test(TestOptional, ["--arg", "1"]) 257 | assert args.arg == 1 258 | 259 | 260 | def test_kwargs(factory): 261 | @factory(order=True) 262 | class Order: 263 | arg: int 264 | 265 | assert Order(0) < Order(1) 266 | 267 | @factory(order=False) 268 | class NoOrder: 269 | arg: int 270 | 271 | with raises(TypeError): 272 | assert NoOrder(0) < NoOrder(1) 273 | 274 | 275 | def test_positional(): 276 | @argsclass 277 | class TestPositional: 278 | arg: str = arg(positional=True) 279 | 280 | _test_required(TestPositional) 281 | 282 | args = parse_test(TestPositional, ["test"]) 283 | assert args.arg == "test" 284 | 285 | 286 | def test_order_bool(): 287 | @argsclass 288 | class TestOrderBool: 289 | not_required: str = arg(default="") 290 | also_not_required: bool = arg() 291 | 292 | args = parse_test(TestOrderBool, []) 293 | assert not args.also_not_required 294 | 295 | 296 | def test_argsclass_on_decorator(): 297 | """ 298 | Does not work with attrs. 299 | """ 300 | 301 | @argsclass 302 | @dataclass 303 | class TestDoubleDecorators: 304 | arg: str = arg(positional=True) 305 | 306 | args = parse(TestDoubleDecorators, ["arg"]) 307 | assert args.arg == "arg" 308 | 309 | description = "desc" 310 | 311 | @argsclass(description=description) 312 | @dataclass 313 | class TestDoubleDecorators: 314 | arg: str = arg(positional=True) 315 | 316 | help_str = make_parser(TestDoubleDecorators).format_help() 317 | assert description in help_str 318 | 319 | 320 | def test_plus_nargs_dataclass(): 321 | @dataclass() 322 | class Nargs: 323 | nums: Sequence[int] = arg(nargs="+") 324 | 325 | args = parse_test(Nargs, ["--nums", "1", "2"]) 326 | assert args.nums == [1, 2] 327 | 328 | 329 | def test_plus_nargs_attrs(): 330 | @attr.s 331 | class Nargs: 332 | nums: Sequence[int] = attr.ib(metadata=dict(nargs="+")) 333 | 334 | args = parse_test(Nargs, ["--nums", "1", "2"]) 335 | assert args.nums == [1, 2] 336 | 337 | 338 | def test_star_nargs_dataclass(): 339 | @dataclass() 340 | class Nargs: 341 | nums: Sequence[int] = arg(nargs="*") 342 | 343 | args = parse_test(Nargs, ["--nums", "1", "2"]) 344 | assert args.nums == [1, 2] 345 | args = parse_test(Nargs, ["--nums"]) 346 | assert args.nums == [] 347 | args = parse_test(Nargs, []) 348 | assert args.nums == [] 349 | 350 | 351 | def test_star_nargs_attrs(): 352 | @attr.s 353 | class Nargs: 354 | nums: Sequence[int] = attr.ib(metadata=dict(nargs="*")) 355 | 356 | args = parse_test(Nargs, ["--nums", "1", "2"]) 357 | assert args.nums == [1, 2] 358 | args = parse_test(Nargs, ["--nums"]) 359 | assert args.nums == [] 360 | args = parse_test(Nargs, []) 361 | assert args.nums == [] 362 | 363 | 364 | is_pre_38 = sys.version_info < (3, 8) 365 | 366 | 367 | @pytest.mark.skipif(is_pre_38, reason="requires python3.8 or higher") 368 | def test_literal_int(factory): 369 | from typing import Literal 370 | 371 | @factory 372 | class Args: 373 | arg: Literal[0, 1, 2] 374 | 375 | args = parse_test(Args, ["--arg", "1"]) 376 | assert args.arg == 1 377 | 378 | with raises(ParserError): 379 | args = parse_test(Args, ["--arg", "5"]) 380 | 381 | 382 | @pytest.mark.skipif(is_pre_38, reason="requires python3.8 or higher") 383 | def test_literal_str(factory): 384 | from typing import Literal 385 | 386 | @factory 387 | class Args: 388 | arg: Literal["option1", "option2"] 389 | 390 | args = parse_test(Args, ["--arg", "option1"]) 391 | assert args.arg == "option1" 392 | 393 | with raises(ParserError): 394 | args = parse_test(Args, ["--arg", "option3"]) 395 | 396 | 397 | @pytest.mark.skipif(is_pre_38, reason="requires python3.8 or higher") 398 | def test_literal_mixed(factory): 399 | from typing import Literal 400 | 401 | @factory 402 | class Args: 403 | arg: Literal[0, "hello"] 404 | 405 | with raises(AssertionError): 406 | args = parse_test(Args, ["--arg", 0]) 407 | 408 | 409 | @pytest.mark.skipif(is_pre_38, reason="requires python3.8 or higher") 410 | def test_optional_literal(factory): 411 | from typing import Literal 412 | 413 | @factory 414 | class Args: 415 | arg: Optional[Literal[0, 1, 2]] 416 | 417 | args = parse_test(Args, ["--arg", 0]) 418 | assert args.arg == 0 419 | 420 | with raises(ParserError): 421 | args = parse_test(Args, ["--arg", "foo"]) 422 | 423 | args = parse_test(Args, []) 424 | assert args.arg == None 425 | 426 | 427 | @pytest.mark.skipif(is_pre_38, reason="requires python3.8 or higher") 428 | def test_literal_override_choices(): 429 | from typing import Literal 430 | 431 | @dataclass 432 | class Args: 433 | arg: Literal[0, 1, 2] = arg(choices=(0, 1)) 434 | 435 | args = parse_test(Args, ["--arg", 0]) 436 | assert args.arg == 0 437 | 438 | with raises(ParserError): 439 | args = parse_test(Args, ["--arg", "2"]) 440 | 441 | 442 | @pytest.mark.skipif(is_pre_38, reason="requires python3.8 or higher") 443 | def test_literal_bad_choices(): 444 | from typing import Literal 445 | 446 | @dataclass 447 | class Args: 448 | arg: Literal[0, 1, 2] = arg(choices=(0, 1, 2, 3)) 449 | 450 | with raises(AssertionError): 451 | args = parse_test(Args, ["--arg", 0]) 452 | 453 | 454 | def _test_required(cls): 455 | with raises(ParserError) as exc_info: 456 | parse_test(cls, []) 457 | assert "required" in exc_info.value.message 458 | 459 | 460 | @attr.s(auto_attribs=True, auto_exc=True) 461 | class ParserError(Exception): 462 | message: str = None 463 | 464 | 465 | T = TypeVar("T") 466 | 467 | 468 | def parse_test(cls: Type[T], args: Sequence[str]) -> T: 469 | return parse(cls, args, parser=ParserTest()) 470 | 471 | 472 | class ParserTest(ArgumentParser): 473 | def error(self, message: Text) -> NoReturn: 474 | raise ParserError(message) 475 | 476 | exit = error 477 | 478 | 479 | if __name__ == "__main__": 480 | pytest.main() 481 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # datargs 2 | 3 | A paper-thin wrapper around `argparse` that creates type-safe parsers 4 | from `dataclass` and `attrs` classes. 5 | 6 | ## Quickstart 7 | 8 | 9 | Install `datargs`: 10 | 11 | ```bash 12 | pip install datargs 13 | ``` 14 | 15 | Create a `dataclass` (or an `attrs` class) describing your command line interface, and call 16 | `datargs.parse()` with the class: 17 | 18 | ```python 19 | # script.py 20 | from dataclasses import dataclass 21 | from pathlib import Path 22 | from datargs import parse 23 | 24 | @dataclass # or @attr.s(auto_attribs=True) 25 | class Args: 26 | url: str 27 | output_path: Path 28 | verbose: bool 29 | retries: int = 3 30 | 31 | def main(): 32 | args = parse(Args) 33 | print(args) 34 | 35 | if __name__ == "__main__": 36 | main() 37 | ``` 38 | 39 | ***(experimental)*** Alternatively: convert an existing parser to a dataclass: 40 | ```python 41 | # script.py 42 | parser = ArgumentParser() 43 | parser.add_argument(...) 44 | from datargs import convert 45 | convert(parser) 46 | ``` 47 | 48 | `convert()` prints a class definition to the console. 49 | Copy it to your script. 50 | 51 | Mypy and pycharm correctly infer the type of `args` as `Args`, and your script is good to go! 52 | ```bash 53 | $ python script.py -h 54 | usage: test.py [-h] --url URL --output-path OUTPUT_PATH [--retries RETRIES] 55 | [--verbose] 56 | 57 | optional arguments: 58 | -h, --help show this help message and exit 59 | --url URL 60 | --output-path OUTPUT_PATH 61 | --retries RETRIES 62 | --verbose 63 | $ python script.py --url "https://..." --output-path out --retries 4 --verbose 64 | Args(url="https://...", output_path=Path("out"), retries=4, verbose=True) 65 | ``` 66 | 67 | ## Table of Contents 68 | 69 | 70 | 71 | - [Features](#features) 72 | * [Static verification](#static-verification) 73 | * [`dataclass`/`attr.s` agnostic](#dataclassattrs-agnostic) 74 | * [Aliases](#aliases) 75 | * [`ArgumentParser` options](#argumentparser-options) 76 | * [Enums](#enums) 77 | * [Sequences, Optionals, and Literals](#sequences-optionals-and-literals) 78 | * [Sub Commands](#sub-commands) 79 | - ["Why not"s and design choices](#why-nots-and-design-choices) 80 | * [Just use argparse?](#just-use-argparse) 81 | * [Use `click`](#use-clickhttpsclickpalletsprojectscomen7x)? 82 | * [Use `clout`](#use-clouthttpscloutreadthedocsioenlatestindexhtml)? 83 | * [Use `simple-parsing`](#use-simple-parsinghttpspypiorgprojectsimple-parsing)? 84 | * [Use `argparse-dataclass`](#use-argparse-dataclasshttpspypiorgprojectargparse-dataclass)? 85 | * [Use `argparse-dataclasses`](#use-argparse-dataclasseshttpspypiorgprojectargparse-dataclasses)? 86 | - [FAQs](#faqs) 87 | * [Is this cross-platform?](#is-this-cross-platform) 88 | * [Why are mutually exclusive options not supported?](#why-are-mutually-exclusive-options-not-supported) 89 | 90 | 91 | 92 | ## Features 93 | 94 | ### Static verification 95 | Mypy/Pycharm have your back when you when you make a mistake: 96 | ```python 97 | ... 98 | def main(): 99 | args = parse(Args) 100 | args.urll # typo 101 | ... 102 | ``` 103 | Pycharm says: `Unresolved attribute reference 'urll' for class 'Args'`. 104 | 105 | Mypy says: `script.py:15: error: "Args" has no attribute "urll"; maybe "url"?` 106 | 107 | 108 | ### `dataclass`/`attr.s` agnostic 109 | ```pycon 110 | >>> import attr, datargs 111 | >>> @attr.s 112 | ... class Args: 113 | ... flag: bool = attr.ib() 114 | >>> datargs.parse(Args, []) 115 | Args(flag=False) 116 | ``` 117 | 118 | ### Aliases 119 | Aliases and `ArgumentParser.add_argument()` parameters are taken from `metadata`: 120 | 121 | ```pycon 122 | >>> from dataclasses import dataclass, field 123 | >>> from datargs import parse 124 | >>> @dataclass 125 | ... class Args: 126 | ... retries: int = field(default=3, metadata=dict(help="number of retries", aliases=["-r"], metavar="RETRIES")) 127 | >>> parse(Args, ["-h"]) 128 | usage: ... 129 | optional arguments: 130 | -h, --help show this help message and exit 131 | --retries RETRIES, -r RETRIES 132 | >>> parse(Args, ["-r", "4"]) 133 | Args(retries=4) 134 | ``` 135 | 136 | `arg` is a replacement for `field` that puts `add_argument()` parameters in `metadata` 137 | and makes `aliases` behaves like in the original method. Use it to save precious keystrokes: 138 | ```pycon 139 | >>> from dataclasses import dataclass 140 | >>> from datargs import parse, arg 141 | >>> @dataclass 142 | ... class Args: 143 | ... retries: int = arg("-r", default=3, help="number of retries", metavar="RETRIES") 144 | >>> parse(Args, ["-h"]) 145 | # exactly the same as before 146 | ``` 147 | 148 | **NOTE**: `arg()` does not currently work with `attr.s`. 149 | 150 | `arg()` also supports all `field`/`attr.ib()` keyword arguments. 151 | 152 | 153 | ### `ArgumentParser` options 154 | You can pass `ArgumnetParser` keyword arguments to `argsclass`. 155 | Description is its own parameter - the rest are passed as the `parser_params` parameter as a `dict`. 156 | 157 | When a class is used as a subcommand (see below), `parser_params` are passed to `add_parser`, including `aliases`. 158 | ```pycon 159 | >>> from datargs import parse, argsclass 160 | >>> @argsclass(description="Romans go home!", parser_params=dict(prog="messiah.py")) 161 | ... class Args: 162 | ... flag: bool 163 | >>> parse(Args, ["-h"], parser=parser) 164 | usage: messiah.py [-h] [--flag] 165 | Romans go home! 166 | ... 167 | ``` 168 | 169 | or you can pass your own parser: 170 | ```pycon 171 | >>> from argparse import ArgumentParser 172 | >>> from datargs import parse, argsclass 173 | >>> @argsclass 174 | ... class Args: 175 | ... flag: bool 176 | >>> parser = ArgumentParser(description="Romans go home!", prog="messiah.py") 177 | >>> parse(Args, ["-h"], parser=parser) 178 | usage: messiah.py [-h] [--flag] 179 | Romans go home! 180 | ... 181 | ``` 182 | 183 | Use `make_parser()` to create a parser and save it for later: 184 | ```pycon 185 | >>> from datargs import make_parser 186 | >>> @dataclass 187 | ... class Args: 188 | ... ... 189 | >>> parser = make_parser(Args) # pass `parser=...` to modify an existing parser 190 | ``` 191 | **NOTE**: passing your own parser ignores `ArgumentParser` params passed to `argsclass()`. 192 | 193 | ### Enums 194 | With `datargs`, enums Just Work™: 195 | 196 | ```pycon 197 | >>> import enum, attr, datargs 198 | >>> class FoodEnum(enum.Enum): 199 | ... ham = 0 200 | ... spam = 1 201 | >>> @attr.dataclass 202 | ... class Args: 203 | ... food: FoodEnum 204 | >>> datargs.parse(Args, ["--food", "ham"]) 205 | Args(food=) 206 | >>> datargs.parse(Args, ["--food", "eggs"]) 207 | usage: enum_test.py [-h] --food {ham,spam} 208 | enum_test.py: error: argument --food: invalid choice: 'eggs' (choose from ['ham', 'spam']) 209 | ``` 210 | 211 | **NOTE**: enums are passed by name on the command line and not by value. 212 | 213 | ## Sequences, Optionals, and Literals 214 | Have a `Sequence` or a `List` of something to 215 | automatically use `nargs`: 216 | 217 | 218 | ```python 219 | from pathlib import Path 220 | from dataclasses import dataclass 221 | from typing import Sequence 222 | from datargs import parse 223 | 224 | @dataclass 225 | class Args: 226 | # same as nargs='*' 227 | files: Sequence[Path] = () 228 | 229 | args = parse(Args, ["--files", "foo.txt", "bar.txt"]) 230 | assert args.files == [Path("foo.txt"), Path("bar.txt")] 231 | ``` 232 | 233 | Specify a list of positional parameters like so: 234 | 235 | ```python 236 | from datargs import argsclass, arg 237 | @argsclass 238 | class Args: 239 | arg: Sequence[int] = arg(default=(), positional=True) 240 | ``` 241 | 242 | `Optional` arguments default to `None`: 243 | 244 | ```python 245 | from pathlib import Path 246 | from dataclasses import dataclass 247 | from typing import Optional 248 | from datargs import parse 249 | 250 | @dataclass 251 | class Args: 252 | path: Optional[Path] = None 253 | 254 | args = parse(Args, ["--path", "foo.txt"]) 255 | assert args.path == Path("foo.txt") 256 | 257 | args = parse(Args, []) 258 | assert args.path is None 259 | ``` 260 | 261 | And `Literal` can be used to specify choices: 262 | 263 | ```python 264 | from pathlib import Path 265 | from dataclasses import dataclass 266 | from typing import Literal 267 | from datargs import parse 268 | 269 | @dataclass 270 | class Args: 271 | path: Literal[Path("foo.txt"), Path("bar.txt")] 272 | 273 | args = parse(Args, ["--path", "foo.txt"]) 274 | assert args.path == Path("foo.txt") 275 | 276 | # Throws an error! 277 | args = parse(Args, ["--path", "bad-option.txt"]) 278 | ``` 279 | 280 | ### Sub Commands 281 | 282 | No need to specify a useless `dest` to dispatch on different commands. 283 | A `Union` of dataclasses/attrs classes automatically becomes a group of subparsers. 284 | The attribute holding the `Union` holds the appropriate instance 285 | upon parsing, making your code type-safe: 286 | 287 | ```python 288 | import typing, logging 289 | from datargs import argsclass, arg, parse 290 | 291 | @argsclass(description="install package") 292 | class Install: 293 | package: str = arg(positional=True, help="package to install") 294 | 295 | @argsclass(description="show all packages") 296 | class Show: 297 | verbose: bool = arg(help="show extra info") 298 | 299 | @argsclass(description="Pip Install Packages!") 300 | class Pip: 301 | action: typing.Union[Install, Show] 302 | log: str = None 303 | 304 | args = parse(Pip, ["--log", "debug.log", "install", "my_package"]) 305 | print(args) 306 | # prints: Pip(action=Install(package='my_package'), log='debug.log') 307 | 308 | # Consume arguments: 309 | if args.log: 310 | logging.basicConfig(filename=args.log) 311 | if isinstance(args.action, Install): 312 | install_package(args.action.package) 313 | # static type error: args.action.verbose 314 | elif isinstance(args.action, Show): 315 | list_all_packages(verbose=args.action.verbose) 316 | else: 317 | assert False, "Unreachable code" 318 | ``` 319 | Command name is derived from class name. To change this, use the `name` parameter to `@argsclass`. 320 | 321 | As with all other parameters to `add_parser`, 322 | `aliases` can be passed as a key in `parser_params` to add subcommand aliases. 323 | 324 | **NOTE**: if the commented-out line above does not issue a type error, try adding an `@dataclass/@attr.s` 325 | before or instead of `@argsclass()`: 326 | 327 | ```python 328 | @argsclass(description="Pip Install Packages!") # optional 329 | @dataclass 330 | class Pip: 331 | action: typing.Union[Install, Show] 332 | log: str = None 333 | ... 334 | if isinstance(args.action, Install): 335 | install_package(args.action.package) 336 | # this should now produce a type error: args.action.verbose 337 | ``` 338 | 339 | ## "Why not"s and design choices 340 | Many libraries out there do similar things. This list serves as documentation for existing solutions and differences. 341 | 342 | So, why not... 343 | 344 | ### Just use argparse? 345 | That's easy. The interface is clumsy and repetitive, a.k.a boilerplate. Additionally, `ArgumentParser.parse_args()` returns a `Namespace`, which is 346 | equivalent to `Any`, meaning that it any attribute access is legal when type checking. Alas, invalid attribute access will fail at runtime. For example: 347 | ```python 348 | def parse_args(): 349 | parser = ArgumentParser() 350 | parser.add_argument("--url") 351 | return parser.parse_args() 352 | 353 | def main(): 354 | args = parse_args() 355 | print(args.url) 356 | ``` 357 | 358 | Let's say for some reason `--url` is changed to `--uri`: 359 | 360 | ```python 361 | parser.add_argument("--uri") 362 | ... 363 | print(args.url) # oops 364 | ``` 365 | You won't discover you made a mistake until you run the code. With `datargs`, a static type checker will issue an error. 366 | Also, why use a carriage when you have a spaceship? 367 | 368 | ### Use [`click`](https://click.palletsprojects.com/en/7.x/)? 369 | `click` is a great library. It provides many utilities for command line programs. 370 | 371 | Use `datargs` if you believe user interface should not be coupled with implementation, or if you 372 | want to use `argparse` without boilerplate. 373 | Use `click` if you don't care. 374 | 375 | 376 | ### Use [`clout`](https://clout.readthedocs.io/en/latest/index.html)? 377 | It seems that `clout` aims to be an end-to-end solution for command line programs à la click. 378 | 379 | Use it if you need a broader solution. Use `datargs` if you want to use `argparse` without boilerplate. 380 | 381 | ### Use [`simple-parsing`](https://pypi.org/project/simple-parsing/)? 382 | This is another impressive library. 383 | 384 | Use it if you have deeply-nested options, or if the following points don't apply 385 | to you. 386 | 387 | Use `datargs` if you: 388 | * need `attrs` support 389 | * want as little magic as possible 390 | * don't have many options or they're not nested 391 | * prefer dashes (`--like-this`) over underscores (`--like_this`) 392 | 393 | ### Use [`argparse-dataclass`](https://pypi.org/project/argparse-dataclass/)? 394 | It's similar to this library. The main differences I found are: 395 | * no `attrs` support 396 | * not on github, so who you gonna call? 397 | 398 | ### Use [`argparse-dataclasses`](https://pypi.org/project/argparse-dataclasses/)? 399 | Same points `argparse-dataclass` but also [Uses inheritance](https://refactoring.guru/replace-inheritance-with-delegation). 400 | 401 | ## FAQs 402 | ### Is this cross-platform? 403 | Yes, just like `argparse`. 404 | If you find a bug on a certain platform (or any other bug), please report it. 405 | 406 | ### Why are mutually exclusive options not supported? 407 | 408 | This library is based on the idea of a one-to-one correspondence between most parsers 409 | and simple classes. Conceptually, mutually exclusive options are analogous to 410 | [sum types](https://en.wikipedia.org/wiki/Tagged_union), just like [subparsers](#sub-commands) are, 411 | but writing a class for each flag is not ergonomic enough. 412 | Contact me if you want this feature or if you come up with a better solution. 413 | -------------------------------------------------------------------------------- /datargs/make.py: -------------------------------------------------------------------------------- 1 | # noinspection PyUnresolvedReferences 2 | """ 3 | Declarative, type safe `argparse` parsers. 4 | 5 | >>> @dataclass 6 | ... class Args: 7 | ... just_a_string: str 8 | ... num: int 9 | ... store_true: bool = False 10 | ... store_false: bool = True 11 | >>> args = parse(Args, ["--just-a-string", "STRING", "--num", "0", "--store-true", "--store-false"]) 12 | >>> args 13 | Args(just_a_string='STRING', num=0, store_true=True, store_false=False) 14 | 15 | Pycharm correctly infers that `args` is of type `Args`. 16 | Trying to access a non-existent member is a type error: 17 | >>> args.nope # doctest: +SKIP 18 | Pycharm says: Unresolved attribute reference 'nope' for class 'Args' 19 | 20 | A flag with no defaults is assumed to be False by default: 21 | >>> @dataclass 22 | ... class Args: 23 | ... no_default: bool 24 | >>> parse(Args, []) 25 | Args(no_default=False) 26 | 27 | Enums are supported. They should be specified by name on the command line: 28 | >>> class FoodEnum(Enum): 29 | ... gnocchi = 0 30 | ... kimchi = 1 31 | >>> @dataclass 32 | ... class Args: 33 | ... food: FoodEnum 34 | >>> parse(Args, ["--food", "kimchi"]) 35 | Args(food=) 36 | >>> parse(Args, ["--food", "poutine"]) # doctest: +SKIP 37 | usage: make.py [-h] --food {gnocchi,kimchi} 38 | make.py: error: argument --food: 'poutine': invalid value 39 | ... 40 | SystemExit: 2 41 | 42 | Specifying enums by name is not currently supported. 43 | """ 44 | 45 | import dataclasses 46 | 47 | # noinspection PyUnresolvedReferences,PyProtectedMember 48 | from argparse import ( 49 | ArgumentParser, 50 | ArgumentTypeError, 51 | _SubParsersAction, 52 | Namespace, 53 | _CountAction, 54 | ) 55 | from dataclasses import dataclass, MISSING 56 | from enum import Enum 57 | from functools import wraps, partial 58 | from inspect import signature 59 | from contextlib import suppress 60 | from typing import ( 61 | Callable, 62 | Dict, 63 | TypeVar, 64 | Type, 65 | Sequence, 66 | Optional, 67 | overload, 68 | Any, 69 | Union, 70 | cast, 71 | get_type_hints, 72 | List, 73 | ) 74 | 75 | try: 76 | from typing import ( 77 | get_origin, 78 | Literal, 79 | ) 80 | except ImportError: 81 | Literal = object() 82 | 83 | def get_origin(tp): 84 | """Get the unsubscripted version of a type. 85 | This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar 86 | and Annotated. Return None for unsupported types. Examples:: 87 | get_origin(Literal[42]) is Literal 88 | get_origin(int) is None 89 | get_origin(ClassVar[int]) is ClassVar 90 | get_origin(Generic) is Generic 91 | get_origin(Generic[T]) is Generic 92 | get_origin(Union[T, int]) is Union 93 | get_origin(List[Tuple[T, T]][int]) == list 94 | get_origin(P.args) is P 95 | """ 96 | with suppress(AttributeError): 97 | return tp.__origin__ 98 | import typing as t 99 | 100 | if tp is t.Generic: 101 | return t.Generic 102 | return None 103 | 104 | 105 | from boltons.strutils import camel2under 106 | 107 | from .compat import ( 108 | RecordField, 109 | RecordClass, 110 | NotARecordClass, 111 | DatargsParams, 112 | is_optional, 113 | UnionType, 114 | ) 115 | 116 | 117 | @dataclass 118 | class Action: 119 | args: Sequence[Any] = dataclasses.field(default_factory=list) 120 | kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) 121 | 122 | 123 | # make an action from a field 124 | DispatchCallback = Callable[[RecordField, dict], Action] 125 | # make an action from a field and its formatted name 126 | DispatchCallbackWithFormattedName = Callable[[str, RecordField, dict], Action] 127 | 128 | 129 | def field_name_to_arg_name(name: str, positional=False) -> str: 130 | if positional: 131 | return name 132 | return f"--{name.replace('_', '-')}" 133 | 134 | 135 | SpecialRule = Callable[[Type["TypeDispatch"], RecordField], Optional[Action]] 136 | 137 | 138 | def is_subclass(cls, parent): 139 | try: 140 | return cls is parent or issubclass(cls, parent) 141 | except TypeError: 142 | return False 143 | 144 | 145 | class TypeDispatch: 146 | dispatch: Dict[type, DispatchCallback] = {} 147 | special_rules: List[SpecialRule] = [] 148 | 149 | @classmethod 150 | def add_arg(cls, field: RecordField, override: dict): 151 | typ = override.get("type", field.type) 152 | override = {**override, "type": typ} 153 | dispatch_type = get_origin(typ) or typ 154 | assert dispatch_type, f"cannot find origin for type {typ!r}" 155 | for typ, func in cls.dispatch.items(): 156 | if is_subclass(dispatch_type, typ): 157 | return func(field, override) 158 | return add_any(field, override) 159 | 160 | @classmethod 161 | def just_register(cls, typ): 162 | """ 163 | Register a type hook. 164 | """ 165 | 166 | def decorator(func): 167 | cls.dispatch[typ] = func 168 | return func 169 | 170 | return decorator 171 | 172 | @classmethod 173 | def register(cls, typ): 174 | """ 175 | Register a hook for type ``typ``. 176 | Expects a function that expects a formatted field name. 177 | Returns a function with that formatted name already partially applied to the original function. 178 | """ 179 | 180 | def decorator(func: DispatchCallbackWithFormattedName) -> DispatchCallback: 181 | new_func = add_name_formatting(func) 182 | cls.just_register(typ)(new_func) 183 | return new_func 184 | 185 | return decorator 186 | 187 | 188 | def add_name_formatting(func: DispatchCallbackWithFormattedName) -> DispatchCallback: 189 | @wraps(func) 190 | def new_func(field: RecordField, override: dict): 191 | return func( 192 | field_name_to_arg_name(field.name, positional=field.is_positional), 193 | field, 194 | override, 195 | ) 196 | 197 | return new_func 198 | 199 | 200 | @add_name_formatting 201 | def add_any(name: str, field: RecordField, extra: dict) -> Action: 202 | return add_default(name, field, extra) 203 | 204 | 205 | def get_option_strings(name: str, field: RecordField): 206 | return [name, *field.metadata.get("aliases", [])] 207 | 208 | 209 | def common_kwargs(field: RecordField): 210 | return {"type": field.type, **subdict(field.metadata, ["aliases", "positional"])} 211 | 212 | 213 | def subdict(dct, remove_keys): 214 | return {key: value for key, value in dct.items() if key not in remove_keys} 215 | 216 | 217 | def add_default(name, field: RecordField, override: dict) -> Action: 218 | override = { 219 | "default": field.default, 220 | **common_kwargs(field), 221 | **override, 222 | } 223 | if ( 224 | override.get("nargs") != "*" 225 | and not field.is_positional 226 | and (override.get("default", object()) is not None) 227 | ): 228 | override["required"] = field.is_required() 229 | return Action(kwargs=override, args=get_option_strings(name, field)) 230 | 231 | 232 | T = TypeVar("T") 233 | 234 | 235 | def call_func_with_matching_kwargs(func: Callable[..., T], *args, **kwargs) -> T: 236 | sig = signature(func) 237 | new_kwargs = {key: value for key, value in kwargs.items() if key in sig.parameters} 238 | return func(*args, **new_kwargs) 239 | 240 | 241 | @TypeDispatch.register(str) 242 | def add_str(name, field, override: dict): 243 | return add_default(name, field, override) 244 | 245 | 246 | @TypeDispatch.register(Sequence) 247 | def sequence_arg(_name: str, field: RecordField, override: dict) -> Action: 248 | nargs = field.metadata.get("nargs") 249 | if nargs: 250 | assert nargs in ("?", "*", "+") or isinstance(nargs, int) 251 | else: 252 | nargs = "+" if field.is_required() else "*" 253 | if nargs == "*" and field.is_required(): 254 | override = {"default": [], **override} 255 | new_type = (override.get("type") or field.type).__args__[0] 256 | return TypeDispatch.add_arg( 257 | field, 258 | {**override, "nargs": nargs, "type": new_type}, 259 | ) 260 | 261 | 262 | @TypeDispatch.just_register(UnionType) 263 | @TypeDispatch.register(Union) 264 | def union_arg(_name: str, field: RecordField, override: dict) -> Action: 265 | inner_type = (set(field.type.__args__) - {type(None)}).pop() 266 | return TypeDispatch.add_arg( 267 | field, {**override, "default": None, "type": inner_type} 268 | ) 269 | 270 | 271 | @TypeDispatch.register(Literal) 272 | def literal_arg(name: str, field: RecordField, override: dict) -> Action: 273 | typ = override.get("type", field.type) 274 | literal_options = set(typ.__args__) 275 | 276 | if "choices" in field.metadata: 277 | choices = set(field.metadata["choices"]) 278 | assert choices.issubset(literal_options) 279 | else: 280 | choices = literal_options 281 | 282 | assert len(set(map(type, choices))) == 1, ( 283 | "All choices in literal must have the same type!" 284 | ) 285 | inner_type = type(typ.__args__[0]) 286 | 287 | return TypeDispatch.add_arg( 288 | field, {**override, "choices": choices, "type": inner_type} 289 | ) 290 | 291 | 292 | @TypeDispatch.register(bool) 293 | def bool_arg(name: str, field: RecordField, override: dict) -> Action: 294 | kwargs = { 295 | **subdict(common_kwargs(field), ["type"]), 296 | **override, 297 | "action": "store_false" 298 | if field.default and field.has_default() 299 | else "store_true", 300 | } 301 | kwargs.pop("type", None) 302 | return Action( 303 | args=get_option_strings(name, field), 304 | kwargs=kwargs, 305 | ) 306 | 307 | 308 | @TypeDispatch.register(Enum) 309 | def enum_arg(name: str, field: RecordField, override: dict) -> Action: 310 | field_type = override.get("type") or field.type 311 | 312 | def enum_type_func(value: str): 313 | result = field_type.__members__.get(value) 314 | if not result: 315 | raise ArgumentTypeError( 316 | f"invalid choice: {value!r} (choose from {[e.name for e in field.type]})" 317 | ) 318 | return result 319 | 320 | return add_default( 321 | name, 322 | field, 323 | { 324 | **override, 325 | "type": enum_type_func, 326 | "choices": field_type, 327 | "metavar": f"{{{','.join(field_type.__members__)}}}", 328 | }, 329 | ) 330 | 331 | 332 | ParserType = TypeVar("ParserType", bound=ArgumentParser) 333 | 334 | 335 | @overload 336 | def make_parser(cls: type) -> ArgumentParser: 337 | pass 338 | 339 | 340 | @overload 341 | def make_parser(cls: type, parser: None = None) -> ArgumentParser: 342 | pass 343 | 344 | 345 | @overload 346 | def make_parser(cls: type, parser: ParserType) -> ParserType: 347 | pass 348 | 349 | 350 | def make_parser(cls, parser=None): 351 | # noinspection PyShadowingNames 352 | """ 353 | Create parser that parses command-line arguments according to the fields of `cls`. 354 | Use this if you want to do anything with the parser other than immediately parsing the command-line arguments. 355 | If you do want to parse immediately, use `parse()`. 356 | :param cls: class according to which argument parser is created 357 | :param parser: parser to add arguments to, by default creates a new parser 358 | :return: instance of `parser_cls` which parses command line according to `cls` 359 | 360 | >>> @dataclass 361 | ... class Args: 362 | ... first_arg: int 363 | >>> parse(Args, ["--first-arg", "0"]) 364 | Args(first_arg=0) 365 | >>> parser = make_parser(Args) 366 | >>> parser.add_argument("--second-arg", type=float) # doctest: +ELLIPSIS 367 | [...] 368 | >>> parser.parse_args(["--first-arg", "0", "--second-arg", "1.5"]) 369 | Namespace(first_arg=0, second_arg=1.5) 370 | """ 371 | record_class = RecordClass.wrap_class(cls) 372 | return _make_parser(record_class, parser=parser) 373 | 374 | 375 | class DatargsSubparsers(_SubParsersAction): 376 | """ 377 | A subparsers action that creates the correct sub-command class upon parsing. 378 | """ 379 | 380 | def __init__(self, name, *args, **kwargs): 381 | self.__name = name 382 | super().__init__(*args, **kwargs) 383 | self._command_type_map = {} 384 | 385 | def add_parser(self, typ: type, name: str, aliases=(), *args, **kwargs): 386 | result = super().add_parser(name, aliases=aliases, *args, **kwargs) 387 | for alias in [name, *aliases]: 388 | self._command_type_map[alias] = typ 389 | return result 390 | 391 | def __call__(self, parser, namespace, values, *args, **kwargs): 392 | new_ns = Namespace() 393 | name, *_ = values 394 | super().__call__(parser, new_ns, values) 395 | 396 | if hasattr(new_ns, self.dest): 397 | delattr(new_ns, self.dest) 398 | 399 | setattr(namespace, self.__name, self._command_type_map[name](**vars(new_ns))) 400 | 401 | 402 | def _make_parser(record_class: RecordClass, parser: ParserType = None) -> ParserType: 403 | if not parser: 404 | parser = ArgumentParser(**record_class.parser_params) 405 | assert parser is not None 406 | for name, field in record_class.fields_dict().items(): 407 | sub_commands = None 408 | try: 409 | if ( 410 | isinstance(field.type, UnionType) or field.type.__origin__ is Union 411 | ) and not is_optional(field.type): 412 | sub_commands = field.type.__args__ 413 | except AttributeError: 414 | pass 415 | if sub_commands is not None: 416 | add_subparsers(parser, record_class, field, sub_commands) 417 | else: 418 | action = TypeDispatch.add_arg(field, {}) 419 | fix_count_action_kwargs(action) 420 | parser.add_argument(*action.args, **action.kwargs) 421 | return parser 422 | 423 | 424 | def fix_count_action_kwargs(action): 425 | """ 426 | If argument action is "count", remove "type" kwarg 427 | """ 428 | if action.kwargs.get("action") == "count": 429 | action.kwargs.pop("type") 430 | 431 | 432 | def add_subparsers( 433 | parser: ArgumentParser, 434 | top_class: RecordClass, 435 | sub_parsers_field: RecordField, 436 | sub_parser_classes: Sequence[type], 437 | ): 438 | # noinspection PyArgumentList 439 | 440 | subparsers = cast( 441 | DatargsSubparsers, 442 | parser.add_subparsers( 443 | **top_class.sub_commands_params, 444 | action=DatargsSubparsers, 445 | name=sub_parsers_field.name, 446 | ), 447 | ) 448 | for command in sub_parser_classes: 449 | try: 450 | sub_parsers_args = top_class.wrap_class(command) 451 | except NotARecordClass: 452 | raise Exception( 453 | f"{top_class.name}.{sub_parsers_field.name}: " 454 | f"Union must be used with dataclass/attrs class and creates a subparser (got: {sub_parsers_field.type})" 455 | ) 456 | sub_parser = subparsers.add_parser( 457 | command, 458 | sub_parsers_args.datargs_params.name 459 | or camel2under(sub_parsers_args.name).replace("_", "-"), 460 | **sub_parsers_args.parser_params, 461 | ) 462 | _make_parser(sub_parsers_args, sub_parser) 463 | 464 | 465 | def parse(cls: Type[T], args: Optional[Sequence[str]] = None, *, parser=None) -> T: 466 | """ 467 | Parse command line arguments according to the fields of `cls` and populate it. 468 | Accepts classes decorated with `dataclass` or `attr.s`. 469 | :param cls: class to parse command-line arguments by 470 | :param parser: existing parser to add arguments to and parse from 471 | :param args: arguments to parse (default: `sys.arg`) 472 | :return: an instance of cls 473 | 474 | >>> @dataclass 475 | ... class Args: 476 | ... is_flag: bool 477 | ... num: int = 0 478 | >>> parse(Args, ["--num", "1"]) 479 | Args(is_flag=False, num=1) 480 | """ 481 | result = vars(make_parser(cls, parser=parser).parse_args(args)) 482 | try: 483 | command_dest = cls.__datargs_params__.sub_commands.get("dest", None) 484 | except AttributeError: 485 | pass 486 | else: 487 | if command_dest is not None and command_dest in result: 488 | del result[command_dest] 489 | return cls(**result) 490 | 491 | 492 | def argsclass( 493 | cls: type = None, 494 | *args, 495 | description: str = None, 496 | parser_params: dict = None, 497 | name: str = None, 498 | **kwargs, 499 | ): 500 | """ 501 | A wrapper around `dataclass` for passing `description` and other params (in `parser_params`) 502 | to the `ArgumentParser` constructor. 503 | 504 | :param cls: class to wrap 505 | :param description: parser description 506 | :param parser_params: dict of arguments to parser class constructor 507 | :param name: Only used in subcommands, string to invoke subcommand. By default, the string used is the class' 508 | name converted to kebab-case, i.e., snake case with hyphens 509 | """ 510 | # sub_commands_params has been disabled until a useful use case is found 511 | datargs_kwargs = { 512 | "description": description, 513 | "parser_params": parser_params, 514 | "name": name, 515 | "sub_commands_params": {}, 516 | } 517 | 518 | if cls is None: 519 | # We're called with parens. 520 | return partial( 521 | make_class, 522 | *args, 523 | **datargs_kwargs, 524 | **kwargs, 525 | ) 526 | 527 | return make_class( 528 | cls, 529 | *args, 530 | **datargs_kwargs, 531 | **kwargs, 532 | ) 533 | 534 | 535 | def make_class( 536 | cls, 537 | description: str = None, 538 | parser_params: dict = None, 539 | name: str = None, 540 | sub_commands_params: dict = None, 541 | *args, 542 | **kwargs, 543 | ): 544 | try: 545 | RecordClass.wrap_class(cls) 546 | except NotARecordClass: 547 | for key, value in list(cls.__dict__.items()): 548 | if not isinstance(value, dataclasses.Field) or value.default is not MISSING: 549 | continue 550 | typ = value.type or get_type_hints(cls)[key] 551 | if typ is bool: 552 | value.default = False 553 | 554 | new_cls = dataclass(*args, **kwargs)(cls) 555 | else: 556 | new_cls = cls 557 | new_cls.__datargs_params__ = DatargsParams( 558 | parser={"description": description, **(parser_params or {})}, 559 | sub_commands=sub_commands_params or {}, 560 | name=name, 561 | ) 562 | return new_cls 563 | 564 | 565 | def arg( 566 | *aliases: str, 567 | positional=False, 568 | nargs=None, 569 | const=None, 570 | default=MISSING, 571 | choices=None, 572 | help=None, 573 | metavar=None, 574 | deprecated=False, 575 | **kwargs, 576 | ) -> Any: 577 | """ 578 | Helper method to more easily add parsing-related behavior. 579 | Supports aliases: 580 | >>> @dataclass 581 | ... class Args: 582 | ... num: int = arg("-n") 583 | >>> parse(Args, ["--num", "0"]) 584 | Args(num=0) 585 | >>> parse(Args, ["-n", "0"]) 586 | Args(num=0) 587 | 588 | Accepts all arguments to both `ArgumentParser.add_argument` and `dataclass.field`: 589 | >>> @dataclass 590 | ... class Args: 591 | ... invisible_arg: int = arg(default=0, repr=False, metavar="MY_ARG", help="argument description") 592 | >>> print(Args()) 593 | Args() 594 | >>> make_parser(Args).print_help() # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE 595 | usage: ... 596 | --invisible-arg MY_ARG argument description 597 | """ 598 | return dataclasses.field( 599 | metadata=remove_dict_nones( 600 | dict( 601 | nargs=nargs, 602 | choices=choices, 603 | const=const, 604 | help=help, 605 | metavar=metavar, 606 | aliases=aliases, 607 | positional=positional, 608 | deprecated=deprecated, 609 | ) 610 | ), 611 | default=default, 612 | **kwargs, 613 | ) 614 | 615 | 616 | def remove_dict_nones(dct: dict) -> dict: 617 | return {key: value for key, value in dct.items() if value is not None} 618 | 619 | 620 | if __name__ == "__main__": 621 | import doctest 622 | 623 | OC = doctest.OutputChecker 624 | 625 | class AEOutputChecker(OC): 626 | def check_output(self, want, got, optionflags): 627 | if optionflags & doctest.ELLIPSIS: 628 | want = want.replace("[...]", doctest.ELLIPSIS_MARKER) 629 | return super().check_output(want, got, optionflags) 630 | 631 | doctest.OutputChecker = AEOutputChecker 632 | doctest.testmod(optionflags=doctest.REPORT_NDIFF) 633 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 2 3 | requires-python = ">=3.7" 4 | resolution-markers = [ 5 | "python_full_version >= '3.11'", 6 | "python_full_version == '3.10.*'", 7 | "python_full_version == '3.9.*'", 8 | "python_full_version == '3.8.*'", 9 | "python_full_version < '3.8'", 10 | ] 11 | 12 | [[package]] 13 | name = "appnope" 14 | version = "0.1.4" 15 | source = { registry = "https://pypi.org/simple" } 16 | sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } 17 | wheels = [ 18 | { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, 19 | ] 20 | 21 | [[package]] 22 | name = "asttokens" 23 | version = "3.0.0" 24 | source = { registry = "https://pypi.org/simple" } 25 | sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } 26 | wheels = [ 27 | { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, 28 | ] 29 | 30 | [[package]] 31 | name = "attrs" 32 | version = "24.2.0" 33 | source = { registry = "https://pypi.org/simple" } 34 | resolution-markers = [ 35 | "python_full_version < '3.8'", 36 | ] 37 | dependencies = [ 38 | { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, 39 | ] 40 | sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678, upload-time = "2024-08-06T14:37:38.364Z" } 41 | wheels = [ 42 | { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001, upload-time = "2024-08-06T14:37:36.958Z" }, 43 | ] 44 | 45 | [[package]] 46 | name = "attrs" 47 | version = "25.3.0" 48 | source = { registry = "https://pypi.org/simple" } 49 | resolution-markers = [ 50 | "python_full_version == '3.8.*'", 51 | ] 52 | sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } 53 | wheels = [ 54 | { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, 55 | ] 56 | 57 | [[package]] 58 | name = "attrs" 59 | version = "25.4.0" 60 | source = { registry = "https://pypi.org/simple" } 61 | resolution-markers = [ 62 | "python_full_version >= '3.11'", 63 | "python_full_version == '3.10.*'", 64 | "python_full_version == '3.9.*'", 65 | ] 66 | sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } 67 | wheels = [ 68 | { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, 69 | ] 70 | 71 | [[package]] 72 | name = "backcall" 73 | version = "0.2.0" 74 | source = { registry = "https://pypi.org/simple" } 75 | sdist = { url = "https://files.pythonhosted.org/packages/a2/40/764a663805d84deee23043e1426a9175567db89c8b3287b5c2ad9f71aa93/backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", size = 18041, upload-time = "2020-06-09T15:11:32.931Z" } 76 | wheels = [ 77 | { url = "https://files.pythonhosted.org/packages/4c/1c/ff6546b6c12603d8dd1070aa3c3d273ad4c07f5771689a7b69a550e8c951/backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255", size = 11157, upload-time = "2020-06-09T15:11:30.87Z" }, 78 | ] 79 | 80 | [[package]] 81 | name = "black" 82 | version = "23.3.0" 83 | source = { registry = "https://pypi.org/simple" } 84 | resolution-markers = [ 85 | "python_full_version < '3.8'", 86 | ] 87 | dependencies = [ 88 | { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 89 | { name = "mypy-extensions", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 90 | { name = "packaging", version = "24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 91 | { name = "pathspec", version = "0.11.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 92 | { name = "platformdirs", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 93 | { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 94 | { name = "typed-ast", marker = "python_full_version < '3.8' and implementation_name == 'cpython'" }, 95 | { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 96 | ] 97 | sdist = { url = "https://files.pythonhosted.org/packages/d6/36/66370f5017b100225ec4950a60caeef60201a10080da57ddb24124453fba/black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", size = 582156, upload-time = "2023-03-29T01:00:54.457Z" } 98 | wheels = [ 99 | { url = "https://files.pythonhosted.org/packages/db/f4/7908f71cc71da08df1317a3619f002cbf91927fb5d3ffc7723905a2113f7/black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", size = 1342273, upload-time = "2023-03-29T01:19:03.787Z" }, 100 | { url = "https://files.pythonhosted.org/packages/27/70/07aab2623cfd3789786f17e051487a41d5657258c7b1ef8f780512ffea9c/black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", size = 2676721, upload-time = "2023-03-29T01:25:23.459Z" }, 101 | { url = "https://files.pythonhosted.org/packages/29/b1/b584fc863c155653963039664a592b3327b002405043b7e761b9b0212337/black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", size = 1520336, upload-time = "2023-03-29T01:28:32.973Z" }, 102 | { url = "https://files.pythonhosted.org/packages/6d/b4/0f13ab7f5e364795ff82b76b0f9a4c9c50afda6f1e2feeb8b03fdd7ec57d/black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", size = 1654611, upload-time = "2023-03-29T01:11:12.718Z" }, 103 | { url = "https://files.pythonhosted.org/packages/de/b4/76f152c5eb0be5471c22cd18380d31d188930377a1a57969073b89d6615d/black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", size = 1286657, upload-time = "2023-03-29T01:15:20.317Z" }, 104 | { url = "https://files.pythonhosted.org/packages/d7/6f/d3832960a3b646b333b7f0d80d336a3c123012e9d9d5dba4a622b2b6181d/black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", size = 1326112, upload-time = "2023-03-29T01:19:05.794Z" }, 105 | { url = "https://files.pythonhosted.org/packages/eb/a5/17b40bfd9b607b69fa726b0b3a473d14b093dcd5191ea1a1dd664eccfee3/black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", size = 2643808, upload-time = "2023-03-29T01:25:27.825Z" }, 106 | { url = "https://files.pythonhosted.org/packages/69/49/7e1f0cf585b0d607aad3f971f95982cc4208fc77f92363d632d23021ee57/black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", size = 1503287, upload-time = "2023-03-29T01:28:35.228Z" }, 107 | { url = "https://files.pythonhosted.org/packages/c0/53/42e312c17cfda5c8fc4b6b396a508218807a3fcbb963b318e49d3ddd11d5/black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", size = 1638625, upload-time = "2023-03-29T01:11:16.193Z" }, 108 | { url = "https://files.pythonhosted.org/packages/3f/0d/81dd4194ce7057c199d4f28e4c2a885082d9d929e7a55c514b23784f7787/black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", size = 1293585, upload-time = "2023-03-29T01:15:22.935Z" }, 109 | { url = "https://files.pythonhosted.org/packages/24/eb/2d2d2c27cb64cfd073896f62a952a802cd83cf943a692a2f278525b57ca9/black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", size = 1447428, upload-time = "2023-03-29T01:28:37.49Z" }, 110 | { url = "https://files.pythonhosted.org/packages/49/36/15d2122f90ff1cd70f06892ebda777b650218cf84b56b5916a993dc1359a/black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", size = 1576467, upload-time = "2023-03-29T01:11:18.293Z" }, 111 | { url = "https://files.pythonhosted.org/packages/ca/44/eb41edd3f558a6139f09eee052dead4a7a464e563b822ddf236f5a8ee286/black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", size = 1226437, upload-time = "2023-03-29T01:15:24.92Z" }, 112 | { url = "https://files.pythonhosted.org/packages/ce/f4/2b0c6ac9e1f8584296747f66dd511898b4ebd51d6510dba118279bff53b6/black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", size = 1331955, upload-time = "2023-03-29T01:19:07.695Z" }, 113 | { url = "https://files.pythonhosted.org/packages/21/14/d5a2bec5fb15f9118baab7123d344646fac0b1c6939d51c2b05259cd2d9c/black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", size = 2658520, upload-time = "2023-03-29T01:25:30.535Z" }, 114 | { url = "https://files.pythonhosted.org/packages/13/0a/ed8b66c299e896780e4528eed4018f5b084da3b9ba4ee48328550567d866/black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", size = 1509852, upload-time = "2023-03-29T01:28:39.106Z" }, 115 | { url = "https://files.pythonhosted.org/packages/12/4b/99c71d1cf1353edd5aff2700b8960f92e9b805c9dab72639b67dbb449d3a/black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", size = 1641852, upload-time = "2023-03-29T01:11:20.065Z" }, 116 | { url = "https://files.pythonhosted.org/packages/d1/6e/5810b6992ed70403124c67e8b3f62858a32b35405177553f1a78ed6b6e31/black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", size = 1297694, upload-time = "2023-03-29T01:15:26.919Z" }, 117 | { url = "https://files.pythonhosted.org/packages/13/25/cfa06788d0a936f2445af88f13604b5bcd5c9d050db618c718e6ebe66f74/black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", size = 1341089, upload-time = "2023-03-29T01:19:09.503Z" }, 118 | { url = "https://files.pythonhosted.org/packages/fd/5b/fc2d7922c1a6bb49458d424b5be71d251f2d0dc97be9534e35d171bdc653/black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3", size = 2674699, upload-time = "2023-03-29T01:25:32.853Z" }, 119 | { url = "https://files.pythonhosted.org/packages/49/d7/f3b7da6c772800f5375aeb050a3dcf682f0bbeb41d313c9c2820d0156e4e/black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", size = 1519946, upload-time = "2023-03-29T01:28:40.746Z" }, 120 | { url = "https://files.pythonhosted.org/packages/3c/d7/85f3d79f9e543402de2244c4d117793f262149e404ea0168841613c33e07/black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", size = 1654176, upload-time = "2023-03-29T01:11:21.715Z" }, 121 | { url = "https://files.pythonhosted.org/packages/06/1e/273d610249f0335afb1ddb03664a03223f4826e3d1a95170a0142cb19fb4/black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", size = 1286299, upload-time = "2023-03-29T01:15:28.898Z" }, 122 | { url = "https://files.pythonhosted.org/packages/ad/e7/4642b7f462381799393fbad894ba4b32db00870a797f0616c197b07129a9/black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", size = 180965, upload-time = "2023-03-29T01:00:52.253Z" }, 123 | ] 124 | 125 | [[package]] 126 | name = "black" 127 | version = "24.8.0" 128 | source = { registry = "https://pypi.org/simple" } 129 | resolution-markers = [ 130 | "python_full_version == '3.8.*'", 131 | ] 132 | dependencies = [ 133 | { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 134 | { name = "mypy-extensions", version = "1.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 135 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 136 | { name = "pathspec", version = "0.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 137 | { name = "platformdirs", version = "4.3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 138 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 139 | { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 140 | ] 141 | sdist = { url = "https://files.pythonhosted.org/packages/04/b0/46fb0d4e00372f4a86a6f8efa3cb193c9f64863615e39010b1477e010578/black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", size = 644810, upload-time = "2024-08-02T17:43:18.405Z" } 142 | wheels = [ 143 | { url = "https://files.pythonhosted.org/packages/47/6e/74e29edf1fba3887ed7066930a87f698ffdcd52c5dbc263eabb06061672d/black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6", size = 1632092, upload-time = "2024-08-02T17:47:26.911Z" }, 144 | { url = "https://files.pythonhosted.org/packages/ab/49/575cb6c3faee690b05c9d11ee2e8dba8fbd6d6c134496e644c1feb1b47da/black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb", size = 1457529, upload-time = "2024-08-02T17:47:29.109Z" }, 145 | { url = "https://files.pythonhosted.org/packages/7a/b4/d34099e95c437b53d01c4aa37cf93944b233066eb034ccf7897fa4e5f286/black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42", size = 1757443, upload-time = "2024-08-02T17:46:20.306Z" }, 146 | { url = "https://files.pythonhosted.org/packages/87/a0/6d2e4175ef364b8c4b64f8441ba041ed65c63ea1db2720d61494ac711c15/black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a", size = 1418012, upload-time = "2024-08-02T17:47:20.33Z" }, 147 | { url = "https://files.pythonhosted.org/packages/08/a6/0a3aa89de9c283556146dc6dbda20cd63a9c94160a6fbdebaf0918e4a3e1/black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1", size = 1615080, upload-time = "2024-08-02T17:48:05.467Z" }, 148 | { url = "https://files.pythonhosted.org/packages/db/94/b803d810e14588bb297e565821a947c108390a079e21dbdcb9ab6956cd7a/black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", size = 1438143, upload-time = "2024-08-02T17:47:30.247Z" }, 149 | { url = "https://files.pythonhosted.org/packages/a5/b5/f485e1bbe31f768e2e5210f52ea3f432256201289fd1a3c0afda693776b0/black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", size = 1738774, upload-time = "2024-08-02T17:46:17.837Z" }, 150 | { url = "https://files.pythonhosted.org/packages/a8/69/a000fc3736f89d1bdc7f4a879f8aaf516fb03613bb51a0154070383d95d9/black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af", size = 1427503, upload-time = "2024-08-02T17:46:22.654Z" }, 151 | { url = "https://files.pythonhosted.org/packages/a2/a8/05fb14195cfef32b7c8d4585a44b7499c2a4b205e1662c427b941ed87054/black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368", size = 1646132, upload-time = "2024-08-02T17:49:52.843Z" }, 152 | { url = "https://files.pythonhosted.org/packages/41/77/8d9ce42673e5cb9988f6df73c1c5c1d4e9e788053cccd7f5fb14ef100982/black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed", size = 1448665, upload-time = "2024-08-02T17:47:54.479Z" }, 153 | { url = "https://files.pythonhosted.org/packages/cc/94/eff1ddad2ce1d3cc26c162b3693043c6b6b575f538f602f26fe846dfdc75/black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018", size = 1762458, upload-time = "2024-08-02T17:46:19.384Z" }, 154 | { url = "https://files.pythonhosted.org/packages/28/ea/18b8d86a9ca19a6942e4e16759b2fa5fc02bbc0eb33c1b866fcd387640ab/black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2", size = 1436109, upload-time = "2024-08-02T17:46:52.97Z" }, 155 | { url = "https://files.pythonhosted.org/packages/9f/d4/ae03761ddecc1a37d7e743b89cccbcf3317479ff4b88cfd8818079f890d0/black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd", size = 1617322, upload-time = "2024-08-02T17:51:20.203Z" }, 156 | { url = "https://files.pythonhosted.org/packages/14/4b/4dfe67eed7f9b1ddca2ec8e4418ea74f0d1dc84d36ea874d618ffa1af7d4/black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2", size = 1442108, upload-time = "2024-08-02T17:50:40.824Z" }, 157 | { url = "https://files.pythonhosted.org/packages/97/14/95b3f91f857034686cae0e73006b8391d76a8142d339b42970eaaf0416ea/black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e", size = 1745786, upload-time = "2024-08-02T17:46:02.939Z" }, 158 | { url = "https://files.pythonhosted.org/packages/95/54/68b8883c8aa258a6dde958cd5bdfada8382bec47c5162f4a01e66d839af1/black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920", size = 1426754, upload-time = "2024-08-02T17:46:38.603Z" }, 159 | { url = "https://files.pythonhosted.org/packages/13/b2/b3f24fdbb46f0e7ef6238e131f13572ee8279b70f237f221dd168a9dba1a/black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c", size = 1631706, upload-time = "2024-08-02T17:49:57.606Z" }, 160 | { url = "https://files.pythonhosted.org/packages/d9/35/31010981e4a05202a84a3116423970fd1a59d2eda4ac0b3570fbb7029ddc/black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e", size = 1457429, upload-time = "2024-08-02T17:49:12.764Z" }, 161 | { url = "https://files.pythonhosted.org/packages/27/25/3f706b4f044dd569a20a4835c3b733dedea38d83d2ee0beb8178a6d44945/black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47", size = 1756488, upload-time = "2024-08-02T17:46:08.067Z" }, 162 | { url = "https://files.pythonhosted.org/packages/63/72/79375cd8277cbf1c5670914e6bd4c1b15dea2c8f8e906dc21c448d0535f0/black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb", size = 1417721, upload-time = "2024-08-02T17:46:42.637Z" }, 163 | { url = "https://files.pythonhosted.org/packages/27/1e/83fa8a787180e1632c3d831f7e58994d7aaf23a0961320d21e84f922f919/black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed", size = 206504, upload-time = "2024-08-02T17:43:15.747Z" }, 164 | ] 165 | 166 | [[package]] 167 | name = "black" 168 | version = "25.9.0" 169 | source = { registry = "https://pypi.org/simple" } 170 | resolution-markers = [ 171 | "python_full_version >= '3.11'", 172 | "python_full_version == '3.10.*'", 173 | "python_full_version == '3.9.*'", 174 | ] 175 | dependencies = [ 176 | { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 177 | { name = "click", version = "8.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 178 | { name = "mypy-extensions", version = "1.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 179 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 180 | { name = "pathspec", version = "0.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 181 | { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 182 | { name = "platformdirs", version = "4.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 183 | { name = "pytokens", marker = "python_full_version >= '3.9'" }, 184 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, 185 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, 186 | ] 187 | sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } 188 | wheels = [ 189 | { url = "https://files.pythonhosted.org/packages/25/40/dbe31fc56b218a858c8fc6f5d8d3ba61c1fa7e989d43d4a4574b8b992840/black-25.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce41ed2614b706fd55fd0b4a6909d06b5bab344ffbfadc6ef34ae50adba3d4f7", size = 1715605, upload-time = "2025-09-19T00:36:13.483Z" }, 190 | { url = "https://files.pythonhosted.org/packages/92/b2/f46800621200eab6479b1f4c0e3ede5b4c06b768e79ee228bc80270bcc74/black-25.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ab0ce111ef026790e9b13bd216fa7bc48edd934ffc4cbf78808b235793cbc92", size = 1571829, upload-time = "2025-09-19T00:32:42.13Z" }, 191 | { url = "https://files.pythonhosted.org/packages/4e/64/5c7f66bd65af5c19b4ea86062bb585adc28d51d37babf70969e804dbd5c2/black-25.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f96b6726d690c96c60ba682955199f8c39abc1ae0c3a494a9c62c0184049a713", size = 1631888, upload-time = "2025-09-19T00:30:54.212Z" }, 192 | { url = "https://files.pythonhosted.org/packages/3b/64/0b9e5bfcf67db25a6eef6d9be6726499a8a72ebab3888c2de135190853d3/black-25.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d119957b37cc641596063cd7db2656c5be3752ac17877017b2ffcdb9dfc4d2b1", size = 1327056, upload-time = "2025-09-19T00:31:08.877Z" }, 193 | { url = "https://files.pythonhosted.org/packages/b7/f4/7531d4a336d2d4ac6cc101662184c8e7d068b548d35d874415ed9f4116ef/black-25.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:456386fe87bad41b806d53c062e2974615825c7a52159cde7ccaeb0695fa28fa", size = 1698727, upload-time = "2025-09-19T00:31:14.264Z" }, 194 | { url = "https://files.pythonhosted.org/packages/28/f9/66f26bfbbf84b949cc77a41a43e138d83b109502cd9c52dfc94070ca51f2/black-25.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a16b14a44c1af60a210d8da28e108e13e75a284bf21a9afa6b4571f96ab8bb9d", size = 1555679, upload-time = "2025-09-19T00:31:29.265Z" }, 195 | { url = "https://files.pythonhosted.org/packages/bf/59/61475115906052f415f518a648a9ac679d7afbc8da1c16f8fdf68a8cebed/black-25.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aaf319612536d502fdd0e88ce52d8f1352b2c0a955cc2798f79eeca9d3af0608", size = 1617453, upload-time = "2025-09-19T00:30:42.24Z" }, 196 | { url = "https://files.pythonhosted.org/packages/7f/5b/20fd5c884d14550c911e4fb1b0dae00d4abb60a4f3876b449c4d3a9141d5/black-25.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:c0372a93e16b3954208417bfe448e09b0de5cc721d521866cd9e0acac3c04a1f", size = 1333655, upload-time = "2025-09-19T00:30:56.715Z" }, 197 | { url = "https://files.pythonhosted.org/packages/fb/8e/319cfe6c82f7e2d5bfb4d3353c6cc85b523d677ff59edc61fdb9ee275234/black-25.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0", size = 1742012, upload-time = "2025-09-19T00:33:08.678Z" }, 198 | { url = "https://files.pythonhosted.org/packages/94/cc/f562fe5d0a40cd2a4e6ae3f685e4c36e365b1f7e494af99c26ff7f28117f/black-25.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4", size = 1581421, upload-time = "2025-09-19T00:35:25.937Z" }, 199 | { url = "https://files.pythonhosted.org/packages/84/67/6db6dff1ebc8965fd7661498aea0da5d7301074b85bba8606a28f47ede4d/black-25.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e", size = 1655619, upload-time = "2025-09-19T00:30:49.241Z" }, 200 | { url = "https://files.pythonhosted.org/packages/10/10/3faef9aa2a730306cf469d76f7f155a8cc1f66e74781298df0ba31f8b4c8/black-25.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a", size = 1342481, upload-time = "2025-09-19T00:31:29.625Z" }, 201 | { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, 202 | { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, 203 | { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, 204 | { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, 205 | { url = "https://files.pythonhosted.org/packages/c4/26/0f724eb152bc9fc03029a9c903ddd77a288285042222a381050d27e64ac1/black-25.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef69351df3c84485a8beb6f7b8f9721e2009e20ef80a8d619e2d1788b7816d47", size = 1715243, upload-time = "2025-09-19T00:34:14.216Z" }, 206 | { url = "https://files.pythonhosted.org/packages/fb/be/cb986ea2f0fabd0ee58668367724ba16c3a042842e9ebe009c139f8221c9/black-25.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e3c1f4cd5e93842774d9ee4ef6cd8d17790e65f44f7cdbaab5f2cf8ccf22a823", size = 1571246, upload-time = "2025-09-19T00:31:39.624Z" }, 207 | { url = "https://files.pythonhosted.org/packages/82/ce/74cf4d66963fca33ab710e4c5817ceeff843c45649f61f41d88694c2e5db/black-25.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:154b06d618233fe468236ba1f0e40823d4eb08b26f5e9261526fde34916b9140", size = 1631265, upload-time = "2025-09-19T00:31:05.341Z" }, 208 | { url = "https://files.pythonhosted.org/packages/ff/f3/9b11e001e84b4d1721f75e20b3c058854a748407e6fc1abe6da0aa22014f/black-25.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:e593466de7b998374ea2585a471ba90553283fb9beefcfa430d84a2651ed5933", size = 1326615, upload-time = "2025-09-19T00:31:25.347Z" }, 209 | { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, 210 | ] 211 | 212 | [[package]] 213 | name = "boltons" 214 | version = "25.0.0" 215 | source = { registry = "https://pypi.org/simple" } 216 | sdist = { url = "https://files.pythonhosted.org/packages/63/54/71a94d8e02da9a865587fb3fff100cb0fc7aa9f4d5ed9ed3a591216ddcc7/boltons-25.0.0.tar.gz", hash = "sha256:e110fbdc30b7b9868cb604e3f71d4722dd8f4dcb4a5ddd06028ba8f1ab0b5ace", size = 246294, upload-time = "2025-02-03T05:57:59.129Z" } 217 | wheels = [ 218 | { url = "https://files.pythonhosted.org/packages/45/7f/0e961cf3908bc4c1c3e027de2794f867c6c89fb4916fc7dba295a0e80a2d/boltons-25.0.0-py3-none-any.whl", hash = "sha256:dc9fb38bf28985715497d1b54d00b62ea866eca3938938ea9043e254a3a6ca62", size = 194210, upload-time = "2025-02-03T05:57:56.705Z" }, 219 | ] 220 | 221 | [[package]] 222 | name = "cachetools" 223 | version = "5.5.2" 224 | source = { registry = "https://pypi.org/simple" } 225 | resolution-markers = [ 226 | "python_full_version == '3.8.*'", 227 | "python_full_version < '3.8'", 228 | ] 229 | sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } 230 | wheels = [ 231 | { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, 232 | ] 233 | 234 | [[package]] 235 | name = "cachetools" 236 | version = "6.2.1" 237 | source = { registry = "https://pypi.org/simple" } 238 | resolution-markers = [ 239 | "python_full_version >= '3.11'", 240 | "python_full_version == '3.10.*'", 241 | "python_full_version == '3.9.*'", 242 | ] 243 | sdist = { url = "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201", size = 31325, upload-time = "2025-10-12T14:55:30.139Z" } 244 | wheels = [ 245 | { url = "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701", size = 11280, upload-time = "2025-10-12T14:55:28.382Z" }, 246 | ] 247 | 248 | [[package]] 249 | name = "chardet" 250 | version = "5.2.0" 251 | source = { registry = "https://pypi.org/simple" } 252 | sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } 253 | wheels = [ 254 | { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, 255 | ] 256 | 257 | [[package]] 258 | name = "click" 259 | version = "8.1.8" 260 | source = { registry = "https://pypi.org/simple" } 261 | resolution-markers = [ 262 | "python_full_version == '3.9.*'", 263 | "python_full_version == '3.8.*'", 264 | "python_full_version < '3.8'", 265 | ] 266 | dependencies = [ 267 | { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, 268 | { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, 269 | ] 270 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } 271 | wheels = [ 272 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, 273 | ] 274 | 275 | [[package]] 276 | name = "click" 277 | version = "8.3.0" 278 | source = { registry = "https://pypi.org/simple" } 279 | resolution-markers = [ 280 | "python_full_version >= '3.11'", 281 | "python_full_version == '3.10.*'", 282 | ] 283 | dependencies = [ 284 | { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, 285 | ] 286 | sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } 287 | wheels = [ 288 | { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, 289 | ] 290 | 291 | [[package]] 292 | name = "colorama" 293 | version = "0.4.6" 294 | source = { registry = "https://pypi.org/simple" } 295 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 296 | wheels = [ 297 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 298 | ] 299 | 300 | [[package]] 301 | name = "datargs" 302 | version = "1.1.0" 303 | source = { editable = "." } 304 | dependencies = [ 305 | { name = "boltons" }, 306 | { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 307 | { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_version < '0'" }, 308 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_version < '0'" }, 309 | ] 310 | 311 | [package.optional-dependencies] 312 | attrs = [ 313 | { name = "attrs", version = "24.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 314 | { name = "attrs", version = "25.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 315 | { name = "attrs", version = "25.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 316 | ] 317 | 318 | [package.dev-dependencies] 319 | dev = [ 320 | { name = "attrs", version = "24.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 321 | { name = "attrs", version = "25.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 322 | { name = "attrs", version = "25.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 323 | { name = "black", version = "23.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 324 | { name = "black", version = "24.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 325 | { name = "black", version = "25.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 326 | { name = "ipdb" }, 327 | { name = "pdbpp", version = "0.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 328 | { name = "pdbpp", version = "0.11.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, 329 | { name = "pytest", version = "7.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 330 | { name = "pytest", version = "8.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 331 | { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 332 | { name = "tox", version = "4.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 333 | { name = "tox", version = "4.25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 334 | { name = "tox", version = "4.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 335 | { name = "tox", version = "4.32.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 336 | { name = "tox-pyenv" }, 337 | ] 338 | 339 | [package.metadata] 340 | requires-dist = [ 341 | { name = "attrs", marker = "extra == 'attrs'", specifier = ">=20.2.0" }, 342 | { name = "boltons", specifier = ">=20.2.1" }, 343 | { name = "typing-extensions", marker = "python_full_version < '3.8'" }, 344 | ] 345 | provides-extras = ["attrs"] 346 | 347 | [package.metadata.requires-dev] 348 | dev = [ 349 | { name = "attrs", specifier = ">=20.2.0" }, 350 | { name = "black", specifier = ">=20.8b1" }, 351 | { name = "ipdb", specifier = ">=0.13.3" }, 352 | { name = "pdbpp", specifier = ">=0.10.2" }, 353 | { name = "pytest", specifier = ">=6.0.2" }, 354 | { name = "tox", specifier = ">=3.24.4" }, 355 | { name = "tox-pyenv", specifier = ">=1.1.0" }, 356 | ] 357 | 358 | [[package]] 359 | name = "decorator" 360 | version = "5.1.1" 361 | source = { registry = "https://pypi.org/simple" } 362 | resolution-markers = [ 363 | "python_full_version < '3.8'", 364 | ] 365 | sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016, upload-time = "2022-01-07T08:20:05.666Z" } 366 | wheels = [ 367 | { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073, upload-time = "2022-01-07T08:20:03.734Z" }, 368 | ] 369 | 370 | [[package]] 371 | name = "decorator" 372 | version = "5.2.1" 373 | source = { registry = "https://pypi.org/simple" } 374 | resolution-markers = [ 375 | "python_full_version >= '3.11'", 376 | "python_full_version == '3.10.*'", 377 | "python_full_version == '3.9.*'", 378 | "python_full_version == '3.8.*'", 379 | ] 380 | sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } 381 | wheels = [ 382 | { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, 383 | ] 384 | 385 | [[package]] 386 | name = "distlib" 387 | version = "0.4.0" 388 | source = { registry = "https://pypi.org/simple" } 389 | sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } 390 | wheels = [ 391 | { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, 392 | ] 393 | 394 | [[package]] 395 | name = "exceptiongroup" 396 | version = "1.3.0" 397 | source = { registry = "https://pypi.org/simple" } 398 | dependencies = [ 399 | { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 400 | { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 401 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, 402 | ] 403 | sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } 404 | wheels = [ 405 | { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, 406 | ] 407 | 408 | [[package]] 409 | name = "executing" 410 | version = "2.2.1" 411 | source = { registry = "https://pypi.org/simple" } 412 | sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } 413 | wheels = [ 414 | { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, 415 | ] 416 | 417 | [[package]] 418 | name = "fancycompleter" 419 | version = "0.9.1" 420 | source = { registry = "https://pypi.org/simple" } 421 | resolution-markers = [ 422 | "python_full_version < '3.8'", 423 | ] 424 | dependencies = [ 425 | { name = "pyreadline", marker = "python_full_version < '3.8' and sys_platform == 'win32'" }, 426 | { name = "pyrepl", version = "0.11.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 427 | ] 428 | sdist = { url = "https://files.pythonhosted.org/packages/a9/95/649d135442d8ecf8af5c7e235550c628056423c96c4bc6787348bdae9248/fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272", size = 10866, upload-time = "2020-02-04T13:30:09.183Z" } 429 | wheels = [ 430 | { url = "https://files.pythonhosted.org/packages/38/ef/c08926112034d017633f693d3afc8343393a035134a29dfc12dcd71b0375/fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080", size = 9681, upload-time = "2020-02-04T13:30:08.106Z" }, 431 | ] 432 | 433 | [[package]] 434 | name = "fancycompleter" 435 | version = "0.11.1" 436 | source = { registry = "https://pypi.org/simple" } 437 | resolution-markers = [ 438 | "python_full_version >= '3.11'", 439 | "python_full_version == '3.10.*'", 440 | "python_full_version == '3.9.*'", 441 | "python_full_version == '3.8.*'", 442 | ] 443 | dependencies = [ 444 | { name = "pyreadline3", marker = "python_full_version >= '3.8' and python_full_version < '3.13' and sys_platform == 'win32'" }, 445 | { name = "pyrepl", version = "0.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8' and python_full_version < '3.13'" }, 446 | ] 447 | sdist = { url = "https://files.pythonhosted.org/packages/4e/4c/d11187dee93eff89d082afda79b63c79320ae1347e49485a38f05ad359d0/fancycompleter-0.11.1.tar.gz", hash = "sha256:5b4ad65d76b32b1259251516d0f1cb2d82832b1ff8506697a707284780757f69", size = 341776, upload-time = "2025-05-26T12:59:11.045Z" } 448 | wheels = [ 449 | { url = "https://files.pythonhosted.org/packages/30/c3/6f0e3896f193528bbd2b4d2122d4be8108a37efab0b8475855556a8c4afa/fancycompleter-0.11.1-py3-none-any.whl", hash = "sha256:44243d7fab37087208ca5acacf8f74c0aa4d733d04d593857873af7513cdf8a6", size = 11207, upload-time = "2025-05-26T12:59:09.857Z" }, 450 | ] 451 | 452 | [[package]] 453 | name = "filelock" 454 | version = "3.12.2" 455 | source = { registry = "https://pypi.org/simple" } 456 | resolution-markers = [ 457 | "python_full_version < '3.8'", 458 | ] 459 | sdist = { url = "https://files.pythonhosted.org/packages/00/0b/c506e9e44e4c4b6c89fcecda23dc115bf8e7ff7eb127e0cb9c114cbc9a15/filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81", size = 12441, upload-time = "2023-06-12T22:02:09.617Z" } 460 | wheels = [ 461 | { url = "https://files.pythonhosted.org/packages/00/45/ec3407adf6f6b5bf867a4462b2b0af27597a26bd3cd6e2534cb6ab029938/filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec", size = 10923, upload-time = "2023-06-12T22:02:08.03Z" }, 462 | ] 463 | 464 | [[package]] 465 | name = "filelock" 466 | version = "3.16.1" 467 | source = { registry = "https://pypi.org/simple" } 468 | resolution-markers = [ 469 | "python_full_version == '3.8.*'", 470 | ] 471 | sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037, upload-time = "2024-09-17T19:02:01.779Z" } 472 | wheels = [ 473 | { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163, upload-time = "2024-09-17T19:02:00.268Z" }, 474 | ] 475 | 476 | [[package]] 477 | name = "filelock" 478 | version = "3.19.1" 479 | source = { registry = "https://pypi.org/simple" } 480 | resolution-markers = [ 481 | "python_full_version == '3.9.*'", 482 | ] 483 | sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } 484 | wheels = [ 485 | { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, 486 | ] 487 | 488 | [[package]] 489 | name = "filelock" 490 | version = "3.20.0" 491 | source = { registry = "https://pypi.org/simple" } 492 | resolution-markers = [ 493 | "python_full_version >= '3.11'", 494 | "python_full_version == '3.10.*'", 495 | ] 496 | sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } 497 | wheels = [ 498 | { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, 499 | ] 500 | 501 | [[package]] 502 | name = "importlib-metadata" 503 | version = "6.7.0" 504 | source = { registry = "https://pypi.org/simple" } 505 | dependencies = [ 506 | { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 507 | { name = "zipp", marker = "python_full_version < '3.8'" }, 508 | ] 509 | sdist = { url = "https://files.pythonhosted.org/packages/a3/82/f6e29c8d5c098b6be61460371c2c5591f4a335923639edec43b3830650a4/importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", size = 53569, upload-time = "2023-06-18T21:44:35.024Z" } 510 | wheels = [ 511 | { url = "https://files.pythonhosted.org/packages/ff/94/64287b38c7de4c90683630338cf28f129decbba0a44f0c6db35a873c73c4/importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5", size = 22934, upload-time = "2023-06-18T21:44:33.441Z" }, 512 | ] 513 | 514 | [[package]] 515 | name = "iniconfig" 516 | version = "2.0.0" 517 | source = { registry = "https://pypi.org/simple" } 518 | resolution-markers = [ 519 | "python_full_version < '3.8'", 520 | ] 521 | sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } 522 | wheels = [ 523 | { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, 524 | ] 525 | 526 | [[package]] 527 | name = "iniconfig" 528 | version = "2.1.0" 529 | source = { registry = "https://pypi.org/simple" } 530 | resolution-markers = [ 531 | "python_full_version == '3.9.*'", 532 | "python_full_version == '3.8.*'", 533 | ] 534 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } 535 | wheels = [ 536 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, 537 | ] 538 | 539 | [[package]] 540 | name = "iniconfig" 541 | version = "2.3.0" 542 | source = { registry = "https://pypi.org/simple" } 543 | resolution-markers = [ 544 | "python_full_version >= '3.11'", 545 | "python_full_version == '3.10.*'", 546 | ] 547 | sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } 548 | wheels = [ 549 | { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, 550 | ] 551 | 552 | [[package]] 553 | name = "ipdb" 554 | version = "0.13.13" 555 | source = { registry = "https://pypi.org/simple" } 556 | dependencies = [ 557 | { name = "decorator", version = "5.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 558 | { name = "decorator", version = "5.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, 559 | { name = "ipython", version = "7.34.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 560 | { name = "ipython", version = "8.12.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 561 | { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 562 | { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 563 | { name = "ipython", version = "9.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, 564 | { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 565 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8' and python_full_version < '3.11'" }, 566 | ] 567 | sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042, upload-time = "2023-03-09T15:40:57.487Z" } 568 | wheels = [ 569 | { url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130, upload-time = "2023-03-09T15:40:55.021Z" }, 570 | ] 571 | 572 | [[package]] 573 | name = "ipython" 574 | version = "7.34.0" 575 | source = { registry = "https://pypi.org/simple" } 576 | resolution-markers = [ 577 | "python_full_version < '3.8'", 578 | ] 579 | dependencies = [ 580 | { name = "appnope", marker = "python_full_version < '3.8' and sys_platform == 'darwin'" }, 581 | { name = "backcall", marker = "python_full_version < '3.8'" }, 582 | { name = "colorama", marker = "python_full_version < '3.8' and sys_platform == 'win32'" }, 583 | { name = "decorator", version = "5.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 584 | { name = "jedi", marker = "python_full_version < '3.8'" }, 585 | { name = "matplotlib-inline", version = "0.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 586 | { name = "pexpect", marker = "python_full_version < '3.8' and sys_platform != 'win32'" }, 587 | { name = "pickleshare", marker = "python_full_version < '3.8'" }, 588 | { name = "prompt-toolkit", version = "3.0.48", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 589 | { name = "pygments", version = "2.17.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 590 | { name = "setuptools", marker = "python_full_version < '3.8'" }, 591 | { name = "traitlets", version = "5.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 592 | ] 593 | sdist = { url = "https://files.pythonhosted.org/packages/db/6c/3fcf0b8ee46656796099ac4b7b72497af5f090da3e43fd305f2a24c73915/ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6", size = 5158632, upload-time = "2022-05-28T12:30:11.258Z" } 594 | wheels = [ 595 | { url = "https://files.pythonhosted.org/packages/7c/6a/1f1365f4bf9fcb349fcaa5b61edfcefa721aa13ff37c5631296b12fab8e5/ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e", size = 793790, upload-time = "2022-05-28T12:30:07.694Z" }, 596 | ] 597 | 598 | [[package]] 599 | name = "ipython" 600 | version = "8.12.3" 601 | source = { registry = "https://pypi.org/simple" } 602 | resolution-markers = [ 603 | "python_full_version == '3.8.*'", 604 | ] 605 | dependencies = [ 606 | { name = "appnope", marker = "python_full_version == '3.8.*' and sys_platform == 'darwin'" }, 607 | { name = "backcall", marker = "python_full_version == '3.8.*'" }, 608 | { name = "colorama", marker = "python_full_version == '3.8.*' and sys_platform == 'win32'" }, 609 | { name = "decorator", version = "5.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 610 | { name = "jedi", marker = "python_full_version == '3.8.*'" }, 611 | { name = "matplotlib-inline", version = "0.1.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 612 | { name = "pexpect", marker = "python_full_version == '3.8.*' and sys_platform != 'win32'" }, 613 | { name = "pickleshare", marker = "python_full_version == '3.8.*'" }, 614 | { name = "prompt-toolkit", version = "3.0.52", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 615 | { name = "pygments", version = "2.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 616 | { name = "stack-data", marker = "python_full_version == '3.8.*'" }, 617 | { name = "traitlets", version = "5.14.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 618 | { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 619 | ] 620 | sdist = { url = "https://files.pythonhosted.org/packages/9e/6a/44ef299b1762f5a73841e87fae8a73a8cc8aee538d6dc8c77a5afe1fd2ce/ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", size = 5470171, upload-time = "2023-09-29T09:14:37.468Z" } 621 | wheels = [ 622 | { url = "https://files.pythonhosted.org/packages/8d/97/8fe103906cd81bc42d3b0175b5534a9f67dccae47d6451131cf8d0d70bb2/ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c", size = 798307, upload-time = "2023-09-29T09:14:34.431Z" }, 623 | ] 624 | 625 | [[package]] 626 | name = "ipython" 627 | version = "8.18.1" 628 | source = { registry = "https://pypi.org/simple" } 629 | resolution-markers = [ 630 | "python_full_version == '3.9.*'", 631 | ] 632 | dependencies = [ 633 | { name = "colorama", marker = "python_full_version == '3.9.*' and sys_platform == 'win32'" }, 634 | { name = "decorator", version = "5.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 635 | { name = "exceptiongroup", marker = "python_full_version == '3.9.*'" }, 636 | { name = "jedi", marker = "python_full_version == '3.9.*'" }, 637 | { name = "matplotlib-inline", version = "0.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 638 | { name = "pexpect", marker = "python_full_version == '3.9.*' and sys_platform != 'win32'" }, 639 | { name = "prompt-toolkit", version = "3.0.52", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 640 | { name = "pygments", version = "2.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 641 | { name = "stack-data", marker = "python_full_version == '3.9.*'" }, 642 | { name = "traitlets", version = "5.14.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 643 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 644 | ] 645 | sdist = { url = "https://files.pythonhosted.org/packages/b1/b9/3ba6c45a6df813c09a48bac313c22ff83efa26cbb55011218d925a46e2ad/ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27", size = 5486330, upload-time = "2023-11-27T09:58:34.596Z" } 646 | wheels = [ 647 | { url = "https://files.pythonhosted.org/packages/47/6b/d9fdcdef2eb6a23f391251fde8781c38d42acd82abe84d054cb74f7863b0/ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397", size = 808161, upload-time = "2023-11-27T09:58:30.538Z" }, 648 | ] 649 | 650 | [[package]] 651 | name = "ipython" 652 | version = "8.37.0" 653 | source = { registry = "https://pypi.org/simple" } 654 | resolution-markers = [ 655 | "python_full_version == '3.10.*'", 656 | ] 657 | dependencies = [ 658 | { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, 659 | { name = "decorator", version = "5.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 660 | { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, 661 | { name = "jedi", marker = "python_full_version == '3.10.*'" }, 662 | { name = "matplotlib-inline", version = "0.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 663 | { name = "pexpect", marker = "python_full_version == '3.10.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, 664 | { name = "prompt-toolkit", version = "3.0.52", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 665 | { name = "pygments", version = "2.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 666 | { name = "stack-data", marker = "python_full_version == '3.10.*'" }, 667 | { name = "traitlets", version = "5.14.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 668 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 669 | ] 670 | sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } 671 | wheels = [ 672 | { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, 673 | ] 674 | 675 | [[package]] 676 | name = "ipython" 677 | version = "9.6.0" 678 | source = { registry = "https://pypi.org/simple" } 679 | resolution-markers = [ 680 | "python_full_version >= '3.11'", 681 | ] 682 | dependencies = [ 683 | { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, 684 | { name = "decorator", version = "5.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, 685 | { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, 686 | { name = "jedi", marker = "python_full_version >= '3.11'" }, 687 | { name = "matplotlib-inline", version = "0.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, 688 | { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, 689 | { name = "prompt-toolkit", version = "3.0.52", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, 690 | { name = "pygments", version = "2.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, 691 | { name = "stack-data", marker = "python_full_version >= '3.11'" }, 692 | { name = "traitlets", version = "5.14.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, 693 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, 694 | ] 695 | sdist = { url = "https://files.pythonhosted.org/packages/2a/34/29b18c62e39ee2f7a6a3bba7efd952729d8aadd45ca17efc34453b717665/ipython-9.6.0.tar.gz", hash = "sha256:5603d6d5d356378be5043e69441a072b50a5b33b4503428c77b04cb8ce7bc731", size = 4396932, upload-time = "2025-09-29T10:55:53.948Z" } 696 | wheels = [ 697 | { url = "https://files.pythonhosted.org/packages/48/c5/d5e07995077e48220269c28a221e168c91123ad5ceee44d548f54a057fc0/ipython-9.6.0-py3-none-any.whl", hash = "sha256:5f77efafc886d2f023442479b8149e7d86547ad0a979e9da9f045d252f648196", size = 616170, upload-time = "2025-09-29T10:55:47.676Z" }, 698 | ] 699 | 700 | [[package]] 701 | name = "ipython-pygments-lexers" 702 | version = "1.1.1" 703 | source = { registry = "https://pypi.org/simple" } 704 | dependencies = [ 705 | { name = "pygments", version = "2.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, 706 | ] 707 | sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } 708 | wheels = [ 709 | { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, 710 | ] 711 | 712 | [[package]] 713 | name = "jedi" 714 | version = "0.19.2" 715 | source = { registry = "https://pypi.org/simple" } 716 | dependencies = [ 717 | { name = "parso" }, 718 | ] 719 | sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } 720 | wheels = [ 721 | { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, 722 | ] 723 | 724 | [[package]] 725 | name = "matplotlib-inline" 726 | version = "0.1.6" 727 | source = { registry = "https://pypi.org/simple" } 728 | resolution-markers = [ 729 | "python_full_version < '3.8'", 730 | ] 731 | dependencies = [ 732 | { name = "traitlets", version = "5.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 733 | ] 734 | sdist = { url = "https://files.pythonhosted.org/packages/d9/50/3af8c0362f26108e54d58c7f38784a3bdae6b9a450bab48ee8482d737f44/matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304", size = 7790, upload-time = "2022-08-18T03:48:18.318Z" } 735 | wheels = [ 736 | { url = "https://files.pythonhosted.org/packages/f2/51/c34d7a1d528efaae3d8ddb18ef45a41f284eacf9e514523b191b7d0872cc/matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311", size = 9408, upload-time = "2022-08-18T03:48:17.066Z" }, 737 | ] 738 | 739 | [[package]] 740 | name = "matplotlib-inline" 741 | version = "0.1.7" 742 | source = { registry = "https://pypi.org/simple" } 743 | resolution-markers = [ 744 | "python_full_version == '3.8.*'", 745 | ] 746 | dependencies = [ 747 | { name = "traitlets", version = "5.14.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 748 | ] 749 | sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" } 750 | wheels = [ 751 | { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, 752 | ] 753 | 754 | [[package]] 755 | name = "matplotlib-inline" 756 | version = "0.2.1" 757 | source = { registry = "https://pypi.org/simple" } 758 | resolution-markers = [ 759 | "python_full_version >= '3.11'", 760 | "python_full_version == '3.10.*'", 761 | "python_full_version == '3.9.*'", 762 | ] 763 | dependencies = [ 764 | { name = "traitlets", version = "5.14.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 765 | ] 766 | sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } 767 | wheels = [ 768 | { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, 769 | ] 770 | 771 | [[package]] 772 | name = "mypy-extensions" 773 | version = "1.0.0" 774 | source = { registry = "https://pypi.org/simple" } 775 | resolution-markers = [ 776 | "python_full_version < '3.8'", 777 | ] 778 | sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433, upload-time = "2023-02-04T12:11:27.157Z" } 779 | wheels = [ 780 | { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload-time = "2023-02-04T12:11:25.002Z" }, 781 | ] 782 | 783 | [[package]] 784 | name = "mypy-extensions" 785 | version = "1.1.0" 786 | source = { registry = "https://pypi.org/simple" } 787 | resolution-markers = [ 788 | "python_full_version >= '3.11'", 789 | "python_full_version == '3.10.*'", 790 | "python_full_version == '3.9.*'", 791 | "python_full_version == '3.8.*'", 792 | ] 793 | sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } 794 | wheels = [ 795 | { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, 796 | ] 797 | 798 | [[package]] 799 | name = "packaging" 800 | version = "24.0" 801 | source = { registry = "https://pypi.org/simple" } 802 | resolution-markers = [ 803 | "python_full_version < '3.8'", 804 | ] 805 | sdist = { url = "https://files.pythonhosted.org/packages/ee/b5/b43a27ac7472e1818c4bafd44430e69605baefe1f34440593e0332ec8b4d/packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9", size = 147882, upload-time = "2024-03-10T09:39:28.33Z" } 806 | wheels = [ 807 | { url = "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", size = 53488, upload-time = "2024-03-10T09:39:25.947Z" }, 808 | ] 809 | 810 | [[package]] 811 | name = "packaging" 812 | version = "25.0" 813 | source = { registry = "https://pypi.org/simple" } 814 | resolution-markers = [ 815 | "python_full_version >= '3.11'", 816 | "python_full_version == '3.10.*'", 817 | "python_full_version == '3.9.*'", 818 | "python_full_version == '3.8.*'", 819 | ] 820 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } 821 | wheels = [ 822 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, 823 | ] 824 | 825 | [[package]] 826 | name = "parso" 827 | version = "0.8.5" 828 | source = { registry = "https://pypi.org/simple" } 829 | sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } 830 | wheels = [ 831 | { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, 832 | ] 833 | 834 | [[package]] 835 | name = "pathspec" 836 | version = "0.11.2" 837 | source = { registry = "https://pypi.org/simple" } 838 | resolution-markers = [ 839 | "python_full_version < '3.8'", 840 | ] 841 | sdist = { url = "https://files.pythonhosted.org/packages/a0/2a/bd167cdf116d4f3539caaa4c332752aac0b3a0cc0174cdb302ee68933e81/pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3", size = 47032, upload-time = "2023-07-29T01:05:04.481Z" } 842 | wheels = [ 843 | { url = "https://files.pythonhosted.org/packages/b4/2a/9b1be29146139ef459188f5e420a66e835dda921208db600b7037093891f/pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", size = 29603, upload-time = "2023-07-29T01:05:02.656Z" }, 844 | ] 845 | 846 | [[package]] 847 | name = "pathspec" 848 | version = "0.12.1" 849 | source = { registry = "https://pypi.org/simple" } 850 | resolution-markers = [ 851 | "python_full_version >= '3.11'", 852 | "python_full_version == '3.10.*'", 853 | "python_full_version == '3.9.*'", 854 | "python_full_version == '3.8.*'", 855 | ] 856 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } 857 | wheels = [ 858 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, 859 | ] 860 | 861 | [[package]] 862 | name = "pdbpp" 863 | version = "0.10.3" 864 | source = { registry = "https://pypi.org/simple" } 865 | resolution-markers = [ 866 | "python_full_version < '3.8'", 867 | ] 868 | dependencies = [ 869 | { name = "fancycompleter", version = "0.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 870 | { name = "pygments", version = "2.17.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 871 | { name = "wmctrl", marker = "python_full_version < '3.8'" }, 872 | ] 873 | sdist = { url = "https://files.pythonhosted.org/packages/1f/a3/c4bd048256fd4b7d28767ca669c505e156f24d16355505c62e6fce3314df/pdbpp-0.10.3.tar.gz", hash = "sha256:d9e43f4fda388eeb365f2887f4e7b66ac09dce9b6236b76f63616530e2f669f5", size = 68116, upload-time = "2021-07-09T22:22:22.134Z" } 874 | wheels = [ 875 | { url = "https://files.pythonhosted.org/packages/93/ee/491e63a57fffa78b9de1c337b06c97d0cd0753e88c00571c7b011680332a/pdbpp-0.10.3-py2.py3-none-any.whl", hash = "sha256:79580568e33eb3d6f6b462b1187f53e10cd8e4538f7d31495c9181e2cf9665d1", size = 23961, upload-time = "2021-07-09T22:22:20.271Z" }, 876 | ] 877 | 878 | [[package]] 879 | name = "pdbpp" 880 | version = "0.11.7" 881 | source = { registry = "https://pypi.org/simple" } 882 | resolution-markers = [ 883 | "python_full_version >= '3.11'", 884 | "python_full_version == '3.10.*'", 885 | "python_full_version == '3.9.*'", 886 | "python_full_version == '3.8.*'", 887 | ] 888 | dependencies = [ 889 | { name = "fancycompleter", version = "0.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, 890 | { name = "pygments", version = "2.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, 891 | ] 892 | sdist = { url = "https://files.pythonhosted.org/packages/c6/4c/118ef9534ac0632859b48c305d8c5dc9d6f963564fdfa66bc785c560247c/pdbpp-0.11.7.tar.gz", hash = "sha256:cb6604ac31a35ed0f2a29650a8c022b26284620be3e01cfd41b683b91da1ff14", size = 76026, upload-time = "2025-07-18T09:36:02.781Z" } 893 | wheels = [ 894 | { url = "https://files.pythonhosted.org/packages/99/e9/704bbc08aace64fee536e4c2c20f63f64f6fdbad72938c5ed46c9723a9f1/pdbpp-0.11.7-py3-none-any.whl", hash = "sha256:51916b63693898cf4881b36b4501c83947758d73f582f1f84893662b163bdb75", size = 30545, upload-time = "2025-07-18T09:36:01.478Z" }, 895 | ] 896 | 897 | [[package]] 898 | name = "pexpect" 899 | version = "4.9.0" 900 | source = { registry = "https://pypi.org/simple" } 901 | dependencies = [ 902 | { name = "ptyprocess" }, 903 | ] 904 | sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } 905 | wheels = [ 906 | { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, 907 | ] 908 | 909 | [[package]] 910 | name = "pickleshare" 911 | version = "0.7.5" 912 | source = { registry = "https://pypi.org/simple" } 913 | sdist = { url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", size = 6161, upload-time = "2018-09-25T19:17:37.249Z" } 914 | wheels = [ 915 | { url = "https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56", size = 6877, upload-time = "2018-09-25T19:17:35.817Z" }, 916 | ] 917 | 918 | [[package]] 919 | name = "platformdirs" 920 | version = "4.0.0" 921 | source = { registry = "https://pypi.org/simple" } 922 | resolution-markers = [ 923 | "python_full_version < '3.8'", 924 | ] 925 | dependencies = [ 926 | { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 927 | ] 928 | sdist = { url = "https://files.pythonhosted.org/packages/31/28/e40d24d2e2eb23135f8533ad33d582359c7825623b1e022f9d460def7c05/platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731", size = 19914, upload-time = "2023-11-10T16:43:08.316Z" } 929 | wheels = [ 930 | { url = "https://files.pythonhosted.org/packages/31/16/70be3b725073035aa5fc3229321d06e22e73e3e09f6af78dcfdf16c7636c/platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b", size = 17562, upload-time = "2023-11-10T16:43:06.949Z" }, 931 | ] 932 | 933 | [[package]] 934 | name = "platformdirs" 935 | version = "4.3.6" 936 | source = { registry = "https://pypi.org/simple" } 937 | resolution-markers = [ 938 | "python_full_version == '3.8.*'", 939 | ] 940 | sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302, upload-time = "2024-09-17T19:06:50.688Z" } 941 | wheels = [ 942 | { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439, upload-time = "2024-09-17T19:06:49.212Z" }, 943 | ] 944 | 945 | [[package]] 946 | name = "platformdirs" 947 | version = "4.4.0" 948 | source = { registry = "https://pypi.org/simple" } 949 | resolution-markers = [ 950 | "python_full_version == '3.9.*'", 951 | ] 952 | sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } 953 | wheels = [ 954 | { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, 955 | ] 956 | 957 | [[package]] 958 | name = "platformdirs" 959 | version = "4.5.0" 960 | source = { registry = "https://pypi.org/simple" } 961 | resolution-markers = [ 962 | "python_full_version >= '3.11'", 963 | "python_full_version == '3.10.*'", 964 | ] 965 | sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } 966 | wheels = [ 967 | { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, 968 | ] 969 | 970 | [[package]] 971 | name = "pluggy" 972 | version = "1.2.0" 973 | source = { registry = "https://pypi.org/simple" } 974 | resolution-markers = [ 975 | "python_full_version < '3.8'", 976 | ] 977 | dependencies = [ 978 | { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, 979 | ] 980 | sdist = { url = "https://files.pythonhosted.org/packages/8a/42/8f2833655a29c4e9cb52ee8a2be04ceac61bcff4a680fb338cbd3d1e322d/pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3", size = 61613, upload-time = "2023-06-21T09:12:28.745Z" } 981 | wheels = [ 982 | { url = "https://files.pythonhosted.org/packages/51/32/4a79112b8b87b21450b066e102d6608907f4c885ed7b04c3fdb085d4d6ae/pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", size = 17695, upload-time = "2023-06-21T09:12:27.397Z" }, 983 | ] 984 | 985 | [[package]] 986 | name = "pluggy" 987 | version = "1.5.0" 988 | source = { registry = "https://pypi.org/simple" } 989 | resolution-markers = [ 990 | "python_full_version == '3.8.*'", 991 | ] 992 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } 993 | wheels = [ 994 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, 995 | ] 996 | 997 | [[package]] 998 | name = "pluggy" 999 | version = "1.6.0" 1000 | source = { registry = "https://pypi.org/simple" } 1001 | resolution-markers = [ 1002 | "python_full_version >= '3.11'", 1003 | "python_full_version == '3.10.*'", 1004 | "python_full_version == '3.9.*'", 1005 | ] 1006 | sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } 1007 | wheels = [ 1008 | { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "prompt-toolkit" 1013 | version = "3.0.48" 1014 | source = { registry = "https://pypi.org/simple" } 1015 | resolution-markers = [ 1016 | "python_full_version < '3.8'", 1017 | ] 1018 | dependencies = [ 1019 | { name = "wcwidth", marker = "python_full_version < '3.8'" }, 1020 | ] 1021 | sdist = { url = "https://files.pythonhosted.org/packages/2d/4f/feb5e137aff82f7c7f3248267b97451da3644f6cdc218edfe549fb354127/prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", size = 424684, upload-time = "2024-09-25T10:20:57.609Z" } 1022 | wheels = [ 1023 | { url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595, upload-time = "2024-09-25T10:20:53.932Z" }, 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "prompt-toolkit" 1028 | version = "3.0.52" 1029 | source = { registry = "https://pypi.org/simple" } 1030 | resolution-markers = [ 1031 | "python_full_version >= '3.11'", 1032 | "python_full_version == '3.10.*'", 1033 | "python_full_version == '3.9.*'", 1034 | "python_full_version == '3.8.*'", 1035 | ] 1036 | dependencies = [ 1037 | { name = "wcwidth", marker = "python_full_version >= '3.8'" }, 1038 | ] 1039 | sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } 1040 | wheels = [ 1041 | { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "ptyprocess" 1046 | version = "0.7.0" 1047 | source = { registry = "https://pypi.org/simple" } 1048 | sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } 1049 | wheels = [ 1050 | { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "pure-eval" 1055 | version = "0.2.3" 1056 | source = { registry = "https://pypi.org/simple" } 1057 | sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } 1058 | wheels = [ 1059 | { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "pygments" 1064 | version = "2.17.2" 1065 | source = { registry = "https://pypi.org/simple" } 1066 | resolution-markers = [ 1067 | "python_full_version < '3.8'", 1068 | ] 1069 | sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772, upload-time = "2023-11-21T20:43:53.875Z" } 1070 | wheels = [ 1071 | { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756, upload-time = "2023-11-21T20:43:49.423Z" }, 1072 | ] 1073 | 1074 | [[package]] 1075 | name = "pygments" 1076 | version = "2.19.2" 1077 | source = { registry = "https://pypi.org/simple" } 1078 | resolution-markers = [ 1079 | "python_full_version >= '3.11'", 1080 | "python_full_version == '3.10.*'", 1081 | "python_full_version == '3.9.*'", 1082 | "python_full_version == '3.8.*'", 1083 | ] 1084 | sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } 1085 | wheels = [ 1086 | { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "pyproject-api" 1091 | version = "1.5.3" 1092 | source = { registry = "https://pypi.org/simple" } 1093 | resolution-markers = [ 1094 | "python_full_version < '3.8'", 1095 | ] 1096 | dependencies = [ 1097 | { name = "packaging", version = "24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1098 | { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1099 | ] 1100 | sdist = { url = "https://files.pythonhosted.org/packages/f7/70/a63493ea5066b32053f80fdc24fae7c5a2fc65d8f01a1883b30fd850aa84/pyproject_api-1.5.3.tar.gz", hash = "sha256:ffb5b2d7cad43f5b2688ab490de7c4d3f6f15e0b819cb588c4b771567c9729eb", size = 22128, upload-time = "2023-07-06T17:34:12.002Z" } 1101 | wheels = [ 1102 | { url = "https://files.pythonhosted.org/packages/05/53/b225115e177eb54664ede5b68a23d6806d9890baa8ee66b8d87f0bdb6346/pyproject_api-1.5.3-py3-none-any.whl", hash = "sha256:14cf09828670c7b08842249c1f28c8ee6581b872e893f81b62d5465bec41502f", size = 12889, upload-time = "2023-07-06T17:34:10.693Z" }, 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "pyproject-api" 1107 | version = "1.8.0" 1108 | source = { registry = "https://pypi.org/simple" } 1109 | resolution-markers = [ 1110 | "python_full_version == '3.8.*'", 1111 | ] 1112 | dependencies = [ 1113 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1114 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1115 | ] 1116 | sdist = { url = "https://files.pythonhosted.org/packages/bb/19/441e0624a8afedd15bbcce96df1b80479dd0ff0d965f5ce8fde4f2f6ffad/pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496", size = 22340, upload-time = "2024-09-18T23:18:37.805Z" } 1117 | wheels = [ 1118 | { url = "https://files.pythonhosted.org/packages/ba/f4/3c4ddfcc0c19c217c6de513842d286de8021af2f2ab79bbb86c00342d778/pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228", size = 13100, upload-time = "2024-09-18T23:18:35.927Z" }, 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "pyproject-api" 1123 | version = "1.9.1" 1124 | source = { registry = "https://pypi.org/simple" } 1125 | resolution-markers = [ 1126 | "python_full_version == '3.9.*'", 1127 | ] 1128 | dependencies = [ 1129 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1130 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1131 | ] 1132 | sdist = { url = "https://files.pythonhosted.org/packages/19/fd/437901c891f58a7b9096511750247535e891d2d5a5a6eefbc9386a2b41d5/pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335", size = 22710, upload-time = "2025-05-12T14:41:58.025Z" } 1133 | wheels = [ 1134 | { url = "https://files.pythonhosted.org/packages/ef/e6/c293c06695d4a3ab0260ef124a74ebadba5f4c511ce3a4259e976902c00b/pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948", size = 13158, upload-time = "2025-05-12T14:41:56.217Z" }, 1135 | ] 1136 | 1137 | [[package]] 1138 | name = "pyproject-api" 1139 | version = "1.10.0" 1140 | source = { registry = "https://pypi.org/simple" } 1141 | resolution-markers = [ 1142 | "python_full_version >= '3.11'", 1143 | "python_full_version == '3.10.*'", 1144 | ] 1145 | dependencies = [ 1146 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1147 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 1148 | ] 1149 | sdist = { url = "https://files.pythonhosted.org/packages/45/7b/c0e1333b61d41c69e59e5366e727b18c4992688caf0de1be10b3e5265f6b/pyproject_api-1.10.0.tar.gz", hash = "sha256:40c6f2d82eebdc4afee61c773ed208c04c19db4c4a60d97f8d7be3ebc0bbb330", size = 22785, upload-time = "2025-10-09T19:12:27.21Z" } 1150 | wheels = [ 1151 | { url = "https://files.pythonhosted.org/packages/54/cc/cecf97be298bee2b2a37dd360618c819a2a7fd95251d8e480c1f0eb88f3b/pyproject_api-1.10.0-py3-none-any.whl", hash = "sha256:8757c41a79c0f4ab71b99abed52b97ecf66bd20b04fa59da43b5840bac105a09", size = 13218, upload-time = "2025-10-09T19:12:24.428Z" }, 1152 | ] 1153 | 1154 | [[package]] 1155 | name = "pyreadline" 1156 | version = "2.1" 1157 | source = { registry = "https://pypi.org/simple" } 1158 | sdist = { url = "https://files.pythonhosted.org/packages/bc/7c/d724ef1ec3ab2125f38a1d53285745445ec4a8f19b9bb0761b4064316679/pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1", size = 109189, upload-time = "2015-09-16T08:24:48.745Z" } 1159 | 1160 | [[package]] 1161 | name = "pyreadline3" 1162 | version = "3.5.4" 1163 | source = { registry = "https://pypi.org/simple" } 1164 | sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } 1165 | wheels = [ 1166 | { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, 1167 | ] 1168 | 1169 | [[package]] 1170 | name = "pyrepl" 1171 | version = "0.11.3" 1172 | source = { registry = "https://pypi.org/simple" } 1173 | resolution-markers = [ 1174 | "python_full_version < '3.8'", 1175 | ] 1176 | sdist = { url = "https://files.pythonhosted.org/packages/37/1d/526b53a452b4f73bfeaa7aaf84f96c0fe93e3dd0b1e6648836629179f376/pyrepl-0.11.3.tar.gz", hash = "sha256:a981a7928811bda57ca3beb0c674d414cf0a71fcb9a37b676ed415fd008174ad", size = 49355, upload-time = "2025-04-13T11:10:09.52Z" } 1177 | wheels = [ 1178 | { url = "https://files.pythonhosted.org/packages/ec/6b/d507c1cf6c7793c8d030f5d6c545618af0ad5b6818c4c5e88cc78ba97590/pyrepl-0.11.3-py3-none-any.whl", hash = "sha256:4134f8bafe258f00fe83decf5087151b56959196939abe2106eeb1d48333fa2d", size = 54238, upload-time = "2025-04-13T11:10:08.195Z" }, 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "pyrepl" 1183 | version = "0.11.4" 1184 | source = { registry = "https://pypi.org/simple" } 1185 | resolution-markers = [ 1186 | "python_full_version >= '3.11'", 1187 | "python_full_version == '3.10.*'", 1188 | "python_full_version == '3.9.*'", 1189 | "python_full_version == '3.8.*'", 1190 | ] 1191 | sdist = { url = "https://files.pythonhosted.org/packages/08/4f/7088417e5465c53a30b918d30542aad89352ea0d635a5d077717c69a7d2b/pyrepl-0.11.4.tar.gz", hash = "sha256:efe988b4a6e5eed587e9769dc2269aeec2b6feec2f5d77995ee85b9ad7cf7063", size = 51089, upload-time = "2025-07-17T22:56:25.42Z" } 1192 | wheels = [ 1193 | { url = "https://files.pythonhosted.org/packages/bd/a5/ce97a778f096aaa27cfcb7ad09f1198cf73277dcab6c68a4b8f332d91e48/pyrepl-0.11.4-py3-none-any.whl", hash = "sha256:ac30d6340267a21c39e1b1934f92bca6b8735017d14b17e40f903b2d1563541d", size = 55596, upload-time = "2025-07-17T22:56:24.537Z" }, 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "pytest" 1198 | version = "7.4.4" 1199 | source = { registry = "https://pypi.org/simple" } 1200 | resolution-markers = [ 1201 | "python_full_version < '3.8'", 1202 | ] 1203 | dependencies = [ 1204 | { name = "colorama", marker = "python_full_version < '3.8' and sys_platform == 'win32'" }, 1205 | { name = "exceptiongroup", marker = "python_full_version < '3.8'" }, 1206 | { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, 1207 | { name = "iniconfig", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1208 | { name = "packaging", version = "24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1209 | { name = "pluggy", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1210 | { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1211 | ] 1212 | sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116, upload-time = "2023-12-31T12:00:18.035Z" } 1213 | wheels = [ 1214 | { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload-time = "2023-12-31T12:00:13.963Z" }, 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "pytest" 1219 | version = "8.3.5" 1220 | source = { registry = "https://pypi.org/simple" } 1221 | resolution-markers = [ 1222 | "python_full_version == '3.8.*'", 1223 | ] 1224 | dependencies = [ 1225 | { name = "colorama", marker = "python_full_version == '3.8.*' and sys_platform == 'win32'" }, 1226 | { name = "exceptiongroup", marker = "python_full_version == '3.8.*'" }, 1227 | { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1228 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1229 | { name = "pluggy", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1230 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1231 | ] 1232 | sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } 1233 | wheels = [ 1234 | { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "pytest" 1239 | version = "8.4.2" 1240 | source = { registry = "https://pypi.org/simple" } 1241 | resolution-markers = [ 1242 | "python_full_version >= '3.11'", 1243 | "python_full_version == '3.10.*'", 1244 | "python_full_version == '3.9.*'", 1245 | ] 1246 | dependencies = [ 1247 | { name = "colorama", marker = "python_full_version >= '3.9' and sys_platform == 'win32'" }, 1248 | { name = "exceptiongroup", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, 1249 | { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1250 | { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1251 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 1252 | { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 1253 | { name = "pygments", version = "2.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 1254 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, 1255 | ] 1256 | sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } 1257 | wheels = [ 1258 | { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "pytokens" 1263 | version = "0.2.0" 1264 | source = { registry = "https://pypi.org/simple" } 1265 | sdist = { url = "https://files.pythonhosted.org/packages/d4/c2/dbadcdddb412a267585459142bfd7cc241e6276db69339353ae6e241ab2b/pytokens-0.2.0.tar.gz", hash = "sha256:532d6421364e5869ea57a9523bf385f02586d4662acbcc0342afd69511b4dd43", size = 15368, upload-time = "2025-10-15T08:02:42.738Z" } 1266 | wheels = [ 1267 | { url = "https://files.pythonhosted.org/packages/89/5a/c269ea6b348b6f2c32686635df89f32dbe05df1088dd4579302a6f8f99af/pytokens-0.2.0-py3-none-any.whl", hash = "sha256:74d4b318c67f4295c13782ddd9abcb7e297ec5630ad060eb90abf7ebbefe59f8", size = 12038, upload-time = "2025-10-15T08:02:41.694Z" }, 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "setuptools" 1272 | version = "68.0.0" 1273 | source = { registry = "https://pypi.org/simple" } 1274 | sdist = { url = "https://files.pythonhosted.org/packages/dc/98/5f896af066c128669229ff1aa81553ac14cfb3e5e74b6b44594132b8540e/setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235", size = 2194111, upload-time = "2023-06-19T15:53:05.082Z" } 1275 | wheels = [ 1276 | { url = "https://files.pythonhosted.org/packages/c7/42/be1c7bbdd83e1bfb160c94b9cafd8e25efc7400346cf7ccdbdb452c467fa/setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", size = 804037, upload-time = "2023-06-19T15:53:03.089Z" }, 1277 | ] 1278 | 1279 | [[package]] 1280 | name = "stack-data" 1281 | version = "0.6.3" 1282 | source = { registry = "https://pypi.org/simple" } 1283 | dependencies = [ 1284 | { name = "asttokens", marker = "python_full_version >= '3.8'" }, 1285 | { name = "executing", marker = "python_full_version >= '3.8'" }, 1286 | { name = "pure-eval", marker = "python_full_version >= '3.8'" }, 1287 | ] 1288 | sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } 1289 | wheels = [ 1290 | { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "tomli" 1295 | version = "2.0.1" 1296 | source = { registry = "https://pypi.org/simple" } 1297 | resolution-markers = [ 1298 | "python_full_version < '3.8'", 1299 | ] 1300 | sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164, upload-time = "2022-02-08T10:54:04.006Z" } 1301 | wheels = [ 1302 | { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757, upload-time = "2022-02-08T10:54:02.017Z" }, 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "tomli" 1307 | version = "2.3.0" 1308 | source = { registry = "https://pypi.org/simple" } 1309 | resolution-markers = [ 1310 | "python_full_version == '3.10.*'", 1311 | "python_full_version == '3.9.*'", 1312 | "python_full_version == '3.8.*'", 1313 | ] 1314 | sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } 1315 | wheels = [ 1316 | { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, 1317 | { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, 1318 | { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, 1319 | { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, 1320 | { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, 1321 | { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, 1322 | { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, 1323 | { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, 1324 | { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, 1325 | { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, 1326 | { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, 1327 | { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, 1328 | { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, 1329 | { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, 1330 | { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, 1331 | { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, 1332 | { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, 1333 | { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, 1334 | { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, 1335 | { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, 1336 | { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, 1337 | { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, 1338 | { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, 1339 | { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, 1340 | { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, 1341 | { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, 1342 | { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, 1343 | { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, 1344 | { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, 1345 | { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, 1346 | { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, 1347 | { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, 1348 | { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, 1349 | { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, 1350 | { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, 1351 | { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, 1352 | { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, 1353 | { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, 1354 | { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, 1355 | { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, 1356 | { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, 1357 | ] 1358 | 1359 | [[package]] 1360 | name = "tox" 1361 | version = "4.8.0" 1362 | source = { registry = "https://pypi.org/simple" } 1363 | resolution-markers = [ 1364 | "python_full_version < '3.8'", 1365 | ] 1366 | dependencies = [ 1367 | { name = "cachetools", version = "5.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1368 | { name = "chardet", marker = "python_full_version < '3.8'" }, 1369 | { name = "colorama", marker = "python_full_version < '3.8'" }, 1370 | { name = "filelock", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1371 | { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, 1372 | { name = "packaging", version = "24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1373 | { name = "platformdirs", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1374 | { name = "pluggy", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1375 | { name = "pyproject-api", version = "1.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1376 | { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1377 | { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1378 | { name = "virtualenv", version = "20.26.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1379 | ] 1380 | sdist = { url = "https://files.pythonhosted.org/packages/00/21/8a0d95e6a502c6a0be81c583af9c11066bf1f4e6eeb6551ee8b6f4c7292d/tox-4.8.0.tar.gz", hash = "sha256:2adacf435b12ccf10b9dfa9975d8ec0afd7cbae44d300463140d2117b968037b", size = 173370, upload-time = "2023-08-12T18:47:10.869Z" } 1381 | wheels = [ 1382 | { url = "https://files.pythonhosted.org/packages/ad/0c/9ad67a4f8ed18c2619b6b8c41cea4c99e7617d5d712670ab4193d439f1f8/tox-4.8.0-py3-none-any.whl", hash = "sha256:4991305a56983d750a0d848a34242be290452aa88d248f1bf976e4036ee8b213", size = 152622, upload-time = "2023-08-12T18:47:09.458Z" }, 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "tox" 1387 | version = "4.25.0" 1388 | source = { registry = "https://pypi.org/simple" } 1389 | resolution-markers = [ 1390 | "python_full_version == '3.8.*'", 1391 | ] 1392 | dependencies = [ 1393 | { name = "cachetools", version = "5.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1394 | { name = "chardet", marker = "python_full_version == '3.8.*'" }, 1395 | { name = "colorama", marker = "python_full_version == '3.8.*'" }, 1396 | { name = "filelock", version = "3.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1397 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1398 | { name = "platformdirs", version = "4.3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1399 | { name = "pluggy", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1400 | { name = "pyproject-api", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1401 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1402 | { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1403 | { name = "virtualenv", version = "20.35.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1404 | ] 1405 | sdist = { url = "https://files.pythonhosted.org/packages/fe/87/692478f0a194f1cad64803692642bd88c12c5b64eee16bf178e4a32e979c/tox-4.25.0.tar.gz", hash = "sha256:dd67f030317b80722cf52b246ff42aafd3ed27ddf331c415612d084304cf5e52", size = 196255, upload-time = "2025-03-27T15:13:37.519Z" } 1406 | wheels = [ 1407 | { url = "https://files.pythonhosted.org/packages/f9/38/33348de6fc4b1afb3d76d8485c8aecbdabcfb3af8da53d40c792332e2b37/tox-4.25.0-py3-none-any.whl", hash = "sha256:4dfdc7ba2cc6fdc6688dde1b21e7b46ff6c41795fb54586c91a3533317b5255c", size = 172420, upload-time = "2025-03-27T15:13:35.703Z" }, 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "tox" 1412 | version = "4.30.3" 1413 | source = { registry = "https://pypi.org/simple" } 1414 | resolution-markers = [ 1415 | "python_full_version == '3.9.*'", 1416 | ] 1417 | dependencies = [ 1418 | { name = "cachetools", version = "6.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1419 | { name = "chardet", marker = "python_full_version == '3.9.*'" }, 1420 | { name = "colorama", marker = "python_full_version == '3.9.*'" }, 1421 | { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1422 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1423 | { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1424 | { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1425 | { name = "pyproject-api", version = "1.9.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1426 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1427 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1428 | { name = "virtualenv", version = "20.35.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1429 | ] 1430 | sdist = { url = "https://files.pythonhosted.org/packages/51/b2/cee55172e5e10ce030b087cd3ac06641e47d08a3dc8d76c17b157dba7558/tox-4.30.3.tar.gz", hash = "sha256:f3dd0735f1cd4e8fbea5a3661b77f517456b5f0031a6256432533900e34b90bf", size = 202799, upload-time = "2025-10-02T16:24:39.974Z" } 1431 | wheels = [ 1432 | { url = "https://files.pythonhosted.org/packages/e2/e4/8bb9ce952820df4165eb34610af347665d6cb436898a234db9d84d093ce6/tox-4.30.3-py3-none-any.whl", hash = "sha256:a9f17b4b2d0f74fe0d76207236925a119095011e5c2e661a133115a8061178c9", size = 175512, upload-time = "2025-10-02T16:24:38.209Z" }, 1433 | ] 1434 | 1435 | [[package]] 1436 | name = "tox" 1437 | version = "4.32.0" 1438 | source = { registry = "https://pypi.org/simple" } 1439 | resolution-markers = [ 1440 | "python_full_version >= '3.11'", 1441 | "python_full_version == '3.10.*'", 1442 | ] 1443 | dependencies = [ 1444 | { name = "cachetools", version = "6.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1445 | { name = "chardet", marker = "python_full_version >= '3.10'" }, 1446 | { name = "colorama", marker = "python_full_version >= '3.10'" }, 1447 | { name = "filelock", version = "3.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1448 | { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1449 | { name = "platformdirs", version = "4.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1450 | { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1451 | { name = "pyproject-api", version = "1.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1452 | { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 1453 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, 1454 | { name = "virtualenv", version = "20.35.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1455 | ] 1456 | sdist = { url = "https://files.pythonhosted.org/packages/59/bf/0e4dbd42724cbae25959f0e34c95d0c730df03ab03f54d52accd9abfc614/tox-4.32.0.tar.gz", hash = "sha256:1ad476b5f4d3679455b89a992849ffc3367560bbc7e9495ee8a3963542e7c8ff", size = 203330, upload-time = "2025-10-24T18:03:38.132Z" } 1457 | wheels = [ 1458 | { url = "https://files.pythonhosted.org/packages/fc/cc/e09c0d663a004945f82beecd4f147053567910479314e8d01ba71e5d5dea/tox-4.32.0-py3-none-any.whl", hash = "sha256:451e81dc02ba8d1ed20efd52ee409641ae4b5d5830e008af10fe8823ef1bd551", size = 175905, upload-time = "2025-10-24T18:03:36.337Z" }, 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "tox-pyenv" 1463 | version = "1.1.0" 1464 | source = { registry = "https://pypi.org/simple" } 1465 | dependencies = [ 1466 | { name = "tox", version = "4.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1467 | { name = "tox", version = "4.25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1468 | { name = "tox", version = "4.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1469 | { name = "tox", version = "4.32.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1470 | ] 1471 | sdist = { url = "https://files.pythonhosted.org/packages/16/0e/0208374ee677ccb9dbb4e9bc23f6d304bef6b881cc6ccb8bec2ae81b1e99/tox-pyenv-1.1.0.tar.gz", hash = "sha256:916c2213577aec0b3b5452c5bfb32fd077f3a3196f50a81ad57d7ef3fc2599e4", size = 4925, upload-time = "2017-08-29T23:13:22.524Z" } 1472 | wheels = [ 1473 | { url = "https://files.pythonhosted.org/packages/b7/56/ee1b984fbfb745cc637009dcb054b5fc9399c838b465c634846fd1550511/tox_pyenv-1.1.0-py2.py3-none-any.whl", hash = "sha256:e470c18af115fe52eeff95e7e3cdd0793613eca19709966fc2724b79d55246cb", size = 6824, upload-time = "2017-08-29T23:13:20.508Z" }, 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "traitlets" 1478 | version = "5.9.0" 1479 | source = { registry = "https://pypi.org/simple" } 1480 | resolution-markers = [ 1481 | "python_full_version < '3.8'", 1482 | ] 1483 | sdist = { url = "https://files.pythonhosted.org/packages/39/c3/205e88f02959712b62008502952707313640369144a7fded4cbc61f48321/traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9", size = 150207, upload-time = "2023-01-30T14:15:59.636Z" } 1484 | wheels = [ 1485 | { url = "https://files.pythonhosted.org/packages/77/75/c28e9ef7abec2b7e9ff35aea3e0be6c1aceaf7873c26c95ae1f0d594de71/traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8", size = 117376, upload-time = "2023-01-30T14:15:57.273Z" }, 1486 | ] 1487 | 1488 | [[package]] 1489 | name = "traitlets" 1490 | version = "5.14.3" 1491 | source = { registry = "https://pypi.org/simple" } 1492 | resolution-markers = [ 1493 | "python_full_version >= '3.11'", 1494 | "python_full_version == '3.10.*'", 1495 | "python_full_version == '3.9.*'", 1496 | "python_full_version == '3.8.*'", 1497 | ] 1498 | sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } 1499 | wheels = [ 1500 | { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, 1501 | ] 1502 | 1503 | [[package]] 1504 | name = "typed-ast" 1505 | version = "1.5.5" 1506 | source = { registry = "https://pypi.org/simple" } 1507 | sdist = { url = "https://files.pythonhosted.org/packages/f9/7e/a424029f350aa8078b75fd0d360a787a273ca753a678d1104c5fa4f3072a/typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd", size = 252841, upload-time = "2023-07-04T18:38:08.524Z" } 1508 | wheels = [ 1509 | { url = "https://files.pythonhosted.org/packages/88/07/5defe18d4fc16281cd18c4374270abc430c3d852d8ac29b5db6599d45cfe/typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b", size = 223267, upload-time = "2023-07-04T18:37:00.129Z" }, 1510 | { url = "https://files.pythonhosted.org/packages/a0/5c/e379b00028680bfcd267d845cf46b60e76d8ac6f7009fd440d6ce030cc92/typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686", size = 208260, upload-time = "2023-07-04T18:37:03.069Z" }, 1511 | { url = "https://files.pythonhosted.org/packages/3b/99/5cc31ef4f3c80e1ceb03ed2690c7085571e3fbf119cbd67a111ec0b6622f/typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769", size = 842272, upload-time = "2023-07-04T18:37:04.916Z" }, 1512 | { url = "https://files.pythonhosted.org/packages/e2/ed/b9b8b794b37b55c9247b1e8d38b0361e8158795c181636d34d6c11b506e7/typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04", size = 824651, upload-time = "2023-07-04T18:37:06.711Z" }, 1513 | { url = "https://files.pythonhosted.org/packages/ca/59/dbbbe5a0e91c15d14a0896b539a5ed01326b0d468e75c1a33274d128d2d1/typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d", size = 854960, upload-time = "2023-07-04T18:37:08.474Z" }, 1514 | { url = "https://files.pythonhosted.org/packages/90/f0/0956d925f87bd81f6e0f8cf119eac5e5c8f4da50ca25bb9f5904148d4611/typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d", size = 839321, upload-time = "2023-07-04T18:37:10.417Z" }, 1515 | { url = "https://files.pythonhosted.org/packages/43/17/4bdece9795da6f3345c4da5667ac64bc25863617f19c28d81f350f515be6/typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02", size = 139380, upload-time = "2023-07-04T18:37:12.157Z" }, 1516 | { url = "https://files.pythonhosted.org/packages/75/53/b685e10da535c7b3572735f8bea0d4abb35a04722a7d44ca9c163a0cf822/typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee", size = 223264, upload-time = "2023-07-04T18:37:13.637Z" }, 1517 | { url = "https://files.pythonhosted.org/packages/96/fd/fc8ccf19fc16a40a23e7c7802d0abc78c1f38f1abb6e2447c474f8a076d8/typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18", size = 208158, upload-time = "2023-07-04T18:37:15.141Z" }, 1518 | { url = "https://files.pythonhosted.org/packages/bf/9a/598e47f2c3ecd19d7f1bb66854d0d3ba23ffd93c846448790a92524b0a8d/typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88", size = 878366, upload-time = "2023-07-04T18:37:16.614Z" }, 1519 | { url = "https://files.pythonhosted.org/packages/60/ca/765e8bf8b24d0ed7b9fc669f6826c5bc3eb7412fc765691f59b83ae195b2/typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2", size = 860314, upload-time = "2023-07-04T18:37:18.215Z" }, 1520 | { url = "https://files.pythonhosted.org/packages/d9/3c/4af750e6c673a0dd6c7b9f5b5e5ed58ec51a2e4e744081781c664d369dfa/typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9", size = 898108, upload-time = "2023-07-04T18:37:20.095Z" }, 1521 | { url = "https://files.pythonhosted.org/packages/03/8d/d0a4d1e060e1e8dda2408131a0cc7633fc4bc99fca5941dcb86c461dfe01/typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8", size = 881971, upload-time = "2023-07-04T18:37:21.912Z" }, 1522 | { url = "https://files.pythonhosted.org/packages/90/83/f28d2c912cd010a09b3677ac69d23181045eb17e358914ab739b7fdee530/typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b", size = 139286, upload-time = "2023-07-04T18:37:23.625Z" }, 1523 | { url = "https://files.pythonhosted.org/packages/d5/00/635353c31b71ed307ab020eff6baed9987da59a1b2ba489f885ecbe293b8/typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e", size = 222315, upload-time = "2023-07-04T18:37:36.008Z" }, 1524 | { url = "https://files.pythonhosted.org/packages/01/95/11be104446bb20212a741d30d40eab52a9cfc05ea34efa074ff4f7c16983/typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e", size = 793541, upload-time = "2023-07-04T18:37:37.614Z" }, 1525 | { url = "https://files.pythonhosted.org/packages/32/f1/75bd58fb1410cb72fbc6e8adf163015720db2c38844b46a9149c5ff6bf38/typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311", size = 778348, upload-time = "2023-07-04T18:37:39.332Z" }, 1526 | { url = "https://files.pythonhosted.org/packages/47/97/0bb4dba688a58ff9c08e63b39653e4bcaa340ce1bb9c1d58163e5c2c66f1/typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2", size = 809447, upload-time = "2023-07-04T18:37:41.017Z" }, 1527 | { url = "https://files.pythonhosted.org/packages/a8/cd/9a867f5a96d83a9742c43914e10d3a2083d8fe894ab9bf60fd467c6c497f/typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4", size = 796707, upload-time = "2023-07-04T18:37:42.625Z" }, 1528 | { url = "https://files.pythonhosted.org/packages/eb/06/73ca55ee5303b41d08920de775f02d2a3e1e59430371f5adf7fbb1a21127/typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431", size = 138403, upload-time = "2023-07-04T18:37:44.399Z" }, 1529 | { url = "https://files.pythonhosted.org/packages/19/e3/88b65e46643006592f39e0fdef3e29454244a9fdaa52acfb047dc68cae6a/typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a", size = 222951, upload-time = "2023-07-04T18:37:45.745Z" }, 1530 | { url = "https://files.pythonhosted.org/packages/15/e0/182bdd9edb6c6a1c068cecaa87f58924a817f2807a0b0d940f578b3328df/typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437", size = 208247, upload-time = "2023-07-04T18:37:47.28Z" }, 1531 | { url = "https://files.pythonhosted.org/packages/8d/09/bba083f2c11746288eaf1859e512130420405033de84189375fe65d839ba/typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede", size = 861010, upload-time = "2023-07-04T18:37:48.847Z" }, 1532 | { url = "https://files.pythonhosted.org/packages/31/f3/38839df509b04fb54205e388fc04b47627377e0ad628870112086864a441/typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4", size = 840026, upload-time = "2023-07-04T18:37:50.631Z" }, 1533 | { url = "https://files.pythonhosted.org/packages/45/1e/aa5f1dae4b92bc665ae9a655787bb2fe007a881fa2866b0408ce548bb24c/typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6", size = 875615, upload-time = "2023-07-04T18:37:52.27Z" }, 1534 | { url = "https://files.pythonhosted.org/packages/94/88/71a1c249c01fbbd66f9f28648f8249e737a7fe19056c1a78e7b3b9250eb1/typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4", size = 858320, upload-time = "2023-07-04T18:37:54.23Z" }, 1535 | { url = "https://files.pythonhosted.org/packages/12/1e/19f53aad3984e351e6730e4265fde4b949a66c451e10828fdbc4dfb050f1/typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b", size = 139414, upload-time = "2023-07-04T18:37:55.912Z" }, 1536 | { url = "https://files.pythonhosted.org/packages/b1/88/6e7f36f5fab6fbf0586a2dd866ac337924b7d4796a4d1b2b04443a864faf/typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10", size = 223329, upload-time = "2023-07-04T18:37:57.344Z" }, 1537 | { url = "https://files.pythonhosted.org/packages/71/30/09d27e13824495547bcc665bd07afc593b22b9484f143b27565eae4ccaac/typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814", size = 208314, upload-time = "2023-07-04T18:37:59.073Z" }, 1538 | { url = "https://files.pythonhosted.org/packages/07/3d/564308b7a432acb1f5399933cbb1b376a1a64d2544b90f6ba91894674260/typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8", size = 840900, upload-time = "2023-07-04T18:38:00.562Z" }, 1539 | { url = "https://files.pythonhosted.org/packages/ea/f4/262512d14f777ea3666a089e2675a9b1500a85b8329a36de85d63433fb0e/typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274", size = 823435, upload-time = "2023-07-04T18:38:02.532Z" }, 1540 | { url = "https://files.pythonhosted.org/packages/a1/25/b3ccb948166d309ab75296ac9863ebe2ff209fbc063f1122a2d3979e47c3/typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a", size = 853125, upload-time = "2023-07-04T18:38:04.128Z" }, 1541 | { url = "https://files.pythonhosted.org/packages/1c/09/012da182242f168bb5c42284297dcc08dc0a1b3668db5b3852aec467f56f/typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba", size = 837280, upload-time = "2023-07-04T18:38:05.968Z" }, 1542 | { url = "https://files.pythonhosted.org/packages/30/bd/c815051404c4293265634d9d3e292f04fcf681d0502a9484c38b8f224d04/typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155", size = 139486, upload-time = "2023-07-04T18:38:07.249Z" }, 1543 | ] 1544 | 1545 | [[package]] 1546 | name = "typing-extensions" 1547 | version = "4.7.1" 1548 | source = { registry = "https://pypi.org/simple" } 1549 | resolution-markers = [ 1550 | "python_full_version < '3.8'", 1551 | ] 1552 | sdist = { url = "https://files.pythonhosted.org/packages/3c/8b/0111dd7d6c1478bf83baa1cab85c686426c7a6274119aceb2bd9d35395ad/typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2", size = 72876, upload-time = "2023-07-02T14:20:55.045Z" } 1553 | wheels = [ 1554 | { url = "https://files.pythonhosted.org/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", size = 33232, upload-time = "2023-07-02T14:20:53.275Z" }, 1555 | ] 1556 | 1557 | [[package]] 1558 | name = "typing-extensions" 1559 | version = "4.13.2" 1560 | source = { registry = "https://pypi.org/simple" } 1561 | resolution-markers = [ 1562 | "python_full_version == '3.8.*'", 1563 | ] 1564 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } 1565 | wheels = [ 1566 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, 1567 | ] 1568 | 1569 | [[package]] 1570 | name = "typing-extensions" 1571 | version = "4.15.0" 1572 | source = { registry = "https://pypi.org/simple" } 1573 | resolution-markers = [ 1574 | "python_full_version >= '3.11'", 1575 | "python_full_version == '3.10.*'", 1576 | "python_full_version == '3.9.*'", 1577 | ] 1578 | sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } 1579 | wheels = [ 1580 | { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "virtualenv" 1585 | version = "20.26.6" 1586 | source = { registry = "https://pypi.org/simple" } 1587 | resolution-markers = [ 1588 | "python_full_version < '3.8'", 1589 | ] 1590 | dependencies = [ 1591 | { name = "distlib", marker = "python_full_version < '3.8'" }, 1592 | { name = "filelock", version = "3.12.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1593 | { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, 1594 | { name = "platformdirs", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1595 | ] 1596 | sdist = { url = "https://files.pythonhosted.org/packages/3f/40/abc5a766da6b0b2457f819feab8e9203cbeae29327bd241359f866a3da9d/virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48", size = 9372482, upload-time = "2024-09-27T16:28:57.502Z" } 1597 | wheels = [ 1598 | { url = "https://files.pythonhosted.org/packages/59/90/57b8ac0c8a231545adc7698c64c5a36fa7cd8e376c691b9bde877269f2eb/virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2", size = 5999862, upload-time = "2024-09-27T16:28:54.798Z" }, 1599 | ] 1600 | 1601 | [[package]] 1602 | name = "virtualenv" 1603 | version = "20.35.3" 1604 | source = { registry = "https://pypi.org/simple" } 1605 | resolution-markers = [ 1606 | "python_full_version >= '3.11'", 1607 | "python_full_version == '3.10.*'", 1608 | "python_full_version == '3.9.*'", 1609 | "python_full_version == '3.8.*'", 1610 | ] 1611 | dependencies = [ 1612 | { name = "distlib", marker = "python_full_version >= '3.8'" }, 1613 | { name = "filelock", version = "3.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1614 | { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1615 | { name = "filelock", version = "3.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1616 | { name = "platformdirs", version = "4.3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1617 | { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, 1618 | { name = "platformdirs", version = "4.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 1619 | { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, 1620 | { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, 1621 | ] 1622 | sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907, upload-time = "2025-10-10T21:23:33.178Z" } 1623 | wheels = [ 1624 | { url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061, upload-time = "2025-10-10T21:23:30.433Z" }, 1625 | ] 1626 | 1627 | [[package]] 1628 | name = "wcwidth" 1629 | version = "0.2.14" 1630 | source = { registry = "https://pypi.org/simple" } 1631 | sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } 1632 | wheels = [ 1633 | { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, 1634 | ] 1635 | 1636 | [[package]] 1637 | name = "wmctrl" 1638 | version = "0.5" 1639 | source = { registry = "https://pypi.org/simple" } 1640 | dependencies = [ 1641 | { name = "attrs", version = "24.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, 1642 | ] 1643 | sdist = { url = "https://files.pythonhosted.org/packages/60/d9/6625ead93412c5ce86db1f8b4f2a70b8043e0a7c1d30099ba3c6a81641ff/wmctrl-0.5.tar.gz", hash = "sha256:7839a36b6fe9e2d6fd22304e5dc372dbced2116ba41283ea938b2da57f53e962", size = 5202, upload-time = "2023-09-16T23:02:08.09Z" } 1644 | wheels = [ 1645 | { url = "https://files.pythonhosted.org/packages/13/ca/723e3f8185738d7947f14ee7dc663b59415c6dee43bd71575f8c7f5cd6be/wmctrl-0.5-py2.py3-none-any.whl", hash = "sha256:ae695c1863a314c899e7cf113f07c0da02a394b968c4772e1936219d9234ddd7", size = 4268, upload-time = "2023-09-16T23:02:05.563Z" }, 1646 | ] 1647 | 1648 | [[package]] 1649 | name = "zipp" 1650 | version = "3.15.0" 1651 | source = { registry = "https://pypi.org/simple" } 1652 | sdist = { url = "https://files.pythonhosted.org/packages/00/27/f0ac6b846684cecce1ee93d32450c45ab607f65c2e0255f0092032d91f07/zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", size = 18454, upload-time = "2023-02-25T02:17:22.503Z" } 1653 | wheels = [ 1654 | { url = "https://files.pythonhosted.org/packages/5b/fa/c9e82bbe1af6266adf08afb563905eb87cab83fde00a0a08963510621047/zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556", size = 6758, upload-time = "2023-02-25T02:17:20.807Z" }, 1655 | ] 1656 | --------------------------------------------------------------------------------