lockss-pybasic 0.2.0.dev17__py3-none-any.whl → 0.3.0.dev2__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.
@@ -5,7 +5,7 @@ Basic Python utilities.
5
5
  """
6
6
 
7
7
  __copyright__ = '''
8
- Copyright (c) 2000-2025, Board of Trustees of Leland Stanford Jr. University
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.2.0-dev17'
39
+ __version__ = '0.3.0-dev2'
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- # Copyright (c) 2000-2025, Board of Trustees of Leland Stanford Jr. University
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-2025, Board of Trustees of Leland Stanford Jr. University
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 = True
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)
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- # Copyright (c) 2000-2025, Board of Trustees of Leland Stanford Jr. University
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:
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- # Copyright (c) 2000-2025, Board of Trustees of Leland Stanford Jr. University
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.dev2
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.16.0,<7.17.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-dev2
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=c5oAL1taDdPzGD1IxLXhG8k-0GEdxr9duOohhsF4JA4,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.dev2.dist-info/METADATA,sha256=mtfXJBQQ36ruZpFsntyU_-sF9FOdAS_O5XtdxmydcMY,2290
8
+ lockss_pybasic-0.3.0.dev2.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
9
+ lockss_pybasic-0.3.0.dev2.dist-info/licenses/LICENSE,sha256=EOxPunNz3XP6AjgbPFolu-d9BS_AF9TtKn1WXgeYPsE,1506
10
+ lockss_pybasic-0.3.0.dev2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.4.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,4 +1,4 @@
1
- Copyright (c) 2000-2025, Board of Trustees of Leland Stanford Jr. University
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,,