lsst-daf-butler 30.2026.200__py3-none-any.whl → 30.2026.500__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.
- lsst/daf/butler/_butler.py +18 -8
- lsst/daf/butler/_butler_collections.py +4 -4
- lsst/daf/butler/_butler_metrics.py +51 -2
- lsst/daf/butler/_dataset_provenance.py +1 -1
- lsst/daf/butler/_dataset_ref.py +1 -1
- lsst/daf/butler/_exceptions.py +2 -2
- lsst/daf/butler/_file_dataset.py +2 -1
- lsst/daf/butler/_formatter.py +12 -0
- lsst/daf/butler/_labeled_butler_factory.py +28 -8
- lsst/daf/butler/_query_all_datasets.py +2 -0
- lsst/daf/butler/cli/cmd/_remove_runs.py +1 -12
- lsst/daf/butler/column_spec.py +4 -4
- lsst/daf/butler/configs/datastores/formatters.yaml +1 -0
- lsst/daf/butler/configs/storageClasses.yaml +15 -0
- lsst/daf/butler/datastore/_datastore.py +21 -1
- lsst/daf/butler/datastore/stored_file_info.py +2 -2
- lsst/daf/butler/datastores/chainedDatastore.py +4 -0
- lsst/daf/butler/datastores/fileDatastore.py +4 -0
- lsst/daf/butler/datastores/file_datastore/get.py +4 -4
- lsst/daf/butler/datastores/file_datastore/transfer.py +2 -2
- lsst/daf/butler/datastores/inMemoryDatastore.py +8 -0
- lsst/daf/butler/ddl.py +2 -2
- lsst/daf/butler/dimensions/_coordinate.py +11 -8
- lsst/daf/butler/dimensions/_record_set.py +1 -1
- lsst/daf/butler/dimensions/_records.py +9 -3
- lsst/daf/butler/direct_butler/_direct_butler.py +48 -28
- lsst/daf/butler/direct_query_driver/_driver.py +5 -4
- lsst/daf/butler/direct_query_driver/_result_page_converter.py +1 -1
- lsst/daf/butler/formatters/parquet.py +6 -6
- lsst/daf/butler/logging.py +9 -3
- lsst/daf/butler/nonempty_mapping.py +1 -1
- lsst/daf/butler/persistence_context.py +8 -5
- lsst/daf/butler/queries/_general_query_results.py +1 -1
- lsst/daf/butler/queries/driver.py +1 -1
- lsst/daf/butler/queries/expression_factory.py +2 -2
- lsst/daf/butler/queries/expressions/parser/exprTree.py +1 -1
- lsst/daf/butler/queries/expressions/parser/parserYacc.py +1 -1
- lsst/daf/butler/queries/overlaps.py +2 -2
- lsst/daf/butler/queries/tree/_column_set.py +1 -1
- lsst/daf/butler/registry/_collection_record_cache.py +1 -1
- lsst/daf/butler/registry/_collection_summary_cache.py +5 -4
- lsst/daf/butler/registry/_registry.py +4 -0
- lsst/daf/butler/registry/databases/postgresql.py +2 -1
- lsst/daf/butler/registry/datasets/byDimensions/_dataset_type_cache.py +1 -1
- lsst/daf/butler/registry/datasets/byDimensions/_manager.py +4 -2
- lsst/daf/butler/registry/datasets/byDimensions/summaries.py +3 -2
- lsst/daf/butler/registry/expand_data_ids.py +93 -0
- lsst/daf/butler/registry/interfaces/_datasets.py +2 -1
- lsst/daf/butler/registry/interfaces/_obscore.py +1 -1
- lsst/daf/butler/registry/obscore/_records.py +1 -1
- lsst/daf/butler/registry/obscore/_spatial.py +2 -2
- lsst/daf/butler/registry/queries/_results.py +2 -2
- lsst/daf/butler/registry/sql_registry.py +3 -25
- lsst/daf/butler/registry/wildcards.py +5 -5
- lsst/daf/butler/remote_butler/_get.py +1 -1
- lsst/daf/butler/remote_butler/_remote_butler.py +5 -1
- lsst/daf/butler/remote_butler/_remote_file_transfer_source.py +4 -0
- lsst/daf/butler/remote_butler/authentication/cadc.py +4 -3
- lsst/daf/butler/script/_pruneDatasets.py +4 -2
- lsst/daf/butler/script/configValidate.py +2 -2
- lsst/daf/butler/script/queryCollections.py +2 -2
- lsst/daf/butler/script/removeCollections.py +2 -0
- lsst/daf/butler/script/removeRuns.py +2 -0
- lsst/daf/butler/tests/cliCmdTestBase.py +2 -0
- lsst/daf/butler/tests/cliLogTestBase.py +2 -0
- lsst/daf/butler/tests/hybrid_butler.py +4 -1
- lsst/daf/butler/tests/registry_data/lsstcam-subset.yaml +191 -0
- lsst/daf/butler/tests/registry_data/spatial.py +4 -2
- lsst/daf/butler/tests/utils.py +1 -1
- lsst/daf/butler/timespan_database_representation.py +3 -3
- lsst/daf/butler/transfers/_context.py +7 -6
- lsst/daf/butler/version.py +1 -1
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/METADATA +3 -2
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/RECORD +82 -80
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/WHEEL +1 -1
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/entry_points.txt +0 -0
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/licenses/COPYRIGHT +0 -0
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/licenses/LICENSE +0 -0
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/licenses/bsd_license.txt +0 -0
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/licenses/gpl-v3.0.txt +0 -0
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/top_level.txt +0 -0
- {lsst_daf_butler-30.2026.200.dist-info → lsst_daf_butler-30.2026.500.dist-info}/zip-safe +0 -0
lsst/daf/butler/_butler.py
CHANGED
|
@@ -138,7 +138,10 @@ class Butler(LimitedButler): # numpydoc ignore=PR02
|
|
|
138
138
|
without_datastore : `bool`, optional
|
|
139
139
|
If `True` do not attach a datastore to this butler. Any attempts
|
|
140
140
|
to use a datastore will fail.
|
|
141
|
-
|
|
141
|
+
metrics : `ButlerMetrics` or `None`
|
|
142
|
+
External metrics object to be used for tracking butler usage. If `None`
|
|
143
|
+
a new metrics object is created.
|
|
144
|
+
**kwargs : `typing.Any`
|
|
142
145
|
Additional keyword arguments passed to a constructor of actual butler
|
|
143
146
|
class.
|
|
144
147
|
|
|
@@ -240,7 +243,7 @@ class Butler(LimitedButler): # numpydoc ignore=PR02
|
|
|
240
243
|
to use a datastore will fail.
|
|
241
244
|
metrics : `ButlerMetrics` or `None`, optional
|
|
242
245
|
Metrics object to record butler usage statistics.
|
|
243
|
-
**kwargs : `Any`
|
|
246
|
+
**kwargs : `typing.Any`
|
|
244
247
|
Default data ID key-value pairs. These may only identify
|
|
245
248
|
"governor" dimensions like ``instrument`` and ``skymap``.
|
|
246
249
|
|
|
@@ -1390,6 +1393,10 @@ class Butler(LimitedButler): # numpydoc ignore=PR02
|
|
|
1390
1393
|
raised if any datasets with the same dataset ID already exist
|
|
1391
1394
|
in the datastore.
|
|
1392
1395
|
|
|
1396
|
+
Returns
|
|
1397
|
+
-------
|
|
1398
|
+
None
|
|
1399
|
+
|
|
1393
1400
|
Raises
|
|
1394
1401
|
------
|
|
1395
1402
|
TypeError
|
|
@@ -1566,7 +1573,7 @@ class Butler(LimitedButler): # numpydoc ignore=PR02
|
|
|
1566
1573
|
|
|
1567
1574
|
@abstractmethod
|
|
1568
1575
|
def transfer_dimension_records_from(
|
|
1569
|
-
self, source_butler: LimitedButler | Butler, source_refs: Iterable[DatasetRef]
|
|
1576
|
+
self, source_butler: LimitedButler | Butler, source_refs: Iterable[DatasetRef | DataCoordinate]
|
|
1570
1577
|
) -> None:
|
|
1571
1578
|
"""Transfer dimension records to this Butler from another Butler.
|
|
1572
1579
|
|
|
@@ -1578,10 +1585,9 @@ class Butler(LimitedButler): # numpydoc ignore=PR02
|
|
|
1578
1585
|
`Butler` whose registry will be used to expand data IDs. If the
|
|
1579
1586
|
source refs contain coordinates that are used to populate other
|
|
1580
1587
|
records then this will also need to be a full `Butler`.
|
|
1581
|
-
source_refs : iterable of `DatasetRef`
|
|
1582
|
-
Datasets defined in the source butler whose dimension
|
|
1583
|
-
should be transferred to this butler.
|
|
1584
|
-
transfer is faster if the dataset refs are expanded.
|
|
1588
|
+
source_refs : iterable of `DatasetRef` or `DataCoordinate`
|
|
1589
|
+
Datasets or data IDs defined in the source butler whose dimension
|
|
1590
|
+
records should be transferred to this butler.
|
|
1585
1591
|
"""
|
|
1586
1592
|
raise NotImplementedError()
|
|
1587
1593
|
|
|
@@ -2025,7 +2031,7 @@ class Butler(LimitedButler): # numpydoc ignore=PR02
|
|
|
2025
2031
|
|
|
2026
2032
|
Returns
|
|
2027
2033
|
-------
|
|
2028
|
-
records : `list`[`DimensionRecord`]
|
|
2034
|
+
records : `list` [`DimensionRecord`]
|
|
2029
2035
|
Dimension records matching the given query parameters.
|
|
2030
2036
|
|
|
2031
2037
|
Raises
|
|
@@ -2227,3 +2233,7 @@ class Butler(LimitedButler): # numpydoc ignore=PR02
|
|
|
2227
2233
|
@abstractmethod
|
|
2228
2234
|
def close(self) -> None:
|
|
2229
2235
|
raise NotImplementedError()
|
|
2236
|
+
|
|
2237
|
+
@abstractmethod
|
|
2238
|
+
def _expand_data_ids(self, data_ids: Iterable[DataCoordinate]) -> list[DataCoordinate]:
|
|
2239
|
+
raise NotImplementedError()
|
|
@@ -360,10 +360,10 @@ class ButlerCollections(ABC, Sequence):
|
|
|
360
360
|
name : `str`
|
|
361
361
|
The name of the collection of interest.
|
|
362
362
|
include_parents : `bool`, optional
|
|
363
|
-
|
|
363
|
+
If `True` any parents of this collection will be included.
|
|
364
364
|
include_summary : `bool`, optional
|
|
365
|
-
|
|
366
|
-
|
|
365
|
+
If `True` dataset type names and governor dimensions of datasets
|
|
366
|
+
stored in this collection will be included in the result.
|
|
367
367
|
|
|
368
368
|
Returns
|
|
369
369
|
-------
|
|
@@ -464,7 +464,7 @@ class ButlerCollections(ABC, Sequence):
|
|
|
464
464
|
|
|
465
465
|
Returns
|
|
466
466
|
-------
|
|
467
|
-
filtered : `~collections.abc.Mapping` [`str`, `list`[`str`]]
|
|
467
|
+
filtered : `~collections.abc.Mapping` [`str`, `list` [`str`]]
|
|
468
468
|
Mapping of the dataset type name to its corresponding list of
|
|
469
469
|
collection names.
|
|
470
470
|
"""
|
|
@@ -27,14 +27,19 @@
|
|
|
27
27
|
|
|
28
28
|
from __future__ import annotations
|
|
29
29
|
|
|
30
|
+
__all__ = ["ButlerMetrics"]
|
|
31
|
+
|
|
30
32
|
from collections.abc import Callable, Iterator
|
|
31
33
|
from contextlib import contextmanager
|
|
34
|
+
from typing import Concatenate, ParamSpec
|
|
32
35
|
|
|
33
36
|
from pydantic import BaseModel
|
|
34
37
|
|
|
35
38
|
from lsst.utils.logging import LsstLoggers
|
|
36
39
|
from lsst.utils.timer import time_this
|
|
37
40
|
|
|
41
|
+
P = ParamSpec("P")
|
|
42
|
+
|
|
38
43
|
|
|
39
44
|
class ButlerMetrics(BaseModel):
|
|
40
45
|
"""Metrics collected during Butler operations."""
|
|
@@ -45,18 +50,26 @@ class ButlerMetrics(BaseModel):
|
|
|
45
50
|
time_in_get: float = 0.0
|
|
46
51
|
"""Wall-clock time, in seconds, spent in get()."""
|
|
47
52
|
|
|
53
|
+
time_in_ingest: float = 0.0
|
|
54
|
+
"""Wall-clock time, in seconds, spent in ingest()."""
|
|
55
|
+
|
|
48
56
|
n_get: int = 0
|
|
49
57
|
"""Number of datasets retrieved with get()."""
|
|
50
58
|
|
|
51
59
|
n_put: int = 0
|
|
52
60
|
"""Number of datasets stored with put()."""
|
|
53
61
|
|
|
62
|
+
n_ingest: int = 0
|
|
63
|
+
"""Number of datasets ingested."""
|
|
64
|
+
|
|
54
65
|
def reset(self) -> None:
|
|
55
66
|
"""Reset all metrics."""
|
|
56
67
|
self.time_in_put = 0.0
|
|
57
68
|
self.time_in_get = 0.0
|
|
69
|
+
self.time_in_ingest = 0.0
|
|
58
70
|
self.n_get = 0
|
|
59
71
|
self.n_put = 0
|
|
72
|
+
self.n_ingest = 0
|
|
60
73
|
|
|
61
74
|
def increment_get(self, duration: float) -> None:
|
|
62
75
|
"""Increment time for get().
|
|
@@ -80,13 +93,31 @@ class ButlerMetrics(BaseModel):
|
|
|
80
93
|
self.time_in_put += duration
|
|
81
94
|
self.n_put += 1
|
|
82
95
|
|
|
96
|
+
def increment_ingest(self, duration: float, n_datasets: int) -> None:
|
|
97
|
+
"""Increment time and datasets for ingest().
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
duration : `float`
|
|
102
|
+
Duration to add to the ingest() statistics.
|
|
103
|
+
n_datasets : `int`
|
|
104
|
+
Number of datasets to be ingested for this call.
|
|
105
|
+
"""
|
|
106
|
+
self.time_in_ingest += duration
|
|
107
|
+
self.n_ingest += n_datasets
|
|
108
|
+
|
|
83
109
|
@contextmanager
|
|
84
110
|
def _timer(
|
|
85
|
-
self,
|
|
111
|
+
self,
|
|
112
|
+
handler: Callable[Concatenate[float, P], None],
|
|
113
|
+
log: LsstLoggers | None = None,
|
|
114
|
+
msg: str | None = None,
|
|
115
|
+
*args: P.args,
|
|
116
|
+
**kwargs: P.kwargs,
|
|
86
117
|
) -> Iterator[None]:
|
|
87
118
|
with time_this(log=log, msg=msg) as timer:
|
|
88
119
|
yield
|
|
89
|
-
handler(timer.duration)
|
|
120
|
+
handler(timer.duration, *args, **kwargs)
|
|
90
121
|
|
|
91
122
|
@contextmanager
|
|
92
123
|
def instrument_get(self, log: LsstLoggers | None = None, msg: str | None = None) -> Iterator[None]:
|
|
@@ -115,3 +146,21 @@ class ButlerMetrics(BaseModel):
|
|
|
115
146
|
"""
|
|
116
147
|
with self._timer(self.increment_put, log=log, msg=msg):
|
|
117
148
|
yield
|
|
149
|
+
|
|
150
|
+
@contextmanager
|
|
151
|
+
def instrument_ingest(
|
|
152
|
+
self, n_datasets: int, log: LsstLoggers | None = None, msg: str | None = None
|
|
153
|
+
) -> Iterator[None]:
|
|
154
|
+
"""Run code and increment ingest statistics.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
n_datasets : `int`
|
|
159
|
+
Number of datasets being ingested.
|
|
160
|
+
log : `logging.Logger` or `None`
|
|
161
|
+
Logger to use for any timing information.
|
|
162
|
+
msg : `str` or `None`
|
|
163
|
+
Any message to be included in log output.
|
|
164
|
+
"""
|
|
165
|
+
with self._timer(self.increment_ingest, n_datasets=n_datasets, log=log, msg=msg):
|
|
166
|
+
yield
|
|
@@ -267,7 +267,7 @@ class DatasetProvenance(pydantic.BaseModel):
|
|
|
267
267
|
use_upper : `bool` or `None`
|
|
268
268
|
If `True` use upper case for provenance keys, if `False` use lower
|
|
269
269
|
case, if `None` match the case of the prefix.
|
|
270
|
-
keys : `tuple` of `str` | `int`
|
|
270
|
+
*keys : `tuple` of `str` | `int`
|
|
271
271
|
Components of key to combine with prefix and separator.
|
|
272
272
|
|
|
273
273
|
Returns
|
lsst/daf/butler/_dataset_ref.py
CHANGED
|
@@ -479,7 +479,7 @@ class DatasetRef:
|
|
|
479
479
|
|
|
480
480
|
Parameters
|
|
481
481
|
----------
|
|
482
|
-
simple : `dict` of [`str`, `Any`]
|
|
482
|
+
simple : `dict` of [`str`, `typing.Any`]
|
|
483
483
|
The value returned by `to_simple()`.
|
|
484
484
|
universe : `DimensionUniverse`
|
|
485
485
|
The special graph of all known dimensions.
|
lsst/daf/butler/_exceptions.py
CHANGED
|
@@ -196,8 +196,8 @@ class ValidationError(RuntimeError):
|
|
|
196
196
|
|
|
197
197
|
|
|
198
198
|
class EmptyQueryResultError(Exception):
|
|
199
|
-
"""Exception raised when query methods return an empty result and
|
|
200
|
-
flag is set.
|
|
199
|
+
"""Exception raised when query methods return an empty result and
|
|
200
|
+
``explain`` flag is set.
|
|
201
201
|
|
|
202
202
|
Parameters
|
|
203
203
|
----------
|
lsst/daf/butler/_file_dataset.py
CHANGED
|
@@ -129,7 +129,8 @@ class FileDataset:
|
|
|
129
129
|
----------
|
|
130
130
|
dataset : `SerializedFileDataset`
|
|
131
131
|
Object to deserialize.
|
|
132
|
-
dataset_type_loader :
|
|
132
|
+
dataset_type_loader : `~collections.abc.Callable` \
|
|
133
|
+
[[ `str` ], `DatasetType` ]
|
|
133
134
|
Function that takes a string dataset type name as its
|
|
134
135
|
only parameter, and returns an instance of `DatasetType`.
|
|
135
136
|
Used to deserialize the `DatasetRef` instances contained
|
lsst/daf/butler/_formatter.py
CHANGED
|
@@ -910,6 +910,10 @@ class FormatterV2:
|
|
|
910
910
|
provenance : `DatasetProvenance` | `None`, optional
|
|
911
911
|
Provenance to attach to the file being written.
|
|
912
912
|
|
|
913
|
+
Returns
|
|
914
|
+
-------
|
|
915
|
+
None
|
|
916
|
+
|
|
913
917
|
Raises
|
|
914
918
|
------
|
|
915
919
|
FormatterNotImplementedError
|
|
@@ -1137,6 +1141,10 @@ class FormatterV2:
|
|
|
1137
1141
|
location : `Location`
|
|
1138
1142
|
Location from which to extract a file extension.
|
|
1139
1143
|
|
|
1144
|
+
Returns
|
|
1145
|
+
-------
|
|
1146
|
+
None
|
|
1147
|
+
|
|
1140
1148
|
Raises
|
|
1141
1149
|
------
|
|
1142
1150
|
ValueError
|
|
@@ -1583,6 +1591,10 @@ class Formatter(metaclass=ABCMeta):
|
|
|
1583
1591
|
location : `Location`
|
|
1584
1592
|
Location from which to extract a file extension.
|
|
1585
1593
|
|
|
1594
|
+
Returns
|
|
1595
|
+
-------
|
|
1596
|
+
None
|
|
1597
|
+
|
|
1586
1598
|
Raises
|
|
1587
1599
|
------
|
|
1588
1600
|
NotImplementedError
|
|
@@ -30,7 +30,9 @@ from __future__ import annotations
|
|
|
30
30
|
__all__ = ("LabeledButlerFactory", "LabeledButlerFactoryProtocol")
|
|
31
31
|
|
|
32
32
|
from collections.abc import Mapping
|
|
33
|
-
from
|
|
33
|
+
from contextlib import AbstractContextManager
|
|
34
|
+
from logging import getLogger
|
|
35
|
+
from typing import Any, Literal, Protocol, Self
|
|
34
36
|
|
|
35
37
|
from lsst.resources import ResourcePathExpression
|
|
36
38
|
|
|
@@ -40,6 +42,8 @@ from ._butler_repo_index import ButlerRepoIndex
|
|
|
40
42
|
from ._utilities.named_locks import NamedLocks
|
|
41
43
|
from ._utilities.thread_safe_cache import ThreadSafeCache
|
|
42
44
|
|
|
45
|
+
_LOG = getLogger(__name__)
|
|
46
|
+
|
|
43
47
|
|
|
44
48
|
class LabeledButlerFactoryProtocol(Protocol):
|
|
45
49
|
"""Callable to retrieve a butler from a label."""
|
|
@@ -47,7 +51,7 @@ class LabeledButlerFactoryProtocol(Protocol):
|
|
|
47
51
|
def __call__(self, label: str) -> Butler: ...
|
|
48
52
|
|
|
49
53
|
|
|
50
|
-
class LabeledButlerFactory:
|
|
54
|
+
class LabeledButlerFactory(AbstractContextManager):
|
|
51
55
|
"""Factory for efficiently instantiating Butler instances from the
|
|
52
56
|
repository index file. This is intended for use from long-lived services
|
|
53
57
|
that want to instantiate a separate Butler instance for each end user
|
|
@@ -60,6 +64,9 @@ class LabeledButlerFactory:
|
|
|
60
64
|
files. If not provided, defaults to the global repository index
|
|
61
65
|
configured by the ``DAF_BUTLER_REPOSITORY_INDEX`` environment variable
|
|
62
66
|
-- see `ButlerRepoIndex`.
|
|
67
|
+
writeable : `bool`, optional
|
|
68
|
+
If `True`, Butler instances created by this factory will be writeable.
|
|
69
|
+
If `False` (the default), instances will be read-only.
|
|
63
70
|
|
|
64
71
|
Notes
|
|
65
72
|
-----
|
|
@@ -76,11 +83,12 @@ class LabeledButlerFactory:
|
|
|
76
83
|
safely be used by separate threads.
|
|
77
84
|
"""
|
|
78
85
|
|
|
79
|
-
def __init__(self, repositories: Mapping[str, str] | None = None) -> None:
|
|
86
|
+
def __init__(self, repositories: Mapping[str, str] | None = None, writeable: bool = False) -> None:
|
|
80
87
|
if repositories is None:
|
|
81
88
|
self._repositories = None
|
|
82
89
|
else:
|
|
83
90
|
self._repositories = dict(repositories)
|
|
91
|
+
self._writeable = writeable
|
|
84
92
|
|
|
85
93
|
self._factories = ThreadSafeCache[str, _ButlerFactory]()
|
|
86
94
|
self._initialization_locks = NamedLocks()
|
|
@@ -88,6 +96,16 @@ class LabeledButlerFactory:
|
|
|
88
96
|
# This may be overridden by unit tests.
|
|
89
97
|
self._preload_unsafe_direct_butler_caches = True
|
|
90
98
|
|
|
99
|
+
def __enter__(self) -> Self:
|
|
100
|
+
return self
|
|
101
|
+
|
|
102
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Literal[False]:
|
|
103
|
+
try:
|
|
104
|
+
self.close()
|
|
105
|
+
except Exception:
|
|
106
|
+
_LOG.exception("An exception occurred during LabeledButlerFactory.close()")
|
|
107
|
+
return False
|
|
108
|
+
|
|
91
109
|
def bind(self, access_token: str | None) -> LabeledButlerFactoryProtocol:
|
|
92
110
|
"""Create a callable factory function for generating Butler instances
|
|
93
111
|
with out needing to specify access tokans again.
|
|
@@ -109,7 +127,7 @@ class LabeledButlerFactory:
|
|
|
109
127
|
|
|
110
128
|
return create
|
|
111
129
|
|
|
112
|
-
def create_butler(self,
|
|
130
|
+
def create_butler(self, label: str, *, access_token: str | None = None) -> Butler:
|
|
113
131
|
"""Create a Butler instance.
|
|
114
132
|
|
|
115
133
|
Parameters
|
|
@@ -118,7 +136,7 @@ class LabeledButlerFactory:
|
|
|
118
136
|
Label of the repository to instantiate, from the ``repositories``
|
|
119
137
|
parameter to the `LabeledButlerFactory` constructor or the global
|
|
120
138
|
repository index file.
|
|
121
|
-
access_token : `str` | `None
|
|
139
|
+
access_token : `str` | `None`, optional
|
|
122
140
|
Gafaelfawr access token used to authenticate to a Butler server.
|
|
123
141
|
This is required for any repositories configured to use
|
|
124
142
|
`RemoteButler`. If you only use `DirectButler`, this may be
|
|
@@ -167,7 +185,9 @@ class LabeledButlerFactory:
|
|
|
167
185
|
|
|
168
186
|
match butler_type:
|
|
169
187
|
case ButlerType.DIRECT:
|
|
170
|
-
return _DirectButlerFactory(
|
|
188
|
+
return _DirectButlerFactory(
|
|
189
|
+
config, self._preload_unsafe_direct_butler_caches, self._writeable
|
|
190
|
+
)
|
|
171
191
|
case ButlerType.REMOTE:
|
|
172
192
|
return _RemoteButlerFactory(config)
|
|
173
193
|
case _:
|
|
@@ -189,12 +209,12 @@ class _ButlerFactory(Protocol):
|
|
|
189
209
|
|
|
190
210
|
|
|
191
211
|
class _DirectButlerFactory(_ButlerFactory):
|
|
192
|
-
def __init__(self, config: ButlerConfig, preload_unsafe_caches: bool) -> None:
|
|
212
|
+
def __init__(self, config: ButlerConfig, preload_unsafe_caches: bool, writeable: bool) -> None:
|
|
193
213
|
import lsst.daf.butler.direct_butler
|
|
194
214
|
|
|
195
215
|
# Create a 'template' Butler that will be cloned when callers request
|
|
196
216
|
# an instance.
|
|
197
|
-
self._butler = Butler.from_config(config)
|
|
217
|
+
self._butler = Butler.from_config(config, writeable=writeable)
|
|
198
218
|
assert isinstance(self._butler, lsst.daf.butler.direct_butler.DirectButler)
|
|
199
219
|
|
|
200
220
|
# Load caches so that data is available in cloned instances without
|
|
@@ -114,18 +114,7 @@ def remove_runs(context: click.Context, confirm: bool, force: bool, **kwargs: An
|
|
|
114
114
|
|
|
115
115
|
This command can be used to remove RUN collections and the datasets within
|
|
116
116
|
them.
|
|
117
|
-
|
|
118
|
-
Parameters
|
|
119
|
-
----------
|
|
120
|
-
context : `click.Context`
|
|
121
|
-
Context provided by Click.
|
|
122
|
-
confirm : `bool`
|
|
123
|
-
Confirmation for removal of the run.
|
|
124
|
-
force : `bool`
|
|
125
|
-
Force removal.
|
|
126
|
-
**kwargs : `dict` [`str`, `str`]
|
|
127
|
-
The parameters to pass to `~lsst.daf.butler.script.removeRuns`.
|
|
128
|
-
"""
|
|
117
|
+
""" # numpydoc ignore=PR01
|
|
129
118
|
result = script.removeRuns(**kwargs)
|
|
130
119
|
canRemoveRuns = len(result.runs)
|
|
131
120
|
if not canRemoveRuns:
|
lsst/daf/butler/column_spec.py
CHANGED
|
@@ -109,12 +109,12 @@ class ColumnValueSerializer(ABC):
|
|
|
109
109
|
|
|
110
110
|
Parameters
|
|
111
111
|
----------
|
|
112
|
-
value : `Any`
|
|
112
|
+
value : `typing.Any`
|
|
113
113
|
Column value to be serialized.
|
|
114
114
|
|
|
115
115
|
Returns
|
|
116
116
|
-------
|
|
117
|
-
value : `Any`
|
|
117
|
+
value : `typing.Any`
|
|
118
118
|
Column value in serializable format.
|
|
119
119
|
"""
|
|
120
120
|
raise NotImplementedError
|
|
@@ -125,12 +125,12 @@ class ColumnValueSerializer(ABC):
|
|
|
125
125
|
|
|
126
126
|
Parameters
|
|
127
127
|
----------
|
|
128
|
-
value : `Any`
|
|
128
|
+
value : `typing.Any`
|
|
129
129
|
Serialized column value.
|
|
130
130
|
|
|
131
131
|
Returns
|
|
132
132
|
-------
|
|
133
|
-
value : `Any`
|
|
133
|
+
value : `typing.Any`
|
|
134
134
|
Deserialized column value.
|
|
135
135
|
"""
|
|
136
136
|
raise NotImplementedError
|
|
@@ -100,3 +100,4 @@ VisitBackgroundModel: lsst.daf.butler.formatters.json.JsonFormatter
|
|
|
100
100
|
VignettingCorrection: lsst.ts.observatory.control.utils.extras.vignetting_storage.VignettingCorrectionFormatter
|
|
101
101
|
SSPAuxiliaryFile: lsst.pipe.tasks.sspAuxiliaryFile.SSPAuxiliaryFileFormatter
|
|
102
102
|
VisitGeometry: lsst.daf.butler.formatters.json.JsonFormatter
|
|
103
|
+
ProvenanceQuantumGraph: lsst.pipe.base.quantum_graph.formatter.ProvenanceFormatter
|
|
@@ -443,3 +443,18 @@ storageClasses:
|
|
|
443
443
|
pytype: lsst.pipe.tasks.sspAuxiliaryFile.SSPAuxiliaryFile
|
|
444
444
|
VisitGeometry:
|
|
445
445
|
pytype: lsst.obs.base.visit_geometry.VisitGeometry
|
|
446
|
+
ProvenanceQuantumGraph:
|
|
447
|
+
pytype: lsst.pipe.base.quantum_graph.ProvenanceQuantumGraph
|
|
448
|
+
parameters:
|
|
449
|
+
- import_mode # lsst.pipe.base.pipeline_graph.TaskImportMode
|
|
450
|
+
- quanta # iterable of uuid.UUID; quanta to read
|
|
451
|
+
- datasets # iterable of uuid.UUID; datasets to read
|
|
452
|
+
- read_init_quanta # bool, defaults to True; whether to read pre-exec-init info
|
|
453
|
+
derivedComponents:
|
|
454
|
+
packages: Packages # ignores node parameters
|
|
455
|
+
|
|
456
|
+
# UUID keys can be quantum or data IDs (whichever is passed in via
|
|
457
|
+
# parameters). Nested lists are attempts to run the quantum (last is
|
|
458
|
+
# most recent).
|
|
459
|
+
logs: StructuredDataDict # dict[uuid.UUID, list[ButlerLogRecords]]
|
|
460
|
+
metadata: StructuredDataDict # dict[uuid.UUID, list[TaskMetadata]]
|
|
@@ -284,6 +284,14 @@ class DatasetRefURIs(abc.Sequence):
|
|
|
284
284
|
def __repr__(self) -> str:
|
|
285
285
|
return f"DatasetRefURIs({repr(self.primaryURI)}, {repr(self.componentURIs)})"
|
|
286
286
|
|
|
287
|
+
def iter_all(self) -> Iterator[ResourcePath]:
|
|
288
|
+
"""Iterate over all URIs without regard to whether they are primary
|
|
289
|
+
or component.
|
|
290
|
+
"""
|
|
291
|
+
if self.primaryURI is not None:
|
|
292
|
+
yield self.primaryURI
|
|
293
|
+
yield from self.componentURIs.values()
|
|
294
|
+
|
|
287
295
|
|
|
288
296
|
class Datastore(FileTransferSource, metaclass=ABCMeta):
|
|
289
297
|
"""Datastore interface.
|
|
@@ -536,7 +544,7 @@ class Datastore(FileTransferSource, metaclass=ABCMeta):
|
|
|
536
544
|
|
|
537
545
|
Returns
|
|
538
546
|
-------
|
|
539
|
-
exists : `dict`[`DatasetRef`, `bool`]
|
|
547
|
+
exists : `dict` [`DatasetRef`, `bool`]
|
|
540
548
|
Mapping of dataset to boolean indicating whether the dataset
|
|
541
549
|
is known to the datastore.
|
|
542
550
|
"""
|
|
@@ -825,6 +833,10 @@ class Datastore(FileTransferSource, metaclass=ABCMeta):
|
|
|
825
833
|
in an external system or if the file is to be compressed in place.
|
|
826
834
|
It is up to the datastore whether this parameter is relevant.
|
|
827
835
|
|
|
836
|
+
Returns
|
|
837
|
+
-------
|
|
838
|
+
None
|
|
839
|
+
|
|
828
840
|
Raises
|
|
829
841
|
------
|
|
830
842
|
NotImplementedError
|
|
@@ -1143,6 +1155,10 @@ class Datastore(FileTransferSource, metaclass=ABCMeta):
|
|
|
1143
1155
|
Determine whether errors should be ignored. When multiple
|
|
1144
1156
|
refs are being trashed there will be no per-ref check.
|
|
1145
1157
|
|
|
1158
|
+
Returns
|
|
1159
|
+
-------
|
|
1160
|
+
None
|
|
1161
|
+
|
|
1146
1162
|
Raises
|
|
1147
1163
|
------
|
|
1148
1164
|
FileNotFoundError
|
|
@@ -1278,6 +1294,10 @@ class Datastore(FileTransferSource, metaclass=ABCMeta):
|
|
|
1278
1294
|
Entity to compare with configuration retrieved using the
|
|
1279
1295
|
specified lookup key.
|
|
1280
1296
|
|
|
1297
|
+
Returns
|
|
1298
|
+
-------
|
|
1299
|
+
None
|
|
1300
|
+
|
|
1281
1301
|
Raises
|
|
1282
1302
|
------
|
|
1283
1303
|
DatastoreValidationError
|
|
@@ -423,8 +423,8 @@ def make_datastore_path_relative(path: str) -> str:
|
|
|
423
423
|
path : `str`
|
|
424
424
|
The file path from a `StoredFileInfo`.
|
|
425
425
|
|
|
426
|
-
|
|
427
|
-
|
|
426
|
+
Returns
|
|
427
|
+
-------
|
|
428
428
|
normalized_path : `str`
|
|
429
429
|
The original path, if it was relative. Otherwise, a version of it that
|
|
430
430
|
was converted to a relative path, stripping URI scheme and netloc from
|
|
@@ -97,12 +97,12 @@ def generate_datastore_get_information(
|
|
|
97
97
|
|
|
98
98
|
Parameters
|
|
99
99
|
----------
|
|
100
|
-
fileLocations : `list`[`DatasetLocationInformation`]
|
|
100
|
+
fileLocations : `list` [`DatasetLocationInformation`]
|
|
101
101
|
List of file locations for this artifact and their associated datastore
|
|
102
102
|
records.
|
|
103
103
|
ref : `DatasetRef`
|
|
104
104
|
The registry information associated with this artifact.
|
|
105
|
-
parameters :
|
|
105
|
+
parameters : `~collections.abc.Mapping` [`str`, `typing.Any`]
|
|
106
106
|
`StorageClass` and `Formatter` parameters.
|
|
107
107
|
readStorageClass : `StorageClass` | `None`, optional
|
|
108
108
|
The StorageClass to use when ultimately returning the resulting object
|
|
@@ -255,12 +255,12 @@ def get_dataset_as_python_object_from_get_info(
|
|
|
255
255
|
|
|
256
256
|
Parameters
|
|
257
257
|
----------
|
|
258
|
-
allGetInfo : `list`[`DatastoreFileGetInformation`]
|
|
258
|
+
allGetInfo : `list` [`DatastoreFileGetInformation`]
|
|
259
259
|
Pre-processed information about each file associated with this
|
|
260
260
|
artifact.
|
|
261
261
|
ref : `DatasetRef`
|
|
262
262
|
The registry information associated with this artifact.
|
|
263
|
-
parameters :
|
|
263
|
+
parameters : `~collections.abc.Mapping` [`str`, `typing.Any`]
|
|
264
264
|
`StorageClass` and `Formatter` parameters.
|
|
265
265
|
cache_manager : `AbstractDatastoreCacheManager`
|
|
266
266
|
The cache manager to use for caching retrieved files.
|
|
@@ -55,8 +55,8 @@ def retrieve_file_transfer_records(
|
|
|
55
55
|
Cache mapping datastore artifact to existence. Updated by
|
|
56
56
|
this method with details of all artifacts tested.
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
60
|
files : `FileTransferMap`
|
|
61
61
|
A dictionary from `DatasetId` to a list of `FileTransferRecord`,
|
|
62
62
|
containing information about the files that were found for these
|
|
@@ -590,6 +590,10 @@ class InMemoryDatastore(GenericBaseDatastore[StoredMemoryItemInfo]):
|
|
|
590
590
|
ignore_errors : `bool`, optional
|
|
591
591
|
Indicate that errors should be ignored.
|
|
592
592
|
|
|
593
|
+
Returns
|
|
594
|
+
-------
|
|
595
|
+
None
|
|
596
|
+
|
|
593
597
|
Raises
|
|
594
598
|
------
|
|
595
599
|
FileNotFoundError
|
|
@@ -721,6 +725,10 @@ class InMemoryDatastore(GenericBaseDatastore[StoredMemoryItemInfo]):
|
|
|
721
725
|
If `True`, output a log message for every validation error
|
|
722
726
|
detected.
|
|
723
727
|
|
|
728
|
+
Returns
|
|
729
|
+
-------
|
|
730
|
+
None
|
|
731
|
+
|
|
724
732
|
Raises
|
|
725
733
|
------
|
|
726
734
|
DatastoreValidationError
|
lsst/daf/butler/ddl.py
CHANGED
|
@@ -537,7 +537,7 @@ class IndexSpec:
|
|
|
537
537
|
----------
|
|
538
538
|
*columns : `str`
|
|
539
539
|
Names of the columns to index.
|
|
540
|
-
**kwargs : `Any`
|
|
540
|
+
**kwargs : `typing.Any`
|
|
541
541
|
Additional keyword arguments to pass directly to
|
|
542
542
|
`sqlalchemy.schema.Index` constructor. This could be used to provide
|
|
543
543
|
backend-specific options, e.g. to create a ``GIST`` index in PostgreSQL
|
|
@@ -556,7 +556,7 @@ class IndexSpec:
|
|
|
556
556
|
|
|
557
557
|
kwargs: dict[str, Any]
|
|
558
558
|
"""Additional keyword arguments passed directly to
|
|
559
|
-
`sqlalchemy.schema.Index` constructor (`dict` [ `str`, `Any` ]).
|
|
559
|
+
`sqlalchemy.schema.Index` constructor (`dict` [ `str`, `typing.Any` ]).
|
|
560
560
|
"""
|
|
561
561
|
|
|
562
562
|
|