Skip to content

Commit 6964a3e

Browse files
TallJimbotimj
authored andcommitted
Use ResourcePath instead of ButlerURI throughout.
Also replaces a few Union[str, ButlerURI] and similar with the new ResourcePathExpression type alias.
1 parent 193fcfe commit 6964a3e

7 files changed

Lines changed: 79 additions & 69 deletions

File tree

doc/lsst.pipe.base/creating-a-pipeline.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ s, and discussing common conventions when creating `Pipelines`.
2727
A Basic Pipeline
2828
----------------
2929

30-
`Pipeline` documents are written using yaml syntax. If you are unfamiliar with
30+
`Pipeline` documents are written using yaml syntax. If you are unfamiliar with
3131
yaml, there are many guides across the internet, but the basic idea is that it
3232
is a simple markup language to describe key, value mappings, and lists of
3333
values (which may be further mappings).
@@ -109,12 +109,12 @@ configuration options that alter the way the task executes. Because
109109
description field) some tasks may need specific configurations set to
110110
enable/disable behavior in the context of the specific `Pipeline`.
111111

112-
To configure a task associated with a particular label, the value associated
112+
To configure a task associated with a particular label, the value associated
113113
with the label must be changed from the qualified task name to a new
114114
sub-mapping. This new sub mapping should have two keys, ``class`` and
115115
``config``.
116116

117-
The ``class`` key should point to the same qualified task name as before. The
117+
The ``class`` key should point to the same qualified task name as before. The
118118
value associated with the ``config`` keyword is itself a mapping where
119119
configuration overrides are declared. The example below shows this behavior
120120
in action.
@@ -371,7 +371,7 @@ extend the total `Pipeline`.
371371

372372
If a ``label`` declared in the the ``tasks`` section was declared in one of
373373
the imported ``Pipelines``, one of two things happen. If the label is
374-
associated with the same `PipelineTask` that was declared in the imported
374+
associated with the same `PipelineTask` that was declared in the imported
375375
pipeline, this definition will be extended. This means that any configs
376376
declared in the imported `Pipeline` will be merged with configs declared in
377377
the current `Pipeline` with the current declaration taking config precedence.
@@ -421,7 +421,7 @@ is loaded.
421421

422422
The simplest form of a `Pipeline` specification is the URI at which the
423423
`Pipeline` can be found. This URI may be any supported by
424-
`lsst.daf.butler.ButlerURI`. In the case that the pipeline resides in a file
424+
`lsst.resources.ResourcePath`. In the case that the pipeline resides in a file
425425
located on a filesystem accessible by the machine that will be processing the
426426
`Pipeline` (i.e. a file URI), there is no need to preface the URI with
427427
``file://``, a bare file path is assumed to be a file based URI.
@@ -493,4 +493,4 @@ consistency throughout the software stack.
493493
level of a package.
494494
* Instrument packages should provide `Pipeline`\ s that override standard
495495
`Pipeline`\ s and are specifically configured for that instrument (if
496-
applicable).
496+
applicable).

python/lsst/pipe/base/configOverrides.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from enum import Enum
3030
from operator import attrgetter
3131

32-
from lsst.daf.butler import ButlerURI
32+
from lsst.resources import ResourcePath
3333
from lsst.utils import doImport
3434

3535
OverrideTypes = Enum("OverrideTypes", "Value File Python Instrument")
@@ -148,11 +148,11 @@ def addFileOverride(self, filename):
148148
149149
Parameters
150150
----------
151-
filename : `str` or `ButlerURI`
151+
filename : convertible to `ResourcePath`
152152
Path or URI to the override file. All URI schemes supported by
153-
`ButlerURI` are supported.
153+
`ResourcePath` are supported.
154154
"""
155-
self._overrides.append((OverrideTypes.File, ButlerURI(filename)))
155+
self._overrides.append((OverrideTypes.File, ResourcePath(filename)))
156156

157157
def addValueOverride(self, field, value):
158158
"""Add override for a specific field.

python/lsst/pipe/base/executionButlerBuilder.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
from collections import defaultdict
2828
from typing import Callable, DefaultDict, Iterable, List, Mapping, Optional, Set, Tuple, Union
2929

30-
from lsst.daf.butler import Butler, ButlerURI, Config, DataCoordinate, DatasetRef, DatasetType
30+
from lsst.daf.butler import Butler, Config, DataCoordinate, DatasetRef, DatasetType
3131
from lsst.daf.butler.core.repoRelocation import BUTLER_ROOT_TAG
3232
from lsst.daf.butler.transfers import RepoExportContext
33+
from lsst.resources import ResourcePath, ResourcePathExpression
3334
from lsst.utils.introspection import get_class_of
3435

3536
from .graph import QuantumGraph, QuantumNode
@@ -142,7 +143,7 @@ def _export(
142143
return yamlBuffer
143144

144145

145-
def _setupNewButler(butler: Butler, outputLocation: ButlerURI, dirExists: bool) -> Butler:
146+
def _setupNewButler(butler: Butler, outputLocation: ResourcePath, dirExists: bool) -> Butler:
146147
# Set up the new butler object at the specified location
147148
if dirExists:
148149
# Remove the existing table, if the code got this far and this exists
@@ -218,7 +219,7 @@ def _import(
218219
def buildExecutionButler(
219220
butler: Butler,
220221
graph: QuantumGraph,
221-
outputLocation: Union[str, ButlerURI],
222+
outputLocation: ResourcePathExpression,
222223
run: str,
223224
*,
224225
clobber: bool = False,
@@ -242,9 +243,9 @@ def buildExecutionButler(
242243
graph : `QuantumGraph`
243244
Graph containing nodes that are to be exported into an execution
244245
butler
245-
outputLocation : `str` or `~lsst.daf.butler.ButlerURI`
246+
outputLocation : convertible to `ResourcePath
246247
URI Location at which the execution butler is to be exported. May be
247-
specified as a string or a ButlerURI instance.
248+
specified as a string or a `ResourcePath` instance.
248249
run : `str` optional
249250
The run collection that the exported datasets are to be placed in. If
250251
None, the default value in registry.defaults will be used.
@@ -282,7 +283,7 @@ def buildExecutionButler(
282283
Raised if specified output URI does not correspond to a directory
283284
"""
284285
# We know this must refer to a directory.
285-
outputLocation = ButlerURI(outputLocation, forceDirectory=True)
286+
outputLocation = ResourcePath(outputLocation, forceDirectory=True)
286287

287288
# Do this first to Fail Fast if the output exists
288289
if (dirExists := outputLocation.exists()) and not clobber:

python/lsst/pipe/base/graph/graph.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
)
5555

5656
import networkx as nx
57-
from lsst.daf.butler import ButlerURI, DatasetRef, DimensionRecordsAccumulator, DimensionUniverse, Quantum
57+
from lsst.daf.butler import DatasetRef, DimensionRecordsAccumulator, DimensionUniverse, Quantum
58+
from lsst.resources import ResourcePath, ResourcePathExpression
5859
from networkx.drawing.nx_agraph import write_dot
5960

6061
from ..connections import iterConnections
@@ -717,14 +718,14 @@ def saveUri(self, uri):
717718
718719
Parameters
719720
----------
720-
uri : `ButlerURI` or `str`
721+
uri : convertible to `ResourcePath`
721722
URI to where the graph should be saved.
722723
"""
723724
buffer = self._buildSaveObject()
724-
butlerUri = ButlerURI(uri)
725-
if butlerUri.getExtension() not in (".qgraph"):
725+
path = ResourcePath(uri)
726+
if path.getExtension() not in (".qgraph"):
726727
raise TypeError(f"Can currently only save a graph in qgraph format not {uri}")
727-
butlerUri.write(buffer) # type: ignore # Ignore because bytearray is safe to use in place of bytes
728+
path.write(buffer) # type: ignore # Ignore because bytearray is safe to use in place of bytes
728729

729730
@property
730731
def metadata(self) -> Optional[MappingProxyType[str, Any]]:
@@ -736,7 +737,7 @@ def metadata(self) -> Optional[MappingProxyType[str, Any]]:
736737
@classmethod
737738
def loadUri(
738739
cls,
739-
uri: Union[ButlerURI, str],
740+
uri: ResourcePathExpression,
740741
universe: DimensionUniverse,
741742
nodes: Optional[Iterable[Union[str, uuid.UUID]]] = None,
742743
graphID: Optional[BuildId] = None,
@@ -746,7 +747,7 @@ def loadUri(
746747
747748
Parameters
748749
----------
749-
uri : `ButlerURI` or `str`
750+
uri : convertible to `ResourcePath`
750751
URI from where to load the graph.
751752
universe: `~lsst.daf.butler.DimensionUniverse`
752753
DimensionUniverse instance, not used by the method itself but
@@ -789,8 +790,8 @@ def loadUri(
789790
initialization. To make sure that DimensionUniverse exists this method
790791
accepts dummy DimensionUniverse argument.
791792
"""
792-
uri = ButlerURI(uri)
793-
# With ButlerURI we have the choice of always using a local file
793+
uri = ResourcePath(uri)
794+
# With ResourcePath we have the choice of always using a local file
794795
# or reading in the bytes directly. Reading in bytes can be more
795796
# efficient for reasonably-sized pickle files when the resource
796797
# is remote. For now use the local file variant. For a local file
@@ -810,16 +811,15 @@ def loadUri(
810811
return qgraph
811812

812813
@classmethod
813-
def readHeader(cls, uri: Union[ButlerURI, str], minimumVersion: int = 3) -> Optional[str]:
814+
def readHeader(cls, uri: ResourcePathExpression, minimumVersion: int = 3) -> Optional[str]:
814815
"""Read the header of a `QuantumGraph` pointed to by the uri parameter
815816
and return it as a string.
816817
817818
Parameters
818819
----------
819-
uri : `~lsst.daf.butler.ButlerURI` or `str`
820+
uri : convertible to `ResourcePath`
820821
The location of the `QuantumGraph` to load. If the argument is a
821-
string, it must correspond to a valid `~lsst.daf.butler.ButlerURI`
822-
path.
822+
string, it must correspond to a valid `ResourcePath` path.
823823
minimumVersion : int
824824
Minimum version of a save file to load. Set to -1 to load all
825825
versions. Older versions may need to be loaded, and re-saved
@@ -839,7 +839,7 @@ def readHeader(cls, uri: Union[ButlerURI, str], minimumVersion: int = 3) -> Opti
839839
Raised if the extention of the file specified by uri is not a
840840
`QuantumGraph` extention.
841841
"""
842-
uri = ButlerURI(uri)
842+
uri = ResourcePath(uri)
843843
if uri.getExtension() in (".pickle", ".pkl"):
844844
raise ValueError("Reading a header from a pickle save is not supported")
845845
elif uri.getExtension() in (".qgraph"):

python/lsst/pipe/base/pipeline.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353

5454
# -----------------------------
5555
# Imports for other modules --
56-
from lsst.daf.butler import ButlerURI, DatasetType, NamedValueSet, Registry, SkyPixDimension
56+
from lsst.daf.butler import DatasetType, NamedValueSet, Registry, SkyPixDimension
57+
from lsst.resources import ResourcePath, ResourcePathExpression
5758
from lsst.utils import doImport
5859

5960
from . import pipelineIR, pipeTools
@@ -246,25 +247,27 @@ def fromFile(cls, filename: str) -> Pipeline:
246247
return cls.from_uri(filename)
247248

248249
@classmethod
249-
def from_uri(cls, uri: Union[str, ButlerURI]) -> Pipeline:
250+
def from_uri(cls, uri: ResourcePathExpression) -> Pipeline:
250251
"""Load a pipeline defined in a pipeline yaml file at a location
251252
specified by a URI.
252253
253254
Parameters
254255
----------
255-
uri: `str` or `ButlerURI`
256-
If a string is supplied this should be a URI path that points to a
257-
pipeline defined in yaml format. This uri may also supply
258-
additional labels to be used in subsetting the loaded Pipeline.
259-
These labels are separated from the path by a \\#, and may be
260-
specified as a comma separated list, or a range denoted as
261-
beginning..end. Beginning or end may be empty, in which case the
262-
range will be a half open interval. Unlike python iteration
263-
bounds, end bounds are *INCLUDED*. Note that range based selection
264-
is not well defined for pipelines that are not linear in nature,
265-
and correct behavior is not guaranteed, or may vary from run to
266-
run. The same specifiers can be used with a ButlerURI object, by
267-
being the sole contents in the fragments attribute.
256+
uri: convertible to `ResourcePath`
257+
If a string is supplied this should be a URI path that points to a
258+
pipeline defined in yaml format, either as a direct path to the
259+
yaml file, or as a directory containing a "pipeline.yaml" file (the
260+
form used by `write_to_uri` with ``expand=True``). This uri may
261+
also supply additional labels to be used in subsetting the loaded
262+
Pipeline. These labels are separated from the path by a \\#, and
263+
may be specified as a comma separated list, or a range denoted as
264+
beginning..end. Beginning or end may be empty, in which case the
265+
range will be a half open interval. Unlike python iteration bounds,
266+
end bounds are *INCLUDED*. Note that range based selection is not
267+
well defined for pipelines that are not linear in nature, and
268+
correct behavior is not guaranteed, or may vary from run to run.
269+
The same specifiers can be used with a `ResourcePath` object, by
270+
being the sole contents in the fragments attribute.
268271
269272
Returns
270273
-------
@@ -356,7 +359,7 @@ def subsetFromLabels(self, labelSpecifier: LabelSpecifier) -> Pipeline:
356359
return Pipeline.fromIR(self._pipelineIR.subset_from_labels(labelSet))
357360

358361
@staticmethod
359-
def _parse_file_specifier(uri: Union[str, ButlerURI]) -> Tuple[ButlerURI, Optional[LabelSpecifier]]:
362+
def _parse_file_specifier(uri: ResourcePathExpression) -> Tuple[ResourcePath, Optional[LabelSpecifier]]:
360363
"""Split appart a uri and any possible label subsets"""
361364
if isinstance(uri, str):
362365
# This is to support legacy pipelines during transition
@@ -370,7 +373,8 @@ def _parse_file_specifier(uri: Union[str, ButlerURI]) -> Tuple[ButlerURI, Option
370373
)
371374
if uri.count("#") > 1:
372375
raise ValueError("Only one set of labels is allowed when specifying a pipeline to load")
373-
uri = ButlerURI(uri)
376+
# Everything else can be converted directly to ResourcePath.
377+
uri = ResourcePath(uri)
374378
label_subset = uri.fragment or None
375379

376380
specifier: Optional[LabelSpecifier]
@@ -593,10 +597,16 @@ def _addConfigImpl(self, label: str, newConfig: pipelineIR.ConfigIR) -> None:
593597
def toFile(self, filename: str) -> None:
594598
self._pipelineIR.to_file(filename)
595599

596-
def write_to_uri(self, uri: Union[str, ButlerURI]) -> None:
597-
# tasks need sorted each call because someone might have added or
598-
# removed task, and caching changes does not seem worth the small
599-
# overhead
600+
def write_to_uri(self, uri: ResourcePathExpression) -> None:
601+
"""Write the pipeline to a file or directory.
602+
603+
Parameters
604+
----------
605+
uri : convertible to `ResourcePath`
606+
URI to write to; may have any scheme with `ResourcePath` write
607+
support or no scheme for a local file/directory. Should have a
608+
``.yaml``.
609+
"""
600610
labels = [td.label for td in self._toExpandedPipelineImpl(checkContracts=False)]
601611
self._pipelineIR.reorder_tasks(labels)
602612
self._pipelineIR.write_to_uri(uri)

python/lsst/pipe/base/pipelineIR.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
import yaml
3535
from deprecated.sphinx import deprecated
36-
from lsst.daf.butler import ButlerURI
36+
from lsst.resources import ResourcePath, ResourcePathExpression
3737

3838

3939
class KeepInstrument:
@@ -859,31 +859,26 @@ def from_file(cls, filename: str) -> PipelineIR:
859859
return cls.from_uri(filename)
860860

861861
@classmethod
862-
def from_uri(cls, uri: Union[str, ButlerURI]) -> PipelineIR:
862+
def from_uri(cls, uri: ResourcePathExpression) -> PipelineIR:
863863
"""Create a `PipelineIR` object from the document specified by the
864864
input uri.
865865
866866
Parameters
867867
----------
868-
uri: `str` or `ButlerURI`
868+
uri: convertible to `ResourcePath`
869869
Location of document to use in creating a `PipelineIR` object.
870870
871871
Returns
872872
-------
873873
pipelineIR : `PipelineIR`
874874
The loaded pipeline
875875
"""
876-
loaded_uri = ButlerURI(uri)
877-
# With ButlerURI we have the choice of always using a local file or
878-
# reading in the bytes directly. Reading in bytes can be more
879-
# efficient for reasonably-sized files when the resource is remote.
880-
# For now use the local file variant. For a local file as_local() does
881-
# nothing.
882-
with loaded_uri.as_local() as local:
876+
loaded_uri = ResourcePath(uri)
877+
with loaded_uri.open("r") as buffer:
883878
# explicitly read here, there was some issue with yaml trying
884-
# to read the ButlerURI itself (I think because it only
879+
# to read the ResourcePath itself (I think because it only
885880
# pretends to be conformant to the io api)
886-
loaded_yaml = yaml.load(local.read(), Loader=PipelineYamlLoader)
881+
loaded_yaml = yaml.load(buffer.read(), Loader=PipelineYamlLoader)
887882
return cls(loaded_yaml)
888883

889884
@deprecated(
@@ -902,17 +897,20 @@ def to_file(self, filename: str):
902897
"""
903898
self.write_to_uri(filename)
904899

905-
def write_to_uri(self, uri: Union[ButlerURI, str]):
900+
def write_to_uri(
901+
self,
902+
uri: ResourcePathExpression,
903+
):
906904
"""Serialize this `PipelineIR` object into a yaml formatted string and
907905
write the output to a file at the specified uri.
908906
909907
Parameters
910908
----------
911-
uri: `str` or `ButlerURI`
909+
uri: convertible to `ResourcePath`
912910
Location of document to write a `PipelineIR` object.
913911
"""
914-
butlerUri = ButlerURI(uri)
915-
butlerUri.write(yaml.dump(self.to_primitives(), sort_keys=False).encode())
912+
with ResourcePath(uri).open("w") as buffer:
913+
yaml.dump(self.to_primitives(), buffer, sort_keys=False)
916914

917915
def to_primitives(self) -> Dict[str, Any]:
918916
"""Convert to a representation used in yaml serialization"""

python/lsst/pipe/base/tests/simpleQGraph.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131
import lsst.pex.config as pexConfig
3232
import numpy
3333
from lsst.base import Packages
34-
from lsst.daf.butler import Butler, ButlerURI, Config, DatasetType
34+
from lsst.daf.butler import Butler, Config, DatasetType
3535
from lsst.daf.butler.core.logging import ButlerLogRecords
36+
from lsst.resources import ResourcePath
3637
from lsst.utils import doImport
3738

3839
from ... import base as pipeBase
@@ -236,7 +237,7 @@ def makeSimpleButler(root: str, run: str = "test", inMemory: bool = True) -> But
236237
butler : `~lsst.daf.butler.Butler`
237238
Data butler instance.
238239
"""
239-
root = ButlerURI(root, forceDirectory=True)
240+
root = ResourcePath(root, forceDirectory=True)
240241
if not root.isLocal:
241242
raise ValueError(f"Only works with local root not {root}")
242243
config = Config()

0 commit comments

Comments
 (0)