├── .github
└── workflows
│ └── unittest.yml
├── .gitignore
├── LICENSE
├── README.md
├── classopt
├── __init__.py
├── config.py
├── decorator.py
├── inheritance.py
└── utils.py
├── examples
├── head_decorator.py
├── head_inheritance.py
├── poetry.lock
├── print_args.py
└── pyproject.toml
├── poetry.lock
├── pyproject.toml
└── tests
├── __init__.py
├── conftest.py
├── test_decorator.py
└── test_inheritance.py
/.github/workflows/unittest.yml:
--------------------------------------------------------------------------------
1 | name: Unit test
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | build:
9 | runs-on: ${{ matrix.os }}
10 | strategy:
11 | matrix:
12 | os: ["ubuntu-20.04", "macos-12", "windows-2022"]
13 | python-version: ["3.7", "3.8", "3.9", "3.10"]
14 | defaults:
15 | run:
16 | shell: bash
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Set up Python
20 | uses: actions/setup-python@v4
21 | with:
22 | python-version: ${{ matrix.python-version }}
23 |
24 | - name: Install Poetry
25 | uses: snok/install-poetry@v1
26 | with:
27 | virtualenvs-create: true
28 | virtualenvs-in-project: true
29 |
30 | - name: Install library
31 | run: poetry install --no-interaction
32 |
33 | - name: Run test
34 | run: poetry run pytest
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 moisutsu
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Welcome to ClassOpt 👋
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | > Arguments parser with class for Python, inspired by [StructOpt](https://github.com/TeXitoi/structopt)
13 |
14 | ## Install
15 |
16 | ```sh
17 | pip install classopt
18 | ```
19 |
20 | ## Usage
21 |
22 |
23 | Import `classopt` and define the Opt class with decorator.
24 |
25 | ```python
26 | from classopt import classopt
27 |
28 | @classopt(default_long=True)
29 | class Opt:
30 | file: str
31 | count: int = 3
32 | numbers: list[int]
33 | flag: bool
34 |
35 | if __name__ == "__main__":
36 | opt = Opt.from_args()
37 | print(opt)
38 | print(opt.file)
39 | ```
40 |
41 | Run with command line arguments.
42 |
43 | ```bash
44 | $ python example.py --file example.txt --numbers 1 2 3 --flag
45 | Opt(file='example.txt', count=3, numbers=[1, 2, 3], flag=True)
46 | example.txt
47 | ```
48 | You can specify most of the arguments to [argparse.ArgumentParser.add_argument](https://docs.python.org/ja/3/library/argparse.html#argparse.ArgumentParser.add_argument) in `config` (except name_or_flags).
49 |
50 |
51 | ```python
52 | from classopt import classopt, config
53 |
54 | @classopt
55 | class Opt:
56 | file: str
57 | count: int = config(long=True)
58 | numbers: list = config(long=True, short=True, nargs="+", type=int)
59 | flag: bool = config(long=True, action="store_false")
60 |
61 | if __name__ == "__main__":
62 | opt = Opt.from_args()
63 | print(opt)
64 | ```
65 |
66 | ```bash
67 | $ python example.py example.txt --count 5 -n 1 2 3 --flag
68 | Opt(file='example.txt', count=5, numbers=[1, 2, 3], flag=False)
69 | ```
70 |
71 | Some details
72 | ```python
73 | # `default_long=True` is equivalent to `config(long=True)' for all members
74 | # Similarly, you can do `default_short=True`
75 | @classopt(default_long=True)
76 | class Opt:
77 | # `long=False` overrides `default_long=True`
78 | file: str = config(long=False)
79 |
80 | # equivalent to `numbers: list = config(nargs="*", type=int)`
81 | # and `numbers: typing.List[int]`
82 | numbers: list[int]
83 |
84 | # equivalent to `flag: bool = config(action="store_true")`
85 | flag: bool
86 | ```
87 |
88 | ### Other Way
89 |
90 | You can also define an argument parser by inheriting from `ClassOpt`.
91 |
92 | ```python
93 | from classopt import ClassOpt, config
94 |
95 | class Opt(ClassOpt):
96 | file: str
97 | count: int = config(long=True)
98 | numbers: list[int] = config(long=True, short="-c")
99 | flag: bool = config(long=True)
100 |
101 | if __name__ == "__main__":
102 | opt = Opt.from_args()
103 | print(opt)
104 | print(opt.file)
105 | ```
106 |
107 | Run with command line arguments.
108 |
109 | ```bash
110 | $ python example.py example.txt --count 5 -c 1 2 3 --flag
111 | Opt(file='example.txt', count=5, numbers=[1, 2, 3], flag=True)
112 | example.txt
113 | ```
114 |
115 | The inherited method does not support some features and may disappear in the future.
116 | So we recommend the decorator method.
117 |
118 | ## Run tests
119 |
120 | ```sh
121 | poetry run pytest
122 | ```
123 |
124 | ## Author
125 |
126 | 👤 **moisutsu**
127 |
128 | * Twitter: [@moisutsu](https://twitter.com/moisutsu)
129 | * Github: [@moisutsu](https://github.com/moisutsu)
130 |
131 | ## Show your support
132 |
133 | Give a ⭐️ if this project helped you!
134 |
135 | ## 📝 License
136 |
137 | Copyright © 2021 [moisutsu](https://github.com/moisutsu).
138 | This project is [MIT](https://github.com/moisutsu/classopt/blob/main/LICENSE) licensed.
139 |
140 | ***
141 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_
142 |
--------------------------------------------------------------------------------
/classopt/__init__.py:
--------------------------------------------------------------------------------
1 | from .config import config
2 | from .decorator import classopt
3 | from .inheritance import ClassOpt
4 |
5 | __all__ = ["classopt", "ClassOpt", "config"]
6 |
--------------------------------------------------------------------------------
/classopt/config.py:
--------------------------------------------------------------------------------
1 | from dataclasses import Field, field
2 | from typing import Any, Iterable, Optional, Tuple, Union
3 |
4 |
5 | def config(
6 | long: Optional[bool] = None,
7 | short: Optional[Union[bool, str]] = None,
8 | action: Optional[str] = None,
9 | nargs: Optional[Union[int, str]] = None,
10 | const: Any = None,
11 | default: Any = None,
12 | type: Optional[type] = None,
13 | choices: Optional[Iterable] = None,
14 | required: Optional[bool] = None,
15 | help: Optional[str] = None,
16 | metavar: Optional[Union[str, Tuple[str]]] = None,
17 | dest: Optional[str] = None,
18 | version: Optional[str] = None,
19 | **kwargs: Any,
20 | ) -> Field:
21 | metadata = {}
22 | metadata.update(kwargs)
23 |
24 | assign_if_not_none(metadata, "long", long)
25 | assign_if_not_none(metadata, "short", short)
26 | assign_if_not_none(metadata, "action", action)
27 | assign_if_not_none(metadata, "nargs", nargs)
28 | assign_if_not_none(metadata, "const", const)
29 | assign_if_not_none(metadata, "default", default)
30 | assign_if_not_none(metadata, "type", type)
31 | assign_if_not_none(metadata, "choices", choices)
32 | assign_if_not_none(metadata, "required", required)
33 | assign_if_not_none(metadata, "help", help)
34 | assign_if_not_none(metadata, "metavar", metavar)
35 | assign_if_not_none(metadata, "dest", dest)
36 | assign_if_not_none(metadata, "version", version)
37 |
38 | # to avoid errors like below:
39 | # ValueError: mutable default for field hoge is not allowed: use default_factory
40 | if isinstance(default, (list, dict, set)):
41 | return field(
42 | default_factory=lambda: default,
43 | metadata=metadata,
44 | )
45 | else:
46 | return field(
47 | default=default,
48 | metadata=metadata,
49 | )
50 |
51 |
52 | def assign_if_not_none(d: dict, key: str, value: Any):
53 | if value is None:
54 | return
55 | d[key] = value
56 |
--------------------------------------------------------------------------------
/classopt/decorator.py:
--------------------------------------------------------------------------------
1 | import typing
2 | from argparse import ArgumentParser
3 | from dataclasses import MISSING, Field, asdict, dataclass
4 | from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, overload
5 |
6 | from classopt import config
7 | from classopt.utils import (
8 | GENERIC_ALIASES,
9 | convert_non_primitives_to_string,
10 | revert_non_primitives_from_string,
11 | )
12 |
13 | if TYPE_CHECKING:
14 | from typing import Callable, Generic, Literal, Type, TypeVar, Union
15 |
16 | _C = TypeVar("_C")
17 | _T = TypeVar("_T")
18 |
19 | class _ClassOptGeneric(Generic[_T]):
20 | @classmethod
21 | def from_args(cls) -> _T:
22 | ...
23 |
24 | def to_dict(self) -> dict:
25 | ...
26 |
27 | @classmethod
28 | def from_dict(cls, data: dict) -> _T:
29 | ...
30 |
31 |
32 | @overload
33 | def classopt(
34 | cls: "Type[_C]",
35 | default_long: bool = False,
36 | default_short: bool = False,
37 | ) -> "Union[Type[_C], Type[_ClassOptGeneric[_C]]]":
38 | ...
39 |
40 |
41 | @overload
42 | def classopt(
43 | cls: "Literal[None]" = None,
44 | default_long: bool = False,
45 | default_short: bool = False,
46 | ) -> "Callable[[Type[_C]], Union[Type[_C], Type[_ClassOptGeneric[_C]]]]":
47 | ...
48 |
49 |
50 | def classopt(cls=None, default_long=False, default_short=False, parser=None):
51 | def wrap(cls):
52 | return _process_class(cls, default_long, default_short, parser)
53 |
54 | if cls is None:
55 | return wrap
56 |
57 | return wrap(cls)
58 |
59 |
60 | def _process_class(cls, default_long: bool, default_short: bool, external_parser: ArgumentParser):
61 | @classmethod
62 | def from_args(cls, args: Optional[List[str]] = None):
63 | parser = external_parser if external_parser is not None else ArgumentParser()
64 |
65 | for arg_name, arg_field in cls.__dataclass_fields__.items():
66 | kwargs = {}
67 | kwargs.update(arg_field.metadata)
68 | kwargs["type"] = arg_field.type
69 | kwargs.pop("long", None)
70 | kwargs.pop("short", None)
71 |
72 | name_or_flags = []
73 | if isinstance(arg_field.metadata.get("long"), bool):
74 | if arg_field.metadata.get("long"):
75 | name_or_flags.append(f"--{arg_name}")
76 | elif default_long:
77 | name_or_flags.append(f"--{arg_name}")
78 |
79 | if isinstance(arg_field.metadata.get("short"), str):
80 | name_or_flags.append(arg_field.metadata.get("short"))
81 | elif isinstance(arg_field.metadata.get("short"), bool):
82 | if arg_field.metadata.get("short"):
83 | name_or_flags.append(f"-{arg_name[0]}")
84 | elif default_short:
85 | name_or_flags.append(f"-{arg_name[0]}")
86 |
87 | if len(name_or_flags) == 0:
88 | name_or_flags.append(arg_name)
89 |
90 | if "action" in arg_field.metadata:
91 | kwargs.pop("type")
92 | elif arg_field.type == bool:
93 | kwargs.pop("type")
94 | kwargs["action"] = "store_true"
95 |
96 | if (
97 | arg_field.default == MISSING and arg_field.default_factory == MISSING
98 | ) or arg_field.default is None:
99 | kwargs["default"] = None
100 | elif arg_field.default != MISSING:
101 | kwargs["default"] = arg_field.type(arg_field.default)
102 | elif arg_field.default_factory != MISSING:
103 | kwargs["default"] = arg_field.type(arg_field.default_factory())
104 |
105 | if type(arg_field.type) in GENERIC_ALIASES and arg_field.type.__origin__ == list:
106 | kwargs["type"] = arg_field.type.__args__[0]
107 | if not "nargs" in arg_field.metadata:
108 | kwargs["nargs"] = "*"
109 |
110 | if "type" in arg_field.metadata:
111 | kwargs["type"] = arg_field.metadata["type"]
112 |
113 | parser.add_argument(*name_or_flags, **kwargs)
114 |
115 | ns = parser.parse_args(args=args)
116 | return cls(**vars(ns))
117 |
118 | for arg_name in cls.__annotations__.keys():
119 | if not hasattr(cls, arg_name):
120 | setattr(cls, arg_name, None)
121 | elif not isinstance(getattr(cls, arg_name), Field):
122 | setattr(cls, arg_name, config(default=getattr(cls, arg_name)))
123 |
124 | setattr(cls, "from_args", from_args)
125 |
126 | def to_dict(self):
127 | def classopt_dict_factory(items: List[Tuple[str, Any]]) -> Dict[str, Any]:
128 | converted_dict = {key: convert_non_primitives_to_string(value) for key, value in items}
129 |
130 | return converted_dict
131 |
132 | return asdict(self, dict_factory=classopt_dict_factory)
133 |
134 | setattr(cls, "to_dict", to_dict)
135 |
136 | @classmethod
137 | def from_dict(cls, data: dict):
138 | reverted_data = {
139 | key: revert_non_primitives_from_string(value, original_type=cls.__annotations__[key])
140 | for key, value in data.items()
141 | if key in cls.__annotations__
142 | }
143 |
144 | default_data = {}
145 | for arg_name, arg_field in cls.__dataclass_fields__.items():
146 | if arg_name in reverted_data:
147 | continue
148 | if (
149 | arg_field.default == MISSING and arg_field.default_factory == MISSING
150 | ) or arg_field.default is None:
151 | default_data[arg_name] = None
152 | elif arg_field.default != MISSING:
153 | default_data[arg_name] = arg_field.type(arg_field.default)
154 | elif arg_field.default_factory != MISSING:
155 | type_ = arg_field.type.__origin__
156 | default_data[arg_name] = type_(arg_field.default_factory())
157 |
158 | return cls(**default_data, **reverted_data)
159 |
160 | setattr(cls, "from_dict", from_dict)
161 |
162 | return dataclass(cls)
163 |
--------------------------------------------------------------------------------
/classopt/inheritance.py:
--------------------------------------------------------------------------------
1 | import typing
2 | from argparse import ArgumentParser
3 | from dataclasses import dataclass
4 | from typing import TypeVar, Optional, List
5 |
6 | T = TypeVar("T")
7 |
8 |
9 | class ClassOpt:
10 | @classmethod
11 | def _parser_factory(cls: T) -> ArgumentParser:
12 | return ArgumentParser()
13 |
14 | @classmethod
15 | def from_args(cls: T, args: Optional[List[str]] = None) -> T:
16 | parser = cls._parser_factory()
17 |
18 | for arg_name, arg_type in cls.__annotations__.items():
19 | kwargs = {}
20 |
21 | metadata = {}
22 | if hasattr(cls, arg_name):
23 | metadata.update(getattr(cls, arg_name).metadata)
24 |
25 | kwargs.update(metadata)
26 | kwargs["type"] = arg_type
27 | kwargs.pop("long", None)
28 | kwargs.pop("short", None)
29 |
30 | name_or_flags = []
31 | if isinstance(metadata.get("long"), bool):
32 | if metadata.get("long"):
33 | name_or_flags.append(f"--{arg_name}")
34 |
35 | if isinstance(metadata.get("short"), str):
36 | name_or_flags.append(metadata.get("short"))
37 | elif isinstance(metadata.get("short"), bool):
38 | if metadata.get("short"):
39 | name_or_flags.append(f"-{arg_name[0]}")
40 |
41 | if len(name_or_flags) == 0:
42 | name_or_flags.append(arg_name)
43 |
44 | if "action" in metadata:
45 | kwargs.pop("type")
46 | elif arg_type == bool:
47 | kwargs.pop("type")
48 | kwargs["action"] = "store_true"
49 |
50 | generic_aliases = [typing._GenericAlias]
51 | try:
52 | from types import GenericAlias
53 |
54 | generic_aliases.append(GenericAlias)
55 | except ImportError:
56 | pass
57 | if type(arg_type) in generic_aliases and arg_type.__origin__ == list:
58 | kwargs["type"] = arg_type.__args__[0]
59 | if not "nargs" in metadata:
60 | kwargs["nargs"] = "*"
61 |
62 | if "type" in metadata:
63 | kwargs["type"] = metadata["type"]
64 |
65 | parser.add_argument(*name_or_flags, **kwargs)
66 |
67 | ns = parser.parse_args(args=args)
68 |
69 | return dataclass(cls)(**vars(ns))
70 |
--------------------------------------------------------------------------------
/classopt/utils.py:
--------------------------------------------------------------------------------
1 | import typing
2 | from typing import Any, Union
3 |
4 | GENERIC_ALIASES = {typing._GenericAlias}
5 | try:
6 | from types import GenericAlias
7 |
8 | GENERIC_ALIASES.add(GenericAlias)
9 | except ImportError:
10 | pass
11 |
12 |
13 | PRIMITIVE_TYPES = {
14 | int,
15 | float,
16 | complex,
17 | bool,
18 | str,
19 | list,
20 | tuple,
21 | set,
22 | dict,
23 | type(None),
24 | }
25 |
26 |
27 | def revert_type_from_generic(type_: type) -> type:
28 | if type(type_) in GENERIC_ALIASES:
29 | return type_.__origin__
30 | else:
31 | return type_
32 |
33 |
34 | def convert_non_primitives_to_string(value: Any) -> Union[Any, str]:
35 | if revert_type_from_generic(type(value)) in PRIMITIVE_TYPES:
36 | return value
37 | else:
38 | return str(value)
39 |
40 |
41 | def revert_non_primitives_from_string(value: Any, original_type: type):
42 | if revert_type_from_generic(original_type) in PRIMITIVE_TYPES:
43 | return value
44 | else:
45 | return original_type(value)
46 |
--------------------------------------------------------------------------------
/examples/head_decorator.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | from classopt import classopt, config
4 |
5 |
6 | @classopt(default_long=True, default_short=True)
7 | class Opt:
8 | input_file: Path = config(long=False, short=False)
9 | lines: int = config(short="-n", default=10, help="print the first LINES lines")
10 | index: bool = config(help="number all output lines")
11 |
12 |
13 | def main(opt: Opt):
14 | prefix_width = len(str(opt.lines))
15 | with opt.input_file.open() as f:
16 | for line_number, line in enumerate(f, 1):
17 | prefix = f"{str(line_number).rjust(prefix_width)}: " if opt.index else ""
18 | print(f"{prefix}{line}", end="")
19 | if line_number >= opt.lines:
20 | break
21 |
22 |
23 | if __name__ == "__main__":
24 | opt = Opt.from_args()
25 | main(opt)
26 |
--------------------------------------------------------------------------------
/examples/head_inheritance.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | from classopt import ClassOpt, config
4 |
5 |
6 | class Opt(ClassOpt):
7 | input_file: Path
8 | lines: int = config(
9 | long=True, short="-n", default=10, help="print the first LINES lines"
10 | )
11 | index: bool = config(long=True, short=True, help="number all output lines")
12 |
13 |
14 | def main(opt: Opt):
15 | prefix_width = len(str(opt.lines))
16 | with opt.input_file.open() as f:
17 | for line_number, line in enumerate(f, 1):
18 | prefix = f"{str(line_number).rjust(prefix_width)}: " if opt.index else ""
19 | print(f"{prefix}{line}", end="")
20 | if line_number >= opt.lines:
21 | break
22 |
23 |
24 | if __name__ == "__main__":
25 | opt = Opt.from_args()
26 | main(opt)
27 |
--------------------------------------------------------------------------------
/examples/poetry.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "appdirs"
3 | version = "1.4.4"
4 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
5 | category = "dev"
6 | optional = false
7 | python-versions = "*"
8 |
9 | [[package]]
10 | name = "atomicwrites"
11 | version = "1.4.0"
12 | description = "Atomic file writes."
13 | category = "dev"
14 | optional = false
15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
16 |
17 | [[package]]
18 | name = "attrs"
19 | version = "21.2.0"
20 | description = "Classes Without Boilerplate"
21 | category = "dev"
22 | optional = false
23 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
24 |
25 | [package.extras]
26 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
27 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
28 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
29 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
30 |
31 | [[package]]
32 | name = "black"
33 | version = "21.6b0"
34 | description = "The uncompromising code formatter."
35 | category = "dev"
36 | optional = false
37 | python-versions = ">=3.6.2"
38 |
39 | [package.dependencies]
40 | appdirs = "*"
41 | click = ">=7.1.2"
42 | mypy-extensions = ">=0.4.3"
43 | pathspec = ">=0.8.1,<1"
44 | regex = ">=2020.1.8"
45 | toml = ">=0.10.1"
46 |
47 | [package.extras]
48 | colorama = ["colorama (>=0.4.3)"]
49 | d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"]
50 | python2 = ["typed-ast (>=1.4.2)"]
51 | uvloop = ["uvloop (>=0.15.2)"]
52 |
53 | [[package]]
54 | name = "classopt"
55 | version = "0.1.7"
56 | description = "Arguments parser with class for Python, inspired by StructOpt"
57 | category = "main"
58 | optional = false
59 | python-versions = ">=3.7,<4.0"
60 |
61 | [[package]]
62 | name = "click"
63 | version = "8.0.1"
64 | description = "Composable command line interface toolkit"
65 | category = "dev"
66 | optional = false
67 | python-versions = ">=3.6"
68 |
69 | [package.dependencies]
70 | colorama = {version = "*", markers = "platform_system == \"Windows\""}
71 |
72 | [[package]]
73 | name = "colorama"
74 | version = "0.4.4"
75 | description = "Cross-platform colored terminal text."
76 | category = "dev"
77 | optional = false
78 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
79 |
80 | [[package]]
81 | name = "more-itertools"
82 | version = "8.8.0"
83 | description = "More routines for operating on iterables, beyond itertools"
84 | category = "dev"
85 | optional = false
86 | python-versions = ">=3.5"
87 |
88 | [[package]]
89 | name = "mypy-extensions"
90 | version = "0.4.3"
91 | description = "Experimental type system extensions for programs checked with the mypy typechecker."
92 | category = "dev"
93 | optional = false
94 | python-versions = "*"
95 |
96 | [[package]]
97 | name = "packaging"
98 | version = "21.0"
99 | description = "Core utilities for Python packages"
100 | category = "dev"
101 | optional = false
102 | python-versions = ">=3.6"
103 |
104 | [package.dependencies]
105 | pyparsing = ">=2.0.2"
106 |
107 | [[package]]
108 | name = "pathspec"
109 | version = "0.8.1"
110 | description = "Utility library for gitignore style pattern matching of file paths."
111 | category = "dev"
112 | optional = false
113 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
114 |
115 | [[package]]
116 | name = "pluggy"
117 | version = "0.13.1"
118 | description = "plugin and hook calling mechanisms for python"
119 | category = "dev"
120 | optional = false
121 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
122 |
123 | [package.extras]
124 | dev = ["pre-commit", "tox"]
125 |
126 | [[package]]
127 | name = "py"
128 | version = "1.10.0"
129 | description = "library with cross-python path, ini-parsing, io, code, log facilities"
130 | category = "dev"
131 | optional = false
132 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
133 |
134 | [[package]]
135 | name = "pyparsing"
136 | version = "2.4.7"
137 | description = "Python parsing module"
138 | category = "dev"
139 | optional = false
140 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
141 |
142 | [[package]]
143 | name = "pytest"
144 | version = "5.4.3"
145 | description = "pytest: simple powerful testing with Python"
146 | category = "dev"
147 | optional = false
148 | python-versions = ">=3.5"
149 |
150 | [package.dependencies]
151 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
152 | attrs = ">=17.4.0"
153 | colorama = {version = "*", markers = "sys_platform == \"win32\""}
154 | more-itertools = ">=4.0.0"
155 | packaging = "*"
156 | pluggy = ">=0.12,<1.0"
157 | py = ">=1.5.0"
158 | wcwidth = "*"
159 |
160 | [package.extras]
161 | checkqa-mypy = ["mypy (==v0.761)"]
162 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
163 |
164 | [[package]]
165 | name = "regex"
166 | version = "2021.7.6"
167 | description = "Alternative regular expression module, to replace re."
168 | category = "dev"
169 | optional = false
170 | python-versions = "*"
171 |
172 | [[package]]
173 | name = "toml"
174 | version = "0.10.2"
175 | description = "Python Library for Tom's Obvious, Minimal Language"
176 | category = "dev"
177 | optional = false
178 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
179 |
180 | [[package]]
181 | name = "wcwidth"
182 | version = "0.2.5"
183 | description = "Measures the displayed width of unicode strings in a terminal"
184 | category = "dev"
185 | optional = false
186 | python-versions = "*"
187 |
188 | [metadata]
189 | lock-version = "1.1"
190 | python-versions = "^3.9"
191 | content-hash = "41d377884e10958c8a0df4c8527f82949f039ae178b45a2c930f6cbf02776a62"
192 |
193 | [metadata.files]
194 | appdirs = [
195 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
196 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
197 | ]
198 | atomicwrites = [
199 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
200 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
201 | ]
202 | attrs = [
203 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
204 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
205 | ]
206 | black = [
207 | {file = "black-21.6b0-py3-none-any.whl", hash = "sha256:dfb8c5a069012b2ab1e972e7b908f5fb42b6bbabcba0a788b86dc05067c7d9c7"},
208 | {file = "black-21.6b0.tar.gz", hash = "sha256:dc132348a88d103016726fe360cb9ede02cecf99b76e3660ce6c596be132ce04"},
209 | ]
210 | classopt = [
211 | {file = "classopt-0.1.7-py3-none-any.whl", hash = "sha256:28b57ba45b41049640e0fe21aa6ae073627973200486c1715c573ffb88e98f7d"},
212 | {file = "classopt-0.1.7.tar.gz", hash = "sha256:164907ce9bed2917b9c8fbc13060d21dd91c7a5d662476ad326a51f9594c5969"},
213 | ]
214 | click = [
215 | {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
216 | {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
217 | ]
218 | colorama = [
219 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
220 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
221 | ]
222 | more-itertools = [
223 | {file = "more-itertools-8.8.0.tar.gz", hash = "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a"},
224 | {file = "more_itertools-8.8.0-py3-none-any.whl", hash = "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d"},
225 | ]
226 | mypy-extensions = [
227 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
228 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
229 | ]
230 | packaging = [
231 | {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
232 | {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
233 | ]
234 | pathspec = [
235 | {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
236 | {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
237 | ]
238 | pluggy = [
239 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
240 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
241 | ]
242 | py = [
243 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
244 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
245 | ]
246 | pyparsing = [
247 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
248 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
249 | ]
250 | pytest = [
251 | {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
252 | {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
253 | ]
254 | regex = [
255 | {file = "regex-2021.7.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e6a1e5ca97d411a461041d057348e578dc344ecd2add3555aedba3b408c9f874"},
256 | {file = "regex-2021.7.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6afe6a627888c9a6cfbb603d1d017ce204cebd589d66e0703309b8048c3b0854"},
257 | {file = "regex-2021.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ccb3d2190476d00414aab36cca453e4596e8f70a206e2aa8db3d495a109153d2"},
258 | {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:ed693137a9187052fc46eedfafdcb74e09917166362af4cc4fddc3b31560e93d"},
259 | {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99d8ab206a5270c1002bfcf25c51bf329ca951e5a169f3b43214fdda1f0b5f0d"},
260 | {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:b85ac458354165405c8a84725de7bbd07b00d9f72c31a60ffbf96bb38d3e25fa"},
261 | {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3f5716923d3d0bfb27048242a6e0f14eecdb2e2a7fac47eda1d055288595f222"},
262 | {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5983c19d0beb6af88cb4d47afb92d96751fb3fa1784d8785b1cdf14c6519407"},
263 | {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf1d2d183abc7faa101ebe0b8d04fd19cb9138820abc8589083035c9440b8ca6"},
264 | {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1947e7de155063e1c495c50590229fb98720d4c383af5031bbcb413db33fa1be"},
265 | {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17d8a3f99b18d87ac54a449b836d485cc8c195bb6f5e4379c84c8519045facc9"},
266 | {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d30895ec80cc80358392841add9dde81ea1d54a4949049269115e6b0555d0498"},
267 | {file = "regex-2021.7.6-cp36-cp36m-win32.whl", hash = "sha256:c92831dac113a6e0ab28bc98f33781383fe294df1a2c3dfd1e850114da35fd5b"},
268 | {file = "regex-2021.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:791aa1b300e5b6e5d597c37c346fb4d66422178566bbb426dd87eaae475053fb"},
269 | {file = "regex-2021.7.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59506c6e8bd9306cd8a41511e32d16d5d1194110b8cfe5a11d102d8b63cf945d"},
270 | {file = "regex-2021.7.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:564a4c8a29435d1f2256ba247a0315325ea63335508ad8ed938a4f14c4116a5d"},
271 | {file = "regex-2021.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:59c00bb8dd8775473cbfb967925ad2c3ecc8886b3b2d0c90a8e2707e06c743f0"},
272 | {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9a854b916806c7e3b40e6616ac9e85d3cdb7649d9e6590653deb5b341a736cec"},
273 | {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:db2b7df831c3187a37f3bb80ec095f249fa276dbe09abd3d35297fc250385694"},
274 | {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:173bc44ff95bc1e96398c38f3629d86fa72e539c79900283afa895694229fe6a"},
275 | {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:15dddb19823f5147e7517bb12635b3c82e6f2a3a6b696cc3e321522e8b9308ad"},
276 | {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ddeabc7652024803666ea09f32dd1ed40a0579b6fbb2a213eba590683025895"},
277 | {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8244c681018423a0d1784bc6b9af33bdf55f2ab8acb1f3cd9dd83d90e0813253"},
278 | {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a4c742089faf0e51469c6a1ad7e3d3d21afae54a16a6cead85209dfe0a1ce65"},
279 | {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914e626dc8e75fe4fc9b7214763f141d9f40165d00dfe680b104fa1b24063bbf"},
280 | {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fabb19c82ecf39832a3f5060dfea9a7ab270ef156039a1143a29a83a09a62de"},
281 | {file = "regex-2021.7.6-cp37-cp37m-win32.whl", hash = "sha256:f080248b3e029d052bf74a897b9d74cfb7643537fbde97fe8225a6467fb559b5"},
282 | {file = "regex-2021.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d8bbce0c96462dbceaa7ac4a7dfbbee92745b801b24bce10a98d2f2b1ea9432f"},
283 | {file = "regex-2021.7.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edd1a68f79b89b0c57339bce297ad5d5ffcc6ae7e1afdb10f1947706ed066c9c"},
284 | {file = "regex-2021.7.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:422dec1e7cbb2efbbe50e3f1de36b82906def93ed48da12d1714cabcd993d7f0"},
285 | {file = "regex-2021.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cbe23b323988a04c3e5b0c387fe3f8f363bf06c0680daf775875d979e376bd26"},
286 | {file = "regex-2021.7.6-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:0eb2c6e0fcec5e0f1d3bcc1133556563222a2ffd2211945d7b1480c1b1a42a6f"},
287 | {file = "regex-2021.7.6-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1c78780bf46d620ff4fff40728f98b8afd8b8e35c3efd638c7df67be2d5cddbf"},
288 | {file = "regex-2021.7.6-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bc84fb254a875a9f66616ed4538542fb7965db6356f3df571d783f7c8d256edd"},
289 | {file = "regex-2021.7.6-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:598c0a79b4b851b922f504f9f39a863d83ebdfff787261a5ed061c21e67dd761"},
290 | {file = "regex-2021.7.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875c355360d0f8d3d827e462b29ea7682bf52327d500a4f837e934e9e4656068"},
291 | {file = "regex-2021.7.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfc0957c4a4b91eff5ad036088769e600a25774256cd0e1154378591ce573f08"},
292 | {file = "regex-2021.7.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efb4af05fa4d2fc29766bf516f1f5098d6b5c3ed846fde980c18bf8646ad3979"},
293 | {file = "regex-2021.7.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7423aca7cc30a6228ccdcf2ea76f12923d652c5c7c6dc1959a0b004e308f39fb"},
294 | {file = "regex-2021.7.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb9834c1e77493efd7343b8e38950dee9797d2d6f2d5fd91c008dfaef64684b9"},
295 | {file = "regex-2021.7.6-cp38-cp38-win32.whl", hash = "sha256:e586f448df2bbc37dfadccdb7ccd125c62b4348cb90c10840d695592aa1b29e0"},
296 | {file = "regex-2021.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:2fe5e71e11a54e3355fa272137d521a40aace5d937d08b494bed4529964c19c4"},
297 | {file = "regex-2021.7.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6110bab7eab6566492618540c70edd4d2a18f40ca1d51d704f1d81c52d245026"},
298 | {file = "regex-2021.7.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4f64fc59fd5b10557f6cd0937e1597af022ad9b27d454e182485f1db3008f417"},
299 | {file = "regex-2021.7.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:89e5528803566af4df368df2d6f503c84fbfb8249e6631c7b025fe23e6bd0cde"},
300 | {file = "regex-2021.7.6-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2366fe0479ca0e9afa534174faa2beae87847d208d457d200183f28c74eaea59"},
301 | {file = "regex-2021.7.6-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f9392a4555f3e4cb45310a65b403d86b589adc773898c25a39184b1ba4db8985"},
302 | {file = "regex-2021.7.6-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:2bceeb491b38225b1fee4517107b8491ba54fba77cf22a12e996d96a3c55613d"},
303 | {file = "regex-2021.7.6-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:f98dc35ab9a749276f1a4a38ab3e0e2ba1662ce710f6530f5b0a6656f1c32b58"},
304 | {file = "regex-2021.7.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:319eb2a8d0888fa6f1d9177705f341bc9455a2c8aca130016e52c7fe8d6c37a3"},
305 | {file = "regex-2021.7.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598ee917dbe961dcf827217bf2466bb86e4ee5a8559705af57cbabb3489dd37e"},
306 | {file = "regex-2021.7.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:56fc7045a1999a8d9dd1896715bc5c802dfec5b9b60e883d2cbdecb42adedea4"},
307 | {file = "regex-2021.7.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8363ac90ea63c3dd0872dfdb695f38aff3334bfa5712cffb238bd3ffef300e3"},
308 | {file = "regex-2021.7.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:716a6db91b3641f566531ffcc03ceec00b2447f0db9942b3c6ea5d2827ad6be3"},
309 | {file = "regex-2021.7.6-cp39-cp39-win32.whl", hash = "sha256:eaf58b9e30e0e546cdc3ac06cf9165a1ca5b3de8221e9df679416ca667972035"},
310 | {file = "regex-2021.7.6-cp39-cp39-win_amd64.whl", hash = "sha256:4c9c3155fe74269f61e27617529b7f09552fbb12e44b1189cebbdb24294e6e1c"},
311 | {file = "regex-2021.7.6.tar.gz", hash = "sha256:8394e266005f2d8c6f0bc6780001f7afa3ef81a7a2111fa35058ded6fce79e4d"},
312 | ]
313 | toml = [
314 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
315 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
316 | ]
317 | wcwidth = [
318 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
319 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
320 | ]
321 |
--------------------------------------------------------------------------------
/examples/print_args.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | from classopt import classopt, config
4 |
5 |
6 | @classopt(default_long=True, default_short=True)
7 | class Opt:
8 | a: int = 0
9 | b: int = config(default=1)
10 | c: Path = Path("./tmp.txt")
11 | d: list = config(default=[0, 1, 2], nargs="+", type=int)
12 |
13 |
14 | if __name__ == "__main__":
15 | opt: Opt = Opt.from_args()
16 | print(opt)
17 |
--------------------------------------------------------------------------------
/examples/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "examples"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["moisutsu "]
6 |
7 | [tool.poetry.dependencies]
8 | python = "^3.9"
9 | classopt = "^0.1.7"
10 |
11 | [tool.poetry.dev-dependencies]
12 | pytest = "^5.2"
13 | black = "^21.6b0"
14 |
15 | [build-system]
16 | requires = ["poetry-core>=1.0.0"]
17 | build-backend = "poetry.core.masonry.api"
18 |
--------------------------------------------------------------------------------
/poetry.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "appdirs"
3 | version = "1.4.4"
4 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
5 | category = "dev"
6 | optional = false
7 | python-versions = "*"
8 |
9 | [[package]]
10 | name = "atomicwrites"
11 | version = "1.4.0"
12 | description = "Atomic file writes."
13 | category = "dev"
14 | optional = false
15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
16 |
17 | [[package]]
18 | name = "attrs"
19 | version = "21.2.0"
20 | description = "Classes Without Boilerplate"
21 | category = "dev"
22 | optional = false
23 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
24 |
25 | [package.extras]
26 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
27 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
28 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
29 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
30 |
31 | [[package]]
32 | name = "black"
33 | version = "21.6b0"
34 | description = "The uncompromising code formatter."
35 | category = "dev"
36 | optional = false
37 | python-versions = ">=3.6.2"
38 |
39 | [package.dependencies]
40 | appdirs = "*"
41 | click = ">=7.1.2"
42 | mypy-extensions = ">=0.4.3"
43 | pathspec = ">=0.8.1,<1"
44 | regex = ">=2020.1.8"
45 | toml = ">=0.10.1"
46 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""}
47 | typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
48 |
49 | [package.extras]
50 | colorama = ["colorama (>=0.4.3)"]
51 | d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"]
52 | python2 = ["typed-ast (>=1.4.2)"]
53 | uvloop = ["uvloop (>=0.15.2)"]
54 |
55 | [[package]]
56 | name = "click"
57 | version = "8.0.1"
58 | description = "Composable command line interface toolkit"
59 | category = "dev"
60 | optional = false
61 | python-versions = ">=3.6"
62 |
63 | [package.dependencies]
64 | colorama = {version = "*", markers = "platform_system == \"Windows\""}
65 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
66 |
67 | [[package]]
68 | name = "colorama"
69 | version = "0.4.4"
70 | description = "Cross-platform colored terminal text."
71 | category = "dev"
72 | optional = false
73 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
74 |
75 | [[package]]
76 | name = "importlib-metadata"
77 | version = "4.6.0"
78 | description = "Read metadata from Python packages"
79 | category = "dev"
80 | optional = false
81 | python-versions = ">=3.6"
82 |
83 | [package.dependencies]
84 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
85 | zipp = ">=0.5"
86 |
87 | [package.extras]
88 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
89 | perf = ["ipython"]
90 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
91 |
92 | [[package]]
93 | name = "iniconfig"
94 | version = "1.1.1"
95 | description = "iniconfig: brain-dead simple config-ini parsing"
96 | category = "dev"
97 | optional = false
98 | python-versions = "*"
99 |
100 | [[package]]
101 | name = "mypy-extensions"
102 | version = "0.4.3"
103 | description = "Experimental type system extensions for programs checked with the mypy typechecker."
104 | category = "dev"
105 | optional = false
106 | python-versions = "*"
107 |
108 | [[package]]
109 | name = "packaging"
110 | version = "21.0"
111 | description = "Core utilities for Python packages"
112 | category = "dev"
113 | optional = false
114 | python-versions = ">=3.6"
115 |
116 | [package.dependencies]
117 | pyparsing = ">=2.0.2"
118 |
119 | [[package]]
120 | name = "pathspec"
121 | version = "0.8.1"
122 | description = "Utility library for gitignore style pattern matching of file paths."
123 | category = "dev"
124 | optional = false
125 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
126 |
127 | [[package]]
128 | name = "pluggy"
129 | version = "0.13.1"
130 | description = "plugin and hook calling mechanisms for python"
131 | category = "dev"
132 | optional = false
133 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
134 |
135 | [package.dependencies]
136 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
137 |
138 | [package.extras]
139 | dev = ["pre-commit", "tox"]
140 |
141 | [[package]]
142 | name = "py"
143 | version = "1.10.0"
144 | description = "library with cross-python path, ini-parsing, io, code, log facilities"
145 | category = "dev"
146 | optional = false
147 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
148 |
149 | [[package]]
150 | name = "pyparsing"
151 | version = "2.4.7"
152 | description = "Python parsing module"
153 | category = "dev"
154 | optional = false
155 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
156 |
157 | [[package]]
158 | name = "pytest"
159 | version = "6.2.4"
160 | description = "pytest: simple powerful testing with Python"
161 | category = "dev"
162 | optional = false
163 | python-versions = ">=3.6"
164 |
165 | [package.dependencies]
166 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
167 | attrs = ">=19.2.0"
168 | colorama = {version = "*", markers = "sys_platform == \"win32\""}
169 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
170 | iniconfig = "*"
171 | packaging = "*"
172 | pluggy = ">=0.12,<1.0.0a1"
173 | py = ">=1.8.2"
174 | toml = "*"
175 |
176 | [package.extras]
177 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
178 |
179 | [[package]]
180 | name = "regex"
181 | version = "2021.7.1"
182 | description = "Alternative regular expression module, to replace re."
183 | category = "dev"
184 | optional = false
185 | python-versions = "*"
186 |
187 | [[package]]
188 | name = "toml"
189 | version = "0.10.2"
190 | description = "Python Library for Tom's Obvious, Minimal Language"
191 | category = "dev"
192 | optional = false
193 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
194 |
195 | [[package]]
196 | name = "typed-ast"
197 | version = "1.4.3"
198 | description = "a fork of Python 2 and 3 ast modules with type comment support"
199 | category = "dev"
200 | optional = false
201 | python-versions = "*"
202 |
203 | [[package]]
204 | name = "typing-extensions"
205 | version = "3.10.0.0"
206 | description = "Backported and Experimental Type Hints for Python 3.5+"
207 | category = "dev"
208 | optional = false
209 | python-versions = "*"
210 |
211 | [[package]]
212 | name = "zipp"
213 | version = "3.5.0"
214 | description = "Backport of pathlib-compatible object wrapper for zip files"
215 | category = "dev"
216 | optional = false
217 | python-versions = ">=3.6"
218 |
219 | [package.extras]
220 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
221 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
222 |
223 | [metadata]
224 | lock-version = "1.1"
225 | python-versions = "^3.7"
226 | content-hash = "db95c83fbfb5628534161185f94874dd07a84ab8db130198902859790231d6f5"
227 |
228 | [metadata.files]
229 | appdirs = [
230 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
231 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
232 | ]
233 | atomicwrites = [
234 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
235 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
236 | ]
237 | attrs = [
238 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
239 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
240 | ]
241 | black = [
242 | {file = "black-21.6b0-py3-none-any.whl", hash = "sha256:dfb8c5a069012b2ab1e972e7b908f5fb42b6bbabcba0a788b86dc05067c7d9c7"},
243 | {file = "black-21.6b0.tar.gz", hash = "sha256:dc132348a88d103016726fe360cb9ede02cecf99b76e3660ce6c596be132ce04"},
244 | ]
245 | click = [
246 | {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
247 | {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
248 | ]
249 | colorama = [
250 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
251 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
252 | ]
253 | importlib-metadata = [
254 | {file = "importlib_metadata-4.6.0-py3-none-any.whl", hash = "sha256:c6513572926a96458f8c8f725bf0e00108fba0c9583ade9bd15b869c9d726e33"},
255 | {file = "importlib_metadata-4.6.0.tar.gz", hash = "sha256:4a5611fea3768d3d967c447ab4e93f567d95db92225b43b7b238dbfb855d70bb"},
256 | ]
257 | iniconfig = [
258 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
259 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
260 | ]
261 | mypy-extensions = [
262 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
263 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
264 | ]
265 | packaging = [
266 | {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
267 | {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
268 | ]
269 | pathspec = [
270 | {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
271 | {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
272 | ]
273 | pluggy = [
274 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
275 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
276 | ]
277 | py = [
278 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
279 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
280 | ]
281 | pyparsing = [
282 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
283 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
284 | ]
285 | pytest = [
286 | {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"},
287 | {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"},
288 | ]
289 | regex = [
290 | {file = "regex-2021.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:494d0172774dc0beeea984b94c95389143db029575f7ca908edd74469321ea99"},
291 | {file = "regex-2021.7.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:8cf6728f89b071bd3ab37cb8a0e306f4de897553a0ed07442015ee65fbf53d62"},
292 | {file = "regex-2021.7.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1806370b2bef4d4193eebe8ee59a9fd7547836a34917b7badbe6561a8594d9cb"},
293 | {file = "regex-2021.7.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d0cf2651a8804f6325747c7e55e3be0f90ee2848e25d6b817aa2728d263f9abb"},
294 | {file = "regex-2021.7.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:268fe9dd1deb4a30c8593cabd63f7a241dfdc5bd9dd0233906c718db22cdd49a"},
295 | {file = "regex-2021.7.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:7743798dfb573d006f1143d745bf17efad39775a5190b347da5d83079646be56"},
296 | {file = "regex-2021.7.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0e46c1191b2eb293a6912269ed08b4512e7e241bbf591f97e527492e04c77e93"},
297 | {file = "regex-2021.7.1-cp36-cp36m-win32.whl", hash = "sha256:b1dbeef938281f240347d50f28ae53c4b046a23389cd1fc4acec5ea0eae646a1"},
298 | {file = "regex-2021.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6c72ebb72e64e9bd195cb35a9b9bbfb955fd953b295255b8ae3e4ad4a146b615"},
299 | {file = "regex-2021.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf819c5b77ff44accc9a24e31f1f7ceaaf6c960816913ed3ef8443b9d20d81b6"},
300 | {file = "regex-2021.7.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e80d2851109e56420b71f9702ad1646e2f0364528adbf6af85527bc61e49f394"},
301 | {file = "regex-2021.7.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a1b6a3f600d6aff97e3f28c34192c9ed93fee293bd96ef327b64adb51a74b2f6"},
302 | {file = "regex-2021.7.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ed77b97896312bc2deafe137ca2626e8b63808f5bedb944f73665c68093688a7"},
303 | {file = "regex-2021.7.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a548bb51c4476332ce4139df8e637386730f79a92652a907d12c696b6252b64d"},
304 | {file = "regex-2021.7.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:210c359e6ee5b83f7d8c529ba3c75ba405481d50f35a420609b0db827e2e3bb5"},
305 | {file = "regex-2021.7.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:1d386402ae7f3c9b107ae5863f7ecccb0167762c82a687ae6526b040feaa5ac6"},
306 | {file = "regex-2021.7.1-cp37-cp37m-win32.whl", hash = "sha256:5049d00dbb78f9d166d1c704e93934d42cce0570842bb1a61695123d6b01de09"},
307 | {file = "regex-2021.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:361be4d311ac995a8c7ad577025a3ae3a538531b1f2cf32efd8b7e5d33a13e5a"},
308 | {file = "regex-2021.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f32f47fb22c988c0b35756024b61d156e5c4011cb8004aa53d93b03323c45657"},
309 | {file = "regex-2021.7.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b024ee43ee6b310fad5acaee23e6485b21468718cb792a9d1693eecacc3f0b7e"},
310 | {file = "regex-2021.7.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b092754c06852e8a8b022004aff56c24b06310189186805800d09313c37ce1f8"},
311 | {file = "regex-2021.7.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a8a5826d8a1b64e2ff9af488cc179e1a4d0f144d11ce486a9f34ea38ccedf4ef"},
312 | {file = "regex-2021.7.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:444723ebaeb7fa8125f29c01a31101a3854ac3de293e317944022ae5effa53a4"},
313 | {file = "regex-2021.7.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:fdad3122b69cdabdb3da4c2a4107875913ac78dab0117fc73f988ad589c66b66"},
314 | {file = "regex-2021.7.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4b1999ef60c45357598935c12508abf56edbbb9c380df6f336de38a6c3a294ae"},
315 | {file = "regex-2021.7.1-cp38-cp38-win32.whl", hash = "sha256:e07e92935040c67f49571779d115ecb3e727016d42fb36ee0d8757db4ca12ee0"},
316 | {file = "regex-2021.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:6b8b629f93246e507287ee07e26744beaffb4c56ed520576deac8b615bd76012"},
317 | {file = "regex-2021.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56bef6b414949e2c9acf96cb5d78de8b529c7b99752619494e78dc76f99fd005"},
318 | {file = "regex-2021.7.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:78a2a885345a2d60b5e68099e877757d5ed12e46ba1e87507175f14f80892af3"},
319 | {file = "regex-2021.7.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3f7a92e60930f8fca2623d9e326c173b7cf2c8b7e4fdcf984b75a1d2fb08114d"},
320 | {file = "regex-2021.7.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4fc86b729ab88fe8ac3ec92287df253c64aa71560d76da5acd8a2e245839c629"},
321 | {file = "regex-2021.7.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:59845101de68fd5d3a1145df9ea022e85ecd1b49300ea68307ad4302320f6f61"},
322 | {file = "regex-2021.7.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ce269e903b00d1ab4746793e9c50a57eec5d5388681abef074d7b9a65748fca5"},
323 | {file = "regex-2021.7.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c11f2fca544b5e30a0e813023196a63b1cb9869106ef9a26e9dae28bce3e4e26"},
324 | {file = "regex-2021.7.1-cp39-cp39-win32.whl", hash = "sha256:1ccbd41dbee3a31e18938096510b7d4ee53aa9fce2ee3dcc8ec82ae264f6acfd"},
325 | {file = "regex-2021.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:18040755606b0c21281493ec309214bd61e41a170509e5014f41d6a5a586e161"},
326 | {file = "regex-2021.7.1.tar.gz", hash = "sha256:849802379a660206277675aa5a5c327f5c910c690649535863ddf329b0ba8c87"},
327 | ]
328 | toml = [
329 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
330 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
331 | ]
332 | typed-ast = [
333 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"},
334 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"},
335 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"},
336 | {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"},
337 | {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"},
338 | {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"},
339 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"},
340 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"},
341 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"},
342 | {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"},
343 | {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"},
344 | {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"},
345 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"},
346 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"},
347 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"},
348 | {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"},
349 | {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"},
350 | {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"},
351 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"},
352 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"},
353 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"},
354 | {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"},
355 | {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"},
356 | {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"},
357 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"},
358 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"},
359 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"},
360 | {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"},
361 | {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"},
362 | {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
363 | ]
364 | typing-extensions = [
365 | {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
366 | {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
367 | {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"},
368 | ]
369 | zipp = [
370 | {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
371 | {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
372 | ]
373 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "classopt"
3 | version = "0.2.1"
4 | description = "Arguments parser with class for Python, inspired by StructOpt"
5 | authors = ["moisutsu "]
6 | readme = "README.md"
7 | repository = "https://github.com/moisutsu/classopt"
8 |
9 | [tool.poetry.dependencies]
10 | python = "^3.7"
11 |
12 | [tool.poetry.dev-dependencies]
13 | black = "^21.6b0"
14 | pytest = "^6.2.4"
15 |
16 | [build-system]
17 | requires = ["poetry-core>=1.0.0"]
18 | build-backend = "poetry.core.masonry.api"
19 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moisutsu/classopt/6d0781164696bc021b856768a56eaef22a2c0cd5/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | import pytest
4 |
5 |
6 | @pytest.fixture(scope="function", autouse=True)
7 | def cleanup_args():
8 | # Cleanup args before each test
9 | # otherwise tests fail with e.g. "pytest -s" because sys.argv[1:] becomes ["-s"]
10 | # and pytest options will be parsed by `ArgumentParser`
11 | del_args()
12 |
13 | # Run a test...
14 | yield
15 |
16 | # Cleanup args after each test
17 | del_args()
18 |
19 |
20 | def set_args(*args):
21 | del_args()
22 | for arg in args:
23 | sys.argv.append(arg)
24 |
25 |
26 | def del_args():
27 | del sys.argv[1:]
28 |
--------------------------------------------------------------------------------
/tests/test_decorator.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import unittest
3 | from typing import List
4 |
5 | import pytest
6 |
7 | from classopt import classopt, config
8 |
9 | from .conftest import del_args, set_args
10 |
11 |
12 | class TestClassOpt(unittest.TestCase):
13 | def test_classopt(self):
14 | @classopt
15 | class Opt:
16 | arg_int: int
17 | arg_str: str
18 | arg_float: float
19 |
20 | set_args("5", "hello", "3.2")
21 |
22 | opt = Opt.from_args()
23 |
24 | assert opt.arg_int == 5
25 | assert opt.arg_str == "hello"
26 | assert opt.arg_float == 3.2
27 |
28 | def test_advanced_usage(self):
29 | @classopt()
30 | class Opt:
31 | long_arg: str = config(long=True)
32 | short_arg1: str = config(long=True, short=True)
33 | short_arg2: str = config(long=True, short="-x")
34 | default_int: int = config(long=True, default=3)
35 | store_true: bool = config(long=True, action="store_true")
36 | nargs: List[int] = config(long=True, nargs="+", type=int)
37 |
38 | set_args(
39 | "--long_arg",
40 | "long_arg",
41 | "-s",
42 | "short_arg1",
43 | "-x",
44 | "short_arg2",
45 | "--store_true",
46 | "--nargs",
47 | "1",
48 | "2",
49 | "3",
50 | )
51 |
52 | opt = Opt.from_args()
53 |
54 | assert opt.long_arg == "long_arg"
55 | assert opt.short_arg1 == "short_arg1"
56 | assert opt.short_arg2 == "short_arg2"
57 | assert opt.default_int == 3
58 | assert opt.store_true
59 | assert opt.nargs == [1, 2, 3]
60 |
61 | def test_default_long(self):
62 | @classopt(default_long=True)
63 | class Opt:
64 | arg0: str = config(long=False)
65 | arg1: int
66 | arg2: str
67 |
68 | set_args("hogehoge", "--arg1", "3", "--arg2", "hello")
69 |
70 | opt = Opt.from_args()
71 |
72 | assert opt.arg0 == "hogehoge"
73 | assert opt.arg1 == 3
74 | assert opt.arg2 == "hello"
75 |
76 | def test_default_short(self):
77 | @classopt(default_long=True, default_short=True)
78 | class Opt:
79 | a_arg: int
80 | b_arg: str
81 |
82 | set_args("-a", "3", "-b", "hello")
83 |
84 | opt = Opt.from_args()
85 |
86 | assert opt.a_arg == 3
87 | assert opt.b_arg == "hello"
88 |
89 | def test_generic_alias(self):
90 | @classopt(default_long=True)
91 | class Opt:
92 | list_a: List[int] = config(nargs="+")
93 | list_b: List[str] = config(nargs="*")
94 |
95 | set_args("--list_a", "3", "2", "1", "--list_b", "hello", "world")
96 |
97 | opt = Opt.from_args()
98 |
99 | assert opt.list_a == [3, 2, 1]
100 | assert opt.list_b == ["hello", "world"]
101 |
102 | @pytest.mark.skipif(
103 | sys.version_info < (3, 9),
104 | reason="These version does not support `list` type with subscription.",
105 | )
106 | def test_generic_alias_for_python3_9_or_later(self):
107 | @classopt(default_long=True)
108 | class Opt:
109 | list_a: list[int] = config(nargs="+")
110 | list_b: list[str] = config(nargs="*")
111 |
112 | set_args("--list_a", "3", "2", "1", "--list_b", "hello", "world")
113 |
114 | opt = Opt.from_args()
115 |
116 | assert opt.list_a == [3, 2, 1]
117 | assert opt.list_b == ["hello", "world"]
118 |
119 | def test_default_value(self):
120 | @classopt(default_long=True)
121 | class Opt:
122 | numbers: List[int]
123 | flag: bool
124 |
125 | set_args("--numbers", "1", "2", "3", "--flag")
126 |
127 | opt = Opt.from_args()
128 |
129 | assert opt.numbers == [1, 2, 3]
130 | assert opt.flag
131 |
132 | def test_external_parser(self):
133 | from argparse import ArgumentParser
134 |
135 | class userArgumentParserException(Exception):
136 | pass
137 |
138 | class userArgumentParser(ArgumentParser):
139 | def error(self, message):
140 | raise userArgumentParserException()
141 |
142 | @classopt(parser=userArgumentParser())
143 | class Opt:
144 | arg_int: int
145 | arg_str: str
146 | arg_float: float
147 |
148 | set_args("5", "hello", "3.2")
149 |
150 | opt = Opt.from_args()
151 |
152 | assert opt.arg_int == 5
153 | assert opt.arg_str == "hello"
154 | assert opt.arg_float == 3.2
155 |
156 | set_args("5", "hello")
157 |
158 | with self.assertRaises(userArgumentParserException):
159 | opt = Opt.from_args()
160 |
161 | def test_simple_default_value_passing(self):
162 | @classopt(default_long=True)
163 | class Opt:
164 | arg0: int = 3
165 | arg1: list = ["hello", "world"]
166 | arg2: int
167 | arg3: int = config(default=5)
168 | arg4: list = config(default=[1, 2, 3])
169 | arg5: str
170 |
171 | set_args("--arg5", "hello")
172 |
173 | opt = Opt.from_args()
174 |
175 | assert opt.arg0 == 3
176 | assert opt.arg1 == ["hello", "world"]
177 | assert opt.arg2 == None
178 | assert opt.arg3 == 5
179 | assert opt.arg4 == [1, 2, 3]
180 | assert opt.arg5 == "hello"
181 |
182 | def test_convert_default_value_type_to_specified_type(self):
183 | from pathlib import Path
184 |
185 | @classopt(default_long=True)
186 | class Opt:
187 | arg0: Path = "test.py"
188 |
189 | set_args()
190 |
191 | opt = Opt.from_args()
192 |
193 | assert opt.arg0 == Path("test.py")
194 |
195 | def test_args_from_scipt(self):
196 | @classopt
197 | class Opt:
198 | arg_int: int
199 | arg_str: str
200 | arg_float: float
201 |
202 | set_args("5", "hello", "3.2")
203 |
204 | opt1 = Opt.from_args()
205 |
206 | del_args()
207 |
208 | opt2 = Opt.from_args(["5", "hello", "3.2"])
209 |
210 | assert opt1.arg_int == opt2.arg_int
211 | assert opt1.arg_str == opt2.arg_str
212 | assert opt1.arg_float == opt2.arg_float
213 |
214 | def test_to_dict(self):
215 | from pathlib import Path
216 | from typing import List
217 |
218 | @classopt
219 | class Opt:
220 | arg_int: int
221 | arg_float: float
222 | arg_path: Path
223 | arg_list: List[str]
224 |
225 | set_args("3", "3.2", "test.txt", "a", "b", "c")
226 |
227 | opt = Opt.from_args()
228 |
229 | opt_dict = opt.to_dict()
230 | correct_dict = {
231 | "arg_int": 3,
232 | "arg_float": 3.2,
233 | "arg_path": "test.txt",
234 | "arg_list": ["a", "b", "c"],
235 | }
236 |
237 | assert all(
238 | opt_dict[key] == correct_dict[key]
239 | for key in set(list(opt_dict.keys()) + list(correct_dict.keys()))
240 | )
241 |
242 | def test_from_dict(self):
243 | from pathlib import Path
244 | from typing import List
245 |
246 | @classopt
247 | class Opt:
248 | arg_int: int
249 | arg_float: float
250 | arg_path: Path
251 | arg_list: List[str]
252 |
253 | args_dict = {
254 | "arg_int": 3,
255 | "arg_float": 3.2,
256 | "arg_path": "test.txt",
257 | "arg_list": ["a", "b", "c"],
258 | }
259 |
260 | opt = Opt.from_dict(args_dict)
261 |
262 | assert opt.arg_int == args_dict["arg_int"]
263 | assert opt.arg_float == args_dict["arg_float"]
264 | assert opt.arg_path == Path(args_dict["arg_path"])
265 | assert opt.arg_list == args_dict["arg_list"]
266 |
267 | def test_from_dict_partial(self):
268 | from pathlib import Path
269 | from typing import List, Set
270 |
271 | @classopt
272 | class Opt:
273 | arg_int: int
274 | arg_float: float = 0.0
275 | arg_str: str = "test"
276 | arg_path: Path = "test.txt"
277 | arg_list: List[str] = ["a", "b"]
278 | # test for implicit type conversion
279 | arg_set: Set[str] = ["a", "b"]
280 |
281 | args_dict = {
282 | "arg_int": 3,
283 | "arg_float": 3.2,
284 | }
285 |
286 | opt = Opt.from_dict(args_dict)
287 |
288 | assert opt.arg_int == args_dict["arg_int"]
289 | assert opt.arg_float == args_dict["arg_float"]
290 | assert opt.arg_str == "test"
291 | assert opt.arg_path == Path("test.txt")
292 | assert opt.arg_list == ["a", "b"]
293 | assert opt.arg_set == {"a", "b"}
294 |
--------------------------------------------------------------------------------
/tests/test_inheritance.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from typing import List
3 |
4 | from classopt import ClassOpt, config
5 |
6 | from .conftest import del_args, set_args
7 |
8 |
9 | class TestClassOpt(unittest.TestCase):
10 | def test_classopt(self):
11 | class Opt(ClassOpt):
12 | arg_int: int
13 | arg_str: str
14 | arg_float: float
15 |
16 | set_args("5", "hello", "3.2")
17 |
18 | opt = Opt.from_args()
19 |
20 | assert opt.arg_int == 5
21 | assert opt.arg_str == "hello"
22 | assert opt.arg_float == 3.2
23 |
24 | def test_advanced_usage(self):
25 | class Opt(ClassOpt):
26 | long_arg: str = config(long=True)
27 | short_arg1: str = config(long=True, short=True)
28 | short_arg2: str = config(long=True, short="-x")
29 | default_int: int = config(long=True, default=3)
30 | store_true: bool = config(long=True, action="store_true")
31 | nargs: List[int] = config(long=True, nargs="+", type=int)
32 |
33 | set_args(
34 | "--long_arg",
35 | "long_arg",
36 | "-s",
37 | "short_arg1",
38 | "-x",
39 | "short_arg2",
40 | "--store_true",
41 | "--nargs",
42 | "1",
43 | "2",
44 | "3",
45 | )
46 |
47 | opt = Opt.from_args()
48 |
49 | assert opt.long_arg == "long_arg"
50 | assert opt.short_arg1 == "short_arg1"
51 | assert opt.short_arg2 == "short_arg2"
52 | assert opt.default_int == 3
53 | assert opt.store_true
54 | assert opt.nargs == [1, 2, 3]
55 |
56 | def test_generic_alias(self):
57 | class Opt(ClassOpt):
58 | list_a: List[int] = config(long=True, nargs="+")
59 | list_b: List[str] = config(long=True, nargs="*")
60 |
61 | set_args("--list_a", "3", "2", "1", "--list_b", "hello", "world")
62 |
63 | opt = Opt.from_args()
64 |
65 | assert opt.list_a == [3, 2, 1]
66 | assert opt.list_b == ["hello", "world"]
67 |
68 | def test_default_value(self):
69 | class Opt(ClassOpt):
70 | numbers: List[int] = config(long=True)
71 | flag: bool = config(long=True)
72 |
73 | set_args("--numbers", "1", "2", "3", "--flag")
74 |
75 | opt = Opt.from_args()
76 |
77 | assert opt.numbers == [1, 2, 3]
78 | assert opt.flag
79 |
80 | def test_external_parser(self):
81 | from argparse import ArgumentParser
82 |
83 | class userArgumentParserException(Exception):
84 | pass
85 |
86 | class userArgumentParser(ArgumentParser):
87 | def error(self, message):
88 | raise userArgumentParserException()
89 |
90 | class Opt(ClassOpt):
91 | arg_int: int
92 | arg_str: str
93 | arg_float: float
94 |
95 | @classmethod
96 | def _parser_factory(cls) -> ArgumentParser:
97 | return userArgumentParser()
98 |
99 | set_args("5", "hello", "3.2")
100 |
101 | opt = Opt.from_args()
102 |
103 | assert opt.arg_int == 5
104 | assert opt.arg_str == "hello"
105 | assert opt.arg_float == 3.2
106 |
107 | set_args("5", "hello")
108 |
109 | with self.assertRaises(userArgumentParserException):
110 | opt = Opt.from_args()
111 |
112 | def test_args_from_script(self):
113 | class Opt(ClassOpt):
114 | arg_int: int
115 | arg_str: str
116 | arg_float: float
117 |
118 | set_args("5", "hello", "3.2")
119 |
120 | opt1 = Opt.from_args()
121 |
122 | del_args()
123 |
124 | opt2 = Opt.from_args(["5", "hello", "3.2"])
125 |
126 | assert opt1.arg_int == opt2.arg_int
127 | assert opt1.arg_str == opt2.arg_str
128 | assert opt1.arg_float == opt2.arg_float
129 |
--------------------------------------------------------------------------------