lockss-debugpanel 0.10.0.dev1__py3-none-any.whl → 0.10.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.
@@ -4,7 +4,7 @@
4
4
  Library and command line tool to interact with the LOCKSS 1.x DebugPanel servlet.
5
5
  """
6
6
 
7
- __version__ = '0.10.0-dev1'
7
+ __version__ = '0.10.0-dev2'
8
8
 
9
9
  __copyright__ = '''
10
10
  Copyright (c) 2000-2026, Board of Trustees of Leland Stanford Jr. University
lockss/debugpanel/cli.py CHANGED
@@ -33,20 +33,19 @@ Command line tool to interact with the LOCKSS 1.x DebugPanel servlet.
33
33
  """
34
34
 
35
35
  from collections.abc import Callable, Iterator
36
- from concurrent.futures import Executor, Future, ThreadPoolExecutor, as_completed
36
+ from concurrent.futures import Future, ThreadPoolExecutor, as_completed
37
37
  from contextlib import nullcontext
38
38
  from dataclasses import dataclass, field
39
- from enum import Enum
40
39
  from importlib.metadata import entry_points
41
40
  from inspect import ismethod
42
41
  from itertools import chain
43
42
  from pathlib import Path
44
43
  from typing import Any, Optional, TypeAlias
45
44
 
46
- from click_extra import ExtraContext, Section, accessible_option, color_option, echo, group, jobs_option, option, option_group, pass_context, pass_obj, print_sorted_table, progressbar, prompt, show_params_option, table_format_option, timer_option
47
- from click_extra.execution import DEFAULT_JOBS
45
+ from click_extra import Context, ProgressOption, Section, accessible_option, color_option, echo, group, jobs_option, no_color_option, option, option_group, pass_context, pass_obj, print_table, progressbar, prompt, show_params_option, table_format_option, timer_option
46
+ from click_extra.context import JOBS, PROGRESS, TABLE_FORMAT
47
+ from click_extra.decorators import decorator_factory
48
48
  from click_plugins import with_plugins
49
- from cloup.constraints import mutually_exclusive
50
49
  from pydantic import ValidationError
51
50
  import yaml
52
51
 
@@ -62,14 +61,7 @@ from ._core import DebugPanelClient, UrlOpenT, DEFAULT_DEPTH
62
61
  YamlT: TypeAlias = Any
63
62
 
64
63
 
65
- class _JobPoolType(Enum):
66
- """An enum of job pool types."""
67
- THREAD_POOL = 'thread-pool'
68
- PROCESS_POOL = 'process-pool'
69
-
70
-
71
- #: The default ``_JobPoolType``.
72
- _DEFAULT_JOB_POOL_TYPE: _JobPoolType = _JobPoolType.THREAD_POOL
64
+ progress_option = decorator_factory(dec=option, cls=ProgressOption)
73
65
 
74
66
 
75
67
  class _DebugPanelCli(object):
@@ -89,20 +81,15 @@ class _DebugPanelCli(object):
89
81
  auids: tuple[Path, ...] = ()
90
82
  # Depth options
91
83
  depth: Optional[int] = None
92
- # Job options
93
- jobs: Optional[int] = None
94
- pool_size: Optional[int] = None # DEPRECATED
95
84
  # Tabular output options
96
85
  headings: Optional[bool] = None
97
- table_format: Optional[str] = None
98
- # Display options
99
- accessible: Optional[bool] = None
100
- color: Optional[bool] = None
101
- progress: Optional[bool] = None
102
- theme: Optional[str] = None
103
- time: Optional[bool] = None
104
-
105
- def __init__(self, ctx: ExtraContext) -> None:
86
+
87
+ _ctx: Context
88
+ _opts: _DebugPanelCli._Opts
89
+ _auids: list[str]
90
+ _clients: list[DebugPanelClient]
91
+
92
+ def __init__(self, ctx: Context) -> None:
106
93
  """
107
94
  Constructor.
108
95
 
@@ -110,11 +97,7 @@ class _DebugPanelCli(object):
110
97
  :type ctx: ExtraContext
111
98
  """
112
99
  super().__init__()
113
- self._ctx: ExtraContext = ctx
114
- self._opts: Optional[_DebugPanelCli._Opts] = None
115
- self._auids: Optional[list[str]] = None
116
- self._clients: list[DebugPanelClient] = list()
117
- self._executor: Optional[Executor] = None
100
+ self._ctx = ctx
118
101
 
119
102
  def check_substance(self) -> None:
120
103
  """Implementation of the ``check-substance`` command."""
@@ -179,26 +162,25 @@ class _DebugPanelCli(object):
179
162
  :type func_client_auid: Callable[[DebugPanelClient, str], UrlOpenT]
180
163
  """
181
164
  self._initialize_auid_operation()
182
- opts: _DebugPanelCli._Opts = self._opts
183
- futures: dict[Future[UrlOpenT], tuple[DebugPanelClient, str]] = {self._executor.submit(func_client_auid, client, auid, **kwargs): (client, auid) for auid in self._auids for client in self._clients}
184
- completed: Iterator[Future[UrlOpenT]] = as_completed(futures)
185
165
  results: dict[tuple[NodeIdentifier, str], str] = {}
186
- with progressbar(completed, length=len(futures), label='Progress') if opts.progress else nullcontext(completed) as bar:
187
- for future in bar:
188
- client, auid = futures[future]
189
- k: tuple[NodeIdentifier, str] = (client.get_id(), auid)
190
- try:
191
- with future.result() as resp:
192
- status: int = resp.status
193
- reason: str = resp.reason
194
- results[k] = 'Requested' if status == 200 else reason
195
- except Exception as exc:
196
- results[k] = str(exc)
197
- sorted_ids = sorted(client.get_id() for client in self._clients)
198
- print_sorted_table([('AUID', 'auid'), *[(i, None) for i in sorted_ids]],
199
- [[a, *[results[(i, a)] for i in sorted_ids]] for a in self._auids],
200
- sort_columns=['auid'],
201
- table_format=opts.table_format)
166
+ with ThreadPoolExecutor(max_workers=(meta := self._ctx.meta)[JOBS]) as executor:
167
+ futures: dict[Future[UrlOpenT], tuple[DebugPanelClient, str]] = {executor.submit(func_client_auid, client, auid, **kwargs): (client, auid) for auid in self._auids for client in self._clients}
168
+ completed: Iterator[Future[UrlOpenT]] = as_completed(futures)
169
+ with progressbar(completed, length=len(futures), label='Progress', item_show_func=lambda f: f and futures[f][0].get_id() or None) if (meta := self._ctx.meta)[PROGRESS] else nullcontext(completed) as bar:
170
+ for future in bar:
171
+ client, auid = futures[future]
172
+ k: tuple[NodeIdentifier, str] = (client.get_id(), auid)
173
+ try:
174
+ with future.result() as resp:
175
+ status: int = resp.status
176
+ reason: str = resp.reason
177
+ results[k] = 'Requested' if status == 200 else reason
178
+ except Exception as exc:
179
+ results[k] = str(exc)
180
+ sorted_nodes: list[NodeIdentifier] = sorted(client.get_id() for client in self._clients)
181
+ print_table([[a, *[results[(i, a)] for i in sorted_nodes]] for a in sorted(self._auids)],
182
+ headers=('AUID', *sorted_nodes),
183
+ table_format=meta[TABLE_FORMAT])
202
184
 
203
185
  def _do_node_command(self,
204
186
  func_client: Callable[[DebugPanelClient], UrlOpenT],
@@ -211,25 +193,24 @@ class _DebugPanelCli(object):
211
193
  :type func_client: Callable[[DebugPanelClient], UrlOpenT]
212
194
  """
213
195
  self._initialize_node_operation()
214
- opts: _DebugPanelCli._Opts = self._opts
215
- futures: dict[Future[UrlOpenT], DebugPanelClient] = {self._executor.submit(func_client, client, **kwargs): client for client in self._clients}
216
- completed: Iterator[Future[UrlOpenT]] = as_completed(futures)
217
196
  results: dict[NodeIdentifier, str] = {}
218
- with progressbar(completed, length=len(futures), label='Progress') if opts.progress else nullcontext(completed) as bar:
219
- for future in bar:
220
- client: DebugPanelClient = futures[future]
221
- k: NodeIdentifier = client.get_id()
222
- try:
223
- with future.result() as resp:
224
- status: int = resp.status
225
- reason: str = resp.reason
226
- results[k] = 'Requested' if status == 200 else reason
227
- except Exception as exc:
228
- results[k] = str(exc)
229
- print_sorted_table([('Node', 'node'), ('Result', None)],
230
- [[i, results[i]] for i in results],
231
- sort_columns=['node'],
232
- table_format=opts.table_format)
197
+ with ThreadPoolExecutor(max_workers=(meta := self._ctx.meta)[JOBS]) as executor:
198
+ futures: dict[Future[UrlOpenT], DebugPanelClient] = {executor.submit(func_client, client, **kwargs): client for client in self._clients}
199
+ completed: Iterator[Future[UrlOpenT]] = as_completed(futures)
200
+ with progressbar(completed, length=len(futures), label='Progress') if meta[PROGRESS] else nullcontext(completed) as bar:
201
+ for future in bar:
202
+ client: DebugPanelClient = futures[future]
203
+ k: NodeIdentifier = client.get_id()
204
+ try:
205
+ with future.result() as resp:
206
+ status: int = resp.status
207
+ reason: str = resp.reason
208
+ results[k] = 'Requested' if status == 200 else reason
209
+ except Exception as exc:
210
+ results[k] = str(exc)
211
+ print_table([[node, result] for node, result in sorted(results.items())],
212
+ headers=['Node', 'Result'],
213
+ table_format=meta[TABLE_FORMAT])
233
214
 
234
215
  def _initialize_auid_operation(self) -> None:
235
216
  """
@@ -266,14 +247,12 @@ class _DebugPanelCli(object):
266
247
  self._ctx.fail(str(exc))
267
248
  if len(clients) == 0:
268
249
  self._ctx.fail('The list of nodes to process is empty')
269
- # Then, initialize the thread pool
270
- self._executor = ThreadPoolExecutor(max_workers=opts.pool_size or opts.jobs)
271
250
  # Finally, authenticate
272
251
  u, opts.username = opts.username if opts.username else prompt('UI username'), None
273
252
  p, opts.password = opts.password if opts.password else prompt('UI password', hide_input=True), None
274
253
  for client in clients:
275
254
  client.authenticate(u, p)
276
- self._clients.extend(clients)
255
+ self._clients = clients
277
256
 
278
257
 
279
258
  #: The AUID option group: --auid/-a, --auids/-A
@@ -298,7 +277,7 @@ _node_option_group = option_group(
298
277
  option('--node-spec', '--node', '-n', metavar='NODE', multiple=True, help='Add the compact node specification NODE to the list of nodes to process.'),
299
278
  option('--node-specs', '--nodes', '-N', metavar='FILE', type=click_path('ferz'), multiple=True, help='Add the compact node specifications in FILE to the list of nodes to process.'),
300
279
  option('--username', '-U', metavar='USER', show_default='interactive prompt', help='Set the UI username to USER.'),
301
- option('--password', '-P', metavar='PASS', show_default='interactive prompt', help='Set the UI password to PASS.'),
280
+ option('--password', '-P', metavar='PASS', show_default='interactive prompt', help='Set the UI password to PASS.')
302
281
  )
303
282
 
304
283
 
@@ -306,26 +285,25 @@ _node_option_group = option_group(
306
285
  _tabular_output_option_group = option_group(
307
286
  'Tabular output options',
308
287
  option('--headings/--no-headings', is_flag=True, default=True, help='Set whether to include column headings in tabular output.'),
309
- table_format_option('--table-format', '-T', expose_value=True)
288
+ table_format_option('--table-format', '-T')
310
289
  )
311
290
 
312
291
 
313
292
  #: The job option group: --pool-size, --pool-type
314
293
  _job_option_group = option_group(
315
294
  'Job options',
316
- jobs_option(expose_value=True),
317
- option('--pool-size', metavar='SIZE', type=NonNegativeInt, default=DEFAULT_JOBS, deprecated='Use --jobs instead.'),
318
- constraint=mutually_exclusive
295
+ jobs_option('--jobs', '--pool-size', deprecated='--pool-size is deprecated, use --jobs')
319
296
  )
320
297
 
321
298
 
322
- #: The display option group: --accessible, --color/--no-color, --ansi/--no-ansi, --progress/--no-progress, --time
299
+ #: The display option group: --accessible, --color/--no-color --progress/--no-progress, --time
323
300
  _display_option_group = option_group(
324
301
  'Display options',
325
- accessible_option(expose_value=True),
326
- color_option(expose_value=True),
327
- option('--progress/--no-progress', is_flag=True, default=True, help='Set whether to display a progress bar during processing.'),
328
- timer_option(expose_value=True),
302
+ accessible_option,
303
+ color_option,
304
+ no_color_option,
305
+ progress_option,
306
+ timer_option
329
307
  )
330
308
 
331
309
 
@@ -341,100 +319,100 @@ _node_operation = compose_decorators(_node_option_group, _job_option_group, _tab
341
319
  @group(params=None)
342
320
  @show_params_option
343
321
  @pass_context
344
- def debugpanel(ctx: ExtraContext, **kwargs):
322
+ def debugpanel(ctx: Context, **kwargs):
345
323
  """Command line tool to interact with the LOCKSS 1.x DebugPanel servlet."""
346
324
  ctx.obj = _DebugPanelCli(ctx)
347
325
 
348
326
 
349
- #: A subcommand section for AUID commands.
350
- _AUID_COMMANDS = Section('AUID commands')
327
+ @debugpanel.command(help='Show the copyright and exit.')
328
+ def copyright(cli: _DebugPanelCli, **kwargs) -> None:
329
+ """Show the copyright and exit"""
330
+ echo(__copyright__)
331
+
332
+
333
+ @debugpanel.command(help='Show the software license and exit.')
334
+ def license(cli: _DebugPanelCli, **kwargs) -> None:
335
+ """Show the software license and exit"""
336
+ echo(__license__)
337
+
338
+
339
+ @debugpanel.command('version', help='Show the version number and exit.')
340
+ def version(cli: _DebugPanelCli, **kwargs) -> None:
341
+ """Show the version number and exit"""
342
+ echo(__version__)
351
343
 
352
344
 
353
345
  #: A subcommand section for node commands.
354
346
  _NODE_COMMANDS = Section('Node commands')
355
347
 
356
348
 
349
+ @debugpanel.command(aliases=['cp'], section=_NODE_COMMANDS, help='Cause nodes to crawl plugins.')
350
+ @_node_operation
351
+ def crawl_plugins(cli: _DebugPanelCli, **kwargs) -> None:
352
+ """Cause nodes to crawl plugins"""
353
+ cli.dispatch(cli.crawl_plugins, **kwargs)
354
+
355
+
356
+ @debugpanel.command(aliases=['rc'], section=_NODE_COMMANDS, help='Cause nodes to reload their configuration.')
357
+ @_node_operation
358
+ def reload_config(cli: _DebugPanelCli, **kwargs) -> None:
359
+ """Cause nodes to reload their configuration"""
360
+ cli.dispatch(cli.reload_config, **kwargs)
361
+
362
+
363
+ #: A subcommand section for AUID commands.
364
+ _AUID_COMMANDS = Section('AUID commands')
365
+
366
+
357
367
  @debugpanel.command(aliases=['cs'], section=_AUID_COMMANDS, help='Cause nodes to check the substance of AUs.')
358
368
  @_auid_operation
359
369
  def check_substance(cli: _DebugPanelCli, **kwargs) -> None:
360
- """Cause nodes to check the substance of AUs."""
370
+ """Cause nodes to check the substance of AUs"""
361
371
  cli.dispatch(cli.check_substance, **kwargs)
362
372
 
363
373
 
364
- @debugpanel.command(help='Show the copyright and exit.')
365
- def copyright(cli: _DebugPanelCli, **kwargs) -> None:
366
- """Show the copyright and exit."""
367
- echo(__copyright__)
368
-
369
-
370
374
  @debugpanel.command(aliases=['cr'], section=_AUID_COMMANDS, help='Cause nodes to crawl AUs.')
371
375
  @_auid_operation
372
376
  def crawl(cli: _DebugPanelCli, **kwargs) -> None:
373
- """Cause nodes to crawl AUs."""
377
+ """Cause nodes to crawl AUs"""
374
378
  cli.dispatch(cli.crawl, **kwargs)
375
379
 
376
380
 
377
- @debugpanel.command(aliases=['cp'], section=_NODE_COMMANDS, help='Cause nodes to crawl plugins.')
378
- @_node_operation
379
- def crawl_plugins(cli: _DebugPanelCli, **kwargs) -> None:
380
- """Cause nodes to crawl plugins."""
381
- cli.dispatch(cli.crawl_plugins, **kwargs)
382
-
383
-
384
381
  @debugpanel.command(aliases=['dc'], section=_AUID_COMMANDS, help='Cause nodes to deep-crawl AUs.')
385
382
  @compose_decorators(_node_option_group, _auid_option_group, _depth_option_group, _job_option_group, _tabular_output_option_group, _display_option_group, pass_obj)
386
383
  def deep_crawl(cli: _DebugPanelCli, **kwargs) -> None:
387
- """Cause nodes to deep-crawl AUs."""
384
+ """Cause nodes to deep-crawl AUs"""
388
385
  cli.dispatch(cli.deep_crawl, **kwargs)
389
386
 
390
387
 
391
388
  @debugpanel.command(aliases=['di'], section=_AUID_COMMANDS, help='Cause nodes to disable metadata indexing for AUs.')
392
389
  @_auid_operation
393
390
  def disable_indexing(cli: _DebugPanelCli, **kwargs) -> None:
394
- """Cause nodes to disable metadata indexing for AUs."""
391
+ """Cause nodes to disable metadata indexing for AUs"""
395
392
  cli.dispatch(cli.disable_indexing, **kwargs)
396
393
 
397
394
 
398
- @debugpanel.command(help='Show the software license and exit.')
399
- def license(cli: _DebugPanelCli, **kwargs) -> None:
400
- """Show the software license and exit."""
401
- echo(__license__)
402
-
403
-
404
395
  @debugpanel.command(aliases=['po'], section=_AUID_COMMANDS, help='Cause nodes to poll AUs.')
405
396
  @_auid_operation
406
397
  def poll(cli: _DebugPanelCli, **kwargs) -> None:
407
- """Cause nodes to poll AUs."""
398
+ """Cause nodes to poll AUs"""
408
399
  cli.dispatch(cli.poll, **kwargs)
409
400
 
410
401
 
411
- @debugpanel.command(aliases=['rc'], section=_NODE_COMMANDS, help='Cause nodes to reload their configuration.')
412
- @_node_operation
413
- def reload_config(cli: _DebugPanelCli, **kwargs) -> None:
414
- """Cause nodes to reload their configuration."""
415
- cli.dispatch(cli.reload_config, **kwargs)
416
-
417
-
418
402
  @debugpanel.command(aliases=['ri'], section=_AUID_COMMANDS, help='Cause nodes to reindex the metadata of AUs.')
419
403
  @_auid_operation
420
404
  def reindex_metadata(cli: _DebugPanelCli, **kwargs) -> None:
421
- """Cause nodes to reindex the metadata of AUs."""
405
+ """Cause nodes to reindex the metadata of AUs"""
422
406
  cli.dispatch(cli.reindex_metadata, **kwargs)
423
407
 
424
408
 
425
409
  @debugpanel.command(aliases=['vf'], section=_AUID_COMMANDS, help='Cause nodes to validate the files of AUs.')
426
410
  @_auid_operation
427
411
  def validate_files(cli: _DebugPanelCli, **kwargs) -> None:
428
- """Cause nodes to validate the files of AUs."""
412
+ """Cause nodes to validate the files of AUs"""
429
413
  cli.dispatch(cli.validate_files, **kwargs)
430
414
 
431
415
 
432
- @debugpanel.command('version', help='Show the version number and exit.')
433
- def version(cli: _DebugPanelCli, **kwargs) -> None:
434
- """Show the version number and exit."""
435
- echo(__version__)
436
-
437
-
438
416
  def main() -> None:
439
417
  """Main entry point of the module."""
440
418
  debugpanel()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lockss-debugpanel
3
- Version: 0.10.0.dev1
3
+ Version: 0.10.0.dev2
4
4
  Summary: Command line tool and Python library to interact with the LOCKSS 1.x DebugPanel servlet
5
5
  License: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -21,10 +21,10 @@ Classifier: Topic :: System :: Archiving
21
21
  Classifier: Topic :: Utilities
22
22
  Requires-Dist: PyYAML (>=6.0.0,<6.1.0)
23
23
  Requires-Dist: click-command-tree (>=1.2.0,<1.3.0)
24
- Requires-Dist: click-extra[pygments] (>=7.18.0,<7.19.0)
24
+ Requires-Dist: click-extra[hjson,toml,xml,yaml] (>=8.1.1,<8.2.0)
25
25
  Requires-Dist: click-plugins (>=1.1.1.2,<1.2.0)
26
26
  Requires-Dist: cloup (>=3.0.7)
27
- Requires-Dist: lockss-pybasic (==0.3.0.dev9)
27
+ Requires-Dist: lockss-pybasic (==0.3.0.dev11)
28
28
  Requires-Dist: pydantic (>=2.13.0,<2.14.0)
29
29
  Project-URL: Documentation, https://docs.lockss.org/en/latest/software/debugpanel
30
30
  Project-URL: Repository, https://github.com/lockss/lockss-debugpanel
@@ -36,7 +36,7 @@ Description-Content-Type: text/x-rst
36
36
  Debugpanel
37
37
  ==========
38
38
 
39
- .. |RELEASE| replace:: 0.10.0-dev1
39
+ .. |RELEASE| replace:: 0.10.0-dev2
40
40
  .. |RELEASE_DATE| replace:: NOT YET RELEASED
41
41
  .. |DEBUGPANEL| replace:: **Debugpanel**
42
42
 
@@ -0,0 +1,9 @@
1
+ lockss/debugpanel/__init__.py,sha256=xe9cWU-FQRz_mLbDCmRFOcHbFYGUIZ0q4SA2xtmZlGk,1759
2
+ lockss/debugpanel/__main__.py,sha256=JQQzqWrvNdYYTZ5DkhyojRQsaGtmfLnkkmiJSo7rgHU,1669
3
+ lockss/debugpanel/_core.py,sha256=vz3UeupZy1180OklQsFRvgXgtDqLmTIwkodMpm8O2dU,14105
4
+ lockss/debugpanel/cli.py,sha256=sQj7iHQ5aL9zJI4bDh34Jo_AHEoyoxkybH9j5MUuW40,18177
5
+ lockss_debugpanel-0.10.0.dev2.dist-info/METADATA,sha256=MPn3y4JLlLJoDTSyuF5DnIRQFKlttu9bQJN1mk9zcm0,2937
6
+ lockss_debugpanel-0.10.0.dev2.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
7
+ lockss_debugpanel-0.10.0.dev2.dist-info/entry_points.txt,sha256=GuLRH8NaRxFwHAYSrH8J57t-c45qslpHnTMrRPXrjfM,57
8
+ lockss_debugpanel-0.10.0.dev2.dist-info/licenses/LICENSE,sha256=EOxPunNz3XP6AjgbPFolu-d9BS_AF9TtKn1WXgeYPsE,1506
9
+ lockss_debugpanel-0.10.0.dev2.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- lockss/debugpanel/__init__.py,sha256=SMLbdixwjGqyQQvdxGGocJwJSd0IuUk54hnWNyicwKI,1759
2
- lockss/debugpanel/__main__.py,sha256=JQQzqWrvNdYYTZ5DkhyojRQsaGtmfLnkkmiJSo7rgHU,1669
3
- lockss/debugpanel/_core.py,sha256=vz3UeupZy1180OklQsFRvgXgtDqLmTIwkodMpm8O2dU,14105
4
- lockss/debugpanel/cli.py,sha256=_fUTGQb2v-S1WUAWhWbw0GRpjF9s6Zq1gzShz0eotJA,19108
5
- lockss_debugpanel-0.10.0.dev1.dist-info/METADATA,sha256=aF3fMslZh7HL6BCOymz81ltmz1fhxcse2Ljyfjqp0Vk,2927
6
- lockss_debugpanel-0.10.0.dev1.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
7
- lockss_debugpanel-0.10.0.dev1.dist-info/entry_points.txt,sha256=GuLRH8NaRxFwHAYSrH8J57t-c45qslpHnTMrRPXrjfM,57
8
- lockss_debugpanel-0.10.0.dev1.dist-info/licenses/LICENSE,sha256=EOxPunNz3XP6AjgbPFolu-d9BS_AF9TtKn1WXgeYPsE,1506
9
- lockss_debugpanel-0.10.0.dev1.dist-info/RECORD,,