lockss-turtles 0.6.0.dev21__py3-none-any.whl → 0.6.0.dev22__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.
@@ -29,7 +29,7 @@
29
29
  # POSSIBILITY OF SUCH DAMAGE.
30
30
 
31
31
  """
32
- Representations of plugin sets.
32
+ Library to represent plugin sets and plugin set catalogs.
33
33
  """
34
34
 
35
35
  # Remove in Python 3.14
@@ -49,6 +49,7 @@ from pydantic import BaseModel, Field
49
49
  from .plugin import Plugin, PluginIdentifier
50
50
  from .util import BaseModelWithRoot
51
51
 
52
+
52
53
  #: A type alias for the plugin set catalog kind.
53
54
  PluginSetCatalogKind = Literal['PluginSetCatalog']
54
55
 
@@ -58,10 +59,16 @@ class PluginSetCatalog(BaseModelWithRoot):
58
59
  A Pydantic model (``lockss.turtles.util.BaseModelWithRoot``) to represent a
59
60
  plugin set catalog.
60
61
  """
62
+
61
63
  #: This object's kind.
62
- kind: PluginSetCatalogKind = Field(title='Kind', description="This object's kind")
64
+ kind: PluginSetCatalogKind = Field(title='Kind',
65
+ description="This object's kind")
66
+
63
67
  #: A non-empty list of plugin set files.
64
- plugin_set_files: list[str] = Field(alias='plugin-set-files', min_length=1, title='Plugin Set Files', description="A non-empty list of plugin set files")
68
+ plugin_set_files: list[str] = Field(alias='plugin-set-files',
69
+ min_length=1,
70
+ title='Plugin Set Files',
71
+ description="A non-empty list of plugin set files")
65
72
 
66
73
  def get_plugin_set_files(self) -> list[Path]:
67
74
  """
@@ -82,18 +89,27 @@ class BasePluginSetBuilder(BaseModelWithRoot, ABC):
82
89
  """
83
90
  An abstract Pydantic model (``lockss.turtles.util.BaseModelWithRoot``) to
84
91
  represent a plugin set builder, with concrete implementations
85
- ``AntPluginSetBuilder`` and ``MavenPluginSetBuilder``.
92
+ ``MavenPluginSetBuilder`` and ``AntPluginSetBuilder``.
86
93
  """
87
94
 
88
95
  #: Pydantic definition of the ``type`` field.
89
- TYPE_FIELD: ClassVar[dict[str, str]] = dict(title='Plugin Builder Type', description='A plugin builder type')
96
+ TYPE_FIELD: ClassVar[dict[str, str]] = dict(title='Plugin Builder Type',
97
+ description='A plugin builder type')
98
+
90
99
  #: Pydantic definition of the ``main`` field.
91
- MAIN_FIELD: ClassVar[dict[str, str]] = dict(title='Main Code Path', description="The path to the plugins' source code, relative to the root of the project")
100
+ MAIN_FIELD: ClassVar[dict[str, str]] = dict(title='Main Code Path',
101
+ description="The path to the plugins' source code, relative to the root of the project")
102
+
92
103
  #: Pydantic definition of the ``test`` field.
93
- TEST_FIELD: ClassVar[dict[str, str]] = dict(title='Test Code Path', description="The path to the plugins' unit tests, relative to the root of the project")
104
+ TEST_FIELD: ClassVar[dict[str, str]] = dict(title='Test Code Path',
105
+ description="The path to the plugins' unit tests, relative to the root of the project")
94
106
 
95
107
  @abstractmethod
96
- def build_plugin(self, plugin_id: PluginIdentifier, keystore_path: Path, keystore_alias: str, keystore_password=None) -> tuple[Path, Plugin]:
108
+ def build_plugin(self,
109
+ plugin_id: PluginIdentifier,
110
+ keystore_path: Path,
111
+ keystore_alias: str,
112
+ keystore_password=None) -> tuple[Path, Plugin]:
97
113
  """
98
114
  Builds the given plugin, using the given plugin signing credentials.
99
115
 
@@ -143,7 +159,8 @@ class BasePluginSetBuilder(BaseModelWithRoot, ABC):
143
159
  """
144
160
  return getattr(self, 'type')
145
161
 
146
- def has_plugin(self, plugin_id: PluginIdentifier) -> bool:
162
+ def has_plugin(self,
163
+ plugin_id: PluginIdentifier) -> bool:
147
164
  """
148
165
  Determines if the given plugin identifier represents a plugin that is
149
166
  present in the plugin set.
@@ -155,7 +172,8 @@ class BasePluginSetBuilder(BaseModelWithRoot, ABC):
155
172
  """
156
173
  return self._plugin_path(plugin_id).is_file()
157
174
 
158
- def make_plugin(self, plugin_id: PluginIdentifier) -> Plugin:
175
+ def make_plugin(self,
176
+ plugin_id: PluginIdentifier) -> Plugin:
159
177
  """
160
178
  Makes a ``Plugin`` object from the given plugin identifier.
161
179
 
@@ -184,7 +202,8 @@ class BasePluginSetBuilder(BaseModelWithRoot, ABC):
184
202
  """
185
203
  return getattr(self, 'test')
186
204
 
187
- def _plugin_path(self, plugin_id: PluginIdentifier) -> Path:
205
+ def _plugin_path(self,
206
+ plugin_id: PluginIdentifier) -> Path:
188
207
  """
189
208
  Returns the path of the plugin file for the given plugin identifier
190
209
  relative to the plugin set's main code path.
@@ -197,23 +216,188 @@ class BasePluginSetBuilder(BaseModelWithRoot, ABC):
197
216
  return self.get_main().joinpath(Plugin.id_to_file(plugin_id))
198
217
 
199
218
 
219
+ class MavenPluginSetBuilder(BasePluginSetBuilder):
220
+ """
221
+ A plugin set builder that uses `Java <https://www.oracle.com/java/>`_
222
+ Development Kit (JDK) 17 and `Apache Maven <https://maven.apache.org/>`_,
223
+ with the parent POM
224
+ `org.lockss:lockss-plugins-parent-pom <https://central.sonatype.com/artifact/org.lockss/lockss-plugins-parent-pom>`_.
225
+ """
226
+
227
+ #: The default value for ``main``.
228
+ DEFAULT_MAIN: ClassVar[str] = 'src/main/java'
229
+
230
+ #: The default value for ``test``.
231
+ DEFAULT_TEST: ClassVar[str] = 'src/test/java'
232
+
233
+ #: This Plugin set builder's type.
234
+ type: Literal['maven'] = Field(**BasePluginSetBuilder.TYPE_FIELD)
235
+
236
+ #: This plugin set builder's main code path.
237
+ main: Optional[str] = Field(DEFAULT_MAIN,
238
+ **BasePluginSetBuilder.MAIN_FIELD)
239
+
240
+ #: This plugin set builder's unit test path.
241
+ test: Optional[str] = Field(DEFAULT_TEST,
242
+ **BasePluginSetBuilder.TEST_FIELD)
243
+
244
+ #: An internal flag to remember if a build has occurred.
245
+ _built: bool
246
+
247
+ def build_plugin(self,
248
+ plugin_id: PluginIdentifier,
249
+ keystore_path: Path,
250
+ keystore_alias: str,
251
+ keystore_password=None) -> tuple[Path, Plugin]:
252
+ """
253
+ Builds the given plugin with the supplied plugin signing credentials.
254
+
255
+ :param plugin_id: A plugin identifier.
256
+ :type plugin_id: PluginIdentifier
257
+ :param keystore_path: The path to the plugin signing keystore.
258
+ :type keystore_path: Path
259
+ :param keystore_alias: The alias to use in the plugin signing keystore.
260
+ :type keystore_alias: str
261
+ :param keystore_password: The plugin signing password.
262
+ :type keystore_password:
263
+ :return: A tuple of the built and signed JAR file and a Plugin object
264
+ instantiated from it.
265
+ :rtype: tuple[Path, Plugin]
266
+ :raises subprocess.CalledProcessError: If a subprocess fails.
267
+ :raises FileNotFoundError: If the expected built and signed plugin JAR
268
+ path is unexpectedly not found despite the
269
+ build.
270
+ """
271
+ self._big_build(keystore_path, keystore_alias, keystore_password=keystore_password)
272
+ return self._little_build(plugin_id)
273
+
274
+ def model_post_init(self,
275
+ context: Any) -> None:
276
+ """
277
+ Pydantic post-initialization method to initialize the ``_built`` flag.
278
+
279
+ :param context: The Pydantic context.
280
+ :type context: Any
281
+ """
282
+ super().model_post_init(context)
283
+ self._built = False
284
+
285
+ def _big_build(self,
286
+ keystore_path: Path,
287
+ keystore_alias: str,
288
+ keystore_password: str=None) -> None:
289
+ """
290
+ Runs ``mvn package`` on the project if the ``_built`` flag is False.
291
+
292
+ :param keystore_path: The path to the plugin signing keystore.
293
+ :type keystore_path: Path
294
+ :param keystore_alias: The alias to use in the plugin signing keystore.
295
+ :type keystore_alias: str
296
+ :param keystore_password: The plugin signing password.
297
+ :type keystore_password:
298
+ :raises subprocess.CalledProcessError: If a subprocess fails.
299
+ """
300
+ if not self._built:
301
+ # Do build
302
+ cmd = ['mvn', 'package',
303
+ f'-Dkeystore.file={keystore_path!s}',
304
+ f'-Dkeystore.alias={keystore_alias}',
305
+ f'-Dkeystore.password={keystore_password}']
306
+ try:
307
+ subprocess.run(cmd, cwd=self.get_root(), check=True, stdout=sys.stdout, stderr=sys.stderr)
308
+ except subprocess.CalledProcessError as cpe:
309
+ raise self._sanitize(cpe)
310
+ self._built = True
311
+
312
+ def _little_build(self,
313
+ plugin_id: PluginIdentifier) -> tuple[Path, Plugin]:
314
+ """
315
+ In the Maven implementation, essentially a no-op (keeping for parallel
316
+ structure with the legacy Ant plugin set builder).
317
+
318
+ :param plugin_id: A plugin identifier.
319
+ :type plugin_id: PluginIdentifier
320
+ :return: A tuple of the built and signed JAR file and a Plugin object
321
+ instantiated from it.
322
+ :rtype: tuple[Path, Plugin]
323
+ :raises FileNotFoundError: If the expected built and signed plugin JAR
324
+ path is unexpectedly not found despite the
325
+ build.
326
+ """
327
+ jar_path = self.get_root().joinpath('target', 'pluginjars', f'{plugin_id}.jar')
328
+ if not jar_path.is_file():
329
+ raise FileNotFoundError(str(jar_path))
330
+ return jar_path, Plugin.from_jar(jar_path)
331
+
332
+ def _sanitize(self,
333
+ called_process_error: subprocess.CalledProcessError) -> subprocess.CalledProcessError:
334
+ """
335
+ Alters the ``-Dkeystore.password=`` portion of a called process error.
336
+
337
+ :param called_process_error: A called process error.
338
+ :return: The same called process error, with ``-Dkeystore.password=``
339
+ altered in ``cmd``.
340
+ """
341
+ cmd = called_process_error.cmd[:]
342
+ for i in range(len(cmd)):
343
+ if cmd[i].startswith('-Dkeystore.password='):
344
+ cmd[i] = '-Dkeystore.password=<password>'
345
+ called_process_error.cmd = ' '.join([shlex.quote(c) for c in cmd])
346
+ return called_process_error
347
+
348
+
200
349
  class AntPluginSetBuilder(BasePluginSetBuilder):
350
+ """
351
+ A plugin set builder that uses `Java <https://www.oracle.com/java/>`_
352
+ Development Kit (JDK) 8 and `Apache Ant <https://ant.apache.org/>`_,
353
+ with the legacy LOCKSS 1.x build system.
354
+ """
355
+
201
356
  #: Default value for the ``main`` field.
202
357
  DEFAULT_MAIN: ClassVar[str] = 'plugins/src'
358
+
203
359
  #: Default value for the ``test`` field.
204
360
  DEFAULT_TEST: ClassVar[str] = 'plugins/test/src'
205
361
 
206
362
  #: This plugin set builder's type.
207
363
  type: Literal['ant'] = Field(**BasePluginSetBuilder.TYPE_FIELD)
364
+
208
365
  #: This plugin set builder's main code path.
209
- main: Optional[str] = Field(DEFAULT_MAIN, **BasePluginSetBuilder.MAIN_FIELD)
366
+ main: Optional[str] = Field(DEFAULT_MAIN,
367
+ **BasePluginSetBuilder.MAIN_FIELD)
368
+
210
369
  #: This plugin set builder's unit test path.
211
- test: Optional[str] = Field(DEFAULT_TEST, **BasePluginSetBuilder.TEST_FIELD)
370
+ test: Optional[str] = Field(DEFAULT_TEST,
371
+ **BasePluginSetBuilder.TEST_FIELD)
212
372
 
213
373
  #: An internal flag to remember if a build has occurred.
214
374
  _built: bool
215
375
 
216
- def build_plugin(self, plugin_id: PluginIdentifier, keystore_path: Path, keystore_alias: str, keystore_password=None) -> tuple[Path, Plugin]:
376
+ def build_plugin(self,
377
+ plugin_id: PluginIdentifier,
378
+ keystore_path: Path,
379
+ keystore_alias: str,
380
+ keystore_password=None) -> tuple[Path, Plugin]:
381
+ """
382
+ Builds the given plugin with the supplied plugin signing credentials.
383
+
384
+ :param plugin_id: A plugin identifier.
385
+ :type plugin_id: PluginIdentifier
386
+ :param keystore_path: The path to the plugin signing keystore.
387
+ :type keystore_path: Path
388
+ :param keystore_alias: The alias to use in the plugin signing keystore.
389
+ :type keystore_alias: str
390
+ :param keystore_password: The plugin signing password.
391
+ :type keystore_password:
392
+ :return: A tuple of the built and signed JAR file and a Plugin object
393
+ instantiated from it.
394
+ :rtype: tuple[Path, Plugin]
395
+ :raises Exception: If ``JAVA_HOME`` is not set in the environment.
396
+ :raises subprocess.CalledProcessError: If a subprocess fails.
397
+ :raises FileNotFoundError: If the expected built and signed plugin JAR
398
+ path is unexpectedly not found despite the
399
+ build.
400
+ """
217
401
  # Prerequisites
218
402
  if 'JAVA_HOME' not in os.environ:
219
403
  raise Exception('error: JAVA_HOME must be set in your environment')
@@ -222,13 +406,22 @@ class AntPluginSetBuilder(BasePluginSetBuilder):
222
406
  # Little build
223
407
  return self._little_build(plugin_id, keystore_path, keystore_alias, keystore_password=keystore_password)
224
408
 
225
- def model_post_init(self, context: Any) -> None:
409
+ def model_post_init(self,
410
+ context: Any) -> None:
411
+ """
412
+ Pydantic post-initialization method to initialize the ``_built`` flag.
413
+
414
+ :param context: The Pydantic context.
415
+ :type context: Any
416
+ """
226
417
  super().model_post_init(context)
227
418
  self._built = False
228
419
 
229
420
  def _big_build(self) -> None:
230
421
  """
231
- Optionally performs the "big build".
422
+ Runs ``ant load-plugins`` if the ``_built`` flag is False.
423
+
424
+ :raises subprocess.CalledProcessError: If a subprocess fails.
232
425
  """
233
426
  if not self._built:
234
427
  # Do build
@@ -236,7 +429,11 @@ class AntPluginSetBuilder(BasePluginSetBuilder):
236
429
  shell=True, cwd=self.get_root(), check=True, stdout=sys.stdout, stderr=sys.stderr)
237
430
  self._built = True
238
431
 
239
- def _little_build(self, plugin_id: PluginIdentifier, keystore_path: Path, keystore_alias: str, keystore_password: str=None) -> tuple[Path, Plugin]:
432
+ def _little_build(self,
433
+ plugin_id: PluginIdentifier,
434
+ keystore_path: Path,
435
+ keystore_alias: str,
436
+ keystore_password: str=None) -> tuple[Path, Plugin]:
240
437
  """
241
438
  Performs the "little build" of the given plugin.
242
439
 
@@ -244,14 +441,17 @@ class AntPluginSetBuilder(BasePluginSetBuilder):
244
441
  :type plugin_id: PluginIdentifier
245
442
  :param keystore_path: The path to the plugin signing keystore.
246
443
  :type keystore_path: Path
247
- :param keystore_alias: The signing alias to use from the plugin signing
248
- keystore.
444
+ :param keystore_alias: The alias to use in the plugin signing keystore.
249
445
  :type keystore_alias: str
250
- :param keystore_password: The signing password.
251
- :type keystore_password: str
252
- :return: A tuple of the plugin JAR path and the corresponding ``Plugin``
253
- object.
446
+ :param keystore_password: The plugin signing password.
447
+ :type keystore_password:
448
+ :return: A tuple of the built and signed JAR file and a Plugin object
449
+ instantiated from it.
254
450
  :rtype: tuple[Path, Plugin]
451
+ :raises subprocess.CalledProcessError: If a subprocess fails.
452
+ :raises FileNotFoundError: If the expected built and signed plugin JAR
453
+ path is unexpectedly not found despite the
454
+ build.
255
455
  """
256
456
  orig_plugin = None
257
457
  cur_id = plugin_id
@@ -296,7 +496,15 @@ class AntPluginSetBuilder(BasePluginSetBuilder):
296
496
  # def _plugin_path(self, plugin_id: PluginIdentifier) -> Path:
297
497
  # return self.get_main().joinpath(Plugin.id_to_file(plugin_id))
298
498
 
299
- def _sanitize(self, called_process_error: subprocess.CalledProcessError) -> subprocess.CalledProcessError:
499
+ def _sanitize(self,
500
+ called_process_error: subprocess.CalledProcessError) -> subprocess.CalledProcessError:
501
+ """
502
+ Alters the value of ``--password`` in a called process error.
503
+
504
+ :param called_process_error: A called process error.
505
+ :return: The same called process error, with the value of ``--password``
506
+ altered in ``cmd``.
507
+ """
300
508
  cmd = called_process_error.cmd[:]
301
509
  for i in range(1, len(cmd)):
302
510
  if cmd[i - 1] == '--password':
@@ -305,85 +513,123 @@ class AntPluginSetBuilder(BasePluginSetBuilder):
305
513
  return called_process_error
306
514
 
307
515
 
308
- class MavenPluginSetBuilder(BasePluginSetBuilder):
309
- DEFAULT_MAIN: ClassVar[str] = 'src/main/java'
310
- DEFAULT_TEST: ClassVar[str] = 'src/test/java'
311
-
312
- type: Literal['maven'] = Field(**BasePluginSetBuilder.TYPE_FIELD)
313
- main: Optional[str] = Field(DEFAULT_MAIN, **BasePluginSetBuilder.MAIN_FIELD)
314
- test: Optional[str] = Field(DEFAULT_TEST, **BasePluginSetBuilder.TEST_FIELD)
315
-
316
- _built: bool
317
-
318
- def build_plugin(self, plugin_id: PluginIdentifier, keystore_path: Path, keystore_alias: str, keystore_password=None) -> tuple[Path, Plugin]:
319
- self._big_build(keystore_path, keystore_alias, keystore_password=keystore_password)
320
- return self._little_build(plugin_id)
321
-
322
- def model_post_init(self, context: Any) -> None:
323
- super().model_post_init(context)
324
- self._built = False
325
-
326
- def _big_build(self, keystore_path: Path, keystore_alias: str, keystore_password: str=None) -> None:
327
- if not self._built:
328
- # Do build
329
- cmd = ['mvn', 'package',
330
- f'-Dkeystore.file={keystore_path!s}',
331
- f'-Dkeystore.alias={keystore_alias}',
332
- f'-Dkeystore.password={keystore_password}']
333
- try:
334
- subprocess.run(cmd, cwd=self.get_root(), check=True, stdout=sys.stdout, stderr=sys.stderr)
335
- except subprocess.CalledProcessError as cpe:
336
- raise self._sanitize(cpe)
337
- self._built = True
338
-
339
- def _little_build(self, plugin_id: PluginIdentifier) -> tuple[Path, Plugin]:
340
- jar_path = self.get_root().joinpath('target', 'pluginjars', f'{plugin_id}.jar')
341
- if not jar_path.is_file():
342
- raise FileNotFoundError(str(jar_path))
343
- return jar_path, Plugin.from_jar(jar_path)
344
-
345
- def _sanitize(self, called_process_error: subprocess.CalledProcessError) -> subprocess.CalledProcessError:
346
- cmd = called_process_error.cmd[:]
347
- for i in range(len(cmd)):
348
- if cmd[i].startswith('-Dkeystore.password='):
349
- cmd[i] = '-Dkeystore.password=<password>'
350
- called_process_error.cmd = ' '.join([shlex.quote(c) for c in cmd])
351
- return called_process_error
352
-
353
-
354
- PluginSetBuilder = Annotated[Union[AntPluginSetBuilder, MavenPluginSetBuilder], Field(discriminator='type')]
516
+ #: A type alias for plugin set builders, which is the union of
517
+ #: ``MavenPluginSetBuilder`` and ``AntPluginSetBuilder`` using ``type`` as the
518
+ #: discriminator field.
519
+ PluginSetBuilder = Annotated[Union[MavenPluginSetBuilder, AntPluginSetBuilder], Field(discriminator='type')]
355
520
 
356
521
 
522
+ #: A type alias for the plugin set kind.
357
523
  PluginSetKind = Literal['PluginSet']
358
524
 
359
525
 
526
+ #: A type alias for plugin set identifiers.
360
527
  PluginSetIdentifier = str
361
528
 
362
529
 
363
530
  class PluginSet(BaseModel):
364
- kind: PluginSetKind = Field(title='Kind', description="This object's kind")
365
- id: PluginSetIdentifier = Field(title='Plugin Set Identifier', description='An identifier for the plugin set')
366
- name: str = Field(title='Plugin Set Name', description='A name for the plugin set')
367
- builder: PluginSetBuilder = Field(title='Plugin Set Builder', description='A builder for the plugin set')
531
+ """
532
+ A Pydantic model for a plugin set.
533
+ """
534
+
535
+ #: This object's kind.
536
+ kind: PluginSetKind = Field(title='Kind',
537
+ description="This object's kind")
538
+
539
+ #: This plugin set's identifier.
540
+ id: PluginSetIdentifier = Field(title='Plugin Set Identifier',
541
+ description='An identifier for the plugin set')
542
+
543
+ #: This plugin set's name.
544
+ name: str = Field(title='Plugin Set Name',
545
+ description='A name for the plugin set')
546
+
547
+ #: This plugin set's builder.
548
+ builder: PluginSetBuilder = Field(title='Plugin Set Builder',
549
+ description='A builder for the plugin set')
550
+
551
+ def build_plugin(self,
552
+ plugin_id: PluginIdentifier,
553
+ keystore_path: Path,
554
+ keystore_alias: str,
555
+ keystore_password=None) -> tuple[Path, Plugin]:
556
+ """
557
+ Builds the given plugin with the supplied plugin signing credentials.
368
558
 
369
- def build_plugin(self, plugin_id: PluginIdentifier, keystore_path: Path, keystore_alias: str, keystore_password=None) -> tuple[Path, Plugin]:
559
+ :param plugin_id: A plugin identifier.
560
+ :type plugin_id: PluginIdentifier
561
+ :param keystore_path: The path to the plugin signing keystore.
562
+ :type keystore_path: Path
563
+ :param keystore_alias: The alias to use in the plugin signing keystore.
564
+ :type keystore_alias: str
565
+ :param keystore_password: The plugin signing password.
566
+ :type keystore_password:
567
+ :return: A tuple of the built and signed JAR file and a Plugin object
568
+ instantiated from it.
569
+ """
370
570
  return self.builder.build_plugin(plugin_id, keystore_path, keystore_alias, keystore_password)
371
571
 
372
572
  def get_builder(self) -> PluginSetBuilder:
573
+ """
574
+ Returns this plugin set's builder.
575
+
576
+ :return: This plugin set's builder.
577
+ :rtype: PluginSetBuilder
578
+ """
373
579
  return self.builder
374
580
 
375
581
  def get_id(self) -> PluginSetIdentifier:
582
+ """
583
+ Returns this plugin set's identifier.
584
+
585
+ :return: This plugin set's identifier.
586
+ :rtype: PluginSetIdentifier
587
+ """
376
588
  return self.id
377
589
 
378
590
  def get_name(self) -> str:
591
+ """
592
+ Returns this plugin set's name.
593
+
594
+ :return: This plugin set's name.
595
+ :rtype: str
596
+ """
379
597
  return self.name
380
598
 
381
- def has_plugin(self, plugin_id: PluginIdentifier) -> bool:
599
+ def has_plugin(self,
600
+ plugin_id: PluginIdentifier) -> bool:
601
+ """
602
+ Determines if the given plugin identifier represents a plugin that is
603
+ present in the plugin set.
604
+
605
+ :param plugin_id: A plugin identifier.
606
+ :type plugin_id: PluginIdentifier
607
+ :return: Whether the plugin is present in the plugin set.
608
+ :rtype: bool
609
+ """
382
610
  return self.get_builder().has_plugin(plugin_id)
383
611
 
384
- def initialize(self, root: Path) -> PluginSet:
612
+ def initialize(self,
613
+ root: Path) -> PluginSet:
614
+ """
615
+ Mandatory initialization of the builder.
616
+
617
+ :param root: This plugin set's root path.
618
+ :type root: Path
619
+ :return: This plugin set, for chaining.
620
+ :rtype: PluginSet
621
+ """
385
622
  self.get_builder().initialize(root)
386
623
  return self
387
624
 
388
- def make_plugin(self, plugin_id: PluginIdentifier) -> Plugin:
625
+ def make_plugin(self,
626
+ plugin_id: PluginIdentifier) -> Plugin:
627
+ """
628
+ Makes a ``Plugin`` object from the given plugin identifier.
629
+
630
+ :param plugin_id: A plugin identifier.
631
+ :type plugin_id: PluginIdentifier
632
+ :return: The corresponding ``Plugin`` object.
633
+ :rtype: Plugin
634
+ """
389
635
  return self.get_builder().make_plugin(plugin_id)
lockss/turtles/util.py CHANGED
@@ -28,6 +28,10 @@
28
28
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
29
  # POSSIBILITY OF SUCH DAMAGE.
30
30
 
31
+ """
32
+ Utilities for the ``lockss.turtles`` package.
33
+ """
34
+
31
35
  # Remove in Python 3.14
32
36
  # See https://stackoverflow.com/questions/33533148/how-do-i-type-hint-a-method-with-the-type-of-the-enclosing-class/33533514#33533514
33
37
  from __future__ import annotations
@@ -40,25 +44,62 @@ from lockss.pybasic.fileutil import path
40
44
  from pydantic import BaseModel
41
45
 
42
46
 
43
- # Candidate for lockss-pybasic
47
+ #: Type alias for the union of ``Path`` and ``str``.
44
48
  PathOrStr = Union[Path, str]
45
49
 
46
50
 
47
51
  class BaseModelWithRoot(BaseModel):
52
+ """
53
+ A Pydantic model with a root path which can be used to resolve relative
54
+ paths.
55
+ """
56
+
57
+ #: An internal root path.
48
58
  _root: Optional[Path]
49
59
 
50
60
  def model_post_init(self, context: Any) -> None:
61
+ """
62
+ Pydantic post-initialization method to create the ``_root`` field.
63
+ """
51
64
  self._root = None
52
65
 
53
66
  def get_root(self) -> Path:
67
+ """
68
+ Returns this object's root path.
69
+
70
+ See ``initialize``.
71
+
72
+ :return: This object's root path.
73
+ :rtype: Path
74
+ :raises ValueError: If this object's ``initialize`` method was not
75
+ called.
76
+ """
54
77
  if self._root is None:
55
78
  raise ValueError('Uninitialized root')
56
79
  return self._root
57
80
 
58
- def initialize(self, root: PathOrStr) -> BaseModelWithRoot:
59
- self._root = path(root)
81
+ def initialize(self,
82
+ root_path_or_str: PathOrStr) -> BaseModelWithRoot:
83
+ """
84
+ Mandatory initialization of the root path.
85
+
86
+ :param root_path_or_str: This object's root path.
87
+ :type root_path_or_str: PathOrStr
88
+ :return: This object, for chaining.
89
+ :rtype: BaseModelWithRoot
90
+ """
91
+ self._root = path(root_path_or_str)
60
92
  return self
61
93
 
62
94
 
63
95
  def file_or(paths: Iterable[Path]) -> str:
96
+ """
97
+ Turns an iterable of file paths and returns them as a ``" or "``-separated
98
+ string.
99
+
100
+ :param paths: A non-null list of file paths.
101
+ :type paths: Iterable[Path]
102
+ :return: A ``" or "``-separated string of the given file paths.
103
+ :rtype: str
104
+ """
64
105
  return ' or '.join(map(str, paths))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lockss-turtles
3
- Version: 0.6.0.dev21
3
+ Version: 0.6.0.dev22
4
4
  Summary: Library and command line tool to manage LOCKSS plugin sets and LOCKSS plugin registries
5
5
  License: BSD-3-Clause
6
6
  Author: Thib Guicherd-Callin
@@ -34,7 +34,7 @@ Description-Content-Type: text/x-rst
34
34
  Turtles
35
35
  =======
36
36
 
37
- .. |RELEASE| replace:: 0.6.0-dev20
37
+ .. |RELEASE| replace:: 0.6.0-dev22
38
38
  .. |RELEASE_DATE| replace:: ?
39
39
 
40
40
  .. |HELP| replace:: ``--help/-h``
@@ -0,0 +1,15 @@
1
+ lockss/turtles/__init__.py,sha256=v-2mPurnlbDsfTo_37mD7fnqclC3pS8DScu46ZawpcQ,1827
2
+ lockss/turtles/__main__.py,sha256=BqRTG3dejCgLh87UJ1Os9DD9unxnQkOdUQM6eqWS3Sg,1680
3
+ lockss/turtles/app.py,sha256=Rxbsq10A830D1r99a5tqLVYWM3bJZ2Org-JKjXD-2Mk,16689
4
+ lockss/turtles/cli.py,sha256=Pgd0GSThxDfnPWKwwujo6ZocfKePoe-aiHJ9qYpm9BQ,21019
5
+ lockss/turtles/plugin.py,sha256=HT01Er9-odvJ4lF8DNC7TV4uxSqTw44UBe02Jgj5xMs,11065
6
+ lockss/turtles/plugin_registry.py,sha256=UlB9faNH8fEh9mfdj6byMcRcZ7axi0DzUP2qdyj9ANk,26935
7
+ lockss/turtles/plugin_set.py,sha256=PuzSWl2k7ikbkKtxStQRJhcXi1JXuSBl9jXz4dzp7Ts,24550
8
+ lockss/turtles/util.py,sha256=kETdjOSDaECs2xSm6ah8UoPVuoo42eFUJEEJn5wqYXE,3647
9
+ unittest/lockss/turtles/__init__.py,sha256=hrgWx4GaP-hhPBuRlbLndJnOQmZicKCLaP-dQTCu1sQ,3162
10
+ unittest/lockss/turtles/test_plugin_set.py,sha256=q6JmooDR-wijAQ8x9DWKTNB6Of4EOr3Dgq8oaX0LTpQ,4179
11
+ lockss_turtles-0.6.0.dev22.dist-info/LICENSE,sha256=O9ONND4uDxY_jucI4jZDf2liAk05ScEJaYu-Al7EOdQ,1506
12
+ lockss_turtles-0.6.0.dev22.dist-info/METADATA,sha256=jgCvdNmmsDAzEDTGldk4MfLGRlq8tHfhvh-Bpilr4JQ,39692
13
+ lockss_turtles-0.6.0.dev22.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
14
+ lockss_turtles-0.6.0.dev22.dist-info/entry_points.txt,sha256=25BAVFSBRKWAWiXIGZgcr1ypt2mV7nj31Jl8WcNZZOk,51
15
+ lockss_turtles-0.6.0.dev22.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- lockss/turtles/__init__.py,sha256=9arhPOwCemb5EemdFE4gisbpLEE6bPqOBGi1yOXJmIA,1827
2
- lockss/turtles/__main__.py,sha256=BqRTG3dejCgLh87UJ1Os9DD9unxnQkOdUQM6eqWS3Sg,1680
3
- lockss/turtles/app.py,sha256=Rxbsq10A830D1r99a5tqLVYWM3bJZ2Org-JKjXD-2Mk,16689
4
- lockss/turtles/cli.py,sha256=ZijrjwW1VXtmRvTDpfLnLvq9aL0CfK-F6mglk04RihI,20887
5
- lockss/turtles/plugin.py,sha256=HT01Er9-odvJ4lF8DNC7TV4uxSqTw44UBe02Jgj5xMs,11065
6
- lockss/turtles/plugin_registry.py,sha256=ZGlVnhP831hmqLQ9cJueUAXlZWQ67rm0cs6U91F7STc,11943
7
- lockss/turtles/plugin_set.py,sha256=Q-d-UfZM8I6f-z4TeYe8jdBbhPv0r4ro2H24oNd8Syc,15898
8
- lockss/turtles/util.py,sha256=8EyWvzTLNycQM_ZKCtWm8FpKJSrwaCSWh8006UYq6vU,2498
9
- unittest/lockss/turtles/__init__.py,sha256=hrgWx4GaP-hhPBuRlbLndJnOQmZicKCLaP-dQTCu1sQ,3162
10
- unittest/lockss/turtles/test_plugin_set.py,sha256=q6JmooDR-wijAQ8x9DWKTNB6Of4EOr3Dgq8oaX0LTpQ,4179
11
- lockss_turtles-0.6.0.dev21.dist-info/LICENSE,sha256=O9ONND4uDxY_jucI4jZDf2liAk05ScEJaYu-Al7EOdQ,1506
12
- lockss_turtles-0.6.0.dev21.dist-info/METADATA,sha256=2HRA5jwOYJaTyU4wXqaIBzQg4aKlxdjTdl9lqL3aT9w,39692
13
- lockss_turtles-0.6.0.dev21.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
14
- lockss_turtles-0.6.0.dev21.dist-info/entry_points.txt,sha256=25BAVFSBRKWAWiXIGZgcr1ypt2mV7nj31Jl8WcNZZOk,51
15
- lockss_turtles-0.6.0.dev21.dist-info/RECORD,,