acbox 0.0.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acbox-0.0.0b1/PKG-INFO +37 -0
- acbox-0.0.0b1/README.md +17 -0
- acbox-0.0.0b1/pyproject.toml +107 -0
- acbox-0.0.0b1/setup.cfg +4 -0
- acbox-0.0.0b1/src/acbox/cli/__init__.py +0 -0
- acbox-0.0.0b1/src/acbox/cli/shared.py +106 -0
- acbox-0.0.0b1/src/acbox/fileops.py +24 -0
- acbox-0.0.0b1/src/acbox/utils.py +22 -0
- acbox-0.0.0b1/src/acbox.egg-info/PKG-INFO +37 -0
- acbox-0.0.0b1/src/acbox.egg-info/SOURCES.txt +15 -0
- acbox-0.0.0b1/src/acbox.egg-info/dependency_links.txt +1 -0
- acbox-0.0.0b1/src/acbox.egg-info/requires.txt +2 -0
- acbox-0.0.0b1/src/acbox.egg-info/top_level.txt +1 -0
- acbox-0.0.0b1/tests/test_cli_shared.py +174 -0
- acbox-0.0.0b1/tests/test_fileops.py +18 -0
- acbox-0.0.0b1/tests/test_internals.py +21 -0
- acbox-0.0.0b1/tests/test_utils.py +13 -0
acbox-0.0.0b1/PKG-INFO
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: acbox
|
|
3
|
+
Version: 0.0.0b1
|
|
4
|
+
Summary: Collection of small tools
|
|
5
|
+
Author-email: Antonio Cavallo <a.cavallo@cavallinux.eu>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Source, https://github.com/cav71/acbox
|
|
8
|
+
Project-URL: Issues, https://github.com/cav71/acbox
|
|
9
|
+
Project-URL: Documentation, https://github.com/cav71/acbox
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Provides-Extra: extra
|
|
20
|
+
|
|
21
|
+
## ACBox my little toolbox
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Development
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
python3.13 -m venv .venv
|
|
28
|
+
source .venv/bin/activate
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
python -m pip install --group dev
|
|
34
|
+
pre-commit install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Ready.
|
acbox-0.0.0b1/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "acbox"
|
|
7
|
+
version = "0.0.0b1"
|
|
8
|
+
description = "Collection of small tools"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
|
|
12
|
+
requires-python = ">= 3.9"
|
|
13
|
+
|
|
14
|
+
authors = [
|
|
15
|
+
{ name = "Antonio Cavallo", email = "a.cavallo@cavallinux.eu" },
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Programming Language :: Python",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
dependencies = []
|
|
29
|
+
|
|
30
|
+
[dependency-groups]
|
|
31
|
+
dev = [
|
|
32
|
+
"build",
|
|
33
|
+
"lxml",
|
|
34
|
+
"mypy",
|
|
35
|
+
"pre-commit",
|
|
36
|
+
"pytest",
|
|
37
|
+
"pytest-asyncio",
|
|
38
|
+
"pytest-cov",
|
|
39
|
+
"pytest-html",
|
|
40
|
+
"ruff",
|
|
41
|
+
"types-python-dateutil",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
extra = []
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Source = "https://github.com/cav71/acbox"
|
|
50
|
+
Issues = "https://github.com/cav71/acbox"
|
|
51
|
+
Documentation = "https://github.com/cav71/acbox"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
where = ["src"]
|
|
55
|
+
namespaces = true
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
target-version = "py39"
|
|
59
|
+
line-length = 140
|
|
60
|
+
src = ["src/acbox"]
|
|
61
|
+
|
|
62
|
+
[tool.ruff.format]
|
|
63
|
+
quote-style = "double"
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
ignore = []
|
|
67
|
+
select = ["F", "E", "W", "Q", "I001"]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint.isort]
|
|
70
|
+
known-first-party = ["acbox"]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
[tool.mypy]
|
|
74
|
+
disallow_untyped_defs = false
|
|
75
|
+
follow_imports = "normal"
|
|
76
|
+
ignore_missing_imports = true
|
|
77
|
+
pretty = true
|
|
78
|
+
show_column_numbers = true
|
|
79
|
+
show_error_codes = true
|
|
80
|
+
warn_no_return = false
|
|
81
|
+
warn_unused_ignores = true
|
|
82
|
+
exclude = [
|
|
83
|
+
"docs/conf\\.py",
|
|
84
|
+
"^docs/\\.*",
|
|
85
|
+
"^build/\\.*",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.coverage.run]
|
|
89
|
+
branch = true
|
|
90
|
+
|
|
91
|
+
[tool.coverage.paths]
|
|
92
|
+
source = [
|
|
93
|
+
"src/",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
[tool.coverage.report]
|
|
97
|
+
exclude_lines = [
|
|
98
|
+
"no cov",
|
|
99
|
+
"if __name__ == .__main__.:",
|
|
100
|
+
"if TYPE_CHECKING:",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.pytest.ini_options]
|
|
104
|
+
markers = [
|
|
105
|
+
"manual: marks tests unsafe for auto-run (eg. better run them manually)",
|
|
106
|
+
]
|
|
107
|
+
asyncio_default_fixture_loop_scope = "function"
|
acbox-0.0.0b1/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import inspect
|
|
5
|
+
import sys
|
|
6
|
+
import types
|
|
7
|
+
from typing import Callable
|
|
8
|
+
|
|
9
|
+
# The return type of add_arguments
|
|
10
|
+
|
|
11
|
+
if sys.version_info >= (3, 10):
|
|
12
|
+
ArgsCallback = Callable[[argparse.Namespace], None | argparse.Namespace]
|
|
13
|
+
else:
|
|
14
|
+
ArgsCallback = Callable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def check_default_constructor(klass: type):
|
|
18
|
+
signature = inspect.signature(klass.__init__) # type: ignore[misc]
|
|
19
|
+
for name, value in signature.parameters.items():
|
|
20
|
+
if name in {"self", "args", "kwargs"}:
|
|
21
|
+
continue
|
|
22
|
+
if value.default is inspect.Signature.empty:
|
|
23
|
+
raise RuntimeError(f"the {klass}() cannot be called without arguments")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ArgumentParserBase(argparse.ArgumentParser):
|
|
27
|
+
def __init__(self, modules: list[types.ModuleType], *args, **kwargs):
|
|
28
|
+
super().__init__(*args, **kwargs)
|
|
29
|
+
self.modules = modules
|
|
30
|
+
self.callbacks: list[ArgsCallback | None] = []
|
|
31
|
+
|
|
32
|
+
def parse_args(self, args=None, namespace=None):
|
|
33
|
+
options = super().parse_args(args, namespace)
|
|
34
|
+
for name in dir(options):
|
|
35
|
+
if isinstance(getattr(options, name), ArgumentTypeBase):
|
|
36
|
+
fallback = getattr(options, name).value
|
|
37
|
+
setattr(
|
|
38
|
+
options,
|
|
39
|
+
name,
|
|
40
|
+
None if fallback is ArgumentTypeBase._NA else fallback,
|
|
41
|
+
)
|
|
42
|
+
return options
|
|
43
|
+
|
|
44
|
+
def add_argument(self, *args, **kwargs):
|
|
45
|
+
typ = kwargs.get("type")
|
|
46
|
+
obj = None
|
|
47
|
+
if isinstance(typ, type) and issubclass(typ, ArgumentTypeBase):
|
|
48
|
+
check_default_constructor(typ)
|
|
49
|
+
obj = typ()
|
|
50
|
+
if isinstance(typ, ArgumentTypeBase):
|
|
51
|
+
obj = typ
|
|
52
|
+
if obj is not None:
|
|
53
|
+
obj.default = kwargs.get("default", ArgumentTypeBase._NA)
|
|
54
|
+
kwargs["default"] = obj
|
|
55
|
+
kwargs["type"] = obj
|
|
56
|
+
super().add_argument(*args, **kwargs)
|
|
57
|
+
|
|
58
|
+
def error(self, message):
|
|
59
|
+
try:
|
|
60
|
+
super().error(message)
|
|
61
|
+
except SystemExit:
|
|
62
|
+
# gh-121018
|
|
63
|
+
raise argparse.ArgumentError(None, message)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ArgumentTypeBase:
|
|
67
|
+
class _NA:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def __init__(self, *args, **kwargs):
|
|
71
|
+
self.args = args
|
|
72
|
+
self.kwargs = kwargs
|
|
73
|
+
self._default = self._NA
|
|
74
|
+
self.__name__ = self.__class__.__name__
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def default(self):
|
|
78
|
+
return self._default
|
|
79
|
+
|
|
80
|
+
@default.setter
|
|
81
|
+
def default(self, value):
|
|
82
|
+
if value is ArgumentTypeBase._NA:
|
|
83
|
+
self._default = ArgumentTypeBase._NA
|
|
84
|
+
else:
|
|
85
|
+
self._default = self._validate(value)
|
|
86
|
+
return self._default
|
|
87
|
+
|
|
88
|
+
def __call__(self, txt):
|
|
89
|
+
self._value = None
|
|
90
|
+
self._value = self._validate(txt)
|
|
91
|
+
return self
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def value(self):
|
|
95
|
+
return getattr(self, "_value", self.default)
|
|
96
|
+
|
|
97
|
+
def _validate(self, value):
|
|
98
|
+
try:
|
|
99
|
+
return self.validate(value)
|
|
100
|
+
except argparse.ArgumentTypeError as exc:
|
|
101
|
+
if not hasattr(self, "_value"):
|
|
102
|
+
raise RuntimeError(f"cannot use {value=} as default: {exc.args[0]}")
|
|
103
|
+
raise
|
|
104
|
+
|
|
105
|
+
def validate(self, txt):
|
|
106
|
+
raise NotImplementedError("need to implement the .validate(self, txt) method")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def which_n(exe: str | Path) -> list[Path] | None:
|
|
8
|
+
candidates: list[Path] | None = None
|
|
9
|
+
for srcdir in os.environ.get("PATH", "").split(os.pathsep):
|
|
10
|
+
for ext in os.environ.get("PATHEXT", "").split(os.pathsep):
|
|
11
|
+
path = srcdir / Path(exe).with_suffix(ext)
|
|
12
|
+
if not path.exists():
|
|
13
|
+
continue
|
|
14
|
+
if candidates is None:
|
|
15
|
+
candidates = []
|
|
16
|
+
candidates.append(path)
|
|
17
|
+
return candidates
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def which(exe: str | Path) -> Path | None:
|
|
21
|
+
candidates = which_n(exe)
|
|
22
|
+
if candidates is None:
|
|
23
|
+
return None
|
|
24
|
+
return candidates[0]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def loadmod(path: Path | str) -> ModuleType:
|
|
8
|
+
from importlib import util
|
|
9
|
+
from types import ModuleType
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
from urllib.request import urlopen
|
|
12
|
+
|
|
13
|
+
if urlparse(str(path)).scheme in {"http", "https"}:
|
|
14
|
+
urltxt = str(urlopen(str(path)).read(), encoding="utf-8")
|
|
15
|
+
mod = ModuleType(str(path).rpartition("/")[2])
|
|
16
|
+
exec(urltxt, mod.__dict__)
|
|
17
|
+
return mod
|
|
18
|
+
|
|
19
|
+
spec = util.spec_from_file_location(Path(path).name, Path(path))
|
|
20
|
+
module = util.module_from_spec(spec) # type: ignore
|
|
21
|
+
spec.loader.exec_module(module) # type: ignore
|
|
22
|
+
return module
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: acbox
|
|
3
|
+
Version: 0.0.0b1
|
|
4
|
+
Summary: Collection of small tools
|
|
5
|
+
Author-email: Antonio Cavallo <a.cavallo@cavallinux.eu>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Source, https://github.com/cav71/acbox
|
|
8
|
+
Project-URL: Issues, https://github.com/cav71/acbox
|
|
9
|
+
Project-URL: Documentation, https://github.com/cav71/acbox
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Provides-Extra: extra
|
|
20
|
+
|
|
21
|
+
## ACBox my little toolbox
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Development
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
python3.13 -m venv .venv
|
|
28
|
+
source .venv/bin/activate
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
python -m pip install --group dev
|
|
34
|
+
pre-commit install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Ready.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/acbox/fileops.py
|
|
4
|
+
src/acbox/utils.py
|
|
5
|
+
src/acbox.egg-info/PKG-INFO
|
|
6
|
+
src/acbox.egg-info/SOURCES.txt
|
|
7
|
+
src/acbox.egg-info/dependency_links.txt
|
|
8
|
+
src/acbox.egg-info/requires.txt
|
|
9
|
+
src/acbox.egg-info/top_level.txt
|
|
10
|
+
src/acbox/cli/__init__.py
|
|
11
|
+
src/acbox/cli/shared.py
|
|
12
|
+
tests/test_cli_shared.py
|
|
13
|
+
tests/test_fileops.py
|
|
14
|
+
tests/test_internals.py
|
|
15
|
+
tests/test_utils.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
acbox
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from acbox.cli import shared
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Flag(shared.ArgumentTypeBase):
|
|
9
|
+
def __init__(self, only_odd=False):
|
|
10
|
+
self.only_odd = only_odd
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
def validate(self, txt):
|
|
14
|
+
value = int(txt)
|
|
15
|
+
if self.only_odd:
|
|
16
|
+
if (value % 2) == 0:
|
|
17
|
+
raise argparse.ArgumentTypeError(f"non odd value '{txt}'")
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_check_default_constructor():
|
|
22
|
+
"""verifies the class constructor requires no arguments"""
|
|
23
|
+
|
|
24
|
+
class A:
|
|
25
|
+
def __init__(self, value):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
pytest.raises(RuntimeError, shared.check_default_constructor, A)
|
|
29
|
+
|
|
30
|
+
class A:
|
|
31
|
+
def __init__(self, value=1):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
assert shared.check_default_constructor(A) is None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_add_argument():
|
|
38
|
+
"""test all failures mode for add_argument default"""
|
|
39
|
+
p = shared.ArgumentParserBase([], exit_on_error=False)
|
|
40
|
+
|
|
41
|
+
with pytest.raises(ValueError) as e:
|
|
42
|
+
p.add_argument("--flag", type=Flag, default="yyy")
|
|
43
|
+
assert e.value.args[0] == "invalid literal for int() with base 10: 'yyy'"
|
|
44
|
+
|
|
45
|
+
with pytest.raises(RuntimeError) as e:
|
|
46
|
+
p.add_argument("--flag", type=Flag(only_odd=True), default="124")
|
|
47
|
+
assert e.value.args[0] == "cannot use value='124' as default: non odd value '124'"
|
|
48
|
+
|
|
49
|
+
with pytest.raises(RuntimeError) as e:
|
|
50
|
+
p.add_argument("--flag", type=Flag(only_odd=True), default=124)
|
|
51
|
+
assert e.value.args[0] == "cannot use value=124 as default: non odd value '124'"
|
|
52
|
+
|
|
53
|
+
with pytest.raises(RuntimeError) as e:
|
|
54
|
+
p.add_argument("--flag", type=Flag(only_odd=True), default=126)
|
|
55
|
+
assert e.value.args[0] == "cannot use value=126 as default: non odd value '126'"
|
|
56
|
+
|
|
57
|
+
with pytest.raises(RuntimeError) as e:
|
|
58
|
+
p.add_argument("--flag", type=Flag(only_odd=True), default="126")
|
|
59
|
+
assert e.value.args[0] == "cannot use value='126' as default: non odd value '126'"
|
|
60
|
+
|
|
61
|
+
p.add_argument("--flag", type=Flag)
|
|
62
|
+
assert len(action := [a for a in p._actions if isinstance(a.type, shared.ArgumentTypeBase)]) == 1
|
|
63
|
+
assert action[0].default.default is shared.ArgumentTypeBase._NA
|
|
64
|
+
|
|
65
|
+
p.add_argument("--flag1", type=Flag, default="124")
|
|
66
|
+
assert len(actions := [a for a in p._actions if isinstance(a.type, shared.ArgumentTypeBase)]) == 2
|
|
67
|
+
assert actions[1].default.default == 124
|
|
68
|
+
|
|
69
|
+
p.add_argument("--flag2", type=Flag, default=126)
|
|
70
|
+
assert len(actions := [a for a in p._actions if isinstance(a.type, shared.ArgumentTypeBase)]) == 3
|
|
71
|
+
assert actions[2].default.default == 126
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_special_flag_no_restriction():
|
|
75
|
+
"""parse arguments with no default and no constrain on Flag"""
|
|
76
|
+
p = shared.ArgumentParserBase([], exit_on_error=False)
|
|
77
|
+
p.add_argument("--flag", type=Flag)
|
|
78
|
+
|
|
79
|
+
a = p.parse_args([])
|
|
80
|
+
assert a.flag is None
|
|
81
|
+
|
|
82
|
+
a = p.parse_args(
|
|
83
|
+
[
|
|
84
|
+
"--flag",
|
|
85
|
+
"123",
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
assert a.flag == 123
|
|
89
|
+
|
|
90
|
+
a = p.parse_args(
|
|
91
|
+
[
|
|
92
|
+
"--flag",
|
|
93
|
+
"124",
|
|
94
|
+
]
|
|
95
|
+
)
|
|
96
|
+
assert a.flag == 124
|
|
97
|
+
|
|
98
|
+
with pytest.raises(argparse.ArgumentError) as e:
|
|
99
|
+
p.parse_args(["--flag", "boo"])
|
|
100
|
+
assert e.value.args[-1] == "invalid Flag value: 'boo'"
|
|
101
|
+
|
|
102
|
+
# same as above but with a default fallback
|
|
103
|
+
p = shared.ArgumentParserBase([], exit_on_error=False)
|
|
104
|
+
p.add_argument("--flag", type=Flag, default="42")
|
|
105
|
+
|
|
106
|
+
a = p.parse_args([])
|
|
107
|
+
assert a.flag == 42
|
|
108
|
+
|
|
109
|
+
a = p.parse_args(
|
|
110
|
+
[
|
|
111
|
+
"--flag",
|
|
112
|
+
"123",
|
|
113
|
+
]
|
|
114
|
+
)
|
|
115
|
+
assert a.flag == 123
|
|
116
|
+
|
|
117
|
+
a = p.parse_args(
|
|
118
|
+
[
|
|
119
|
+
"--flag",
|
|
120
|
+
"124",
|
|
121
|
+
]
|
|
122
|
+
)
|
|
123
|
+
assert a.flag == 124
|
|
124
|
+
|
|
125
|
+
with pytest.raises(argparse.ArgumentError) as e:
|
|
126
|
+
p.parse_args(["--flag", "boo"])
|
|
127
|
+
assert e.value.args[-1] == "invalid Flag value: 'boo'"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_special_flag_with_restriction_no_default():
|
|
131
|
+
p = shared.ArgumentParserBase([], exit_on_error=False)
|
|
132
|
+
p.add_argument("--flag", type=Flag(only_odd=True))
|
|
133
|
+
|
|
134
|
+
a = p.parse_args([])
|
|
135
|
+
assert a.flag is None
|
|
136
|
+
|
|
137
|
+
a = p.parse_args(
|
|
138
|
+
[
|
|
139
|
+
"--flag",
|
|
140
|
+
"123",
|
|
141
|
+
]
|
|
142
|
+
)
|
|
143
|
+
assert a.flag == 123
|
|
144
|
+
|
|
145
|
+
with pytest.raises(argparse.ArgumentError) as e:
|
|
146
|
+
p.parse_args(["--flag", "122"])
|
|
147
|
+
assert e.value.args[-1] == "non odd value '122'"
|
|
148
|
+
|
|
149
|
+
with pytest.raises(argparse.ArgumentError) as e:
|
|
150
|
+
p.parse_args(["--flag", "boo"])
|
|
151
|
+
assert e.value.args[-1] == "invalid Flag value: 'boo'"
|
|
152
|
+
|
|
153
|
+
# same as above but with a default fallback
|
|
154
|
+
p = shared.ArgumentParserBase([], exit_on_error=False)
|
|
155
|
+
p.add_argument("--flag", type=Flag(only_odd=True), default=43)
|
|
156
|
+
|
|
157
|
+
a = p.parse_args([])
|
|
158
|
+
assert a.flag == 43
|
|
159
|
+
|
|
160
|
+
a = p.parse_args(
|
|
161
|
+
[
|
|
162
|
+
"--flag",
|
|
163
|
+
"123",
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
assert a.flag == 123
|
|
167
|
+
|
|
168
|
+
with pytest.raises(argparse.ArgumentError) as e:
|
|
169
|
+
p.parse_args(["--flag", "122"])
|
|
170
|
+
assert e.value.args[-1] == "non odd value '122'"
|
|
171
|
+
|
|
172
|
+
with pytest.raises(argparse.ArgumentError) as e:
|
|
173
|
+
p.parse_args(["--flag", "boo"])
|
|
174
|
+
assert e.value.args[-1] == "invalid Flag value: 'boo'"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from acbox import fileops
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_which():
|
|
8
|
+
exe = "cmd" if sys.platform == "win32" else "sh"
|
|
9
|
+
|
|
10
|
+
path = fileops.which(exe)
|
|
11
|
+
assert path
|
|
12
|
+
assert isinstance(path, Path)
|
|
13
|
+
|
|
14
|
+
paths = fileops.which_n(exe)
|
|
15
|
+
assert isinstance(paths, list)
|
|
16
|
+
assert paths[0] == fileops.which(exe)
|
|
17
|
+
|
|
18
|
+
assert fileops.which_n("xwdwxEW") is None
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
def test_script_lookup(resolver):
|
|
2
|
+
path = resolver.lookup("simple-script.py")
|
|
3
|
+
assert path
|
|
4
|
+
assert path.exists()
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_script_load(resolver):
|
|
8
|
+
data = resolver.load("simple-script.py", "text")
|
|
9
|
+
assert (
|
|
10
|
+
data
|
|
11
|
+
== """
|
|
12
|
+
VALUE = 123
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def hello(msg):
|
|
16
|
+
print(f"Hi {msg}")
|
|
17
|
+
""".lstrip()
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
mod = resolver.load("simple-script.py", "mod")
|
|
21
|
+
assert mod.VALUE, 123
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from acbox import utils
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_load_mod(resolver):
|
|
5
|
+
path = resolver.lookup("simple-script.py")
|
|
6
|
+
|
|
7
|
+
mod = utils.loadmod(path)
|
|
8
|
+
assert mod.VALUE, 123
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_load_remote():
|
|
12
|
+
mod = utils.loadmod("https://raw.githubusercontent.com/cav71/acbox/refs/heads/main/tests/test_utils.py")
|
|
13
|
+
assert hasattr(mod, "test_load_remote")
|