lockss-pybasic 0.2.0.dev1__py3-none-any.whl → 0.2.0.dev3__py3-none-any.whl
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.
- lockss/pybasic/__init__.py +1 -1
- lockss/pybasic/cliutil.py +42 -35
- {lockss_pybasic-0.2.0.dev1.dist-info → lockss_pybasic-0.2.0.dev3.dist-info}/METADATA +1 -1
- lockss_pybasic-0.2.0.dev3.dist-info/RECORD +9 -0
- lockss_pybasic-0.2.0.dev1.dist-info/RECORD +0 -9
- {lockss_pybasic-0.2.0.dev1.dist-info → lockss_pybasic-0.2.0.dev3.dist-info}/LICENSE +0 -0
- {lockss_pybasic-0.2.0.dev1.dist-info → lockss_pybasic-0.2.0.dev3.dist-info}/WHEEL +0 -0
lockss/pybasic/__init__.py
CHANGED
lockss/pybasic/cliutil.py
CHANGED
|
@@ -34,47 +34,50 @@ Command line utilities.
|
|
|
34
34
|
|
|
35
35
|
from collections.abc import Callable
|
|
36
36
|
import sys
|
|
37
|
-
from typing import Any, Dict, Generic, Optional, TypeVar
|
|
37
|
+
from typing import Any, ClassVar, Dict, Generic, Optional, TypeVar, TYPE_CHECKING
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from _typeshed import SupportsWrite
|
|
41
|
+
|
|
42
|
+
from pydantic.v1 import BaseModel, PrivateAttr, create_model
|
|
43
|
+
from pydantic.v1.fields import FieldInfo
|
|
40
44
|
from pydantic_argparse import ArgumentParser
|
|
41
45
|
from pydantic_argparse.argparse.actions import SubParsersAction
|
|
42
46
|
from rich_argparse import RichHelpFormatter
|
|
43
47
|
|
|
44
48
|
|
|
45
|
-
class
|
|
46
|
-
"""
|
|
47
|
-
Base class for a pydantic-argparse style command.
|
|
48
|
-
"""
|
|
49
|
-
pass
|
|
49
|
+
class StringCommand(BaseModel):
|
|
50
50
|
|
|
51
|
+
def display(self, file: SupportsWrite[str]=sys.stdout) -> None:
|
|
52
|
+
print(getattr(self, 'display_string'), file=file)
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
@staticmethod
|
|
55
|
+
def make(model_name: str, option_name: str, description: str, display_string: str):
|
|
56
|
+
return create_model(model_name,
|
|
57
|
+
__base__=StringCommand,
|
|
58
|
+
**{option_name: (Optional[bool], FieldInfo(False, description=description)),
|
|
59
|
+
display_string: PrivateAttr(display_string)})
|
|
55
60
|
|
|
56
|
-
Example of use:
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
class CopyrightCommand(StringCommand):
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
@staticmethod
|
|
65
|
+
def make(display_string: str):
|
|
66
|
+
return StringCommand.make('CopyrightCommand', 'copyright', 'print the copyright and exit', display_string)
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"""
|
|
68
|
+
|
|
69
|
+
class LicenseCommand(StringCommand):
|
|
66
70
|
|
|
67
71
|
@staticmethod
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
def __call__(self, file=sys.stdout, **kwargs):
|
|
71
|
-
print(display_str, file=file)
|
|
72
|
-
return _StringCommand
|
|
72
|
+
def make(display_string: str):
|
|
73
|
+
return StringCommand.make('LicenseCommand', 'license', 'print the software license and exit', display_string)
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
class VersionCommand(StringCommand):
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def make(display_string: str):
|
|
80
|
+
return StringCommand.make('VersionCommand', 'version', 'print the version number and exit', display_string)
|
|
78
81
|
|
|
79
82
|
|
|
80
83
|
BaseModelT = TypeVar('BaseModelT', bound=BaseModel)
|
|
@@ -147,7 +150,7 @@ class BaseCli(Generic[BaseModelT]):
|
|
|
147
150
|
if callable(func):
|
|
148
151
|
func(base_model) # FIXME?
|
|
149
152
|
else:
|
|
150
|
-
self._parser.exit(1, f'internal error: no {func_name} callable for the {" ".join(sub for sub in subcommands)}')
|
|
153
|
+
self._parser.exit(1, f'internal error: no {func_name} callable for the {" ".join(sub for sub in subcommands)} command')
|
|
151
154
|
|
|
152
155
|
def _initialize_rich_argparse(self) -> None:
|
|
153
156
|
"""
|
|
@@ -179,7 +182,7 @@ class BaseCli(Generic[BaseModelT]):
|
|
|
179
182
|
})
|
|
180
183
|
|
|
181
184
|
|
|
182
|
-
def at_most_one_from_enum(model_cls, values: Dict[str, Any], enum_cls) -> Dict[str, Any]:
|
|
185
|
+
def at_most_one_from_enum(model_cls: type[BaseModel], values: Dict[str, Any], enum_cls) -> Dict[str, Any]:
|
|
183
186
|
"""
|
|
184
187
|
Among the fields of a Pydantic-Argparse model whose ``Field`` definition is
|
|
185
188
|
tagged with the ``enum`` keyword set to the given ``Enum`` type, ensures
|
|
@@ -195,7 +198,7 @@ def at_most_one_from_enum(model_cls, values: Dict[str, Any], enum_cls) -> Dict[s
|
|
|
195
198
|
enum_names = [field_name for field_name, model_field in model_cls.__fields__.items() if model_field.field_info.extra.get('enum') == enum_cls]
|
|
196
199
|
ret = [field_name for field_name in enum_names if values.get(field_name)]
|
|
197
200
|
if (length := len(ret)) > 1:
|
|
198
|
-
raise ValueError(f'at most one of {', '.join([option_name(enum_name) for enum_name in enum_names])}
|
|
201
|
+
raise ValueError(f'at most one of {', '.join([option_name(model_cls, enum_name) for enum_name in enum_names])} allowed; got {length} ({', '.join([option_name(enum_name) for enum_name in ret])})')
|
|
199
202
|
return values
|
|
200
203
|
|
|
201
204
|
|
|
@@ -219,25 +222,29 @@ def get_from_enum(model_inst, enum_cls, default=None):
|
|
|
219
222
|
return default
|
|
220
223
|
|
|
221
224
|
|
|
222
|
-
def at_most_one(values: Dict[str, Any], *names: str):
|
|
225
|
+
def at_most_one(model_cls: type[BaseModel], values: Dict[str, Any], *names: str):
|
|
223
226
|
if (length := _matchy_length(values, *names)) > 1:
|
|
224
|
-
raise ValueError(f'at most one of {', '.join([option_name(name) for name in names])}
|
|
227
|
+
raise ValueError(f'at most one of {', '.join([option_name(model_cls, name) for name in names])} allowed; got {length}')
|
|
225
228
|
return values
|
|
226
229
|
|
|
227
230
|
|
|
228
|
-
def exactly_one(values: Dict[str, Any], *names: str):
|
|
231
|
+
def exactly_one(model_cls: type[BaseModel], values: Dict[str, Any], *names: str):
|
|
229
232
|
if (length := _matchy_length(values, *names)) != 1:
|
|
230
|
-
raise ValueError(f'exactly one of {', '.join([option_name(name) for name in names])}
|
|
233
|
+
raise ValueError(f'exactly one of {', '.join([option_name(model_cls, name) for name in names])} required; got {length}')
|
|
231
234
|
return values
|
|
232
235
|
|
|
233
236
|
|
|
234
|
-
def one_or_more(values: Dict[str, Any], *names: str):
|
|
237
|
+
def one_or_more(model_cls: type[BaseModel], values: Dict[str, Any], *names: str):
|
|
235
238
|
if _matchy_length(values, *names) == 0:
|
|
236
|
-
raise ValueError(f'one or more of {', '.join([option_name(name) for name in names])}
|
|
239
|
+
raise ValueError(f'one or more of {', '.join([option_name(model_cls, name) for name in names])} required')
|
|
237
240
|
return values
|
|
238
241
|
|
|
239
242
|
|
|
240
|
-
def option_name(name: str) -> str:
|
|
243
|
+
def option_name(model_cls: type[BaseModel], name: str) -> str:
|
|
244
|
+
if (info := model_cls.__fields__.get(name)) is None:
|
|
245
|
+
raise RuntimeError(f'invalid name: {name}')
|
|
246
|
+
if alias := info.alias:
|
|
247
|
+
name = alias
|
|
241
248
|
return f'{('-' if len(name) == 1 else '--')}{name.replace('_', '-')}'
|
|
242
249
|
|
|
243
250
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
lockss/pybasic/__init__.py,sha256=MxiRIu7H0y8ISCRj4PtTzlNH0hPQ9wvEawdO1nMjFpA,1678
|
|
2
|
+
lockss/pybasic/cliutil.py,sha256=LppP6O1Ocj0d5bxtXUPShAYMOTeotQywKkaPd63xhQ0,11185
|
|
3
|
+
lockss/pybasic/errorutil.py,sha256=XI84PScZ851_-gfoazivJ8ceieMYWaxQr7qih5ltga0,1951
|
|
4
|
+
lockss/pybasic/fileutil.py,sha256=BpdoPWL70xYTuhyQRBEurScRVnPQg0mX-XW8yyKPGjw,2958
|
|
5
|
+
lockss/pybasic/outpututil.py,sha256=JyXKXlkaCcCGvonUvmDGRdI6PxwN5t7DPVIk64S8-2g,2345
|
|
6
|
+
lockss_pybasic-0.2.0.dev3.dist-info/LICENSE,sha256=O9ONND4uDxY_jucI4jZDf2liAk05ScEJaYu-Al7EOdQ,1506
|
|
7
|
+
lockss_pybasic-0.2.0.dev3.dist-info/METADATA,sha256=ijdZVGfvh1NBR4vHPYvUSOd02cqSbmLooDMEHZbpUAM,4247
|
|
8
|
+
lockss_pybasic-0.2.0.dev3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
9
|
+
lockss_pybasic-0.2.0.dev3.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
lockss/pybasic/__init__.py,sha256=eYuJo1k0n0klLe8HFPzmjJvcMcHS7FXQ9JdfQ4WaJVU,1678
|
|
2
|
-
lockss/pybasic/cliutil.py,sha256=gC-Iy2lZX_PNU0GhtSiyPYPokFFjzF9OkMSxGEoB8_c,10503
|
|
3
|
-
lockss/pybasic/errorutil.py,sha256=XI84PScZ851_-gfoazivJ8ceieMYWaxQr7qih5ltga0,1951
|
|
4
|
-
lockss/pybasic/fileutil.py,sha256=BpdoPWL70xYTuhyQRBEurScRVnPQg0mX-XW8yyKPGjw,2958
|
|
5
|
-
lockss/pybasic/outpututil.py,sha256=JyXKXlkaCcCGvonUvmDGRdI6PxwN5t7DPVIk64S8-2g,2345
|
|
6
|
-
lockss_pybasic-0.2.0.dev1.dist-info/LICENSE,sha256=O9ONND4uDxY_jucI4jZDf2liAk05ScEJaYu-Al7EOdQ,1506
|
|
7
|
-
lockss_pybasic-0.2.0.dev1.dist-info/METADATA,sha256=zowTdcLSf79LdOc2wFBKX2966D41kV02RYTZ8syhla4,4247
|
|
8
|
-
lockss_pybasic-0.2.0.dev1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
9
|
-
lockss_pybasic-0.2.0.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|