lockss-pybasic 0.3.0.dev3__py3-none-any.whl → 0.3.0.dev4__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.
@@ -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.3.0-dev3'
39
+ __version__ = '0.3.0-dev4'
@@ -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, model_validator
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.HTTPS
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
- NodeSpec = Annotated[Union[NodeSpec1, NodeSpec2], Field(discriminator='type')]
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 make_node_spec(node_spec_string: NodeSpecStr) -> NodeSpec:
143
- mat: Optional[Match[str]] = RE_NODE_REFERENCE.fullmatch(node_spec_string)
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 get_node_spec_adapter().validate_python(d)
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.dev3
3
+ Version: 0.3.0.dev4
4
4
  Summary: Basic Python utilities
5
5
  License: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -26,7 +26,7 @@ Description-Content-Type: text/x-rst
26
26
  lockss-pybasic
27
27
  ==============
28
28
 
29
- .. |RELEASE| replace:: 0.3.0-dev3
29
+ .. |RELEASE| replace:: 0.3.0-dev4
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=NWFBmNQ6XEuQOYE0WVIYhe0Ci5wIxmzBewZ_oY8u-G8,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=snBT7OPttThBJXVxgLC6Onmuoa8Z-jOOM1LWdhIb-js,7928
7
+ lockss_pybasic-0.3.0.dev4.dist-info/METADATA,sha256=eZ1B2T-R5rn4u2ZXTkcWZFEGChVqOxbbV5FT-pusIpw,2338
8
+ lockss_pybasic-0.3.0.dev4.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
9
+ lockss_pybasic-0.3.0.dev4.dist-info/licenses/LICENSE,sha256=EOxPunNz3XP6AjgbPFolu-d9BS_AF9TtKn1WXgeYPsE,1506
10
+ lockss_pybasic-0.3.0.dev4.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,,