lockss-pybasic 0.1.0.dev23__tar.gz → 0.2.0.dev1__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.
@@ -3,11 +3,17 @@ Release Notes
3
3
  =============
4
4
 
5
5
  -----
6
- 0.1.0
6
+ 0.2.0
7
7
  -----
8
8
 
9
9
  Released: ?
10
10
 
11
+ -----
12
+ 0.1.0
13
+ -----
14
+
15
+ Released: 2025-07-01
16
+
11
17
  Initial release, including:
12
18
 
13
19
  * ``cliutil``
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lockss-pybasic
3
- Version: 0.1.0.dev23
3
+ Version: 0.2.0.dev1
4
4
  Summary: Basic Python utilities
5
5
  License: BSD-3-Clause
6
6
  Author: Thib Guicherd-Callin
@@ -12,12 +12,9 @@ Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Environment :: Console
13
13
  Classifier: Framework :: Pydantic :: 2
14
14
  Classifier: Intended Audience :: Developers
15
- Classifier: Intended Audience :: System Administrators
16
15
  Classifier: License :: OSI Approved :: BSD License
17
16
  Classifier: Programming Language :: Python
18
17
  Classifier: Topic :: Software Development :: Libraries
19
- Classifier: Topic :: System :: Archiving
20
- Classifier: Topic :: Utilities
21
18
  Requires-Dist: pydantic (>=2.11.0,<3.0.0)
22
19
  Requires-Dist: pydantic-argparse (>=0.10.0,<0.11.0)
23
20
  Requires-Dist: rich-argparse (>=1.7.0,<1.8.0)
@@ -29,8 +26,8 @@ Description-Content-Type: text/x-rst
29
26
  lockss-pybasic
30
27
  ==============
31
28
 
32
- .. |RELEASE| replace:: 0.1.0-dev23
33
- .. |RELEASE_DATE| replace:: ?
29
+ .. |RELEASE| replace:: 0.1.0
30
+ .. |RELEASE_DATE| replace:: 2025-07-01
34
31
 
35
32
  **Latest release:** |RELEASE| (|RELEASE_DATE|)
36
33
 
@@ -2,8 +2,8 @@
2
2
  lockss-pybasic
3
3
  ==============
4
4
 
5
- .. |RELEASE| replace:: 0.1.0-dev23
6
- .. |RELEASE_DATE| replace:: ?
5
+ .. |RELEASE| replace:: 0.1.0
6
+ .. |RELEASE_DATE| replace:: 2025-07-01
7
7
 
8
8
  **Latest release:** |RELEASE| (|RELEASE_DATE|)
9
9
 
@@ -28,7 +28,7 @@
28
28
 
29
29
  [project]
30
30
  name = "lockss-pybasic"
31
- version = "0.1.0-dev23" # Always change in __init__.py, and at release time in README.rst and CHANGELOG.rst
31
+ version = "0.2.0-dev1" # Always change in __init__.py, and at release time in README.rst and CHANGELOG.rst
32
32
  description = "Basic Python utilities"
33
33
  license = { text = "BSD-3-Clause" }
34
34
  readme = "README.rst"
@@ -50,12 +50,9 @@ classifiers = [
50
50
  "Environment :: Console",
51
51
  "Framework :: Pydantic :: 2",
52
52
  "Intended Audience :: Developers",
53
- "Intended Audience :: System Administrators",
54
53
  "License :: OSI Approved :: BSD License",
55
54
  "Programming Language :: Python",
56
55
  "Topic :: Software Development :: Libraries",
57
- "Topic :: System :: Archiving",
58
- "Topic :: Utilities",
59
56
  ]
60
57
 
61
58
  [project.urls]
@@ -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.1.0-dev23'
39
+ __version__ = '0.2.0-dev1'
@@ -129,22 +129,25 @@ class BaseCli(Generic[BaseModelT]):
129
129
 
130
130
  def dispatch(self) -> None:
131
131
  """
132
- Dispatches from the first field ``x_y_z`` in ``self.args`` that is a
132
+ Dispatches from the first field ``x_y_z`` in ``self._args`` that is a
133
133
  command (i.e. whose value derives from ``BaseModel``) to a method
134
134
  called ``_x_y_z``.
135
135
  """
136
- field_names = self._args.__class__.__fields__.keys()
136
+ self._dispatch_recursive(self._args, [])
137
+
138
+ def _dispatch_recursive(self, base_model: BaseModel, subcommands: list[str]) -> None:
139
+ field_names = base_model.__class__.__fields__.keys()
137
140
  for field_name in field_names:
138
- field_value = getattr(self._args, field_name)
141
+ field_value = getattr(base_model, field_name)
139
142
  if issubclass(type(field_value), BaseModel):
140
- func = getattr(self, f'_{field_name}')
141
- if callable(func):
142
- func(field_value)
143
- else:
144
- self._parser.exit(1, f'internal error: no _{field_name} callable for the {field_name} command')
145
- break
143
+ self._dispatch_recursive(field_value, [*subcommands, field_name])
144
+ return
145
+ func_name = ''.join(f'_{sub}' for sub in subcommands)
146
+ func = getattr(self, func_name)
147
+ if callable(func):
148
+ func(base_model) # FIXME?
146
149
  else:
147
- self._parser.error(f'unknown command; expected one of {', '.join(field_names)}')
150
+ self._parser.exit(1, f'internal error: no {func_name} callable for the {" ".join(sub for sub in subcommands)}')
148
151
 
149
152
  def _initialize_rich_argparse(self) -> None:
150
153
  """