lsst-ctrl-mpexec 29.2025.1600__py3-none-any.whl → 29.2025.1800__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.
@@ -311,6 +311,7 @@ def pre_exec_init_qbb(repo: str, qgraph: str, **kwargs: Any) -> None:
311
311
  @ctrlMpExecOpts.enable_implicit_threading_option()
312
312
  @ctrlMpExecOpts.cores_per_quantum_option()
313
313
  @ctrlMpExecOpts.memory_per_quantum_option()
314
+ @ctrlMpExecOpts.no_existing_outputs_option()
314
315
  def run_qbb(repo: str, qgraph: str, **kwargs: Any) -> None:
315
316
  """Execute pipeline using Quantum-Backed Butler.
316
317
 
@@ -396,6 +396,16 @@ clobber_outputs_option = MWOptionDecorator(
396
396
  )
397
397
 
398
398
 
399
+ no_existing_outputs_option = MWOptionDecorator(
400
+ "--no-existing-outputs",
401
+ help=(
402
+ "Assume that no predicted outputs already exist in the output run collection. "
403
+ "This will eliminate existence checks that otherwise run before each quantum is executed."
404
+ ),
405
+ is_flag=True,
406
+ )
407
+
408
+
399
409
  skip_init_writes_option = MWOptionDecorator(
400
410
  "--skip-init-writes",
401
411
  help=unwrap(
@@ -53,6 +53,7 @@ def run_qbb(
53
53
  cores_per_quantum: int,
54
54
  memory_per_quantum: str,
55
55
  raise_on_partial_outputs: bool,
56
+ no_existing_outputs: bool,
56
57
  ) -> None:
57
58
  """Implement the command line interface ``pipetask run-qbb`` subcommand.
58
59
 
@@ -104,6 +105,9 @@ def run_qbb(
104
105
  units of MB) or a combination of number and unit.
105
106
  raise_on_partial_outputs : `bool`
106
107
  Consider partial outputs an error instead of a success.
108
+ no_existing_outputs : `bool`
109
+ Whether to assume that no predicted outputs for these quanta already
110
+ exist in the output run collection.
107
111
  """
108
112
  # Fork option still exists for compatibility but we use spawn instead.
109
113
  if start_method == "fork":
@@ -131,6 +135,7 @@ def run_qbb(
131
135
  cores_per_quantum=cores_per_quantum,
132
136
  memory_per_quantum=memory_per_quantum,
133
137
  raise_on_partial_outputs=raise_on_partial_outputs,
138
+ no_existing_outputs=no_existing_outputs,
134
139
  )
135
140
 
136
141
  f = CmdLineFwk()
@@ -75,7 +75,7 @@ from lsst.pipe.base.mermaid_tools import graph2mermaid
75
75
  from lsst.pipe.base.pipeline_graph import NodeType
76
76
  from lsst.resources import ResourcePath
77
77
  from lsst.utils import doImportType
78
- from lsst.utils.logging import getLogger
78
+ from lsst.utils.logging import VERBOSE, getLogger
79
79
  from lsst.utils.threads import disable_implicit_threading
80
80
 
81
81
  from ._pipeline_graph_factory import PipelineGraphFactory
@@ -844,7 +844,7 @@ class CmdLineFwk:
844
844
  _LOG.debug("Will try to import debug.py")
845
845
  import debug # type: ignore # noqa:F401
846
846
  except ImportError:
847
- _LOG.warn("No 'debug' module found.")
847
+ _LOG.warning("No 'debug' module found.")
848
848
 
849
849
  # Save all InitOutputs, configs, etc.
850
850
  preExecInit = PreExecInit(butler, taskFactory, extendRun=args.extend_run)
@@ -977,6 +977,7 @@ class CmdLineFwk:
977
977
  return None
978
978
 
979
979
  def preExecInitQBB(self, task_factory: TaskFactory, args: SimpleNamespace) -> None:
980
+ _LOG.verbose("Reading full quantum graph from %s.", args.qgraph)
980
981
  # Load quantum graph. We do not really need individual Quanta here,
981
982
  # but we need datastore records for initInputs, and those are only
982
983
  # available from Quanta, so load the whole thing.
@@ -986,8 +987,10 @@ class CmdLineFwk:
986
987
  _ButlerFactory.defineDatastoreCache()
987
988
 
988
989
  # Make QBB.
990
+ _LOG.verbose("Initializing quantum-backed butler.")
989
991
  butler = qgraph.make_init_qbb(args.butler_config, config_search_paths=args.config_search_path)
990
992
  # Save all InitOutputs, configs, etc.
993
+ _LOG.verbose("Instantiating tasks and saving init-outputs.")
991
994
  preExecInit = PreExecInitLimited(butler, task_factory)
992
995
  preExecInit.initialize(qgraph)
993
996
 
@@ -997,7 +1000,13 @@ class CmdLineFwk:
997
1000
 
998
1001
  # Load quantum graph.
999
1002
  nodes = args.qgraph_node_id or None
1000
- qgraph = QuantumGraph.loadUri(args.qgraph, nodes=nodes, graphID=args.qgraph_id)
1003
+ with lsst.utils.timer.time_this(
1004
+ _LOG,
1005
+ msg=f"Reading {str(len(nodes)) if nodes is not None else 'all'} quanta.",
1006
+ level=VERBOSE,
1007
+ ) as qg_read_time:
1008
+ qgraph = QuantumGraph.loadUri(args.qgraph, nodes=nodes, graphID=args.qgraph_id)
1009
+ job_metadata = {"qg_read_time": qg_read_time.duration, "qg_size": len(qgraph)}
1001
1010
 
1002
1011
  if qgraph.metadata is None:
1003
1012
  raise ValueError("QuantumGraph is missing metadata, cannot continue.")
@@ -1023,8 +1032,11 @@ class CmdLineFwk:
1023
1032
  enableLsstDebug=args.enableLsstDebug,
1024
1033
  limited_butler_factory=_butler_factory,
1025
1034
  resources=resources,
1026
- assumeNoExistingOutputs=True,
1035
+ assumeNoExistingOutputs=args.no_existing_outputs,
1036
+ skipExisting=True,
1037
+ clobberOutputs=True,
1027
1038
  raise_on_partial_outputs=args.raise_on_partial_outputs,
1039
+ job_metadata=job_metadata,
1028
1040
  )
1029
1041
 
1030
1042
  timeout = self.MP_TIMEOUT if args.timeout is None else args.timeout
@@ -34,12 +34,13 @@ import logging
34
34
  import time
35
35
  import uuid
36
36
  from collections import defaultdict
37
- from collections.abc import Callable
37
+ from collections.abc import Callable, Mapping
38
38
  from itertools import chain
39
39
  from typing import Any, cast
40
40
 
41
41
  from lsst.daf.butler import (
42
42
  Butler,
43
+ ButlerMetrics,
43
44
  CollectionType,
44
45
  DatasetRef,
45
46
  DatasetType,
@@ -127,6 +128,11 @@ class SingleQuantumExecutor(QuantumExecutor):
127
128
  `lsst.pipe.base.AnnotatedPartialOutputError` immediately, instead of
128
129
  considering the partial result a success and continuing to run
129
130
  downstream tasks.
131
+ job_metadata : `~collections.abc.Mapping`
132
+ Mapping with extra metadata to embed within the quantum metadata under
133
+ the "job" key. This is intended to correspond to information common
134
+ to all quanta being executed in a single process, such as the time
135
+ taken to load the quantum graph in a BPS job.
130
136
  """
131
137
 
132
138
  def __init__(
@@ -141,6 +147,7 @@ class SingleQuantumExecutor(QuantumExecutor):
141
147
  skipExisting: bool = False,
142
148
  assumeNoExistingOutputs: bool = False,
143
149
  raise_on_partial_outputs: bool = True,
150
+ job_metadata: Mapping[str, int | str | float] | None = None,
144
151
  ):
145
152
  self.butler = butler
146
153
  self.taskFactory = taskFactory
@@ -150,6 +157,7 @@ class SingleQuantumExecutor(QuantumExecutor):
150
157
  self.resources = resources
151
158
  self.assumeNoExistingOutputs = assumeNoExistingOutputs
152
159
  self.raise_on_partial_outputs = raise_on_partial_outputs
160
+ self.job_metadata = job_metadata
153
161
 
154
162
  if self.butler is None:
155
163
  assert limited_butler_factory is not None, "limited_butler_factory is needed when butler is None"
@@ -246,6 +254,8 @@ class SingleQuantumExecutor(QuantumExecutor):
246
254
  fullMetadata = _TASK_FULL_METADATA_TYPE()
247
255
  fullMetadata[task_node.label] = _TASK_METADATA_TYPE()
248
256
  fullMetadata["quantum"] = quantumMetadata
257
+ if self.job_metadata is not None:
258
+ fullMetadata["job"] = self.job_metadata
249
259
  self.writeMetadata(quantum, fullMetadata, task_node, limited_butler)
250
260
  return quantum
251
261
 
@@ -255,7 +265,7 @@ class SingleQuantumExecutor(QuantumExecutor):
255
265
  _LOG.debug("Will try to import debug.py")
256
266
  import debug # type: ignore # noqa:F401
257
267
  except ImportError:
258
- _LOG.warn("No 'debug' module found.")
268
+ _LOG.warning("No 'debug' module found.")
259
269
 
260
270
  # initialize global state
261
271
  self.initGlobals(quantum)
@@ -273,7 +283,7 @@ class SingleQuantumExecutor(QuantumExecutor):
273
283
  task = self.taskFactory.makeTask(task_node, limited_butler, init_input_refs)
274
284
  logInfo(None, "start", metadata=quantumMetadata) # type: ignore[arg-type]
275
285
  try:
276
- caveats, outputsPut = self.runQuantum(
286
+ caveats, outputsPut, butler_metrics = self.runQuantum(
277
287
  task, quantum, task_node, limited_butler, quantum_id=quantum_id
278
288
  )
279
289
  except Exception as e:
@@ -286,6 +296,7 @@ class SingleQuantumExecutor(QuantumExecutor):
286
296
  )
287
297
  raise
288
298
  else:
299
+ quantumMetadata["butler_metrics"] = butler_metrics.model_dump()
289
300
  quantumMetadata["caveats"] = caveats.value
290
301
  # Stringify the UUID for easier compatibility with
291
302
  # PropertyList.
@@ -293,6 +304,8 @@ class SingleQuantumExecutor(QuantumExecutor):
293
304
  logInfo(None, "end", metadata=quantumMetadata) # type: ignore[arg-type]
294
305
  fullMetadata = task.getFullMetadata()
295
306
  fullMetadata["quantum"] = quantumMetadata
307
+ if self.job_metadata is not None:
308
+ fullMetadata["job"] = self.job_metadata
296
309
  self.writeMetadata(quantum, fullMetadata, task_node, limited_butler)
297
310
  stopTime = time.time()
298
311
  _LOG.info(
@@ -488,7 +501,7 @@ class SingleQuantumExecutor(QuantumExecutor):
488
501
  /,
489
502
  limited_butler: LimitedButler,
490
503
  quantum_id: uuid.UUID | None = None,
491
- ) -> tuple[QuantumSuccessCaveats, list[uuid.UUID]]:
504
+ ) -> tuple[QuantumSuccessCaveats, list[uuid.UUID], ButlerMetrics]:
492
505
  """Execute task on a single quantum.
493
506
 
494
507
  Parameters
@@ -511,6 +524,8 @@ class SingleQuantumExecutor(QuantumExecutor):
511
524
  ids_put : list[ `uuid.UUID` ]
512
525
  Record of all the dataset IDs that were written by this quantum
513
526
  being executed.
527
+ metrics : `lsst.daf.butler.ButlerMetrics`
528
+ Butler metrics recorded for this quantum.
514
529
  """
515
530
  flags = QuantumSuccessCaveats.NO_CAVEATS
516
531
 
@@ -522,7 +537,8 @@ class SingleQuantumExecutor(QuantumExecutor):
522
537
 
523
538
  # Call task runQuantum() method.
524
539
  try:
525
- task.runQuantum(butlerQC, inputRefs, outputRefs)
540
+ with limited_butler.record_metrics() as butler_metrics:
541
+ task.runQuantum(butlerQC, inputRefs, outputRefs)
526
542
  except NoWorkFound as err:
527
543
  # Not an error, just an early exit.
528
544
  _LOG.info(
@@ -565,7 +581,7 @@ class SingleQuantumExecutor(QuantumExecutor):
565
581
  if not butlerQC.outputsPut == butlerQC.allOutputs:
566
582
  flags |= QuantumSuccessCaveats.ANY_OUTPUTS_MISSING
567
583
  ids_put = [output[2] for output in butlerQC.outputsPut]
568
- return flags, ids_put
584
+ return flags, ids_put, butler_metrics
569
585
 
570
586
  def writeMetadata(
571
587
  self, quantum: Quantum, metadata: Any, task_node: TaskNode, /, limited_butler: LimitedButler
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "29.2025.1600"
2
+ __version__ = "29.2025.1800"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-ctrl-mpexec
3
- Version: 29.2025.1600
3
+ Version: 29.2025.1800
4
4
  Summary: Pipeline execution infrastructure for the Rubin Observatory LSST Science Pipelines.
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License: BSD 3-Clause License
@@ -2,7 +2,7 @@ lsst/__init__.py,sha256=aXdEOZVrBQISQi6XPS9s1NhBjIJaIwNNxCFRiGchRAw,1369
2
2
  lsst/ctrl/__init__.py,sha256=aXdEOZVrBQISQi6XPS9s1NhBjIJaIwNNxCFRiGchRAw,1369
3
3
  lsst/ctrl/mpexec/__init__.py,sha256=c2mK9--wemp2AEa81CSMkrqaIjJ1_iOs_M-O8CHJxB4,1757
4
4
  lsst/ctrl/mpexec/_pipeline_graph_factory.py,sha256=suzWUn9YGn0CTA_3N1Wd-sUo7TFxuo_6VZ2nO0CJ5a8,3552
5
- lsst/ctrl/mpexec/cmdLineFwk.py,sha256=L8CaSTLZDe9cz4KGSlelMGDjpTKXxC4DTBEw6u9YPS8,43132
5
+ lsst/ctrl/mpexec/cmdLineFwk.py,sha256=hrjDfsaBO0oTqliksQgZywyN2uVY7E7DLZzUSvAOA4Y,43759
6
6
  lsst/ctrl/mpexec/dotTools.py,sha256=nx31DC7NkQM766sC97lPQMI22snVaWS7USFMFVLChqk,3421
7
7
  lsst/ctrl/mpexec/execFixupDataId.py,sha256=MUirmuoQiWj7C7wQ23QGkgMqx_5_CP4_UMV6h_gVfxk,5056
8
8
  lsst/ctrl/mpexec/executionGraphFixup.py,sha256=1z4P4YmtLTMPoMxmFiOnzljLcP9B5V1SCG9haqR_TyI,2830
@@ -15,19 +15,19 @@ lsst/ctrl/mpexec/reports.py,sha256=zxZOkJWhTFdl4eJTOYsM-58tqRbbyF5-cQ-zF-zIR9Y,7
15
15
  lsst/ctrl/mpexec/separablePipelineExecutor.py,sha256=6QvZ9EFHoqXwYjHbcNe9wWMssFbAg2I9mNpjFnTSiM0,12074
16
16
  lsst/ctrl/mpexec/showInfo.py,sha256=elrWQ8QPusZguLqYhvmMjYaNmoJgWpQDr3uQ0QhB1HI,16407
17
17
  lsst/ctrl/mpexec/simple_pipeline_executor.py,sha256=scqQ1MDS6FcZvFTwxtCkmTSg0zJmKIfah91lawr7Dhc,20454
18
- lsst/ctrl/mpexec/singleQuantumExecutor.py,sha256=mlv3nF29N1meiHie3r072Ynz5k6XTpFO8jfhq7BufF8,28036
18
+ lsst/ctrl/mpexec/singleQuantumExecutor.py,sha256=sYYdUe6gKRZELFMVHxQLS5ZnBcrKJpxKnXFwchn-7Z8,29023
19
19
  lsst/ctrl/mpexec/taskFactory.py,sha256=c4xj8cR_Ts5uzzGovh87ZKdVeeXy5E3lIjCwJnDuOqg,2720
20
20
  lsst/ctrl/mpexec/util.py,sha256=y2Rw5PL40_EuLtVxiqSVX0JfPV4IrFl1LfOMUWx2u30,4236
21
- lsst/ctrl/mpexec/version.py,sha256=DWDCyavYRMZByGPGMYcnpz6piNv1sQvCKH8yohX_F3c,55
21
+ lsst/ctrl/mpexec/version.py,sha256=uRdffTNnwH4GQy77H1Wk3Md64craDCIuRxmCtF7ON0g,55
22
22
  lsst/ctrl/mpexec/cli/__init__.py,sha256=6dpDHNBzyicVpFi1fsaiYVbYEMeoL57IHKkPaej24gs,1301
23
23
  lsst/ctrl/mpexec/cli/pipetask.py,sha256=4HnhX9dCizCihVbpHVJX5WXO9TEli9oL6wA-tPh1_vA,2209
24
24
  lsst/ctrl/mpexec/cli/utils.py,sha256=5iOrlj5jJTWtZS0BMLsuiCGAvxbfrjd1MSyXxBthWcc,6503
25
25
  lsst/ctrl/mpexec/cli/cmd/__init__.py,sha256=nRmwwW5d55gAEkyE7NpSK8mxa56HcfEta2r-Y9I07F8,1661
26
- lsst/ctrl/mpexec/cli/cmd/commands.py,sha256=Wevv7Wu3hgLY2tNA4RJdMpOFur9rbH5JtwgtNLPnqiY,17811
26
+ lsst/ctrl/mpexec/cli/cmd/commands.py,sha256=hb4o2xaGRcImIv4FzHw7ABOdqTJWmlB07EWL8layVlM,17856
27
27
  lsst/ctrl/mpexec/cli/opt/__init__.py,sha256=IzUInuJj9igiaNcEqMx0adelgJtQC5_XMYnaiizBn0A,1378
28
28
  lsst/ctrl/mpexec/cli/opt/arguments.py,sha256=vjUw0ZN_4HStp-_3ne6AT5S_eH7sly3OVfL07tgrJnY,1572
29
29
  lsst/ctrl/mpexec/cli/opt/optionGroups.py,sha256=qtpwlgfvEzAs0CNav7t9ylywWBavoeWlZfYhRQEuZTw,8132
30
- lsst/ctrl/mpexec/cli/opt/options.py,sha256=8gu-QYCblBO_RlPGYFw__teRWxtRCyasGKz7wNhv_dc,20563
30
+ lsst/ctrl/mpexec/cli/opt/options.py,sha256=1JK67cMx9F2poURXkA-q3Uru4B86CZoNtSzM6vmZ7pw,20867
31
31
  lsst/ctrl/mpexec/cli/script/__init__.py,sha256=eCuF4FAI5D3pl05IMJj7TCkZq-hireua2mA5Ui-mKSI,1624
32
32
  lsst/ctrl/mpexec/cli/script/build.py,sha256=Ff_8ffBmxuJi45x7-rUb-f0axucagYs6rNvBowwVmq0,6149
33
33
  lsst/ctrl/mpexec/cli/script/cleanup.py,sha256=D7W-azf4mNJcIWhbU5uCRCi94mkb8-Q2ksRFblQGrUw,4990
@@ -37,15 +37,15 @@ lsst/ctrl/mpexec/cli/script/purge.py,sha256=gYwSsZfTBP6oDcDp_YdqQEKGvAStvsj5hwNw
37
37
  lsst/ctrl/mpexec/cli/script/qgraph.py,sha256=ncjbtJ7AMp-Obo524HAC2E4Hnqyk1Aji4mJPAZUGMDM,10048
38
38
  lsst/ctrl/mpexec/cli/script/report.py,sha256=ItJitmYmWIDjj7PxRtP4qXLx-z5FAU6nSfI2gx0GS5k,12800
39
39
  lsst/ctrl/mpexec/cli/script/run.py,sha256=22g7vBhGDGsFSQq7CtruTf-bivo3t53decBAV0wAg1g,9767
40
- lsst/ctrl/mpexec/cli/script/run_qbb.py,sha256=oCEydg13zNAtci8XB0IwXx_D5PPsHbjCPATWoBlhcPk,5294
40
+ lsst/ctrl/mpexec/cli/script/run_qbb.py,sha256=v05gQvngvKCWvFkRifQoUoPnGnkQ57ZBGWaNVFnmCB8,5528
41
41
  lsst/ctrl/mpexec/cli/script/update_graph_run.py,sha256=v_EdOaD6jR_vSlgm_5-pwUjoNEFMrAuYFM1xIaHVU3Q,2597
42
- lsst_ctrl_mpexec-29.2025.1600.dist-info/licenses/COPYRIGHT,sha256=pGCjnRAnyt02a6_9PLzXQikpvYmvMmK9fCdOKlRSV6k,369
43
- lsst_ctrl_mpexec-29.2025.1600.dist-info/licenses/LICENSE,sha256=pRExkS03v0MQW-neNfIcaSL6aiAnoLxYgtZoFzQ6zkM,232
44
- lsst_ctrl_mpexec-29.2025.1600.dist-info/licenses/bsd_license.txt,sha256=7MIcv8QRX9guUtqPSBDMPz2SnZ5swI-xZMqm_VDSfxY,1606
45
- lsst_ctrl_mpexec-29.2025.1600.dist-info/licenses/gpl-v3.0.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
46
- lsst_ctrl_mpexec-29.2025.1600.dist-info/METADATA,sha256=4w8JjdeOxf8HVCuGdbBxHxYx0DTb-RiKmQZ9rqkLLlk,2302
47
- lsst_ctrl_mpexec-29.2025.1600.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
48
- lsst_ctrl_mpexec-29.2025.1600.dist-info/entry_points.txt,sha256=aYE38yqZU8qvpLUUkXzgmUxDJYYknEqPxgxYkowrL4s,64
49
- lsst_ctrl_mpexec-29.2025.1600.dist-info/top_level.txt,sha256=eUWiOuVVm9wwTrnAgiJT6tp6HQHXxIhj2QSZ7NYZH80,5
50
- lsst_ctrl_mpexec-29.2025.1600.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
51
- lsst_ctrl_mpexec-29.2025.1600.dist-info/RECORD,,
42
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/licenses/COPYRIGHT,sha256=pGCjnRAnyt02a6_9PLzXQikpvYmvMmK9fCdOKlRSV6k,369
43
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/licenses/LICENSE,sha256=pRExkS03v0MQW-neNfIcaSL6aiAnoLxYgtZoFzQ6zkM,232
44
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/licenses/bsd_license.txt,sha256=7MIcv8QRX9guUtqPSBDMPz2SnZ5swI-xZMqm_VDSfxY,1606
45
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/licenses/gpl-v3.0.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
46
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/METADATA,sha256=6AvPdrrvpzEHHIb2g9-CN7_O0BsC15DHhSNM0i0tUpU,2302
47
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
48
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/entry_points.txt,sha256=aYE38yqZU8qvpLUUkXzgmUxDJYYknEqPxgxYkowrL4s,64
49
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/top_level.txt,sha256=eUWiOuVVm9wwTrnAgiJT6tp6HQHXxIhj2QSZ7NYZH80,5
50
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
51
+ lsst_ctrl_mpexec-29.2025.1800.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5