lockss-pybasic 0.1.0.dev22__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.
@@ -0,0 +1,25 @@
1
+ =============
2
+ Release Notes
3
+ =============
4
+
5
+ -----
6
+ 0.2.0
7
+ -----
8
+
9
+ Released: ?
10
+
11
+ -----
12
+ 0.1.0
13
+ -----
14
+
15
+ Released: 2025-07-01
16
+
17
+ Initial release, including:
18
+
19
+ * ``cliutil``
20
+
21
+ * ``errorutil``
22
+
23
+ * ``fileutil``
24
+
25
+ * ``outpututil``
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.3
2
+ Name: lockss-pybasic
3
+ Version: 0.2.0.dev1
4
+ Summary: Basic Python utilities
5
+ License: BSD-3-Clause
6
+ Author: Thib Guicherd-Callin
7
+ Author-email: thib@cs.stanford.edu
8
+ Maintainer: Thib Guicherd-Callin
9
+ Maintainer-email: thib@cs.stanford.edu
10
+ Requires-Python: >=3.9,<4.0
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Framework :: Pydantic :: 2
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: BSD License
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Requires-Dist: pydantic (>=2.11.0,<3.0.0)
19
+ Requires-Dist: pydantic-argparse (>=0.10.0,<0.11.0)
20
+ Requires-Dist: rich-argparse (>=1.7.0,<1.8.0)
21
+ Requires-Dist: tabulate (>=0.9.0,<0.10.0)
22
+ Project-URL: Repository, https://github.com/lockss/lockss-pybasic
23
+ Description-Content-Type: text/x-rst
24
+
25
+ ==============
26
+ lockss-pybasic
27
+ ==============
28
+
29
+ .. |RELEASE| replace:: 0.1.0
30
+ .. |RELEASE_DATE| replace:: 2025-07-01
31
+
32
+ **Latest release:** |RELEASE| (|RELEASE_DATE|)
33
+
34
+ ``lockss-pybasic`` provides basic utilities for various LOCKSS projects written in Python.
35
+
36
+ -------
37
+ Modules
38
+ -------
39
+
40
+ ``lockss.pybasic.cliutil``
41
+ Command line utilities.
42
+
43
+ * ``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``.
44
+
45
+ * ``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:
46
+
47
+ .. code-block:: python
48
+
49
+ class MyCliModel(BaseModel):
50
+ copyright: Optional[StringCommand.type(my_copyright_string)] = Field(description=COPYRIGHT_DESCRIPTION)
51
+
52
+ * ``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:
53
+
54
+ .. code-block:: python
55
+
56
+ class Orientation(Enum):
57
+ horizontal = 1
58
+ vertical = 2
59
+ diagonal = 3
60
+
61
+ DEFAULT_ORIENTATION = Orientation.horizontal
62
+
63
+ class MyCliModel(BaseModel):
64
+ diagonal: Optional[bool] = Field(False, description='display diagonally', enum=Orientation)
65
+ horizontal: Optional[bool] = Field(False, description='display horizontally', enum=Orientation)
66
+ unrelated: Optional[bool] = Field(...)
67
+ vertical: Optional[bool] = Field(False, description='display vertically', enum=Orientation)
68
+
69
+ @root_validator
70
+ def _at_most_one_orientation(cls, values):
71
+ return at_most_one_from_enum(cls, values, Orientation)
72
+
73
+ def get_orientation(self) -> Orientation:
74
+ return get_from_enum(self, Orientation, DEFAULT_ORIENTATION)
75
+
76
+ ``lockss.pybasic.errorutil``
77
+ Error and exception utilities.
78
+
79
+ * ``InternalError`` is a no-arg subclass of ``RuntimeError``.
80
+
81
+ ``lockss.pybasic.fileutil``
82
+ File and path utilities.
83
+
84
+ * ``file_lines`` returns the non-empty lines of a file stripped of comments that begin with ``#`` and run to the end of a line.
85
+
86
+ * ``path`` takes a string or ``PurePath`` and returns a ``Path`` for which ``Path.expanduser()`` and ``Path.resolve()`` have been called.
87
+
88
+ ``lockss.pybasic.outpututil``
89
+ Utilities to work with `tabulate <https://pypi.org/project/tabulate/>`_.
90
+
91
+ * ``OutputFormat`` is an ``Enum`` of all the output formats from ``tabulate.tabulate_formats``.
92
+
93
+ * ``OutputFormatOptions`` defines a `pydantic-argparse <https://pypi.org/project/pydantic-argparse/>`_ model for setting an output format from ``OutputFormat``.
94
+
95
+ -------------
96
+ Release Notes
97
+ -------------
98
+
99
+ See `<CHANGELOG.rst>`_.
@@ -0,0 +1,75 @@
1
+ ==============
2
+ lockss-pybasic
3
+ ==============
4
+
5
+ .. |RELEASE| replace:: 0.1.0
6
+ .. |RELEASE_DATE| replace:: 2025-07-01
7
+
8
+ **Latest release:** |RELEASE| (|RELEASE_DATE|)
9
+
10
+ ``lockss-pybasic`` provides basic utilities for various LOCKSS projects written in Python.
11
+
12
+ -------
13
+ Modules
14
+ -------
15
+
16
+ ``lockss.pybasic.cliutil``
17
+ Command line utilities.
18
+
19
+ * ``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``.
20
+
21
+ * ``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:
22
+
23
+ .. code-block:: python
24
+
25
+ class MyCliModel(BaseModel):
26
+ copyright: Optional[StringCommand.type(my_copyright_string)] = Field(description=COPYRIGHT_DESCRIPTION)
27
+
28
+ * ``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:
29
+
30
+ .. code-block:: python
31
+
32
+ class Orientation(Enum):
33
+ horizontal = 1
34
+ vertical = 2
35
+ diagonal = 3
36
+
37
+ DEFAULT_ORIENTATION = Orientation.horizontal
38
+
39
+ class MyCliModel(BaseModel):
40
+ diagonal: Optional[bool] = Field(False, description='display diagonally', enum=Orientation)
41
+ horizontal: Optional[bool] = Field(False, description='display horizontally', enum=Orientation)
42
+ unrelated: Optional[bool] = Field(...)
43
+ vertical: Optional[bool] = Field(False, description='display vertically', enum=Orientation)
44
+
45
+ @root_validator
46
+ def _at_most_one_orientation(cls, values):
47
+ return at_most_one_from_enum(cls, values, Orientation)
48
+
49
+ def get_orientation(self) -> Orientation:
50
+ return get_from_enum(self, Orientation, DEFAULT_ORIENTATION)
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
+ ``lockss.pybasic.outpututil``
65
+ Utilities to work with `tabulate <https://pypi.org/project/tabulate/>`_.
66
+
67
+ * ``OutputFormat`` is an ``Enum`` of all the output formats from ``tabulate.tabulate_formats``.
68
+
69
+ * ``OutputFormatOptions`` defines a `pydantic-argparse <https://pypi.org/project/pydantic-argparse/>`_ model for setting an output format from ``OutputFormat``.
70
+
71
+ -------------
72
+ Release Notes
73
+ -------------
74
+
75
+ See `<CHANGELOG.rst>`_.
@@ -0,0 +1,73 @@
1
+ # Copyright (c) 2000-2025, Board of Trustees of Leland Stanford Jr. University
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice,
7
+ # this list of conditions and the following disclaimer.
8
+ #
9
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+ #
13
+ # 3. Neither the name of the copyright holder nor the names of its contributors
14
+ # may be used to endorse or promote products derived from this software without
15
+ # specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ [project]
30
+ name = "lockss-pybasic"
31
+ version = "0.2.0-dev1" # Always change in __init__.py, and at release time in README.rst and CHANGELOG.rst
32
+ description = "Basic Python utilities"
33
+ license = { text = "BSD-3-Clause" }
34
+ readme = "README.rst"
35
+ requires-python = ">=3.9,<4.0"
36
+ authors = [
37
+ { name = "Thib Guicherd-Callin", email = "thib@cs.stanford.edu" },
38
+ ]
39
+ maintainers = [
40
+ { name = "Thib Guicherd-Callin", email = "thib@cs.stanford.edu" },
41
+ ]
42
+ dependencies = [
43
+ "pydantic (>=2.11.0,<3.0.0)",
44
+ "pydantic-argparse (>=0.10.0,<0.11.0)",
45
+ "rich-argparse (>=1.7.0,<1.8.0)",
46
+ "tabulate (>=0.9.0,<0.10.0)",
47
+ ]
48
+ classifiers = [
49
+ "Development Status :: 4 - Beta",
50
+ "Environment :: Console",
51
+ "Framework :: Pydantic :: 2",
52
+ "Intended Audience :: Developers",
53
+ "License :: OSI Approved :: BSD License",
54
+ "Programming Language :: Python",
55
+ "Topic :: Software Development :: Libraries",
56
+ ]
57
+
58
+ [project.urls]
59
+ repository = "https://github.com/lockss/lockss-pybasic"
60
+
61
+ [tool.poetry]
62
+ include = [
63
+ "CHANGELOG.rst",
64
+ "LICENSE",
65
+ "README.rst",
66
+ ]
67
+ packages = [
68
+ { include = "lockss", from = "src" }
69
+ ]
70
+
71
+ [build-system]
72
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
73
+ build-backend = "poetry.core.masonry.api"
@@ -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-dev22'
39
+ __version__ = '0.2.0-dev1'
@@ -41,19 +41,33 @@ from pydantic_argparse import ArgumentParser
41
41
  from pydantic_argparse.argparse.actions import SubParsersAction
42
42
  from rich_argparse import RichHelpFormatter
43
43
 
44
+
44
45
  class ActionCommand(Callable, BaseModel):
45
46
  """
46
- Base class for a Pydantic-Argparse command
47
+ Base class for a pydantic-argparse style command.
47
48
  """
48
49
  pass
49
50
 
50
51
 
51
52
  class StringCommand(ActionCommand):
53
+ """
54
+ A pydantic-argparse style command that prints a string.
55
+
56
+ Example of use:
57
+
58
+ .. code-block:: python
59
+
60
+ class MyCliModel(BaseModel):
61
+ copyright: Optional[StringCommand.type(my_copyright_string)] = Field(description=COPYRIGHT_DESCRIPTION)
62
+
63
+ See also the convenience constants ``COPYRIGHT_DESCRIPTION``,
64
+ ``LICENSE_DESCRIPTION``, and ``VERSION_DESCRIPTION``.
65
+ """
52
66
 
53
67
  @staticmethod
54
68
  def type(display_str: str):
55
69
  class _StringCommand(StringCommand):
56
- def __call__(self, file=sys.stdout):
70
+ def __call__(self, file=sys.stdout, **kwargs):
57
71
  print(display_str, file=file)
58
72
  return _StringCommand
59
73
 
@@ -78,20 +92,21 @@ class BaseCli(Generic[BaseModelT]):
78
92
  method named ``_x_y_z``.
79
93
  """
80
94
 
81
- def __init__(self, **extra):
95
+ def __init__(self, **kwargs):
82
96
  """
83
- Makes a new BaseCli instance.
84
-
85
- :param extra: Keyword arguments. Must include ``model``, the
86
- ``BaseModel`` type corresponding to ``BaseModelT``. Must
87
- include ``prog``, the command-line name of the program.
88
- Must include ``description``, the description string of
89
- the program.
97
+ Constructs a new ``BaseCli`` instance.
98
+
99
+ :param kwargs: Keyword arguments. Must include ``model``, the
100
+ ``BaseModel`` type corresponding to ``BaseModelT``. Must
101
+ include ``prog``, the command-line name of the program.
102
+ Must include ``description``, the description string of
103
+ the program.
104
+ :type kwargs: Dict[str, Any]
90
105
  """
91
106
  super().__init__()
92
107
  self._args: Optional[BaseModelT] = None
93
108
  self._parser: Optional[ArgumentParser] = None
94
- self.extra: Dict[str, Any] = dict(**extra)
109
+ self.extra: Dict[str, Any] = dict(**kwargs)
95
110
 
96
111
  def run(self) -> None:
97
112
  """
@@ -104,8 +119,6 @@ class BaseCli(Generic[BaseModelT]):
104
119
  * Stores the Pydantic-Argparse parsed arguments in ``self.args``.
105
120
 
106
121
  * Calls ``dispatch()``.
107
-
108
- :return: Nothing.
109
122
  """
110
123
  self._parser: ArgumentParser = ArgumentParser(model=self.extra.get('model'),
111
124
  prog=self.extra.get('prog'),
@@ -116,33 +129,32 @@ class BaseCli(Generic[BaseModelT]):
116
129
 
117
130
  def dispatch(self) -> None:
118
131
  """
119
- 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
120
133
  command (i.e. whose value derives from ``BaseModel``) to a method
121
134
  called ``_x_y_z``.
122
-
123
- :return: Nothing.
124
135
  """
125
- 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()
126
140
  for field_name in field_names:
127
- field_value = getattr(self._args, field_name)
141
+ field_value = getattr(base_model, field_name)
128
142
  if issubclass(type(field_value), BaseModel):
129
- func = getattr(self, f'_{field_name}')
130
- if callable(func):
131
- func(field_value)
132
- else:
133
- self._parser.exit(1, f'internal error: no _{field_name} callable for the {field_name} command')
134
- 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?
135
149
  else:
136
- 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)}')
137
151
 
138
152
  def _initialize_rich_argparse(self) -> None:
139
- for cls in [RichHelpFormatter]:
140
- cls.styles.update({
141
- 'argparse.args': f'bold {cls.styles["argparse.args"]}',
142
- 'argparse.groups': f'bold {cls.styles["argparse.groups"]}',
143
- 'argparse.metavar': f'bold {cls.styles["argparse.metavar"]}',
144
- 'argparse.prog': f'bold {cls.styles["argparse.prog"]}',
145
- })
153
+ """
154
+ Initializes `rich-argparse <https://pypi.org/project/rich-argparse/>`_
155
+ for this instance.
156
+ """
157
+ self._initialize_rich_argparse_styles()
146
158
  def __add_formatter_class(container):
147
159
  container.formatter_class = RichHelpFormatter
148
160
  if hasattr(container, '_actions'):
@@ -152,6 +164,20 @@ class BaseCli(Generic[BaseModelT]):
152
164
  __add_formatter_class(subaction)
153
165
  __add_formatter_class(self._parser)
154
166
 
167
+ def _initialize_rich_argparse_styles(self) -> None:
168
+ # See https://github.com/hamdanal/rich-argparse#customize-the-colors
169
+ for cls in [RichHelpFormatter]:
170
+ cls.styles.update({
171
+ 'argparse.args': 'bold cyan', # for positional-arguments and --options (e.g "--help")
172
+ 'argparse.groups': 'underline dark_orange', # for group names (e.g. "positional arguments")
173
+ 'argparse.help': 'default', # for argument's help text (e.g. "show this help message and exit")
174
+ 'argparse.metavar': 'italic dark_cyan', # for metavariables (e.g. "FILE" in "--file FILE")
175
+ 'argparse.prog': 'bold grey50', # for %(prog)s in the usage (e.g. "foo" in "Usage: foo [options]")
176
+ 'argparse.syntax': 'bold', # for highlights of back-tick quoted text (e.g. "`some text`")
177
+ 'argparse.text': 'default', # for descriptions, epilog, and --version (e.g. "A program to foo")
178
+ 'argparse.default': 'italic', # for %(default)s in the help (e.g. "Value" in "(default: Value)")
179
+ })
180
+
155
181
 
156
182
  def at_most_one_from_enum(model_cls, values: Dict[str, Any], enum_cls) -> Dict[str, Any]:
157
183
  """
@@ -211,7 +237,7 @@ def one_or_more(values: Dict[str, Any], *names: str):
211
237
  return values
212
238
 
213
239
 
214
- def option_name(name: str):
240
+ def option_name(name: str) -> str:
215
241
  return f'{('-' if len(name) == 1 else '--')}{name.replace('_', '-')}'
216
242
 
217
243
 
@@ -34,9 +34,12 @@ Error and exception utilities.
34
34
 
35
35
  class InternalError(RuntimeError):
36
36
  """
37
- A simple ``RuntimeError`` with the message ``internal error``, intended for
38
- situations that should never occur.
37
+ A simple no-arg ``RuntimeError`` with the message ``internal error``,
38
+ intended for situations that should never occur.
39
39
  """
40
40
 
41
- def __init__(self, *args: object) -> None:
41
+ def __init__(self) -> None:
42
+ """
43
+ Constructs a new ``InternalError`` instance.
44
+ """
42
45
  super().__init__('internal error')
@@ -40,10 +40,12 @@ from typing import List, Union
40
40
  def file_lines(fpath: Path) -> List[str]:
41
41
  """
42
42
  Returns a list of lines from the given file, with '#' comments and leading
43
- trailing whitespace removed, ignoring empty lines.
43
+ and trailing whitespace removed, ignoring empty lines.
44
44
 
45
- :param fpath: A ``Path`` to a file (``-`` for ``sys.stdin``).
46
- :return: A list of lines.
45
+ :param fpath: A ``Path`` to a file, ``-`` for ``sys.stdin``.
46
+ :type fpath: Path
47
+ :return: A list of processed lines.
48
+ :rtype: List[str]
47
49
  """
48
50
  f = None
49
51
  try:
@@ -62,7 +64,9 @@ def path(purepath_or_string: Union[PurePath, str]) -> Path:
62
64
 
63
65
  :param purepath_or_string: A ``PurePath`` (or a ``str`` from which to create
64
66
  a ``Path``).
65
- :return: An expanded and resolved ``Path``.
67
+ :type purepath_or_string: Union[PurePath, str]
68
+ :return: An expanded and resolved ``Path``.
69
+ :rtype: Path
66
70
  """
67
71
  if not issubclass(type(purepath_or_string), PurePath):
68
72
  purepath_or_string = Path(purepath_or_string)
@@ -32,10 +32,8 @@
32
32
  Utilities to work with 'tabulate'.
33
33
  """
34
34
 
35
-
36
35
  from enum import Enum
37
36
 
38
-
39
37
  from pydantic.v1 import BaseModel, Field, validator
40
38
  import tabulate
41
39
  from typing import Optional
@@ -1,22 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: lockss-pybasic
3
- Version: 0.1.0.dev22
4
- Summary: Basic Python utilities
5
- License: BSD-3-Clause
6
- Author: Thib Guicherd-Callin
7
- Author-email: thib@cs.stanford.edu
8
- Requires-Python: >=3.9,<4.0
9
- Classifier: License :: OSI Approved :: BSD License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Requires-Dist: pydantic (>=2.11.0,<3.0.0)
17
- Requires-Dist: pydantic-argparse (>=0.10.0,<0.11.0)
18
- Requires-Dist: rich-argparse (>=1.7.0,<1.8.0)
19
- Requires-Dist: tabulate (>=0.9.0,<0.10.0)
20
- Description-Content-Type: text/x-rst
21
-
22
-
File without changes
@@ -1,25 +0,0 @@
1
- [project]
2
- name = "lockss-pybasic"
3
- version = "0.1.0-dev22"
4
- description = "Basic Python utilities"
5
- authors = [
6
- { name = "Thib Guicherd-Callin", email = "thib@cs.stanford.edu" }
7
- ]
8
- license = { text = "BSD-3-Clause" }
9
- readme = "README.rst"
10
- requires-python = ">=3.9,<4.0"
11
- dependencies = [
12
- "pydantic (>=2.11.0,<3.0.0)",
13
- "pydantic-argparse (>=0.10.0,<0.11.0)",
14
- "rich-argparse (>=1.7.0,<1.8.0)",
15
- "tabulate (>=0.9.0,<0.10.0)"
16
- ]
17
-
18
- [tool.poetry]
19
- packages = [
20
- { include = "lockss", from = "src" }
21
- ]
22
-
23
- [build-system]
24
- requires = ["poetry-core>=2.0.0,<3.0.0"]
25
- build-backend = "poetry.core.masonry.api"