lockss-pybasic 0.3.0.dev3__py3-none-any.whl → 0.3.0.dev5__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 +18 -17
- lockss/pybasic/fileutil.py +9 -6
- lockss/pybasic/nodeutil.py +68 -19
- {lockss_pybasic-0.3.0.dev3.dist-info → lockss_pybasic-0.3.0.dev5.dist-info}/METADATA +3 -3
- lockss_pybasic-0.3.0.dev5.dist-info/RECORD +10 -0
- lockss_pybasic-0.3.0.dev3.dist-info/RECORD +0 -10
- {lockss_pybasic-0.3.0.dev3.dist-info → lockss_pybasic-0.3.0.dev5.dist-info}/WHEEL +0 -0
- {lockss_pybasic-0.3.0.dev3.dist-info → lockss_pybasic-0.3.0.dev5.dist-info}/licenses/LICENSE +0 -0
lockss/pybasic/__init__.py
CHANGED
lockss/pybasic/cliutil.py
CHANGED
|
@@ -33,17 +33,18 @@ Command line utilities.
|
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
35
|
from pathlib import Path
|
|
36
|
-
from typing import Any, Optional, Union
|
|
36
|
+
from typing import Any, Optional, TypeAlias, Union
|
|
37
37
|
|
|
38
|
-
import
|
|
39
|
-
from
|
|
40
|
-
from click_extra import ChoiceSource, EnumChoice, ExtraContext, HelpExtraFormatter, Style, TableFormat, option
|
|
41
|
-
from click_extra.colorize import default_theme
|
|
38
|
+
from click_extra import ChoiceSource, EnumChoice, ExtraContext, HelpExtraFormatter, IntRange, ParamType, Path, Style, TableFormat, option
|
|
39
|
+
from click_extra.theme import get_default_theme
|
|
42
40
|
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
ClickPath: TypeAlias = Path
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def click_path(spec: Optional[str]) -> ClickPath:
|
|
45
46
|
"""
|
|
46
|
-
Generates a ``click.Path`` based on a specification string.
|
|
47
|
+
Generates a ``ClickPath`` (``click.Path``) based on a specification string.
|
|
47
48
|
|
|
48
49
|
The specification string can contain the following specifier characters:
|
|
49
50
|
|
|
@@ -147,15 +148,15 @@ def click_path(spec: Optional[str]) -> click.Path:
|
|
|
147
148
|
allow_dash = True
|
|
148
149
|
else:
|
|
149
150
|
raise ValueError(f'unknown specification character "{char}": {spec}')
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
151
|
+
return ClickPath(allow_dash=allow_dash,
|
|
152
|
+
dir_okay=dir_okay,
|
|
153
|
+
executable=executable,
|
|
154
|
+
exists=exists,
|
|
155
|
+
file_okay=file_okay,
|
|
156
|
+
path_type=path_type,
|
|
157
|
+
readable=readable,
|
|
158
|
+
resolve_path=resolve_path,
|
|
159
|
+
writable=writable)
|
|
159
160
|
|
|
160
161
|
|
|
161
162
|
#: Composes the given decorators, so that
|
|
@@ -209,7 +210,7 @@ def make_extra_context_settings() -> dict[str, Any]:
|
|
|
209
210
|
"""
|
|
210
211
|
return ExtraContext.settings(
|
|
211
212
|
formatter_settings=HelpExtraFormatter.settings(
|
|
212
|
-
theme=
|
|
213
|
+
theme=get_default_theme().with_(
|
|
213
214
|
invoked_command=Style(bold=True)
|
|
214
215
|
)
|
|
215
216
|
)
|
lockss/pybasic/fileutil.py
CHANGED
|
@@ -32,9 +32,12 @@
|
|
|
32
32
|
File and path utilities.
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
from pathlib import Path
|
|
35
|
+
from pathlib import Path
|
|
36
36
|
import sys
|
|
37
|
-
from typing import List, Union
|
|
37
|
+
from typing import List, TypeAlias, Union
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
PathOrStr: TypeAlias = Union[Path, str]
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
def file_lines(fpath: Path) -> List[str]:
|
|
@@ -56,7 +59,7 @@ def file_lines(fpath: Path) -> List[str]:
|
|
|
56
59
|
f.close()
|
|
57
60
|
|
|
58
61
|
|
|
59
|
-
def path(
|
|
62
|
+
def path(path_or_string: PathOrStr) -> Path:
|
|
60
63
|
"""
|
|
61
64
|
Returns the given ``PurePath`` (or if given a string, the ``Path`` created
|
|
62
65
|
from that string), expanded with ``expanduser()`` and resolved with
|
|
@@ -68,6 +71,6 @@ def path(purepath_or_string: Union[PurePath, str]) -> Path:
|
|
|
68
71
|
:return: An expanded and resolved ``Path``.
|
|
69
72
|
:rtype: Path
|
|
70
73
|
"""
|
|
71
|
-
if not issubclass(type(
|
|
72
|
-
|
|
73
|
-
return
|
|
74
|
+
if not issubclass(type(path_or_string), Path):
|
|
75
|
+
path_or_string = Path(path_or_string)
|
|
76
|
+
return path_or_string.expanduser().resolve()
|
lockss/pybasic/nodeutil.py
CHANGED
|
@@ -35,33 +35,35 @@ LOCKSS node utilities.
|
|
|
35
35
|
from enum import Enum
|
|
36
36
|
from re import Match, Pattern
|
|
37
37
|
import re
|
|
38
|
-
from typing import Annotated, ClassVar, Literal, Optional, Union
|
|
38
|
+
from typing import Annotated, Any, ClassVar, Literal, Optional, Union
|
|
39
39
|
|
|
40
40
|
from annotated_types import Ge, Le
|
|
41
|
-
from pydantic import BaseModel, Field, TypeAdapter
|
|
42
|
-
|
|
43
|
-
from .errorutil import InternalError
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
RE_NODE_REFERENCE: Pattern[str] = re.compile(r'((?P<protocol>https?)://)?(?P<host>[^:]+)(:(?P<repository>\d+|(?=:))(:(?P<configuration>\d+|(?=:))(:(?P<poller>\d+|(?=:))(:(?P<crawler>\d+|(?=:))(:(?P<metadata>\d+|(?=:))(:(?P<soap>\d+))?)?)?)?)?)?')
|
|
41
|
+
from pydantic import BaseModel, BeforeValidator, Field, TypeAdapter
|
|
47
42
|
|
|
48
43
|
|
|
44
|
+
#: An annotated type for port numbers (0-65535)
|
|
49
45
|
PortNumber = Annotated[int, Ge(0), Le(65535)]
|
|
50
46
|
|
|
51
47
|
|
|
52
48
|
class NodeTypeEnum(Enum):
|
|
49
|
+
"""An enumerated type representing LOCKSS node types."""
|
|
50
|
+
#: An enumerated constant representing LOCKSS 1.x nodes.
|
|
53
51
|
V1 = 'v1'
|
|
52
|
+
#: An enumerated constant representing LOCKSS 2.x nodes.
|
|
54
53
|
V2 = 'v2'
|
|
55
54
|
|
|
56
55
|
|
|
57
56
|
class NodeProtocolEnum(Enum):
|
|
57
|
+
"""An enumerated type representing protocols for reaching LOCKSS nodes."""
|
|
58
|
+
#: An enumerated constant representing HTTP.
|
|
58
59
|
HTTP = 'http'
|
|
60
|
+
#: An enumerated constant representing HTTPS.
|
|
59
61
|
HTTPS = 'https'
|
|
60
62
|
|
|
61
63
|
|
|
62
64
|
class BaseNodeSpec(BaseModel):
|
|
63
65
|
|
|
64
|
-
DEFAULT_PROTOCOL: ClassVar[NodeProtocolEnum] = NodeProtocolEnum.
|
|
66
|
+
DEFAULT_PROTOCOL: ClassVar[NodeProtocolEnum] = NodeProtocolEnum.HTTP
|
|
65
67
|
|
|
66
68
|
TYPE_FIELD: ClassVar[dict[str, str]] = dict(title='Type',
|
|
67
69
|
description="The node's type")
|
|
@@ -126,21 +128,15 @@ class NodeSpec2(BaseNodeSpec):
|
|
|
126
128
|
description="The node's SOAP Compatibility Service REST API Port")
|
|
127
129
|
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
_node_spec_adapter: TypeAdapter[NodeSpec] = TypeAdapter(NodeSpec)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def get_node_spec_adapter() -> TypeAdapter[NodeSpec]:
|
|
136
|
-
return _node_spec_adapter
|
|
131
|
+
_RE_NODE_REFERENCE: Pattern[str] = re.compile(r'((?P<protocol>https?)://)?(?P<host>[^:]+)(:(?P<repository>\d+|(?=:))(:(?P<configuration>\d+|(?=:))(:(?P<poller>\d+|(?=:))(:(?P<crawler>\d+|(?=:))(:(?P<metadata>\d+|(?=:))(:(?P<soap>\d+))?)?)?)?)?)?')
|
|
137
132
|
|
|
138
133
|
|
|
134
|
+
#: A type for LOCKSS node specification strings.
|
|
139
135
|
NodeSpecStr = str
|
|
140
136
|
|
|
141
137
|
|
|
142
|
-
def
|
|
143
|
-
mat: Optional[Match[str]] =
|
|
138
|
+
def _parse_node_spec_string(node_spec_string: NodeSpecStr) -> dict[str, str]:
|
|
139
|
+
mat: Optional[Match[str]] = _RE_NODE_REFERENCE.fullmatch(node_spec_string)
|
|
144
140
|
if mat is None:
|
|
145
141
|
raise ValueError(f'Invalid node specification string: {node_spec_string}')
|
|
146
142
|
d = dict(host=mat.group('host'))
|
|
@@ -164,4 +160,57 @@ def make_node_spec(node_spec_string: NodeSpecStr) -> NodeSpec:
|
|
|
164
160
|
for k in five:
|
|
165
161
|
if p := mat.group(k):
|
|
166
162
|
d[k] = p # string okay, will be coerced to int
|
|
167
|
-
return
|
|
163
|
+
return d
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _maybe_deserialize_node_spec_string(value: Any) -> Any:
|
|
167
|
+
if isinstance(value, NodeSpecStr) and not value.startswith('{'):
|
|
168
|
+
return _parse_node_spec_string(value)
|
|
169
|
+
return value
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
#: A type for LOCKSS node specifications, that also accepts a compact LOCKSS
|
|
173
|
+
#: node specification string.
|
|
174
|
+
NodeSpec = Annotated[
|
|
175
|
+
Annotated[Union[NodeSpec1, NodeSpec2], Field(discriminator='type')],
|
|
176
|
+
BeforeValidator(_maybe_deserialize_node_spec_string)
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
#: A type adapter for the NodeSpec type.
|
|
181
|
+
_node_spec_adapter: TypeAdapter[NodeSpec] = TypeAdapter(NodeSpec)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get_node_spec_adapter() -> TypeAdapter[NodeSpec]:
|
|
185
|
+
"""
|
|
186
|
+
Gets a type adapter for the NodeSpec type, which is a union type and cannot
|
|
187
|
+
be instantiated directly.
|
|
188
|
+
|
|
189
|
+
:return: A type adapter for the NodeSpec type.
|
|
190
|
+
"""
|
|
191
|
+
return _node_spec_adapter
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
NodeSetKind = Literal['NodeSet']
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
NodeSetIdentifier = str
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
NodeIdentifier = str
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class NodeSet(BaseModel):
|
|
204
|
+
|
|
205
|
+
kind: NodeSetKind = Field(title='Kind',
|
|
206
|
+
description="This object's kind")
|
|
207
|
+
|
|
208
|
+
id: NodeSetIdentifier = Field(title='Node Set Identifier',
|
|
209
|
+
description='An identifier for the node set')
|
|
210
|
+
|
|
211
|
+
name: str = Field(title='Node Set Name',
|
|
212
|
+
description='A name for the node set')
|
|
213
|
+
|
|
214
|
+
nodes: dict[NodeIdentifier, NodeSpec] = Field(min_length=1,
|
|
215
|
+
title='Nodes',
|
|
216
|
+
description='A non-empty list of nodes')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lockss-pybasic
|
|
3
|
-
Version: 0.3.0.
|
|
3
|
+
Version: 0.3.0.dev5
|
|
4
4
|
Summary: Basic Python utilities
|
|
5
5
|
License: BSD-3-Clause
|
|
6
6
|
License-File: LICENSE
|
|
@@ -17,7 +17,7 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Topic :: Software Development :: Libraries
|
|
19
19
|
Requires-Dist: annotated-types (>=0.7.0,<0.8.0)
|
|
20
|
-
Requires-Dist: click-extra (>=7.
|
|
20
|
+
Requires-Dist: click-extra (>=7.18.0,<7.19.0)
|
|
21
21
|
Requires-Dist: pydantic (>=2.13.0,<2.14.0)
|
|
22
22
|
Project-URL: Repository, https://github.com/lockss/lockss-pybasic
|
|
23
23
|
Description-Content-Type: text/x-rst
|
|
@@ -26,7 +26,7 @@ Description-Content-Type: text/x-rst
|
|
|
26
26
|
lockss-pybasic
|
|
27
27
|
==============
|
|
28
28
|
|
|
29
|
-
.. |RELEASE| replace:: 0.3.0-
|
|
29
|
+
.. |RELEASE| replace:: 0.3.0-dev5
|
|
30
30
|
.. |RELEASE_DATE| replace:: NOT YET RELEASED
|
|
31
31
|
|
|
32
32
|
**Latest release:** |RELEASE| (|RELEASE_DATE|)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
lockss/pybasic/__init__.py,sha256=XiPgHX6Sah_mC2zIQLn_vTG1Celb8_sl1tZoDNm7RYw,1678
|
|
2
|
+
lockss/pybasic/auidutil.py,sha256=Q4vjjGfymiXVwPu35RyyLZBnViv8mDJKCjOyJb-sbS8,13921
|
|
3
|
+
lockss/pybasic/cliutil.py,sha256=-o5YIh5JPDxwyZ61U9iZshsmzYwOHW-HHFa2ndFO__g,8130
|
|
4
|
+
lockss/pybasic/errorutil.py,sha256=4EaO0a1yIG1DbWltASeT15bg1bGg5kOYspsW0iJdVLc,1951
|
|
5
|
+
lockss/pybasic/fileutil.py,sha256=P_XW_UX6hVvP45vRV8fduclbGmoFflW83a6ZfFE-8Hc,2966
|
|
6
|
+
lockss/pybasic/nodeutil.py,sha256=5ocngBCDbeMM7aqGCM10VC314p7VZD0DUMzHrpt3e4s,7911
|
|
7
|
+
lockss_pybasic-0.3.0.dev5.dist-info/METADATA,sha256=jYuOR61q0mUYeB1I_8Uc00K_ac5oW4uavN8gXOVak7I,2338
|
|
8
|
+
lockss_pybasic-0.3.0.dev5.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
9
|
+
lockss_pybasic-0.3.0.dev5.dist-info/licenses/LICENSE,sha256=EOxPunNz3XP6AjgbPFolu-d9BS_AF9TtKn1WXgeYPsE,1506
|
|
10
|
+
lockss_pybasic-0.3.0.dev5.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
lockss/pybasic/__init__.py,sha256=wBkseM3G1zUhxYaKUdsw7FygMFNiaPmwpPmDRGCEIsA,1678
|
|
2
|
-
lockss/pybasic/auidutil.py,sha256=Q4vjjGfymiXVwPu35RyyLZBnViv8mDJKCjOyJb-sbS8,13921
|
|
3
|
-
lockss/pybasic/cliutil.py,sha256=F970MhLcQCYS3INLQT1Ij8nKt_ICmttiS3nSnuWuN7s,8106
|
|
4
|
-
lockss/pybasic/errorutil.py,sha256=4EaO0a1yIG1DbWltASeT15bg1bGg5kOYspsW0iJdVLc,1951
|
|
5
|
-
lockss/pybasic/fileutil.py,sha256=IIS2AFDgYtmBLPVHqQi1AkIFj6da04b6NQtjX9bIVqQ,2958
|
|
6
|
-
lockss/pybasic/nodeutil.py,sha256=MUd8JoJmn_cZSN8iwG5KOLSRorIfHIEySeYhojm5rlw,6206
|
|
7
|
-
lockss_pybasic-0.3.0.dev3.dist-info/METADATA,sha256=aTqzPE7mJFwwc8oZtYtZGdp4JVuEpBKHk-1K0Rn1BpU,2338
|
|
8
|
-
lockss_pybasic-0.3.0.dev3.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
9
|
-
lockss_pybasic-0.3.0.dev3.dist-info/licenses/LICENSE,sha256=EOxPunNz3XP6AjgbPFolu-d9BS_AF9TtKn1WXgeYPsE,1506
|
|
10
|
-
lockss_pybasic-0.3.0.dev3.dist-info/RECORD,,
|
|
File without changes
|
{lockss_pybasic-0.3.0.dev3.dist-info → lockss_pybasic-0.3.0.dev5.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|