lockss-pybasic 0.2.0.dev17__py3-none-any.whl → 0.3.0.dev1__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 +2 -2
- lockss/pybasic/auidutil.py +1 -1
- lockss/pybasic/cliutil.py +99 -4
- lockss/pybasic/errorutil.py +1 -1
- lockss/pybasic/fileutil.py +1 -1
- lockss/pybasic/nodeutil.py +121 -0
- lockss_pybasic-0.3.0.dev1.dist-info/METADATA +69 -0
- lockss_pybasic-0.3.0.dev1.dist-info/RECORD +10 -0
- {lockss_pybasic-0.2.0.dev17.dist-info → lockss_pybasic-0.3.0.dev1.dist-info}/WHEEL +1 -1
- {lockss_pybasic-0.2.0.dev17.dist-info → lockss_pybasic-0.3.0.dev1.dist-info}/licenses/LICENSE +1 -1
- lockss_pybasic-0.2.0.dev17.dist-info/METADATA +0 -97
- lockss_pybasic-0.2.0.dev17.dist-info/RECORD +0 -9
lockss/pybasic/__init__.py
CHANGED
|
@@ -5,7 +5,7 @@ Basic Python utilities.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
__copyright__ = '''
|
|
8
|
-
Copyright (c) 2000-
|
|
8
|
+
Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
|
|
9
9
|
'''.strip()
|
|
10
10
|
|
|
11
11
|
__license__ = __copyright__ + '\n\n' + '''
|
|
@@ -36,4 +36,4 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
36
36
|
POSSIBILITY OF SUCH DAMAGE.
|
|
37
37
|
'''.strip()
|
|
38
38
|
|
|
39
|
-
__version__ = '0.
|
|
39
|
+
__version__ = '0.3.0-dev1'
|
lockss/pybasic/auidutil.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
-
# Copyright (c) 2000-
|
|
3
|
+
# Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
|
|
4
4
|
#
|
|
5
5
|
# Redistribution and use in source and binary forms, with or without
|
|
6
6
|
# modification, are permitted provided that the following conditions are met:
|
lockss/pybasic/cliutil.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
-
# Copyright (c) 2000-
|
|
3
|
+
# Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
|
|
4
4
|
#
|
|
5
5
|
# Redistribution and use in source and binary forms, with or without
|
|
6
6
|
# modification, are permitted provided that the following conditions are met:
|
|
@@ -33,7 +33,7 @@ Command line utilities.
|
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
35
|
from pathlib import Path
|
|
36
|
-
from typing import Any, Optional
|
|
36
|
+
from typing import Any, Optional, Union
|
|
37
37
|
|
|
38
38
|
import click
|
|
39
39
|
from click.types import ParamType, IntRange
|
|
@@ -42,6 +42,61 @@ from click_extra.colorize import default_theme
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def click_path(spec: Optional[str]) -> click.Path:
|
|
45
|
+
"""
|
|
46
|
+
Generates a ``click.Path`` based on a specification string.
|
|
47
|
+
|
|
48
|
+
The specification string can contain the following specifier characters:
|
|
49
|
+
|
|
50
|
+
.. list-table::
|
|
51
|
+
:header-rows: 1
|
|
52
|
+
|
|
53
|
+
* * Specifier
|
|
54
|
+
* Present
|
|
55
|
+
* Mutually exclusive with
|
|
56
|
+
* * ``f``
|
|
57
|
+
* Must be a file
|
|
58
|
+
* ``d`` [*]
|
|
59
|
+
* * ``d``
|
|
60
|
+
* Must be a directory
|
|
61
|
+
* ``f`` [*]
|
|
62
|
+
* * ``e``
|
|
63
|
+
* File or directory must exist
|
|
64
|
+
* ``E``
|
|
65
|
+
* * ``E``
|
|
66
|
+
* File or directory may or may not exist, but if it does not exist,
|
|
67
|
+
other checks are skipped (default)
|
|
68
|
+
* ``e``
|
|
69
|
+
* * ``r``
|
|
70
|
+
* File or directory must be readable
|
|
71
|
+
*
|
|
72
|
+
* * ``w``
|
|
73
|
+
* File or directory must be writable
|
|
74
|
+
*
|
|
75
|
+
* * ``x``
|
|
76
|
+
* File or directory must be executable
|
|
77
|
+
*
|
|
78
|
+
* * ``p``
|
|
79
|
+
* Resulting path will be ``pathlib.Path`` (default)
|
|
80
|
+
* ``s``
|
|
81
|
+
* * ``s``
|
|
82
|
+
* Resulting path will be ``str``
|
|
83
|
+
* ``p``
|
|
84
|
+
* * ``-``
|
|
85
|
+
* Path is allowed to be ``-``
|
|
86
|
+
*
|
|
87
|
+
* * ``z``
|
|
88
|
+
* Path will be absolute and resolved, with ``pathlib.Path.resolve``
|
|
89
|
+
*
|
|
90
|
+
|
|
91
|
+
When two mutual exclusive specifiers are present, ``ValueError`` is raised.
|
|
92
|
+
|
|
93
|
+
:param spec: A specification string.
|
|
94
|
+
:type spec: str
|
|
95
|
+
:return: A ``click.Path``.
|
|
96
|
+
:rtype: click.Path
|
|
97
|
+
:raises ValueError: If two mutually exclusive specifiers are present in the
|
|
98
|
+
specification string.
|
|
99
|
+
"""
|
|
45
100
|
if spec is None:
|
|
46
101
|
spec = ''
|
|
47
102
|
allow_dash = False
|
|
@@ -66,7 +121,7 @@ def click_path(spec: Optional[str]) -> click.Path:
|
|
|
66
121
|
elif char == 'E':
|
|
67
122
|
if 'e' in spec:
|
|
68
123
|
raise ValueError(f'"E" and "e" are mutually exclusive: {spec}')
|
|
69
|
-
exists =
|
|
124
|
+
exists = False
|
|
70
125
|
elif char == 'f':
|
|
71
126
|
if 'd' in spec:
|
|
72
127
|
raise ValueError(f'"f" and "d" are mutually exclusive: {spec}')
|
|
@@ -103,6 +158,16 @@ def click_path(spec: Optional[str]) -> click.Path:
|
|
|
103
158
|
writable=writable)
|
|
104
159
|
|
|
105
160
|
|
|
161
|
+
#: Composes the given decorators, so that
|
|
162
|
+
#: @compose_decorators(f, g, h)
|
|
163
|
+
#: def foo():
|
|
164
|
+
#: pass
|
|
165
|
+
#: is equivalent to:
|
|
166
|
+
#: @f
|
|
167
|
+
# @g
|
|
168
|
+
# @h
|
|
169
|
+
#: def foo():
|
|
170
|
+
#: pass
|
|
106
171
|
def compose_decorators(*decorators):
|
|
107
172
|
def wrapped(decorated):
|
|
108
173
|
for dec in reversed(decorators):
|
|
@@ -111,12 +176,37 @@ def compose_decorators(*decorators):
|
|
|
111
176
|
return wrapped
|
|
112
177
|
|
|
113
178
|
|
|
114
|
-
def make_table_format_option(switches: tuple[str, ...] = ('--table-format', '-T'),
|
|
179
|
+
def make_table_format_option(switches: Union[str, tuple[str, ...]] = ('--table-format', '-T'),
|
|
115
180
|
default: TableFormat = TableFormat.SIMPLE):
|
|
181
|
+
"""
|
|
182
|
+
Makes an equivalent of ``click_Extra.table_format_option`` with the given
|
|
183
|
+
command line switches and the given table format default.
|
|
184
|
+
|
|
185
|
+
The standard ``click_Extra.table_format_option`` attaches to the top-level
|
|
186
|
+
command only.
|
|
187
|
+
|
|
188
|
+
:param switches: A string or tuple of strings for the command line switches.
|
|
189
|
+
:type switches: Union[str, tuple[str, ...]]
|
|
190
|
+
:param default: A ``click_extra.TableFormat`` default.
|
|
191
|
+
:type default: TableFormat
|
|
192
|
+
:return: A remixed ``click_Extra.table_format_option``.
|
|
193
|
+
:rtype:
|
|
194
|
+
"""
|
|
195
|
+
if type(switches) == str:
|
|
196
|
+
switches = (switches,)
|
|
116
197
|
return option(*switches, type=EnumChoice(TableFormat, choice_source=ChoiceSource.VALUE), default=default, show_default=True, help='Set the rendering of tables to the given style.')
|
|
117
198
|
|
|
118
199
|
|
|
119
200
|
def make_extra_context_settings() -> dict[str, Any]:
|
|
201
|
+
"""
|
|
202
|
+
Makes a custom ``click_Extra.ExtraContext`` with essential changes.
|
|
203
|
+
|
|
204
|
+
Currently, the only change is that the help formatter styles the invoked
|
|
205
|
+
command in bold.
|
|
206
|
+
|
|
207
|
+
:return: A custom ``click_Extra.ExtraContext``.
|
|
208
|
+
:rtype: dict[str, Any]
|
|
209
|
+
"""
|
|
120
210
|
return ExtraContext.settings(
|
|
121
211
|
formatter_settings=HelpExtraFormatter.settings(
|
|
122
212
|
theme=default_theme.with_(
|
|
@@ -126,16 +216,21 @@ def make_extra_context_settings() -> dict[str, Any]:
|
|
|
126
216
|
)
|
|
127
217
|
|
|
128
218
|
|
|
219
|
+
#: A ``click.ParamType`` for strictly positive integers (1 to infinity).
|
|
129
220
|
PositiveInt: ParamType = IntRange(min=1, max=None)
|
|
130
221
|
|
|
131
222
|
|
|
223
|
+
#: A ``click.ParamType`` for non-negative integers (0 to infinity).
|
|
132
224
|
NonNegativeInt: ParamType = IntRange(min=0, max=None)
|
|
133
225
|
|
|
134
226
|
|
|
227
|
+
#: A ``click.ParamType`` for strictly negative integers (negative infinity to -1).
|
|
135
228
|
NegativeInt: ParamType = IntRange(min=None, max=-1)
|
|
136
229
|
|
|
137
230
|
|
|
231
|
+
#: A ``click.ParamType`` for non-positive integers (negative infinity to 0).
|
|
138
232
|
NonPositiveInt: ParamType = IntRange(min=None, max=0)
|
|
139
233
|
|
|
140
234
|
|
|
235
|
+
#: A ``click.ParamType`` for unsigned 16-bit integers (0 to 65535).
|
|
141
236
|
UInt16: ParamType = IntRange(min=0, max=65535)
|
lockss/pybasic/errorutil.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
-
# Copyright (c) 2000-
|
|
3
|
+
# Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
|
|
4
4
|
#
|
|
5
5
|
# Redistribution and use in source and binary forms, with or without
|
|
6
6
|
# modification, are permitted provided that the following conditions are met:
|
lockss/pybasic/fileutil.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
-
# Copyright (c) 2000-
|
|
3
|
+
# Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
|
|
4
4
|
#
|
|
5
5
|
# Redistribution and use in source and binary forms, with or without
|
|
6
6
|
# modification, are permitted provided that the following conditions are met:
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
|
|
4
|
+
#
|
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
|
7
|
+
#
|
|
8
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
# this list of conditions and the following disclaimer.
|
|
10
|
+
#
|
|
11
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
# and/or other materials provided with the distribution.
|
|
14
|
+
#
|
|
15
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
|
16
|
+
# may be used to endorse or promote products derived from this software without
|
|
17
|
+
# specific prior written permission.
|
|
18
|
+
#
|
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
23
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
24
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
25
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
26
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
27
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
28
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
LOCKSS node utilities.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from enum import Enum
|
|
36
|
+
from re import Pattern
|
|
37
|
+
import re
|
|
38
|
+
from typing import Any, Optional
|
|
39
|
+
|
|
40
|
+
from pydantic import BaseModel, Field, model_validator
|
|
41
|
+
|
|
42
|
+
from .errorutil import InternalError
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
RE_NODE_REFERENCE: Pattern = 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+))?)?)?)?)?)?')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class LockssNodeTypeEnum(Enum):
|
|
49
|
+
V1 = 'v1'
|
|
50
|
+
V2 = 'v2'
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class LockssNodeProtocolEnum(Enum):
|
|
54
|
+
HTTP = 'http'
|
|
55
|
+
HTTPS = 'https'
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
DEFAULT_UI_PORT_V1: int = 8081
|
|
59
|
+
|
|
60
|
+
DEFAULT_REPO_PORT: int = 24611
|
|
61
|
+
|
|
62
|
+
DEFAULT_CFG_PORT: int = 24612
|
|
63
|
+
|
|
64
|
+
DEFAULT_POL_PORT: int = 24613
|
|
65
|
+
|
|
66
|
+
DEFAULT_CRW_PORT: int = 24614
|
|
67
|
+
|
|
68
|
+
DEFAULT_MD_PORT: int = 24615
|
|
69
|
+
|
|
70
|
+
DEFAULT_SOAP_PORT: int = 24616
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class LockssNodeModel(BaseModel):
|
|
74
|
+
host: str = Field(title='Host', description="The LOCKSS node's host")
|
|
75
|
+
type: LockssNodeTypeEnum = Field(default=LockssNodeTypeEnum.V2, title='Type', description='LOCKSS node type')
|
|
76
|
+
protocol: LockssNodeProtocolEnum = Field(default=LockssNodeProtocolEnum.HTTPS, title='Protocol', description="The protocol for reaching the node")
|
|
77
|
+
repository: int = Field(default=DEFAULT_REPO_PORT, title='Repository Port', description="The node's Repository Service REST API Port")
|
|
78
|
+
configuration: int = Field(default=DEFAULT_CFG_PORT, title='Configuration Port', description="The node's Configuration Service REST API Port")
|
|
79
|
+
poller: int = Field(default=DEFAULT_POL_PORT, title='Poller Port', description="The node's Poller Service REST API Port")
|
|
80
|
+
crawler: int = Field(default=DEFAULT_CRW_PORT, title='Crawler Port', description="The node's Crawler Service REST API Port")
|
|
81
|
+
metadata: int = Field(default=DEFAULT_MD_PORT, title='Metadata Port', description="The node's Metadata Service REST API Port")
|
|
82
|
+
soap: int = Field(default=DEFAULT_SOAP_PORT, title='SOAP Port', description="The node's SOAP Compatibility Service REST API Port")
|
|
83
|
+
ui: int = Field(default=DEFAULT_UI_PORT_V1, title='UI Port', description="The LOCKSS 1.x node's Web user interface port")
|
|
84
|
+
|
|
85
|
+
@model_validator(mode='before')
|
|
86
|
+
@classmethod
|
|
87
|
+
def _validate_model_type(cls, data: Any) -> Any:
|
|
88
|
+
_has, _get, _set = (dict.__contains__, dict.get, dict.__setitem__) if isinstance(data, dict) else (hasattr, getattr, setattr)
|
|
89
|
+
if (host_orig := _get(data, 'host', None)) is not None and isinstance(host_orig, str):
|
|
90
|
+
host_ref: str = host_orig
|
|
91
|
+
mat = RE_NODE_REFERENCE.fullmatch(host_ref)
|
|
92
|
+
if mat is None:
|
|
93
|
+
raise ValueError(f'Invalid node reference: {host_ref}')
|
|
94
|
+
# Avoid conflicting definitions
|
|
95
|
+
five = ('configuration', 'poller', 'crawler', 'metadata', 'soap')
|
|
96
|
+
for k in ('protocol', *five):
|
|
97
|
+
if h_val := mat.group(k):
|
|
98
|
+
if _get(data, k, None) is not None:
|
|
99
|
+
raise ValueError(f"Node reference conflicts with '{k}' definition: {h_val}")
|
|
100
|
+
_set(data, k, h_val)
|
|
101
|
+
# 'repository' + 'ui' is more complicated
|
|
102
|
+
if h_repo_or_ui := mat.group('repository'):
|
|
103
|
+
for k in ('repository', 'ui'):
|
|
104
|
+
if _get(data, k, None) is not None:
|
|
105
|
+
raise ValueError(f"Node reference conflicts with '{k}' definition: {h_repo_or_ui}")
|
|
106
|
+
try:
|
|
107
|
+
repo_or_ui_int: int = int(h_repo_or_ui)
|
|
108
|
+
except ValueError as ve:
|
|
109
|
+
raise InternalError from ve # shouldn't happen, should be \d+
|
|
110
|
+
if any(mat.group(x) for x in five) or repo_or_ui_int >= 10000:
|
|
111
|
+
_set(data, 'repository', h_repo_or_ui) # Assume v2
|
|
112
|
+
elif 1000 <= repo_or_ui_int < 10000:
|
|
113
|
+
_set(data, 'ui', h_repo_or_ui) # Assume v1
|
|
114
|
+
if _get(data, 'type', None) is None:
|
|
115
|
+
_set(data, 'type', LockssNodeTypeEnum.V1.value)
|
|
116
|
+
else:
|
|
117
|
+
raise ValueError(f'Invalid repository/UI port in node reference: {repo_or_ui_int}')
|
|
118
|
+
# Finally, reset 'host'
|
|
119
|
+
if any(mat.group(x) for x in ('protocol', 'repository', *five)):
|
|
120
|
+
_set(data, 'host', mat.group('host'))
|
|
121
|
+
return data
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lockss-pybasic
|
|
3
|
+
Version: 0.3.0.dev1
|
|
4
|
+
Summary: Basic Python utilities
|
|
5
|
+
License: BSD-3-Clause
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Thib Guicherd-Callin
|
|
8
|
+
Author-email: thib@cs.stanford.edu
|
|
9
|
+
Maintainer: Thib Guicherd-Callin
|
|
10
|
+
Maintainer-email: thib@cs.stanford.edu
|
|
11
|
+
Requires-Python: >=3.10,<4.0
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Framework :: Pydantic :: 2
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Requires-Dist: click-extra (>=7.5.0,<7.6.0)
|
|
20
|
+
Requires-Dist: pydantic (>=2.13.0,<2.14.0)
|
|
21
|
+
Project-URL: Repository, https://github.com/lockss/lockss-pybasic
|
|
22
|
+
Description-Content-Type: text/x-rst
|
|
23
|
+
|
|
24
|
+
==============
|
|
25
|
+
lockss-pybasic
|
|
26
|
+
==============
|
|
27
|
+
|
|
28
|
+
.. |RELEASE| replace:: 0.3.0-dev1
|
|
29
|
+
.. |RELEASE_DATE| replace:: NOT YET RELEASED
|
|
30
|
+
|
|
31
|
+
**Latest release:** |RELEASE| (|RELEASE_DATE|)
|
|
32
|
+
|
|
33
|
+
``lockss-pybasic`` provides basic utilities for various LOCKSS projects written in Python.
|
|
34
|
+
|
|
35
|
+
-------
|
|
36
|
+
Modules
|
|
37
|
+
-------
|
|
38
|
+
|
|
39
|
+
``lockss.pybasic.cliutil``
|
|
40
|
+
Command line utilities based on `Click Extra <https://kdeldycke.github.io/click-extra>`_, `Cloup <https://cloup.readthedocs.io/>`_ and `Click <https://click.palletsprojects.com/>`_.
|
|
41
|
+
|
|
42
|
+
* ``click_path()``: a ``click.Path`` utility.
|
|
43
|
+
|
|
44
|
+
* ``PositiveInt``, ``NonNegativeInt``, ``NegativeInt``, ``NonPositiveInt``, ``UInt16``: ``click.ParamType`` integer types.
|
|
45
|
+
|
|
46
|
+
* ``compose_decorators()``: a decorator utility.
|
|
47
|
+
|
|
48
|
+
* ``make_table_format_option()``: a remix of ``click_extra.table_format_option`` that is not attached to the top-level command.
|
|
49
|
+
|
|
50
|
+
* ``make_extra_context_settings()``: a custom ``click_extra.ExtraContext``.
|
|
51
|
+
|
|
52
|
+
``lockss.pybasic.errorutil``
|
|
53
|
+
Error and exception utilities.
|
|
54
|
+
|
|
55
|
+
* ``InternalError`` is a no-arg subclass of ``RuntimeError``.
|
|
56
|
+
|
|
57
|
+
``lockss.pybasic.fileutil``
|
|
58
|
+
File and path utilities.
|
|
59
|
+
|
|
60
|
+
* ``file_lines`` returns the non-empty lines of a file stripped of comments that begin with ``#`` and run to the end of a line.
|
|
61
|
+
|
|
62
|
+
* ``path`` takes a string or ``PurePath`` and returns a ``Path`` for which ``Path.expanduser()`` and ``Path.resolve()`` have been called.
|
|
63
|
+
|
|
64
|
+
-------------
|
|
65
|
+
Release Notes
|
|
66
|
+
-------------
|
|
67
|
+
|
|
68
|
+
See `<CHANGELOG.rst>`_.
|
|
69
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
lockss/pybasic/__init__.py,sha256=Di827wCOGLbdHTBsHTuC5h1XWJp49Sx9lmQdQFwezYk,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=m9WCVbfSzOKklA9qstoOUUbF0v-l1TtePfx6rjEh_IU,5872
|
|
7
|
+
lockss_pybasic-0.3.0.dev1.dist-info/METADATA,sha256=hI0ISj0Y2_0WPP4HdfRDV8EG2JEWLIqt3SlS5c2w6Yo,2288
|
|
8
|
+
lockss_pybasic-0.3.0.dev1.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
|
|
9
|
+
lockss_pybasic-0.3.0.dev1.dist-info/licenses/LICENSE,sha256=EOxPunNz3XP6AjgbPFolu-d9BS_AF9TtKn1WXgeYPsE,1506
|
|
10
|
+
lockss_pybasic-0.3.0.dev1.dist-info/RECORD,,
|
{lockss_pybasic-0.2.0.dev17.dist-info → lockss_pybasic-0.3.0.dev1.dist-info}/licenses/LICENSE
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2000-
|
|
1
|
+
Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
|
|
2
2
|
|
|
3
3
|
Redistribution and use in source and binary forms, with or without
|
|
4
4
|
modification, are permitted provided that the following conditions are met:
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: lockss-pybasic
|
|
3
|
-
Version: 0.2.0.dev17
|
|
4
|
-
Summary: Basic Python utilities
|
|
5
|
-
License: BSD-3-Clause
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Author: Thib Guicherd-Callin
|
|
8
|
-
Author-email: thib@cs.stanford.edu
|
|
9
|
-
Maintainer: Thib Guicherd-Callin
|
|
10
|
-
Maintainer-email: thib@cs.stanford.edu
|
|
11
|
-
Requires-Python: >=3.10,<4.0
|
|
12
|
-
Classifier: Development Status :: 4 - Beta
|
|
13
|
-
Classifier: Environment :: Console
|
|
14
|
-
Classifier: Framework :: Pydantic :: 2
|
|
15
|
-
Classifier: Intended Audience :: Developers
|
|
16
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
17
|
-
Classifier: Programming Language :: Python
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
-
Requires-Dist: click-extra (>=7.5.0,<7.6.0)
|
|
20
|
-
Project-URL: Repository, https://github.com/lockss/lockss-pybasic
|
|
21
|
-
Description-Content-Type: text/x-rst
|
|
22
|
-
|
|
23
|
-
==============
|
|
24
|
-
lockss-pybasic
|
|
25
|
-
==============
|
|
26
|
-
|
|
27
|
-
.. |RELEASE| replace:: 0.2.0-dev17
|
|
28
|
-
.. |RELEASE_DATE| replace:: NOT YET RELEASED
|
|
29
|
-
|
|
30
|
-
**Latest release:** |RELEASE| (|RELEASE_DATE|)
|
|
31
|
-
|
|
32
|
-
``lockss-pybasic`` provides basic utilities for various LOCKSS projects written in Python.
|
|
33
|
-
|
|
34
|
-
-------
|
|
35
|
-
Modules
|
|
36
|
-
-------
|
|
37
|
-
|
|
38
|
-
``lockss.pybasic.cliutil``
|
|
39
|
-
Command line utilities.
|
|
40
|
-
|
|
41
|
-
* ``BaseCli`` is a base class for command line interfaces that uses `pydantic-argparse <https://pypi.org/project/pydantic-argparse/>`_ to define arguments and subcommands, and `rich-argparse <https://pypi.org/project/rich-argparse/>`_ to display help messages. For each command ``x-y-z`` induced by a ``pydantic-argparse`` field named ``x_y_z`` deriving from ``BaseModel`` , the ``dispatch()`` method expects a method named ``_x_y_z``.
|
|
42
|
-
|
|
43
|
-
* ``StringCommand`` provides a `pydantic-argparse <https://pypi.org/project/pydantic-argparse/>`_ way to define a subcommand whose only purpose is printing a string, with ``COPYRIGHT_DESCRIPTION``, ``LICENSE_DESCRIPTION`` and ``VERSION_DESCRIPTION`` constants provided for convenience. Example:
|
|
44
|
-
|
|
45
|
-
.. code-block:: python
|
|
46
|
-
|
|
47
|
-
class MyCliModel(BaseModel):
|
|
48
|
-
copyright: Optional[StringCommand.type(my_copyright_string)] = Field(description=COPYRIGHT_DESCRIPTION)
|
|
49
|
-
|
|
50
|
-
* ``at_most_one_from_enum`` and ``get_from_enum`` provide a facility for defining a `pydantic-argparse <https://pypi.org/project/pydantic-argparse/>`_ model that defines one command line option per constant of an ``Enum``, using an ``enum`` keyword argument in the ``Field`` definition. Example:
|
|
51
|
-
|
|
52
|
-
.. code-block:: python
|
|
53
|
-
|
|
54
|
-
class Orientation(Enum):
|
|
55
|
-
horizontal = 1
|
|
56
|
-
vertical = 2
|
|
57
|
-
diagonal = 3
|
|
58
|
-
|
|
59
|
-
DEFAULT_ORIENTATION = Orientation.horizontal
|
|
60
|
-
|
|
61
|
-
class MyCliModel(BaseModel):
|
|
62
|
-
diagonal: Optional[bool] = Field(False, description='display diagonally', enum=Orientation)
|
|
63
|
-
horizontal: Optional[bool] = Field(False, description='display horizontally', enum=Orientation)
|
|
64
|
-
unrelated: Optional[bool] = Field(...)
|
|
65
|
-
vertical: Optional[bool] = Field(False, description='display vertically', enum=Orientation)
|
|
66
|
-
|
|
67
|
-
@root_validator
|
|
68
|
-
def _at_most_one_orientation(cls, values):
|
|
69
|
-
return at_most_one_from_enum(cls, values, Orientation)
|
|
70
|
-
|
|
71
|
-
def get_orientation(self) -> Orientation:
|
|
72
|
-
return get_from_enum(self, Orientation, DEFAULT_ORIENTATION)
|
|
73
|
-
|
|
74
|
-
``lockss.pybasic.errorutil``
|
|
75
|
-
Error and exception utilities.
|
|
76
|
-
|
|
77
|
-
* ``InternalError`` is a no-arg subclass of ``RuntimeError``.
|
|
78
|
-
|
|
79
|
-
``lockss.pybasic.fileutil``
|
|
80
|
-
File and path utilities.
|
|
81
|
-
|
|
82
|
-
* ``file_lines`` returns the non-empty lines of a file stripped of comments that begin with ``#`` and run to the end of a line.
|
|
83
|
-
|
|
84
|
-
* ``path`` takes a string or ``PurePath`` and returns a ``Path`` for which ``Path.expanduser()`` and ``Path.resolve()`` have been called.
|
|
85
|
-
|
|
86
|
-
``lockss.pybasic.outpututil``
|
|
87
|
-
Utilities to work with `tabulate <https://pypi.org/project/tabulate/>`_.
|
|
88
|
-
|
|
89
|
-
* ``OutputFormat`` is an ``Enum`` of all the output formats from ``tabulate.tabulate_formats``.
|
|
90
|
-
|
|
91
|
-
* ``OutputFormatOptions`` defines a `pydantic-argparse <https://pypi.org/project/pydantic-argparse/>`_ model for setting an output format from ``OutputFormat``.
|
|
92
|
-
|
|
93
|
-
-------------
|
|
94
|
-
Release Notes
|
|
95
|
-
-------------
|
|
96
|
-
|
|
97
|
-
See `<CHANGELOG.rst>`_.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
lockss/pybasic/__init__.py,sha256=xyOS0r-MCbYk_2x5lxR-r6ztn4kfcYehvOMGEfjWKew,1679
|
|
2
|
-
lockss/pybasic/auidutil.py,sha256=o5IsRLEYROXRVS6oTO1VFtdzw7SImYSR5VcqAMHY4To,13921
|
|
3
|
-
lockss/pybasic/cliutil.py,sha256=JAxNvAIN61gM4s8L3yDTG4InSQDaCaLhPzaoNyrc7G0,5045
|
|
4
|
-
lockss/pybasic/errorutil.py,sha256=XI84PScZ851_-gfoazivJ8ceieMYWaxQr7qih5ltga0,1951
|
|
5
|
-
lockss/pybasic/fileutil.py,sha256=BpdoPWL70xYTuhyQRBEurScRVnPQg0mX-XW8yyKPGjw,2958
|
|
6
|
-
lockss_pybasic-0.2.0.dev17.dist-info/METADATA,sha256=kcm_mrdusmbi6s1qZbVTICQ25iHesLHVjfw75geumnc,4145
|
|
7
|
-
lockss_pybasic-0.2.0.dev17.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
8
|
-
lockss_pybasic-0.2.0.dev17.dist-info/licenses/LICENSE,sha256=O9ONND4uDxY_jucI4jZDf2liAk05ScEJaYu-Al7EOdQ,1506
|
|
9
|
-
lockss_pybasic-0.2.0.dev17.dist-info/RECORD,,
|