diff --git a/doc/changes/DM-41326.removal.md b/doc/changes/DM-41326.removal.md
new file mode 100644
index 0000000000..22414a1493
--- /dev/null
+++ b/doc/changes/DM-41326.removal.md
@@ -0,0 +1,3 @@
+Remove `DimensionGraph` and the `Mapping` interface to `DataCoordinate`, along with most other public interfaces that utilize `DimensionElement` instances instead of just their string names.
+
+See [RFC-834](https://rubinobs.atlassian.net/browse/RFC-834) for full details and rationale.
diff --git a/python/lsst/daf/butler/_dataset_ref.py b/python/lsst/daf/butler/_dataset_ref.py
index 58c9d131ca..8414a75952 100644
--- a/python/lsst/daf/butler/_dataset_ref.py
+++ b/python/lsst/daf/butler/_dataset_ref.py
@@ -50,7 +50,7 @@
from ._dataset_type import DatasetType, SerializedDatasetType
from ._named import NamedKeyDict
from .datastore.stored_file_info import StoredDatastoreItemInfo
-from .dimensions import DataCoordinate, DimensionGraph, DimensionUniverse, SerializedDataCoordinate
+from .dimensions import DataCoordinate, DimensionGroup, DimensionUniverse, SerializedDataCoordinate
from .json import from_json_pydantic, to_json_pydantic
from .persistence_context import PersistenceContextVars
@@ -363,7 +363,7 @@ def __hash__(self) -> int:
return hash((self.datasetType, self.dataId, self.id))
@property
- def dimensions(self) -> DimensionGraph:
+ def dimensions(self) -> DimensionGroup:
"""Dimensions associated with the underlying `DatasetType`."""
return self.datasetType.dimensions
diff --git a/python/lsst/daf/butler/_dataset_type.py b/python/lsst/daf/butler/_dataset_type.py
index 8c6e9f7d2f..aa736a951c 100644
--- a/python/lsst/daf/butler/_dataset_type.py
+++ b/python/lsst/daf/butler/_dataset_type.py
@@ -39,12 +39,12 @@
from ._config_support import LookupKey
from ._storage_class import StorageClass, StorageClassFactory
-from .dimensions import DimensionGraph, DimensionGroup, SerializedDimensionGraph
+from .dimensions import DimensionGroup
from .json import from_json_pydantic, to_json_pydantic
from .persistence_context import PersistenceContextVars
if TYPE_CHECKING:
- from .dimensions import Dimension, DimensionUniverse
+ from .dimensions import DimensionUniverse
from .registry import Registry
@@ -59,7 +59,7 @@ class SerializedDatasetType(BaseModel):
name: StrictStr
storageClass: StrictStr | None = None
- dimensions: SerializedDimensionGraph | list[StrictStr] | None = None
+ dimensions: list[StrictStr] | None = None
parentStorageClass: StrictStr | None = None
isCalibration: StrictBool = False
@@ -69,7 +69,7 @@ def direct(
*,
name: str,
storageClass: str | None = None,
- dimensions: list | dict | None = None,
+ dimensions: list | None = None,
parentStorageClass: str | None = None,
isCalibration: bool = False,
) -> SerializedDatasetType:
@@ -88,7 +88,7 @@ def direct(
The name of the dataset type.
storageClass : `str` or `None`
The name of the storage class.
- dimensions : `list` or `dict` or `None`
+ dimensions : `list` or `None`
The dimensions associated with this dataset type.
parentStorageClass : `str` or `None`
The parent storage class name if this is a component.
@@ -104,16 +104,7 @@ def direct(
key = (name, storageClass or "")
if cache is not None and (type_ := cache.get(key, None)) is not None:
return type_
-
- serialized_dimensions: list[str] | None
- match dimensions:
- case list():
- serialized_dimensions = dimensions
- case dict():
- serialized_dimensions = SerializedDimensionGraph.direct(**dimensions).names
- case None:
- serialized_dimensions = None
-
+ serialized_dimensions = dimensions if dimensions is not None else None
node = cls.model_construct(
name=name,
storageClass=storageClass,
@@ -148,11 +139,9 @@ class DatasetType:
and underscores. Component dataset types should contain a single
period separating the base dataset type name from the component name
(and may be recursive).
- dimensions : `DimensionGroup`, `DimensionGraph`, or \
- `~collections.abc.Iterable` [ `Dimension` or `str` ]
+ dimensions : `DimensionGroup` or `~collections.abc.Iterable` [ `str` ]
Dimensions used to label and relate instances of this `DatasetType`.
- If not a `DimensionGraph` or `DimensionGroup`, ``universe`` must be
- provided as well.
+ If not a `DimensionGroup`, ``universe`` must be provided as well.
storageClass : `StorageClass` or `str`
Instance of a `StorageClass` or name of `StorageClass` that defines
how this `DatasetType` is persisted.
@@ -162,7 +151,7 @@ class DatasetType:
is not a component.
universe : `DimensionUniverse`, optional
Set of all known dimensions, used to normalize ``dimensions`` if it
- is not already a `DimensionGraph`.
+ is not already a `DimensionGroup`.
isCalibration : `bool`, optional
If `True`, this dataset type may be included in
`~CollectionType.CALIBRATION` collections.
@@ -209,7 +198,7 @@ def nameWithComponent(datasetTypeName: str, componentName: str) -> str:
def __init__(
self,
name: str,
- dimensions: DimensionGroup | DimensionGraph | Iterable[Dimension | str],
+ dimensions: DimensionGroup | Iterable[str],
storageClass: StorageClass | str,
parentStorageClass: StorageClass | str | None = None,
*,
@@ -221,9 +210,7 @@ def __init__(
self._name = name
universe = universe or getattr(dimensions, "universe", None)
if universe is None:
- raise ValueError(
- "If dimensions is not a DimensionGroup or DimensionGraph, a universe must be provided."
- )
+ raise ValueError("If dimensions is not a DimensionGroup, a universe must be provided.")
self._dimensions = universe.conform(dimensions)
if name in self._dimensions.universe.governor_dimensions:
raise ValueError(f"Governor dimension name {name} cannot be used as a dataset type name.")
@@ -373,12 +360,12 @@ def name(self) -> str:
return self._name
@property
- def dimensions(self) -> DimensionGraph:
- """Return the dimensions of this dataset type (`DimensionGraph`).
+ def dimensions(self) -> DimensionGroup:
+ """Return the dimensions of this dataset type (`DimensionGroup`).
The dimensions of a define the keys of its datasets' data IDs..
"""
- return self._dimensions._as_graph()
+ return self._dimensions
@property
def storageClass(self) -> StorageClass:
@@ -744,14 +731,9 @@ def from_simple(
if universe is None:
# this is for mypy
raise ValueError("Unable to determine a usable universe")
-
- match simple.dimensions:
- case list():
- dimensions = universe.conform(simple.dimensions)
- case SerializedDimensionGraph():
- dimensions = universe.conform(simple.dimensions.names)
- case None:
- raise ValueError(f"Dimensions must be specified in {simple}")
+ if simple.dimensions is None:
+ raise ValueError(f"Dimensions must be specified in {simple}")
+ dimensions = universe.conform(simple.dimensions)
newType = cls(
name=simple.name,
diff --git a/python/lsst/daf/butler/_registry_shim.py b/python/lsst/daf/butler/_registry_shim.py
index 2bbe5a9fb6..45010c1b83 100644
--- a/python/lsst/daf/butler/_registry_shim.py
+++ b/python/lsst/daf/butler/_registry_shim.py
@@ -36,14 +36,11 @@
from ._dataset_association import DatasetAssociation
from ._dataset_ref import DatasetId, DatasetIdGenEnum, DatasetRef
from ._dataset_type import DatasetType
-from ._named import NameLookupMapping
from ._timespan import Timespan
from .dimensions import (
DataCoordinate,
DataId,
- Dimension,
DimensionElement,
- DimensionGraph,
DimensionGroup,
DimensionRecord,
DimensionUniverse,
@@ -248,15 +245,14 @@ def expandDataId(
self,
dataId: DataId | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
- graph: DimensionGraph | None = None,
- records: NameLookupMapping[DimensionElement, DimensionRecord | None] | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
+ records: Mapping[str, DimensionRecord | None] | None = None,
withDefaults: bool = True,
**kwargs: Any,
) -> DataCoordinate:
# Docstring inherited from a base class.
return self._registry.expandDataId(
- dataId, dimensions=dimensions, graph=graph, records=records, withDefaults=withDefaults, **kwargs
+ dataId, dimensions=dimensions, records=records, withDefaults=withDefaults, **kwargs
)
def insertDimensionData(
@@ -310,7 +306,7 @@ def queryDatasets(
datasetType: Any,
*,
collections: CollectionArgType | None = None,
- dimensions: Iterable[Dimension | str] | None = None,
+ dimensions: Iterable[str] | None = None,
dataId: DataId | None = None,
where: str = "",
findFirst: bool = False,
@@ -335,8 +331,7 @@ def queryDatasets(
def queryDataIds(
self,
- # TODO: Drop Dimension support on DM-41326.
- dimensions: DimensionGroup | Iterable[Dimension | str] | Dimension | str,
+ dimensions: DimensionGroup | Iterable[str] | str,
*,
dataId: DataId | None = None,
datasets: Any = None,
diff --git a/python/lsst/daf/butler/datastore/file_templates.py b/python/lsst/daf/butler/datastore/file_templates.py
index 60ae424c89..85cbc8d93a 100644
--- a/python/lsst/daf/butler/datastore/file_templates.py
+++ b/python/lsst/daf/butler/datastore/file_templates.py
@@ -43,7 +43,7 @@
from .._dataset_ref import DatasetId, DatasetRef
from .._exceptions import ValidationError
from .._storage_class import StorageClass
-from ..dimensions import DataCoordinate, DimensionGraph, DimensionGroup
+from ..dimensions import DataCoordinate, DimensionGroup
if TYPE_CHECKING:
from .._dataset_type import DatasetType
@@ -394,9 +394,7 @@ def __str__(self) -> str:
def __repr__(self) -> str:
return f'{self.__class__.__name__}("{self.template}")'
- def grouped_fields(
- self, dimensions: DimensionGroup | DimensionGraph | None = None
- ) -> tuple[FieldDict, FieldDict]:
+ def grouped_fields(self, dimensions: DimensionGroup | None = None) -> tuple[FieldDict, FieldDict]:
"""Return all the fields, grouped by their type.
Parameters
@@ -723,7 +721,7 @@ def validateTemplate(self, entity: DatasetRef | DatasetType | StorageClass | Non
-----
Validation will always include a check that mandatory fields
are present and that at least one field refers to a dimension.
- If the supplied entity includes a `DimensionGraph` then it will be
+ If the supplied entity includes a `DimensionGroup` then it will be
used to compare the available dimensions with those specified in the
template.
"""
@@ -821,7 +819,7 @@ def validateTemplate(self, entity: DatasetRef | DatasetType | StorageClass | Non
# Fall back to dataId keys if we have them but no links.
# dataId keys must still be present in the template
try:
- minimal = set(entity.dimensions.required.names)
+ minimal = set(entity.dimensions.required)
maximal = set(entity.dimensions.names)
except AttributeError:
try:
@@ -890,5 +888,5 @@ def _determine_skypix_alias(self, entity: DatasetRef | DatasetType) -> str | Non
# update to be more like the real world while still providing our
# only tests of important behavior.
if len(entity.dimensions.skypix) == 1:
- (alias,) = entity.dimensions.skypix.names
+ (alias,) = entity.dimensions.skypix
return alias
diff --git a/python/lsst/daf/butler/dimensions/__init__.py b/python/lsst/daf/butler/dimensions/__init__.py
index 5494a7efed..810b218a0b 100644
--- a/python/lsst/daf/butler/dimensions/__init__.py
+++ b/python/lsst/daf/butler/dimensions/__init__.py
@@ -40,7 +40,6 @@
from ._database import *
from ._elements import *
from ._governor import *
-from ._graph import *
from ._group import *
from ._packer import *
from ._record_set import *
diff --git a/python/lsst/daf/butler/dimensions/_coordinate.py b/python/lsst/daf/butler/dimensions/_coordinate.py
index 3594adc2d1..1817248fe8 100644
--- a/python/lsst/daf/butler/dimensions/_coordinate.py
+++ b/python/lsst/daf/butler/dimensions/_coordinate.py
@@ -42,23 +42,17 @@
)
import numbers
-import warnings
from abc import abstractmethod
-from collections.abc import Iterable, Iterator, Mapping, Set
-from typing import TYPE_CHECKING, Any, ClassVar, cast
+from collections.abc import Iterable, Iterator, Mapping
+from typing import TYPE_CHECKING, Any, ClassVar, overload
import pydantic
-from deprecated.sphinx import deprecated
from lsst.sphgeom import IntersectionRegion, Region
-from lsst.utils.introspection import find_outside_stacklevel
from .._exceptions import DimensionNameError
-from .._named import NamedKeyMapping, NamedValueAbstractSet, NameLookupMapping
from .._timespan import Timespan
from ..json import from_json_pydantic, to_json_pydantic
from ..persistence_context import PersistenceContextVars
-from ._elements import Dimension, DimensionElement
-from ._graph import DimensionGraph
from ._group import DimensionGroup
from ._records import DimensionRecord, SerializedDimensionRecord
@@ -66,7 +60,7 @@
from ..registry import Registry
from ._universe import DimensionUniverse
-DataIdKey = str | Dimension
+DataIdKey = str
"""Type annotation alias for the keys that can be used to index a
DataCoordinate.
"""
@@ -141,7 +135,7 @@ def _intersectRegions(*args: Region) -> Region | None:
return result
-class DataCoordinate(NamedKeyMapping[Dimension, DataIdValue]):
+class DataCoordinate:
"""A validated data ID.
DataCoordinate guarantees that its key-value pairs identify at least all
@@ -171,10 +165,9 @@ class DataCoordinate(NamedKeyMapping[Dimension, DataIdValue]):
@staticmethod
def standardize(
- mapping: NameLookupMapping[Dimension, DataIdValue] | None = None,
+ mapping: Mapping[str, DataIdValue] | DataCoordinate | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
- graph: DimensionGraph | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
universe: DimensionUniverse | None = None,
defaults: DataCoordinate | None = None,
**kwargs: Any,
@@ -186,25 +179,21 @@ def standardize(
Parameters
----------
- mapping : `~collections.abc.Mapping`, optional
+ mapping : `~collections.abc.Mapping` or `DataCoordinate`, optional
An informal data ID that maps dimensions or dimension names to
their primary key values (may also be a true `DataCoordinate`).
- dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup` \
- or `DimensionGraph`, optional
+ dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup`, \
+ optional
The dimensions to be identified by the new `DataCoordinate`. If not
provided, will be inferred from the keys of ``mapping`` and
``**kwargs``, and ``universe`` must be provided unless ``mapping``
is already a `DataCoordinate`.
- graph : `DimensionGraph`, optional
- Like ``dimensions``, but requires a ``DimensionGraph`` instance.
- Ignored if ``dimensions`` is provided. Deprecated and will be
- removed after v27.
universe : `DimensionUniverse`
All known dimensions and their relationships; used to expand and
- validate dependencies when ``graph`` is not provided.
+ validate dependencies when ``dimensions`` is not provided.
defaults : `DataCoordinate`, optional
Default dimension key-value pairs to use when needed. These are
- never used to infer ``graph``, and are ignored if a different value
+ never used to infer ``group``, and are ignored if a different value
is provided for the same key in ``mapping`` or `**kwargs``.
**kwargs
Additional keyword arguments are treated like additional key-value
@@ -222,28 +211,11 @@ def standardize(
DimensionNameError
Raised if a key-value pair for a required dimension is missing.
"""
- universe = (
- universe
- or getattr(dimensions, "universe", None)
- or getattr(graph, "universe", None)
- or getattr(mapping, "universe", None)
- )
+ universe = universe or getattr(dimensions, "universe", None) or getattr(mapping, "universe", None)
if universe is None:
- raise TypeError(
- "universe must be provided, either directly or via dimensions, mapping, or graph."
- )
- if graph is not None:
- # TODO: remove argument on DM-41326.
- warnings.warn(
- "The 'graph' argument to DataCoordinate.standardize is deprecated in favor of the "
- "'dimensions' argument, and will be removed after v27.",
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- )
- dimensions = graph.names
+ raise TypeError("universe must be provided, either directly or via dimensions or mapping.")
if dimensions is not None:
dimensions = universe.conform(dimensions)
- del graph # make sure we don't actualy use this below
new_mapping: dict[str, DataIdValue] = {}
if isinstance(mapping, DataCoordinate):
if dimensions is None:
@@ -260,14 +232,6 @@ def standardize(
new_mapping.update((name, mapping[name]) for name in mapping.dimensions.required)
if mapping.hasFull():
new_mapping.update((name, mapping[name]) for name in mapping.dimensions.implied)
- elif isinstance(mapping, NamedKeyMapping):
- warnings.warn(
- "Passing a NamedKeyMapping to DataCoordinate.standardize is deprecated, and will be "
- "removed after v27.",
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- )
- new_mapping.update(mapping.byName())
elif mapping is not None:
new_mapping.update(mapping)
new_mapping.update(kwargs)
@@ -275,7 +239,7 @@ def standardize(
if defaults is not None:
universe = defaults.universe
elif universe is None:
- raise TypeError("universe must be provided if graph is not.")
+ raise TypeError("universe must be provided if dimensions is not.")
dimensions = DimensionGroup(universe, new_mapping.keys())
if not dimensions:
return DataCoordinate.make_empty(universe)
@@ -376,42 +340,7 @@ def make_empty(universe: DimensionUniverse) -> DataCoordinate:
`hasRecords` are guaranteed to return `True`, because both `full`
and `records` are just empty mappings.
"""
- return _ExpandedTupleDataCoordinate(universe.empty.as_group(), (), {})
-
- # TODO: remove on DM-41326.
- @staticmethod
- @deprecated(
- "fromRequiredValues is deprecated in favor of from_required_values, "
- "which takes a DimensionGroup instead of a DimensionGraph. It will be "
- "removed after v27.",
- version="v27",
- category=FutureWarning,
- )
- def fromRequiredValues(graph: DimensionGraph, values: tuple[DataIdValue, ...]) -> DataCoordinate:
- """Construct a `DataCoordinate` from required dimension values.
-
- This method is deprecated in favor of `from_required_values`.
-
- This is a low-level interface with at most assertion-level checking of
- inputs. Most callers should use `standardize` instead.
-
- Parameters
- ----------
- graph : `DimensionGraph`
- Dimensions this data ID will identify.
- values : `tuple` [ `int` or `str` ]
- Tuple of primary key values corresponding to ``graph.required``,
- in that order.
-
- Returns
- -------
- dataId : `DataCoordinate`
- A data ID object that identifies the given dimensions.
- ``dataId.hasFull()`` will return `True` only if ``graph.implied``
- is empty. ``dataId.hasRecords()`` will return `True`
- if and only if ``graph`` is empty.
- """
- return DataCoordinate.from_required_values(graph._group, values)
+ return _ExpandedTupleDataCoordinate(universe.empty, (), {})
@staticmethod
def from_required_values(dimensions: DimensionGroup, values: tuple[DataIdValue, ...]) -> DataCoordinate:
@@ -425,8 +354,8 @@ def from_required_values(dimensions: DimensionGroup, values: tuple[DataIdValue,
dimensions : `DimensionGroup`
Dimensions this data ID will identify.
values : `tuple` [ `int` or `str` ]
- Tuple of primary key values corresponding to ``graph.required``, in
- that order.
+ Tuple of primary key values corresponding to
+ ``dimensions.required``, in that order.
Returns
-------
@@ -434,7 +363,7 @@ def from_required_values(dimensions: DimensionGroup, values: tuple[DataIdValue,
A data ID object that identifies the given dimensions.
``dataId.hasFull()`` will return `True` only if
``dimensions.implied`` is empty. ``dataId.hasRecords()`` will
- return `True` if and only if ``graph`` is empty.
+ return `True` if and only if ``dimensions`` is empty.
"""
assert len(dimensions.required) == len(
values
@@ -445,43 +374,6 @@ def from_required_values(dimensions: DimensionGroup, values: tuple[DataIdValue,
return _FullTupleDataCoordinate(dimensions, values)
return _RequiredTupleDataCoordinate(dimensions, values)
- # TODO: remove on DM-41326.
- @staticmethod
- @deprecated(
- "fromFullValues is deprecated in favor of from_full_values, "
- "which takes a DimensionGroup instead of a DimensionGraph. It will be "
- "removed after v27.",
- version="v27",
- category=FutureWarning,
- )
- def fromFullValues(graph: DimensionGraph, values: tuple[DataIdValue, ...]) -> DataCoordinate:
- """Construct a `DataCoordinate` from all dimension values.
-
- This method is deprecated in favor of `from_full_values`.
-
- This is a low-level interface with at most assertion-level checking of
- inputs. Most callers should use `standardize` instead.
-
- Parameters
- ----------
- graph : `DimensionGraph`
- Dimensions this data ID will identify.
- values : `tuple` [ `int` or `str` ]
- Tuple of primary key values corresponding to
- ``itertools.chain(graph.required, graph.implied)``, in that order.
- Note that this is _not_ the same order as ``graph.dimensions``,
- though these contain the same elements.
-
- Returns
- -------
- dataId : `DataCoordinate`
- A data ID object that identifies the given dimensions.
- ``dataId.hasFull()`` will always return `True`.
- ``dataId.hasRecords()`` will only return `True` if ``graph`` is
- empty.
- """
- return DataCoordinate.from_full_values(graph._group, values)
-
@staticmethod
def from_full_values(dimensions: DimensionGroup, values: tuple[DataIdValue, ...]) -> DataCoordinate:
"""Construct a `DataCoordinate` from all dimension values.
@@ -495,9 +387,9 @@ def from_full_values(dimensions: DimensionGroup, values: tuple[DataIdValue, ...]
Dimensions this data ID will identify.
values : `tuple` [ `int` or `str` ]
Tuple of primary key values corresponding to
- ``itertools.chain(graph.required, graph.implied)``, in that order.
- Note that this is _not_ the same order as ``graph.dimensions``,
- though these contain the same elements.
+ ``itertools.chain(dimensions.required, dimensions.implied)``, in
+ that order. Note that this is _not_ the same order as
+ ``dimensions.names``, though these contain the same elements.
Returns
-------
@@ -525,6 +417,32 @@ def __eq__(self, other: Any) -> bool:
other = DataCoordinate.standardize(other, universe=self.universe)
return self.dimensions == other.dimensions and self.required_values == other.required_values
+ @abstractmethod
+ def __getitem__(self, key: str) -> DataIdValue:
+ raise NotImplementedError()
+
+ def __contains__(self, key: str) -> bool:
+ try:
+ self.__getitem__(key)
+ return True
+ except KeyError:
+ return False
+
+ @overload
+ def get(self, key: str) -> DataIdValue | None: ...
+
+ @overload
+ def get(self, key: str, default: int) -> int: ...
+
+ @overload
+ def get(self, key: str, default: str) -> str: ...
+
+ def get(self, key: str, default: DataIdValue | None = None) -> DataIdValue | None:
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ return default
+
def __repr__(self) -> str:
# We can't make repr yield something that could be exec'd here without
# printing out the whole DimensionUniverse.
@@ -537,69 +455,21 @@ def __lt__(self, other: Any) -> bool:
# can not be true simultaneously with __lt__ being true.
return self.required_values < other.required_values
- # TODO: remove on DM-41326.
- @deprecated(
- "Using DataCoordinate as a Mapping is deprecated in favor of the "
- ".mapping and .required attributes, and will be dropped after v27.",
- version="v27",
- category=FutureWarning,
- )
- def __iter__(self) -> Iterator[Dimension]:
- return iter(self.keys())
-
- # TODO: remove on DM-41326.
- @deprecated(
- "Using DataCoordinate as a Mapping is deprecated in favor of the "
- ".mapping and .required attributes, and will be dropped after v27.",
- version="v27",
- category=FutureWarning,
- )
- def __len__(self) -> int:
- return len(self.keys())
-
- # TODO: remove on DM-41326.
- @deprecated(
- "Using DataCoordinate as a Mapping is deprecated in favor of the "
- ".mapping and .required attributes, and will be dropped after v27.",
- version="v27",
- category=FutureWarning,
- )
- def keys(self) -> NamedValueAbstractSet[Dimension]: # type: ignore
- return self.graph.required
-
- # TODO: remove on DM-41326.
- @property
- @deprecated(
- "DataCoordinate.names is deprecated in favor of the .dimensions "
- "attribute, and will be dropped after v27.",
- version="v27",
- category=FutureWarning,
- )
- def names(self) -> Set[str]:
- """Names of the required dimensions identified by this data ID.
-
- They are returned in the same order as `keys`
- (`collections.abc.Set` [ `str` ]).
- """
- return self.keys().names
-
@abstractmethod
- def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) -> DataCoordinate:
- """Return a `DataCoordinate` whose graph is a subset of ``self.graph``.
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> DataCoordinate:
+ """Return a `DataCoordinate` whose diensions are a subset of
+ ``self.dimensions``.
Parameters
----------
- dimensions : `DimensionGraph`, `DimensionGroup`, or \
- `~collections.abc.Iterable` [ `str` ]
+ dimensions : `DimensionGroup` or `~collections.abc.Iterable` [ `str` ]
The dimensions identified by the returned `DataCoordinate`.
- Passing a `DimensionGraph` is deprecated and support will be
- dropped after v27.
Returns
-------
coordinate : `DataCoordinate`
A `DataCoordinate` instance that identifies only the given
- dimensions. May be ``self`` if ``graph == self.graph``.
+ dimensions. May be ``self`` if ``dimensions == self.dimensions``.
Raises
------
@@ -619,7 +489,6 @@ def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) ->
return `True` (respectively) on the returned `DataCoordinate` as well.
The converse does not hold.
"""
- # TODO: update docs r.e. deprecation on DM-41326.
raise NotImplementedError()
@abstractmethod
@@ -650,9 +519,7 @@ def union(self, other: DataCoordinate) -> DataCoordinate:
raise NotImplementedError()
@abstractmethod
- def expanded(
- self, records: NameLookupMapping[DimensionElement, DimensionRecord | None]
- ) -> DataCoordinate:
+ def expanded(self, records: Mapping[str, DimensionRecord | None]) -> DataCoordinate:
"""Return a `DataCoordinate` that holds the given records.
Guarantees that `hasRecords` returns `True`.
@@ -664,15 +531,12 @@ def expanded(
----------
records : `~collections.abc.Mapping` [ `str`, `DimensionRecord` or \
`None` ]
- A `NamedKeyMapping` with `DimensionElement` keys or a regular
- `~collections.abc.Mapping` with `str` (`DimensionElement` name)
+ A`~collections.abc.Mapping` with `str` (dimension element name)
keys and `DimensionRecord` values. Keys must cover all elements in
- ``self.graph.elements``. Values may be `None`, but only to reflect
- actual NULL values in the database, not just records that have not
- been fetched. Passing a `NamedKeyMapping` is deprecated and will
- not be supported after v27.
+ ``self.dimensions.elements``. Values may be `None`, but only to
+ reflect actual NULL values in the database, not just records that
+ have not been fetched.
"""
- # TODO: update docs r.e. deprecation on DM-41326.
raise NotImplementedError()
@property
@@ -695,22 +559,6 @@ def dimensions(self) -> DimensionGroup:
"""
raise NotImplementedError()
- # TODO: remove on DM-41326.
- @property
- @deprecated(
- "DataCoordinate.graph is deprecated in favor of .dimensions, and will be dropped after v27.",
- version="v27",
- category=FutureWarning,
- )
- def graph(self) -> DimensionGraph:
- """Dimensions identified by this data ID (`DimensionGraph`).
-
- Note that values are only required to be present for dimensions in
- ``self.graph.required``; all others may be retrieved (from a
- `Registry`) given these.
- """
- return self.dimensions._as_graph()
-
@abstractmethod
def hasFull(self) -> bool:
"""Whether this data ID contains implied and required values.
@@ -727,43 +575,6 @@ def hasFull(self) -> bool:
"""
raise NotImplementedError()
- # TODO: remove on DM-41326.
- @property
- @deprecated(
- "DataCoordinate.full is deprecated in favor of .mapping, and will be dropped after v27.",
- version="v27",
- category=FutureWarning,
- )
- @abstractmethod
- def full(self) -> NamedKeyMapping[Dimension, DataIdValue]:
- """Return mapping for all dimensions in ``self.dimensions``.
-
- The mapping includes key-value pairs for all dimensions in
- ``self.dimensions``, including implied.
-
- Accessing this attribute if `hasFull` returns `False` is a logic error
- that may raise an exception of unspecified type either immediately or
- when implied keys are accessed via the returned mapping, depending on
- the implementation and whether assertions are enabled.
- """
- raise NotImplementedError()
-
- # TODO: remove on DM-41326.
- @deprecated(
- "DataCoordinate.values_tuple() is deprecated in favor of .required_values, and will be dropped "
- "after v27.",
- version="v27",
- category=FutureWarning,
- )
- def values_tuple(self) -> tuple[DataIdValue, ...]:
- """Return the required values (only) of this data ID as a tuple.
-
- In contexts where all data IDs have the same dimensions, comparing and
- hashing these tuples can be *much* faster than comparing the original
- `DataCoordinate` instances.
- """
- return self.required_values
-
@abstractmethod
def hasRecords(self) -> bool:
"""Whether this data ID contains records.
@@ -785,13 +596,10 @@ def hasRecords(self) -> bool:
raise NotImplementedError()
@property
- def records(self) -> NamedKeyMapping[DimensionElement, DimensionRecord | None]:
+ def records(self) -> Mapping[str, DimensionRecord | None]:
"""A mapping that contains `DimensionRecord` objects for all
elements identified by this data ID.
- This mapping will become a regular `~collections.abc.Mapping` with
- `str` keys after v27.
-
Notes
-----
The values of this mapping may be `None` if and only if there is no
@@ -961,48 +769,7 @@ def from_simple(
"""
-# Deprecated by having its only public access (DataCoordinate.full) deprecated.
-# TODO: remove on DM-41326.
-class _DataCoordinateFullView(NamedKeyMapping[Dimension, DataIdValue]):
- """View class for `DataCoordinate.full`.
-
- Provides the default implementation for
- `DataCoordinate.full`.
-
- Parameters
- ----------
- target : `DataCoordinate`
- The `DataCoordinate` instance this object provides a view of.
- """
-
- def __init__(self, target: _BasicTupleDataCoordinate):
- self._target = target
-
- __slots__ = ("_target",)
-
- def __repr__(self) -> str:
- return repr(self._target)
-
- def __getitem__(self, key: DataIdKey) -> DataIdValue:
- return self._target[key]
-
- def __iter__(self) -> Iterator[Dimension]:
- return iter(self.keys())
-
- def __len__(self) -> int:
- return len(self.keys())
-
- def keys(self) -> NamedValueAbstractSet[Dimension]: # type: ignore
- return self._target.graph.dimensions
-
- @property
- def names(self) -> Set[str]:
- # Docstring inherited from `NamedKeyMapping`.
- return self.keys().names
-
-
-# TODO: Make a Mapping[str, DimensionRecord | None] on DM-41326.
-class _DataCoordinateRecordsView(NamedKeyMapping[DimensionElement, DimensionRecord | None]):
+class _DataCoordinateRecordsView(Mapping[str, DimensionRecord | None]):
"""View class for `DataCoordinate.records`.
Provides the default implementation for
@@ -1020,50 +787,20 @@ def __init__(self, target: DataCoordinate):
__slots__ = ("_target",)
def __repr__(self) -> str:
- terms = [f"{d}: {self[d]!r}" for d in self._target.graph.elements.names]
+ terms = [f"{d}: {self[d]!r}" for d in self._target.dimensions.elements]
return "{{{}}}".format(", ".join(terms))
def __str__(self) -> str:
return "\n".join(str(v) for v in self.values())
- def __getitem__(self, key: DimensionElement | str) -> DimensionRecord | None:
- if isinstance(key, DimensionElement):
- warnings.warn(
- "Using Dimension keys in DataCoordinate is deprecated and will not be supported after v27.",
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- )
- key = key.name
+ def __getitem__(self, key: str) -> DimensionRecord | None:
return self._target._record(key)
- # TODO: fix on DM-41326.
- @deprecated(
- "Iteration over DataCoordinate.records is deprecated as the key type will change to 'str' after "
- "v27. Use DataCoordinate.dimensions.elements to get the names of all dimension elements instead.",
- version="v27",
- category=FutureWarning,
- )
- def __iter__(self) -> Iterator[DimensionElement]:
- return iter(self.keys())
+ def __iter__(self) -> Iterator[str]:
+ return iter(self._target.dimensions.elements)
def __len__(self) -> int:
- return len(self.keys())
-
- # TODO: remove on DM-41326.
- # Deprecation warning will come from using .graph.
- def keys(self) -> NamedValueAbstractSet[DimensionElement]: # type: ignore
- return self._target.graph.elements
-
- @property
- @deprecated(
- "DataCoordinate.records.names is deprecated in favor of DataCoordinate.dimensions.elements and "
- "will be removed after v27.",
- version="v27",
- category=FutureWarning,
- )
- def names(self) -> Set[str]:
- # Docstring inherited from `NamedKeyMapping`.
- return self.keys().names
+ return len(self._target.dimensions.elements)
class _BasicTupleDataCoordinate(DataCoordinate):
@@ -1101,16 +838,9 @@ def required(self) -> Mapping[str, DataIdValue]:
# Docstring inherited from DataCoordinate.
return _DataCoordinateRequiredMappingView(self)
- def __getitem__(self, key: DataIdKey) -> DataIdValue:
+ def __getitem__(self, key: str) -> DataIdValue:
# Docstring inherited from DataCoordinate.
- # TODO: remove on DM-41326.
- if isinstance(key, Dimension):
- warnings.warn(
- "Using Dimension keys in DataCoordinate is deprecated and will not be supported after v27.",
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- )
- key = key.name
+
index = self._dimensions._data_coordinate_indices[key]
try:
return self._values[index]
@@ -1119,21 +849,6 @@ def __getitem__(self, key: DataIdKey) -> DataIdValue:
# values for the required ones.
raise KeyError(key) from None
- # TODO: remove on DM-41326.
- @deprecated(
- "Using DataCoordinate as a NamedKeyMapping is deprecated in favor of the "
- ".mapping and .required attributes, and will be dropped after v27. "
- "Use `dict(data_id.required)` as an exact replacement for `data_id.byName()`.",
- version="v27",
- category=FutureWarning,
- )
- def byName(self) -> dict[str, DataIdValue]:
- # Docstring inheritance.
- # Reimplementation is for optimization; `required_values` is much
- # faster to iterate over than values() because it doesn't go through
- # `__getitem__`.
- return dict(zip(self.names, self.required_values, strict=True))
-
def hasRecords(self) -> bool:
# Docstring inherited from DataCoordinate.
return False
@@ -1220,7 +935,7 @@ def required_values(self) -> tuple[DataIdValue, ...]:
# Docstring inherited from DataCoordinate.
return self._values
- def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) -> DataCoordinate:
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> DataCoordinate:
# Docstring inherited from DataCoordinate.
dimensions = self.universe.conform(dimensions)
if self._dimensions == dimensions:
@@ -1248,30 +963,15 @@ def union(self, other: DataCoordinate) -> DataCoordinate:
values.update(other.mapping)
return DataCoordinate.standardize(values, dimensions=dimensions)
- # TODO: remove on DM-41326.
- @property
- def full(self) -> NamedKeyMapping[Dimension, DataIdValue]:
- # Docstring inherited.
- raise AssertionError("full may only be accessed if hasFull() returns True.")
-
- def expanded(
- self, records: NameLookupMapping[DimensionElement, DimensionRecord | None]
- ) -> DataCoordinate:
+ def expanded(self, records: Mapping[str, DimensionRecord | None]) -> DataCoordinate:
# Docstring inherited from DataCoordinate
# Extract a complete values tuple from the attributes of the given
# records. It's possible for these to be inconsistent with
# self._values (which is a serious problem, of course), but we've
# documented this as a no-checking API.
values = self._values + tuple(
- getattr(records[d], cast(Dimension, self.universe[d]).primaryKey.name)
- for d in self._dimensions.implied
+ getattr(records[d], self.universe.dimensions[d].primaryKey.name) for d in self._dimensions.implied
)
- if isinstance(records, NamedKeyMapping):
- warnings.warn(
- "NamedKeyMappings will not be accepted after v27; pass a Mapping with str keys instead.",
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- category=FutureWarning,
- )
return _ExpandedTupleDataCoordinate(self._dimensions, values, records)
def hasFull(self) -> bool:
@@ -1307,7 +1007,7 @@ def full_values(self) -> tuple[DataIdValue, ...]:
# Docstring inherited from DataCoordinate.
return self._values
- def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) -> DataCoordinate:
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> DataCoordinate:
# Docstring inherited from DataCoordinate.
dimensions = self.universe.conform(dimensions)
if self._dimensions == dimensions:
@@ -1331,27 +1031,8 @@ def union(self, other: DataCoordinate) -> DataCoordinate:
values.update(other.mapping)
return DataCoordinate.standardize(values, dimensions=dimensions)
- # TODO: remove on DM-41326.
- @property
- @deprecated(
- "DataCoordinate.full is deprecated in favor of .mapping, and will be dropped after v27.",
- version="v27",
- category=FutureWarning,
- )
- def full(self) -> NamedKeyMapping[Dimension, DataIdValue]:
- # Docstring inherited.
- return _DataCoordinateFullView(self)
-
- def expanded(
- self, records: NameLookupMapping[DimensionElement, DimensionRecord | None]
- ) -> DataCoordinate:
+ def expanded(self, records: Mapping[str, DimensionRecord | None]) -> DataCoordinate:
# Docstring inherited from DataCoordinate
- if isinstance(records, NamedKeyMapping):
- warnings.warn(
- "NamedKeyMappings will not be accepted after v27; pass a Mapping with str keys instead.",
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- category=FutureWarning,
- )
return _ExpandedTupleDataCoordinate(self._dimensions, self._values, records)
def hasFull(self) -> bool:
@@ -1379,8 +1060,7 @@ class _ExpandedTupleDataCoordinate(_FullTupleDataCoordinate):
``dimensions._data_coordinate_indices``. Just include values for all
dimensions.
records : `~collections.abc.Mapping` [ `str`, `DimensionRecord` or `None` ]
- A `NamedKeyMapping` with `DimensionElement` keys or a regular
- `~collections.abc.Mapping` with `str` (`DimensionElement` name) keys
+ A `~collections.abc.Mapping` with `str` (dimension element name) keys
and `DimensionRecord` values. Keys must cover all elements in
``self.dimensions.elements``. Values may be `None`, but only to
reflect actual NULL values in the database, not just records that have
@@ -1391,7 +1071,7 @@ def __init__(
self,
dimensions: DimensionGroup,
values: tuple[DataIdValue, ...],
- records: NameLookupMapping[DimensionElement, DimensionRecord | None],
+ records: Mapping[str, DimensionRecord | None],
):
super().__init__(dimensions, values)
assert super().hasFull(), "This implementation requires full dimension records."
@@ -1399,20 +1079,12 @@ def __init__(
__slots__ = ("_records",)
- def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) -> DataCoordinate:
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> DataCoordinate:
# Docstring inherited from DataCoordinate.
return super().subset(dimensions).expanded(self._records)
- def expanded(
- self, records: NameLookupMapping[DimensionElement, DimensionRecord | None]
- ) -> DataCoordinate:
+ def expanded(self, records: Mapping[str, DimensionRecord | None]) -> DataCoordinate:
# Docstring inherited from DataCoordinate.
- if isinstance(records, NamedKeyMapping):
- warnings.warn(
- "NamedKeyMappings will not be accepted after v27; pass a Mapping with str keys instead.",
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- category=FutureWarning,
- )
return self
def union(self, other: DataCoordinate) -> DataCoordinate:
diff --git a/python/lsst/daf/butler/dimensions/_data_coordinate_iterable.py b/python/lsst/daf/butler/dimensions/_data_coordinate_iterable.py
index 845c9e8cad..0d5e0e9c23 100644
--- a/python/lsst/daf/butler/dimensions/_data_coordinate_iterable.py
+++ b/python/lsst/daf/butler/dimensions/_data_coordinate_iterable.py
@@ -33,16 +33,11 @@
"DataCoordinateSequence",
)
-import warnings
from abc import abstractmethod
from collections.abc import Collection, Iterable, Iterator, Sequence, Set
from typing import Any, overload
-from deprecated.sphinx import deprecated
-from lsst.utils.introspection import find_outside_stacklevel
-
from ._coordinate import DataCoordinate
-from ._graph import DimensionGraph
from ._group import DimensionGroup
from ._universe import DimensionUniverse
@@ -51,7 +46,7 @@ class DataCoordinateIterable(Iterable[DataCoordinate]):
"""An abstract base class for homogeneous iterables of data IDs.
All elements of a `DataCoordinateIterable` identify the same set of
- dimensions (given by the `graph` property) and generally have the same
+ dimensions (given by the `dimensions` property) and generally have the same
`DataCoordinate.hasFull` and `DataCoordinate.hasRecords` flag values.
"""
@@ -78,17 +73,6 @@ def fromScalar(dataId: DataCoordinate) -> _ScalarDataCoordinateIterable:
"""
return _ScalarDataCoordinateIterable(dataId)
- # TODO: remove on DM-41326.
- @property
- @deprecated(
- "Deprecated in favor of .dimensions; will be removed after v26.",
- category=FutureWarning,
- version="v27",
- )
- def graph(self) -> DimensionGraph:
- """Dimensions identified by these data IDs (`DimensionGraph`)."""
- return self.dimensions._as_graph()
-
@property
@abstractmethod
def dimensions(self) -> DimensionGroup:
@@ -166,7 +150,7 @@ def toSequence(self) -> DataCoordinateSequence:
)
@abstractmethod
- def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) -> DataCoordinateIterable:
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> DataCoordinateIterable:
"""Return a subset iterable.
This subset iterable returns data IDs that identify a subset of the
@@ -174,8 +158,7 @@ def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) ->
Parameters
----------
- dimensions : `DimensionGraph`, `DimensionGroup`, or \
- `~collections.abc.Iterable` [ `str` ]
+ dimensions : `DimensionGroup` or `~collections.abc.Iterable` [ `str` ]
Dimensions to be identified by the data IDs in the returned
iterable. Must be a subset of ``self.dimensions``.
@@ -239,9 +222,7 @@ def hasRecords(self) -> bool:
# Docstring inherited from DataCoordinateIterable.
return self._dataId.hasRecords()
- def subset(
- self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]
- ) -> _ScalarDataCoordinateIterable:
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> _ScalarDataCoordinateIterable:
# Docstring inherited from DataCoordinateIterable.
dimensions = self.universe.conform(dimensions)
return _ScalarDataCoordinateIterable(self._dataId.subset(dimensions))
@@ -262,13 +243,8 @@ class _DataCoordinateCollectionBase(DataCoordinateIterable):
dataIds : `collections.abc.Collection` [ `DataCoordinate` ]
A collection of `DataCoordinate` instances, with dimensions equal to
``dimensions``.
- graph : `DimensionGraph`, optional
- Dimensions identified by all data IDs in the collection. Ignored if
- ``dimensions`` is provided, and deprecated with removal after v27.
- dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup`, \
- or `DimensionGraph`, optional
- Dimensions identified by all data IDs in the collection. Must be
- provided unless ``graph`` is.
+ dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup`
+ Dimensions identified by all data IDs in the collection.
hasFull : `bool`, optional
If `True`, the caller guarantees that `DataCoordinate.hasFull` returns
`True` for all given data IDs. If `False`, no such guarantee is made,
@@ -284,7 +260,7 @@ class _DataCoordinateCollectionBase(DataCoordinateIterable):
`False`.
check: `bool`, optional
If `True` (default) check that all data IDs are consistent with the
- given ``graph`` and state flags at construction. If `False`, no
+ given ``dimensions`` and state flags at construction. If `False`, no
checking will occur.
universe : `DimensionUniverse`
Object that manages all dimension definitions.
@@ -293,39 +269,20 @@ class _DataCoordinateCollectionBase(DataCoordinateIterable):
def __init__(
self,
dataIds: Collection[DataCoordinate],
- graph: DimensionGraph | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
hasFull: bool | None = None,
hasRecords: bool | None = None,
check: bool = True,
universe: DimensionUniverse | None = None,
):
- universe = (
- universe
- or getattr(dimensions, "universe", None)
- or getattr(graph, "universe", None)
- or getattr(dataIds, "universe", None)
- )
+ universe = universe or getattr(dimensions, "universe", None) or getattr(dataIds, "universe", None)
if universe is None:
- raise TypeError(
- "universe must be provided, either directly or via dimensions, dataIds, or graph."
- )
- if graph is not None:
- warnings.warn(
- "The 'graph' argument to DataCoordinateIterable constructors is deprecated in favor of "
- " passing an iterable of dimension names as the 'dimensions' argument, and wil be removed "
- "after v27.",
- stacklevel=find_outside_stacklevel("lsst.daf.butler"),
- category=FutureWarning,
- )
+ raise TypeError("universe must be provided, either directly or via dimensions or dataIds.")
if dimensions is not None:
dimensions = universe.conform(dimensions)
- elif graph is not None:
- dimensions = graph.as_group()
- del graph # Avoid accidental use later.
- if dimensions is None:
- raise TypeError("Exactly one of 'graph' or (preferably) 'dimensions' must be provided.")
+ else:
+ raise TypeError("'dimensions' must be provided.")
self._dataIds = dataIds
self._dimensions = dimensions
if check:
@@ -431,15 +388,10 @@ class DataCoordinateSet(_DataCoordinateCollectionBase):
----------
dataIds : `collections.abc.Set` [ `DataCoordinate` ]
A set of `DataCoordinate` instances, with dimensions equal to
- ``graph``. If this is a mutable object, the caller must be able to
- guarantee that it will not be modified by any other holders.
- graph : `DimensionGraph`, optional
- Dimensions identified by all data IDs in the collection. Ignored if
- ``dimensions`` is provided, and deprecated with removal after v27.
- dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup`, \
- or `DimensionGraph`, optional
- Dimensions identified by all data IDs in the collection. Must be
- provided unless ``graph`` is.
+ ``dimensions``. If this is a mutable object, the caller must be able
+ to guarantee that it will not be modified by any other holders.
+ dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup`
+ Dimensions identified by all data IDs in the collection.
hasFull : `bool`, optional
If `True`, the caller guarantees that `DataCoordinate.hasFull` returns
`True` for all given data IDs. If `False`, no such guarantee is made,
@@ -456,7 +408,7 @@ class DataCoordinateSet(_DataCoordinateCollectionBase):
first use if ``check`` is `False`.
check : `bool`, optional
If `True` (default) check that all data IDs are consistent with the
- given ``graph`` and state flags at construction. If `False`, no
+ given ``dimensions`` and state flags at construction. If `False`, no
checking will occur.
universe : `DimensionUniverse`
Object that manages all dimension definitions.
@@ -501,9 +453,8 @@ class DataCoordinateSet(_DataCoordinateCollectionBase):
def __init__(
self,
dataIds: Set[DataCoordinate],
- graph: DimensionGraph | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
hasFull: bool | None = None,
hasRecords: bool | None = None,
check: bool = True,
@@ -511,7 +462,6 @@ def __init__(
):
super().__init__(
dataIds,
- graph,
dimensions=dimensions,
hasFull=hasFull,
hasRecords=hasRecords,
@@ -571,7 +521,8 @@ def issubset(self, other: DataCoordinateIterable) -> bool:
Parameters
----------
other : `DataCoordinateIterable`
- An iterable of data IDs with ``other.graph == self.graph``.
+ An iterable of data IDs with
+ ``other.dimensions == self.dimensions``.
Returns
-------
@@ -747,13 +698,12 @@ def toSet(self) -> DataCoordinateSet:
# Docstring inherited from DataCoordinateIterable.
return self
- def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) -> DataCoordinateSet:
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> DataCoordinateSet:
"""Return a set whose data IDs identify a subset.
Parameters
----------
- dimensions : `DimensionGraph`, `DimensionGroup`, or \
- `~collections.abc.Iterable` [ `str` ]
+ dimensions : `DimensionGroup` or `~collections.abc.Iterable` [ `str` ]
Dimensions to be identified by the data IDs in the returned
iterable. Must be a subset of ``self.dimensions``.
@@ -786,14 +736,9 @@ class DataCoordinateSequence(_DataCoordinateCollectionBase, Sequence[DataCoordin
----------
dataIds : `collections.abc.Sequence` [ `DataCoordinate` ]
A sequence of `DataCoordinate` instances, with dimensions equal to
- ``graph``.
- graph : `DimensionGraph`, optional
- Dimensions identified by all data IDs in the collection. Ignored if
- ``dimensions`` is provided, and deprecated with removal after v27.
- dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup`, \
- `DimensionGraph`, optional
- Dimensions identified by all data IDs in the collection. Must be
- provided unless ``graph`` is.
+ ``dimensions``.
+ dimensions : `~collections.abc.Iterable` [ `str` ], `DimensionGroup`
+ Dimensions identified by all data IDs in the collection.
hasFull : `bool`, optional
If `True`, the caller guarantees that `DataCoordinate.hasFull` returns
`True` for all given data IDs. If `False`, no such guarantee is made,
@@ -810,7 +755,7 @@ class DataCoordinateSequence(_DataCoordinateCollectionBase, Sequence[DataCoordin
first use if ``check`` is `False`.
check : `bool`, optional
If `True` (default) check that all data IDs are consistent with the
- given ``graph`` and state flags at construction. If `False`, no
+ given ``dimensions`` and state flags at construction. If `False`, no
checking will occur.
universe : `DimensionUniverse`
Object that manages all dimension definitions.
@@ -819,9 +764,8 @@ class DataCoordinateSequence(_DataCoordinateCollectionBase, Sequence[DataCoordin
def __init__(
self,
dataIds: Sequence[DataCoordinate],
- graph: DimensionGraph | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
hasFull: bool | None = None,
hasRecords: bool | None = None,
check: bool = True,
@@ -829,7 +773,6 @@ def __init__(
):
super().__init__(
tuple(dataIds),
- graph,
dimensions=dimensions,
hasFull=hasFull,
hasRecords=hasRecords,
@@ -879,22 +822,21 @@ def toSequence(self) -> DataCoordinateSequence:
# Docstring inherited from DataCoordinateIterable.
return self
- def subset(self, dimensions: DimensionGraph | DimensionGroup | Iterable[str]) -> DataCoordinateSequence:
+ def subset(self, dimensions: DimensionGroup | Iterable[str]) -> DataCoordinateSequence:
"""Return a sequence whose data IDs identify a subset.
Parameters
----------
- dimensions : `DimensionGraph`, `DimensionGroup`, \
- or `~collections.abc.Iterable` [ `str` ]
+ dimensions : `DimensionGroup` or `~collections.abc.Iterable` [ `str` ]
Dimensions to be identified by the data IDs in the returned
iterable. Must be a subset of ``self.dimensions``.
Returns
-------
set : `DataCoordinateSequence`
- A `DataCoordinateSequence` with ``set.graph == graph``.
- Will be ``self`` if ``graph == self.graph``. Elements are
- equivalent to those that would be created by calling
+ A `DataCoordinateSequence` with ``set.dimensions == dimensions``.
+ Will be ``self`` if ``dimensions == self.dimensions``. Elements
+ are equivalent to those that would be created by calling
`DataCoordinate.subset` on all elements in ``self``, in the same
order and with no deduplication.
"""
diff --git a/python/lsst/daf/butler/dimensions/_elements.py b/python/lsst/daf/butler/dimensions/_elements.py
index 11e6330c63..8d0f0db91b 100644
--- a/python/lsst/daf/butler/dimensions/_elements.py
+++ b/python/lsst/daf/butler/dimensions/_elements.py
@@ -48,7 +48,6 @@
if TYPE_CHECKING: # Imports needed only for type annotations; may be circular.
from ..registry import Registry
from ._governor import GovernorDimension
- from ._graph import DimensionGraph
from ._group import DimensionGroup
from ._records import DimensionRecord
from ._schema import DimensionRecordSchema
@@ -304,20 +303,6 @@ def dimensions(self) -> NamedValueAbstractSet[Dimension]:
"""
return NamedValueSet(list(self.required) + list(self.implied)).freeze()
- # Deprecated via a warning from its implementation.
- # TODO: remove on DM-41326.
- @property
- def graph(self) -> DimensionGraph:
- """Return minimal graph that includes this element (`DimensionGraph`).
-
- ``self.graph.required`` includes all dimensions whose primary key
- values are sufficient (often necessary) to uniquely identify ``self``
- (including ``self`` if ``isinstance(self, Dimension)``.
- ``self.graph.implied`` includes all dimensions also identified
- (possibly recursively) by this set.
- """
- return self.minimal_group._as_graph()
-
@property
@cached_getter
def minimal_group(self) -> DimensionGroup:
diff --git a/python/lsst/daf/butler/dimensions/_graph.py b/python/lsst/daf/butler/dimensions/_graph.py
deleted file mode 100644
index 81598ca0e3..0000000000
--- a/python/lsst/daf/butler/dimensions/_graph.py
+++ /dev/null
@@ -1,623 +0,0 @@
-# This file is part of daf_butler.
-#
-# Developed for the LSST Data Management System.
-# This product includes software developed by the LSST Project
-# (http://www.lsst.org).
-# See the COPYRIGHT file at the top-level directory of this distribution
-# for details of code ownership.
-#
-# This software is dual licensed under the GNU General Public License and also
-# under a 3-clause BSD license. Recipients may choose which of these licenses
-# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
-# respectively. If you choose the GPL option then the following text applies
-# (but note that there is still no warranty even if you opt for BSD instead):
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-from __future__ import annotations
-
-__all__ = ["DimensionGraph", "SerializedDimensionGraph"]
-
-import warnings
-from collections.abc import Iterable, Iterator, Mapping, Set
-from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, cast
-
-import pydantic
-from deprecated.sphinx import deprecated
-from lsst.utils.classes import cached_getter, immutable
-from lsst.utils.introspection import find_outside_stacklevel
-
-from .._named import NamedValueAbstractSet, NameMappingSetView
-from .._topology import TopologicalFamily, TopologicalSpace
-from ..json import from_json_pydantic, to_json_pydantic
-from ._group import DimensionGroup
-
-if TYPE_CHECKING: # Imports needed only for type annotations; may be circular.
- from ..registry import Registry
- from ._elements import Dimension, DimensionElement
- from ._governor import GovernorDimension
- from ._skypix import SkyPixDimension
- from ._universe import DimensionUniverse
-
-
-class SerializedDimensionGraph(pydantic.BaseModel):
- """Simplified model of a `DimensionGraph` suitable for serialization."""
-
- names: list[str]
-
- @classmethod
- def direct(cls, *, names: list[str]) -> SerializedDimensionGraph:
- """Construct a `SerializedDimensionGraph` directly without validators.
-
- Parameters
- ----------
- names : `list` [`str`]
- The names of the dimensions to include.
-
- Returns
- -------
- graph : `SerializedDimensionGraph`
- Model representing these dimensions.
-
- Notes
- -----
- This differs from the pydantic "construct" method in that the arguments
- are explicitly what the model requires, and it will recurse through
- members, constructing them from their corresponding `direct` methods.
-
- This method should only be called when the inputs are trusted.
- """
- return cls.model_construct(names=names)
-
-
-_T = TypeVar("_T", bound="DimensionElement", covariant=True)
-
-
-# TODO: Remove on DM-41326.
-_NVAS_DEPRECATION_MSG = """DimensionGraph is deprecated in favor of
-DimensionGroup, which uses sets of str names instead of NamedValueAbstractSets
-of Dimension or DimensionElement instances. Support for the
-NamedValueAbstractSet interfaces on this object will be dropped after v27.
-"""
-
-
-class _DimensionGraphNamedValueSet(NameMappingSetView[_T]):
- def __init__(self, keys: Set[str], universe: DimensionUniverse):
- super().__init__({k: cast(_T, universe[k]) for k in keys})
-
- # TODO: Remove on DM-41326.
- @deprecated(
- _NVAS_DEPRECATION_MSG
- + "Use a dict comprehension and DimensionUniverse indexing to construct a mapping when needed.",
- version="v27",
- category=FutureWarning,
- )
- def asMapping(self) -> Mapping[str, _T]:
- return super().asMapping()
-
- # TODO: Remove on DM-41326.
- @deprecated(
- _NVAS_DEPRECATION_MSG + "Use DimensionUniverse for DimensionElement lookups.",
- version="v27",
- category=FutureWarning,
- )
- def __getitem__(self, key: str | _T) -> _T:
- return super().__getitem__(key)
-
- def __contains__(self, key: Any) -> bool:
- from ._elements import DimensionElement
-
- if isinstance(key, DimensionElement):
- warnings.warn(
- _NVAS_DEPRECATION_MSG + "'in' expressions must use str keys.",
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler."),
- )
- return super().__contains__(key)
-
- def __iter__(self) -> Iterator[_T]:
- # TODO: Remove on DM-41326.
- warnings.warn(
- _NVAS_DEPRECATION_MSG
- + (
- "In the future, iteration will yield str names; for now, use .names "
- "to do the same without triggering this warning."
- ),
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler."),
- )
- return super().__iter__()
-
- def __eq__(self, other: Any) -> bool:
- # TODO: Remove on DM-41326.
- warnings.warn(
- _NVAS_DEPRECATION_MSG
- + (
- "In the future, set-equality will assume str keys; for now, use .names "
- "to do the same without triggering this warning."
- ),
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler."),
- )
- return super().__eq__(other)
-
- def __le__(self, other: Set[Any]) -> bool:
- # TODO: Remove on DM-41326.
- warnings.warn(
- _NVAS_DEPRECATION_MSG
- + (
- "In the future, subset tests will assume str keys; for now, use .names "
- "to do the same without triggering this warning."
- ),
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler."),
- )
- return super().__le__(other)
-
- def __ge__(self, other: Set[Any]) -> bool:
- # TODO: Remove on DM-41326.
- warnings.warn(
- _NVAS_DEPRECATION_MSG
- + (
- "In the future, superset tests will assume str keys; for now, use .names "
- "to do the same without triggering this warning."
- ),
- category=FutureWarning,
- stacklevel=find_outside_stacklevel("lsst.daf.butler."),
- )
- return super().__ge__(other)
-
-
-# TODO: Remove on DM-41326.
-@deprecated(
- "DimensionGraph is deprecated in favor of DimensionGroup and will be removed after v27.",
- category=FutureWarning,
- version="v27",
-)
-@immutable
-class DimensionGraph: # numpydoc ignore=PR02
- """An immutable, dependency-complete collection of dimensions.
-
- `DimensionGraph` is deprecated in favor of `DimensionGroup` and will be
- removed after v27. The two types have very similar interfaces, but
- `DimensionGroup` does not support direct iteration and its set-like
- attributes are of dimension element names, not `DimensionElement`
- instances. `DimensionGraph` objects are still returned by certain
- non-deprecated methods and properties (most prominently
- `DatasetType.dimensions`), and to handle these cases deprecation warnings
- are only emitted for operations on `DimensionGraph` that are not
- supported by `DimensionGroup` as well.
-
- Parameters
- ----------
- universe : `DimensionUniverse`
- The special graph of all known dimensions of which this graph will be a
- subset.
- dimensions : iterable of `Dimension`, optional
- An iterable of `Dimension` instances that must be included in the
- graph. All (recursive) dependencies of these dimensions will also be
- included. At most one of ``dimensions`` and ``names`` must be
- provided.
- names : iterable of `str`, optional
- An iterable of the names of dimensions that must be included in the
- graph. All (recursive) dependencies of these dimensions will also be
- included. At most one of ``dimensions`` and ``names`` must be
- provided.
- conform : `bool`, optional
- If `True` (default), expand to include dependencies. `False` should
- only be used for callers that can guarantee that other arguments are
- already correctly expanded, and is primarily for internal use.
-
- Notes
- -----
- `DimensionGraph` should be used instead of other collections in most
- contexts where a collection of dimensions is required and a
- `DimensionUniverse` is available. Exceptions include cases where order
- matters (and is different from the consistent ordering defined by the
- `DimensionUniverse`), or complete `~collection.abc.Set` semantics are
- required.
- """
-
- _serializedType: ClassVar[type[pydantic.BaseModel]] = SerializedDimensionGraph
-
- def __new__(
- cls,
- universe: DimensionUniverse,
- dimensions: Iterable[Dimension] | None = None,
- names: Iterable[str] | None = None,
- conform: bool = True,
- ) -> DimensionGraph:
- if names is None:
- if dimensions is None:
- group = DimensionGroup(universe)
- else:
- group = DimensionGroup(universe, {d.name for d in dimensions}, _conform=conform)
- else:
- if dimensions is not None:
- raise TypeError("Only one of 'dimensions' and 'names' may be provided.")
- group = DimensionGroup(universe, names, _conform=conform)
- return group._as_graph()
-
- @property
- def universe(self) -> DimensionUniverse:
- """Object that manages all known dimensions."""
- return self._group.universe
-
- @property
- @deprecated(
- _NVAS_DEPRECATION_MSG + "Use '.names' instead of '.dimensions' or '.dimensions.names'.",
- version="v27",
- category=FutureWarning,
- )
- @cached_getter
- def dimensions(self) -> NamedValueAbstractSet[Dimension]:
- """A true `~collections.abc.Set` of all true `Dimension` instances in
- the graph.
- """
- return _DimensionGraphNamedValueSet(self._group.names, self._group.universe)
-
- @property
- @cached_getter
- def elements(self) -> NamedValueAbstractSet[DimensionElement]:
- """A true `~collections.abc.Set` of all `DimensionElement` instances in
- the graph; a superset of `dimensions` (`NamedValueAbstractSet` of
- `DimensionElement`).
- """
- return _DimensionGraphNamedValueSet(self._group.elements, self._group.universe)
-
- @property
- @cached_getter
- def governors(self) -> NamedValueAbstractSet[GovernorDimension]:
- """A true `~collections.abc.Set` of all `GovernorDimension` instances
- in the graph.
- """
- return _DimensionGraphNamedValueSet(self._group.governors, self._group.universe)
-
- @property
- @cached_getter
- def skypix(self) -> NamedValueAbstractSet[SkyPixDimension]:
- """A true `~collections.abc.Set` of all `SkyPixDimension` instances
- in the graph.
- """
- return _DimensionGraphNamedValueSet(self._group.skypix, self._group.universe)
-
- @property
- @cached_getter
- def required(self) -> NamedValueAbstractSet[Dimension]:
- """The subset of `dimensions` whose elements must be directly
- identified via their primary keys in a data ID in order to identify the
- rest of the elements in the graph.
- """
- return _DimensionGraphNamedValueSet(self._group.required, self._group.universe)
-
- @property
- @cached_getter
- def implied(self) -> NamedValueAbstractSet[Dimension]:
- """The subset of `dimensions` whose elements need not be directly
- identified via their primary keys in a data ID.
- """
- return _DimensionGraphNamedValueSet(self._group.implied, self._group.universe)
-
- def __getnewargs__(self) -> tuple:
- return (self.universe, None, tuple(self._group.names), False)
-
- def __deepcopy__(self, memo: dict) -> DimensionGraph:
- # DimensionGraph is recursively immutable; see note in @immutable
- # decorator.
- return self
-
- @property
- def names(self) -> Set[str]:
- """Set of the names of all dimensions in the graph."""
- return self._group.names
-
- def to_simple(self, minimal: bool = False) -> SerializedDimensionGraph:
- """Convert this class to a simple python type.
-
- This type is suitable for serialization.
-
- Parameters
- ----------
- minimal : `bool`, optional
- Use minimal serialization. Has no effect on for this class.
-
- Returns
- -------
- names : `list`
- The names of the dimensions.
- """
- # Names are all we can serialize.
- return SerializedDimensionGraph(names=list(self.names))
-
- @classmethod
- def from_simple(
- cls,
- names: SerializedDimensionGraph,
- universe: DimensionUniverse | None = None,
- registry: Registry | None = None,
- ) -> DimensionGraph:
- """Construct a new object from the simplified form.
-
- This is assumed to support data data returned from the `to_simple`
- method.
-
- Parameters
- ----------
- names : `list` of `str`
- The names of the dimensions.
- universe : `DimensionUniverse`
- The special graph of all known dimensions of which this graph will
- be a subset. Can be `None` if `Registry` is provided.
- registry : `lsst.daf.butler.Registry`, optional
- Registry from which a universe can be extracted. Can be `None`
- if universe is provided explicitly.
-
- Returns
- -------
- graph : `DimensionGraph`
- Newly-constructed object.
- """
- if universe is None and registry is None:
- raise ValueError("One of universe or registry is required to convert names to a DimensionGraph")
- if universe is None and registry is not None:
- universe = registry.dimensions
- if universe is None:
- # this is for mypy
- raise ValueError("Unable to determine a usable universe")
-
- return cls(names=names.names, universe=universe)
-
- to_json = to_json_pydantic
- from_json: ClassVar = classmethod(from_json_pydantic)
-
- def __iter__(self) -> Iterator[Dimension]:
- """Iterate over all dimensions in the graph.
-
- (and true `Dimension` instances only).
- """
- return iter(self.dimensions)
-
- def __len__(self) -> int:
- """Return the number of dimensions in the graph.
-
- (and true `Dimension` instances only).
- """
- return len(self._group)
-
- def __contains__(self, element: str | DimensionElement) -> bool:
- """Return `True` if the given element or element name is in the graph.
-
- This test covers all `DimensionElement` instances in ``self.elements``,
- not just true `Dimension` instances).
- """
- return element in self.elements
-
- def __getitem__(self, name: str) -> DimensionElement:
- """Return the element with the given name.
-
- This lookup covers all `DimensionElement` instances in
- ``self.elements``, not just true `Dimension` instances).
- """
- return self.elements[name]
-
- def get(self, name: str, default: Any = None) -> DimensionElement:
- """Return the element with the given name.
-
- This lookup covers all `DimensionElement` instances in
- ``self.elements``, not just true `Dimension` instances).
-
- Parameters
- ----------
- name : `str`
- Name of element to return.
- default : `typing.Any` or `None`
- Default value if named element is not present.
-
- Returns
- -------
- element : `DimensionElement` or `None`
- The element found, or the default.
- """
- return self.elements.get(name, default)
-
- def __str__(self) -> str:
- return str(self.as_group())
-
- def __repr__(self) -> str:
- return f"DimensionGraph({str(self)})"
-
- def as_group(self) -> DimensionGroup:
- """Return a `DimensionGroup` that represents the same set of
- dimensions.
-
- Returns
- -------
- group : `DimensionGroup`
- Group that represents the same set of dimensions.
- """
- return self._group
-
- def isdisjoint(self, other: DimensionGroup | DimensionGraph) -> bool:
- """Test whether the intersection of two graphs is empty.
-
- Parameters
- ----------
- other : `DimensionGroup` or `DimensionGraph`
- Other graph to compare with.
-
- Returns
- -------
- is_disjoint : `bool`
- Returns `True` if either operand is the empty.
- """
- return self._group.isdisjoint(other.as_group())
-
- def issubset(self, other: DimensionGroup | DimensionGraph) -> bool:
- """Test whether all dimensions in ``self`` are also in ``other``.
-
- Parameters
- ----------
- other : `DimensionGroup` or `DimensionGraph`
- Other graph to compare with.
-
- Returns
- -------
- is_subset : `bool`
- Returns `True` if ``self`` is empty.
- """
- return self._group <= other.as_group()
-
- def issuperset(self, other: DimensionGroup | DimensionGraph) -> bool:
- """Test whether all dimensions in ``other`` are also in ``self``.
-
- Parameters
- ----------
- other : `DimensionGroup` or `DimensionGraph`
- Other graph to compare with.
-
- Returns
- -------
- is_superset : `bool`
- Returns `True` if ``other`` is empty.
- """
- return self._group >= other.as_group()
-
- def __eq__(self, other: Any) -> bool:
- """Test the arguments have exactly the same dimensions & elements."""
- if isinstance(other, DimensionGraph | DimensionGroup):
- return self._group == other.as_group()
- return False
-
- def __hash__(self) -> int:
- return hash(self.as_group())
-
- def __le__(self, other: DimensionGroup | DimensionGraph) -> bool:
- """Test whether ``self`` is a subset of ``other``."""
- return self._group <= other.as_group()
-
- def __ge__(self, other: DimensionGroup | DimensionGraph) -> bool:
- """Test whether ``self`` is a superset of ``other``."""
- return self._group >= other.as_group()
-
- def __lt__(self, other: DimensionGroup | DimensionGraph) -> bool:
- """Test whether ``self`` is a strict subset of ``other``."""
- return self._group < other.as_group()
-
- def __gt__(self, other: DimensionGroup | DimensionGraph) -> bool:
- """Test whether ``self`` is a strict superset of ``other``."""
- return self._group > other.as_group()
-
- def union(self, *others: DimensionGroup | DimensionGraph) -> DimensionGraph:
- """Construct a new graph with all dimensions in any of the operands.
-
- Parameters
- ----------
- *others : `DimensionGroup` or `DimensionGraph`
- Other graphs to join with.
-
- Returns
- -------
- union : `DimensionGraph`
- The union of this graph wit hall the others.
-
- Notes
- -----
- The elements of the returned graph may exceed the naive union of
- their elements, as some `DimensionElement` instances are included
- in graphs whenever multiple dimensions are present, and those
- dependency dimensions could have been provided by different operands.
- """
- names = set(self.names).union(*[other.names for other in others])
- return self.universe.conform(names)._as_graph()
-
- def intersection(self, *others: DimensionGroup | DimensionGraph) -> DimensionGraph:
- """Construct a new graph with only dimensions in all of the operands.
-
- Parameters
- ----------
- *others : `DimensionGroup` or `DimensionGraph`
- Other graphs to use.
-
- Returns
- -------
- inter : `DimensionGraph`
- Intersection of all the graphs.
-
- Notes
- -----
- See also `union`.
- """
- names = set(self.names).intersection(*[other.names for other in others])
- return self.universe.conform(names)._as_graph()
-
- def __or__(self, other: DimensionGroup | DimensionGraph) -> DimensionGraph:
- """Construct a new graph with all dimensions in any of the operands.
-
- See `union`.
- """
- return self.union(other)
-
- def __and__(self, other: DimensionGroup | DimensionGraph) -> DimensionGraph:
- """Construct a new graph with only dimensions in all of the operands.
-
- See `intersection`.
- """
- return self.intersection(other)
-
- # TODO: Remove on DM-41326.
- @property
- @deprecated(
- "DimensionGraph is deprecated in favor of DimensionGroup, which does not have this attribute; "
- "use .lookup_order. DimensionGraph will be removed after v27.",
- category=FutureWarning,
- version="v27",
- )
- def primaryKeyTraversalOrder(self) -> tuple[DimensionElement, ...]:
- """A tuple of all elements in specific order.
-
- The order allows records to be found given their primary keys, starting
- from only the primary keys of required dimensions (`tuple` [
- `DimensionRecord` ]).
-
- Unlike the table definition/topological order (which is what
- DimensionUniverse.sorted gives you), when dimension A implies dimension
- B, dimension A appears first.
- """
- return tuple(self.universe[element_name] for element_name in self._group.lookup_order)
-
- @property
- def spatial(self) -> NamedValueAbstractSet[TopologicalFamily]:
- """Families represented by the spatial elements in this graph."""
- return self._group.spatial
-
- @property
- def temporal(self) -> NamedValueAbstractSet[TopologicalFamily]:
- """Families represented by the temporal elements in this graph."""
- return self._group.temporal
-
- # TODO: Remove on DM-41326.
- @property
- @deprecated(
- "DimensionGraph is deprecated in favor of DimensionGroup, which does not have this attribute; "
- "use .spatial or .temporal. DimensionGraph will be removed after v27.",
- category=FutureWarning,
- version="v27",
- )
- def topology(self) -> Mapping[TopologicalSpace, NamedValueAbstractSet[TopologicalFamily]]:
- """Families of elements in this graph that can participate in
- topological relationships.
- """
- return self._group._space_families
-
- _group: DimensionGroup
diff --git a/python/lsst/daf/butler/dimensions/_group.py b/python/lsst/daf/butler/dimensions/_group.py
index 52c68a856c..eee6d9cec0 100644
--- a/python/lsst/daf/butler/dimensions/_group.py
+++ b/python/lsst/daf/butler/dimensions/_group.py
@@ -35,6 +35,7 @@
from typing import TYPE_CHECKING, Any, TypeAlias
import pydantic
+from deprecated.sphinx import deprecated
from lsst.utils.classes import cached_getter, immutable
from pydantic_core import core_schema
@@ -44,7 +45,6 @@
if TYPE_CHECKING: # Imports needed only for type annotations; may be circular.
from ._elements import DimensionElement
- from ._graph import DimensionGraph
from ._universe import DimensionUniverse
@@ -102,14 +102,20 @@ def as_tuple(self) -> tuple[str, ...]:
"""
return self._seq
+ # TODO: remove on DM-45185
@property
+ @deprecated(
+ "Deprecated in favor of direct iteration over the parent set. Will be removed after v28.",
+ version="v28",
+ category=FutureWarning,
+ )
def names(self) -> Set[str]:
"""An alias to ``self``.
- This is a backwards-compatibility API that allows `DimensionGroup`
- to mimic the `DimensionGraph` object it is intended to replace, by
- permitting expressions like ``x.required.names`` when ``x`` can be
- an object of either type.
+ This is a backwards-compatibility API that allows `DimensionGroup` to
+ mimic the old ``DimensionGraph`` object it replaced, by permitting
+ expressions like ``x.required.names`` when ``x`` can be an object of
+ either type.
"""
return self
@@ -266,6 +272,12 @@ def __str__(self) -> str:
def __repr__(self) -> str:
return f"DimensionGroup({self.names})"
+ # TODO: remove on DM-45185
+ @deprecated(
+ "Deprecated as no longer necessary (this method always returns 'self'). Will be removed after v28.",
+ version="v28",
+ category=FutureWarning,
+ )
def as_group(self) -> DimensionGroup:
"""Return ``self``.
@@ -276,33 +288,12 @@ def as_group(self) -> DimensionGroup:
Notes
-----
- This is a backwards-compatibility API that allows both `DimensionGraph`
- and `DimensionGroup` to be coerced to the latter.
+ This is a backwards-compatibility API that allowed both the old
+ ``DimensionGraph`` class and `DimensionGroup` to be coerced to the
+ latter.
"""
return self
- @cached_getter
- def _as_graph(self) -> DimensionGraph:
- """Return a view of ``self`` as a `DimensionGraph`.
-
- Returns
- -------
- graph : `DimensionGraph`
- The deprecated form of `DimensionGroup`.
-
- Notes
- -----
- This is provided as a convenience for methods and properties that must
- return a `DimensionGraph` for backwards compatibility (until v27). It
- is the only way of making a `DimensionGraph` that does not produce
- a warning.
- """
- from ._graph import DimensionGraph
-
- result = object.__new__(DimensionGraph)
- result._group = self
- return result
-
def isdisjoint(self, other: DimensionGroup) -> bool:
"""Test whether the intersection of two groups is empty.
@@ -349,10 +340,7 @@ def issuperset(self, other: DimensionGroup) -> bool:
return self.names >= other.names
def __eq__(self, other: Any) -> bool:
- from ._graph import DimensionGraph
-
- # TODO: Drop DimensionGraph support here on DM-41326.
- if isinstance(other, DimensionGroup | DimensionGraph):
+ if isinstance(other, DimensionGroup):
return self.names == other.names
else:
return False
diff --git a/python/lsst/daf/butler/dimensions/_packer.py b/python/lsst/daf/butler/dimensions/_packer.py
index 4c4cc32c62..e3ee596e37 100644
--- a/python/lsst/daf/butler/dimensions/_packer.py
+++ b/python/lsst/daf/butler/dimensions/_packer.py
@@ -33,7 +33,7 @@
from typing import TYPE_CHECKING, Any
from ._coordinate import DataCoordinate, DataId
-from ._graph import DimensionGraph, DimensionGroup
+from ._group import DimensionGroup
if TYPE_CHECKING: # Imports needed only for type annotations; may be circular.
from ._universe import DimensionUniverse
@@ -52,12 +52,11 @@ class DimensionPacker(metaclass=ABCMeta):
(to these values) in all calls to `pack`, and are used in the results
of calls to `unpack`. Subclasses may ignore particular dimensions, and
are permitted to require that ``fixed.hasRecords()`` return `True`.
- dimensions : `DimensionGroup` or `DimensionGraph`
- The dimensions of data IDs packed by this instance. Only
- `DimensionGroup` will be supported after v27.
+ dimensions : `DimensionGroup`
+ The dimensions of data IDs packed by this instance.
"""
- def __init__(self, fixed: DataCoordinate, dimensions: DimensionGroup | DimensionGraph):
+ def __init__(self, fixed: DataCoordinate, dimensions: DimensionGroup):
self.fixed = fixed
self._dimensions = self.fixed.universe.conform(dimensions)
@@ -67,13 +66,11 @@ def universe(self) -> DimensionUniverse:
return self.fixed.universe
@property
- def dimensions(self) -> DimensionGraph:
+ def dimensions(self) -> DimensionGroup:
"""The dimensions of data IDs packed by this instance
- (`DimensionGraph`).
-
- After v27 this will be a `DimensionGroup`.
+ (`DimensionGroup`).
"""
- return self._dimensions._as_graph()
+ return self._dimensions
@property
@abstractmethod
diff --git a/python/lsst/daf/butler/dimensions/_records.py b/python/lsst/daf/butler/dimensions/_records.py
index 25b8fb183b..278fcb14b5 100644
--- a/python/lsst/daf/butler/dimensions/_records.py
+++ b/python/lsst/daf/butler/dimensions/_records.py
@@ -428,7 +428,7 @@ def from_simple(
Newly-constructed object.
"""
if universe is None and registry is None:
- raise ValueError("One of universe or registry is required to convert names to a DimensionGraph")
+ raise ValueError("One of universe or registry is required to convert names to a DimensionGroup")
if universe is None and registry is not None:
universe = registry.dimensions
if universe is None:
diff --git a/python/lsst/daf/butler/dimensions/_universe.py b/python/lsst/daf/butler/dimensions/_universe.py
index 64fc1efb41..5b08a6b881 100644
--- a/python/lsst/daf/butler/dimensions/_universe.py
+++ b/python/lsst/daf/butler/dimensions/_universe.py
@@ -30,13 +30,11 @@
__all__ = ["DimensionUniverse"]
import logging
-import math
import pickle
from collections import defaultdict
from collections.abc import Iterable, Mapping, Sequence
-from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, cast, overload
+from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, overload
-from deprecated.sphinx import deprecated
from lsst.utils.classes import cached_getter, immutable
from .._config import Config
@@ -47,7 +45,6 @@
from ._database import DatabaseDimensionElement
from ._elements import Dimension, DimensionElement
from ._governor import GovernorDimension
-from ._graph import DimensionGraph
from ._group import DimensionGroup
from ._skypix import SkyPixDimension, SkyPixSystem
@@ -424,91 +421,19 @@ def getDimensionIndex(self, name: str) -> int:
"""
return self._dimensionIndices[name]
- # TODO: remove on DM-41326.
- @deprecated(
- "Deprecated in favor of DimensionUniverse.conform, and will be removed after v27.",
- version="v27",
- category=FutureWarning,
- )
- def expandDimensionNameSet(self, names: set[str]) -> None:
- """Expand a set of dimension names in-place.
-
- Includes recursive dependencies.
-
- This is an advanced interface for cases where constructing a
- `DimensionGraph` (which also expands required dependencies) is
- impossible or undesirable.
-
- Parameters
- ----------
- names : `set` [ `str` ]
- A true `set` of dimension names, to be expanded in-place.
- """
- # Keep iterating until the set of names stops growing. This is not as
- # efficient as it could be, but we work pretty hard cache
- # DimensionGraph instances to keep actual construction rare, so that
- # shouldn't matter.
- oldSize = len(names)
- while True:
- # iterate over a temporary copy so we can modify the original
- for name in tuple(names):
- names.update(self._dimensions[name].required.names)
- names.update(self._dimensions[name].implied.names)
- if oldSize == len(names):
- break
- else:
- oldSize = len(names)
-
- # TODO: remove on DM-41326.
- @deprecated(
- "DimensionUniverse.extract and DimensionGraph are deprecated in favor of DimensionUniverse.conform "
- "and DimensionGroup, and will be removed after v27.",
- version="v27",
- category=FutureWarning,
- )
- def extract(self, iterable: Iterable[Dimension | str]) -> DimensionGraph:
- """Construct graph from iterable.
-
- Constructs a `DimensionGraph` from a possibly-heterogenous iterable
- of `Dimension` instances and string names thereof.
-
- Constructing `DimensionGraph` directly from names or dimension
- instances is slightly more efficient when it is known in advance that
- the iterable is not heterogenous.
-
- Parameters
- ----------
- iterable : iterable of `Dimension` or `str`
- Dimensions that must be included in the returned graph (their
- dependencies will be as well).
-
- Returns
- -------
- graph : `DimensionGraph`
- A `DimensionGraph` instance containing all given dimensions.
- """
- return self.conform(iterable)._as_graph()
-
def conform(
self,
- dimensions: Iterable[str | Dimension] | str | DimensionElement | DimensionGroup | DimensionGraph,
+ dimensions: Iterable[str] | str | DimensionGroup,
/,
) -> DimensionGroup:
"""Construct a dimension group from an iterable of dimension names.
Parameters
----------
- dimensions : `~collections.abc.Iterable` [ `str` or `Dimension` ], \
- `str`, `DimensionElement`, `DimensionGroup`, or \
- `DimensionGraph`
+ dimensions : `~collections.abc.Iterable` [ `str` ], `str`, or \
+ `DimensionGroup`
Dimensions that must be included in the returned group; their
- dependencies will be as well. Support for `Dimension`,
- `DimensionElement` and `DimensionGraph` objects is deprecated and
- will be removed after v27. Passing `DimensionGraph` objects will
- not yield a deprecation warning to allow non-deprecated methods and
- properties that return `DimensionGraph` objects to be passed
- though, since these will be changed to return `DimensionGroup` in
- the future.
+ dependencies will be as well.
Returns
-------
@@ -518,15 +443,10 @@ def conform(
match dimensions:
case DimensionGroup():
return dimensions
- case DimensionGraph():
- return dimensions.as_group()
- case DimensionElement() as d:
- return d.minimal_group
case str() as name:
return self[name].minimal_group
case iterable:
- names: set[str] = {getattr(d, "name", cast(str, d)) for d in iterable}
- return DimensionGroup(self, names)
+ return DimensionGroup(self, set(iterable))
@overload
def sorted(self, elements: Iterable[Dimension], *, reverse: bool = False) -> Sequence[Dimension]: ...
@@ -562,17 +482,6 @@ def sorted(self, elements: Iterable[Any], *, reverse: bool = False) -> list[Any]
result.reverse()
return result
- def getEncodeLength(self) -> int:
- """Return encoded size of graph.
-
- Returns the size (in bytes) of the encoded size of `DimensionGraph`
- instances in this universe.
-
- See `DimensionGraph.encode` and `DimensionGraph.decode` for more
- information.
- """
- return math.ceil(len(self._dimensions) / 8)
-
def get_elements_populated_by(self, dimension: Dimension) -> NamedValueAbstractSet[DimensionElement]:
"""Return the set of `DimensionElement` objects whose
`~DimensionElement.populated_by` attribute is the given dimension.
@@ -591,12 +500,9 @@ def get_elements_populated_by(self, dimension: Dimension) -> NamedValueAbstractS
return self._populates[dimension.name]
@property
- def empty(self) -> DimensionGraph:
- """The `DimensionGraph` that contains no dimensions.
-
- After v27 this will be a `DimensionGroup`.
- """
- return self._empty._as_graph()
+ def empty(self) -> DimensionGroup:
+ """The `DimensionGroup` that contains no dimensions."""
+ return self._empty
@classmethod
def _unpickle(cls, version: int, namespace: str | None = None) -> DimensionUniverse:
diff --git a/python/lsst/daf/butler/direct_butler/_direct_butler.py b/python/lsst/daf/butler/direct_butler/_direct_butler.py
index a71865aed4..68ddec7932 100644
--- a/python/lsst/daf/butler/direct_butler/_direct_butler.py
+++ b/python/lsst/daf/butler/direct_butler/_direct_butler.py
@@ -25,8 +25,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""Butler top level classes.
-"""
+"""Butler top level classes."""
+
from __future__ import annotations
__all__ = (
@@ -460,28 +460,27 @@ def _rewrite_data_id(
**kwargs : `dict`
Any unused keyword arguments (would normally be empty dict).
"""
- # Do nothing if we have a standalone DataCoordinate.
- if isinstance(dataId, DataCoordinate) and not kwargs:
- return dataId, kwargs
-
# Process dimension records that are using record information
# rather than ids
newDataId: dict[str, DataIdValue] = {}
byRecord: dict[str, dict[str, Any]] = defaultdict(dict)
- # if all the dataId comes from keyword parameters we do not need
- # to do anything here because they can't be of the form
- # exposure.obs_id because a "." is not allowed in a keyword parameter.
- if dataId:
+ if isinstance(dataId, DataCoordinate):
+ # Do nothing if we have a DataCoordinate and no kwargs.
+ if not kwargs:
+ return dataId, kwargs
+ # If we have a DataCoordinate with kwargs, we know the
+ # DataCoordinate only has values for real dimensions.
+ newDataId.update(dataId.mapping)
+ elif dataId:
+ # The data is mapping, which means it might have keys like
+ # "exposure.obs_id" (unlike kwargs, because a "." is not allowed in
+ # a keyword parameter).
for k, v in dataId.items():
- # If we have a Dimension we do not need to do anything
- # because it cannot be a compound key.
if isinstance(k, str) and "." in k:
# Someone is using a more human-readable dataId
dimensionName, record = k.split(".", 1)
byRecord[dimensionName][record] = v
- elif isinstance(k, Dimension):
- newDataId[k.name] = v
else:
newDataId[k] = v
diff --git a/python/lsst/daf/butler/direct_query_driver/_driver.py b/python/lsst/daf/butler/direct_query_driver/_driver.py
index d25aa6b610..9ad5c9bf4d 100644
--- a/python/lsst/daf/butler/direct_query_driver/_driver.py
+++ b/python/lsst/daf/butler/direct_query_driver/_driver.py
@@ -371,7 +371,7 @@ def count(
# count deduplicated rows.
builder = builder.nested()
# Replace the columns of the query with just COUNT(*).
- builder.columns = qt.ColumnSet(self._universe.empty.as_group())
+ builder.columns = qt.ColumnSet(self._universe.empty)
count_func: sqlalchemy.ColumnElement[int] = sqlalchemy.func.count()
builder.joiner.special["_ROWCOUNT"] = count_func
# Render and run the query.
@@ -894,7 +894,7 @@ def _analyze_query_tree(self, tree: qt.QueryTree) -> tuple[QueryJoinsPlan, Query
# without constraining their governor dimensions, since that's a
# particularly easy mistake to make and it's almost never intentional.
# We also allow the registry data ID values to provide governor values.
- where_columns = qt.ColumnSet(self.universe.empty.as_group())
+ where_columns = qt.ColumnSet(self.universe.empty)
result.predicate.gather_required_columns(where_columns)
for governor in where_columns.dimensions.governors:
if governor not in result.constraint_data_id:
@@ -1048,13 +1048,13 @@ def _resolve_dataset_search(
if collection_record.type is CollectionType.CALIBRATION:
result.is_calibration_search = True
result.collection_records.append(collection_record)
- if result.dimensions != self.get_dataset_type(dataset_type_name).dimensions.as_group():
+ if result.dimensions != self.get_dataset_type(dataset_type_name).dimensions:
# This is really for server-side defensiveness; it's hard to
# imagine the query getting different dimensions for a dataset
# type in two calls to the same query driver.
raise InvalidQueryError(
f"Incorrect dimensions {result.dimensions} for dataset {dataset_type_name} "
- f"in query (vs. {self.get_dataset_type(dataset_type_name).dimensions.as_group()})."
+ f"in query (vs. {self.get_dataset_type(dataset_type_name).dimensions})."
)
return result
diff --git a/python/lsst/daf/butler/direct_query_driver/_sql_column_visitor.py b/python/lsst/daf/butler/direct_query_driver/_sql_column_visitor.py
index 42d25613c6..987e5bc0bb 100644
--- a/python/lsst/daf/butler/direct_query_driver/_sql_column_visitor.py
+++ b/python/lsst/daf/butler/direct_query_driver/_sql_column_visitor.py
@@ -226,7 +226,7 @@ def visit_in_query_tree(
flags: PredicateVisitFlags,
) -> sqlalchemy.ColumnElement[bool]:
# Docstring inherited.
- columns = qt.ColumnSet(self._driver.universe.empty.as_group())
+ columns = qt.ColumnSet(self._driver.universe.empty)
column.gather_required_columns(columns)
_, builder = self._driver.build_query(query_tree, columns)
if builder.postprocessing:
@@ -235,7 +235,7 @@ def visit_in_query_tree(
)
subquery_visitor = SqlColumnVisitor(builder.joiner, self._driver)
builder.joiner.special["_MEMBER"] = subquery_visitor.expect_scalar(column)
- builder.columns = qt.ColumnSet(self._driver.universe.empty.as_group())
+ builder.columns = qt.ColumnSet(self._driver.universe.empty)
subquery_select = builder.select()
sql_member = self.expect_scalar(member)
return sql_member.in_(subquery_select)
diff --git a/python/lsst/daf/butler/queries/_dataset_query_results.py b/python/lsst/daf/butler/queries/_dataset_query_results.py
index 5dc379a671..462f328ebc 100644
--- a/python/lsst/daf/butler/queries/_dataset_query_results.py
+++ b/python/lsst/daf/butler/queries/_dataset_query_results.py
@@ -94,7 +94,7 @@ def data_ids(self) -> DataCoordinateQueryResults:
self._driver,
tree=self._tree,
spec=DataCoordinateResultSpec.model_construct(
- dimensions=self.dataset_type.dimensions.as_group(),
+ dimensions=self.dataset_type.dimensions,
include_dimension_records=self._spec.include_dimension_records,
),
)
diff --git a/python/lsst/daf/butler/queries/_query.py b/python/lsst/daf/butler/queries/_query.py
index 517c094a6c..dedb8ee36e 100644
--- a/python/lsst/daf/butler/queries/_query.py
+++ b/python/lsst/daf/butler/queries/_query.py
@@ -520,7 +520,7 @@ def _join_dataset_search_impl(
# Handle DatasetType vs. str arg.
if isinstance(dataset_type, DatasetType):
dataset_type_name = dataset_type.name
- dimensions = dataset_type.dimensions.as_group()
+ dimensions = dataset_type.dimensions
storage_class_name = dataset_type.storageClass_name
elif isinstance(dataset_type, str):
dataset_type_name = dataset_type
@@ -548,7 +548,7 @@ def _join_dataset_search_impl(
# for consistency, or get dimensions and storage class if we don't have
# them.
resolved_dataset_type = self._driver.get_dataset_type(dataset_type_name)
- resolved_dimensions = resolved_dataset_type.dimensions.as_group()
+ resolved_dimensions = resolved_dataset_type.dimensions
if dimensions is not None and dimensions != resolved_dimensions:
raise DatasetTypeError(
f"Given dimensions {dimensions} for dataset type {dataset_type_name!r} do not match the "
diff --git a/python/lsst/daf/butler/queries/tree/_query_tree.py b/python/lsst/daf/butler/queries/tree/_query_tree.py
index 5d39e1aca4..61e4e65a45 100644
--- a/python/lsst/daf/butler/queries/tree/_query_tree.py
+++ b/python/lsst/daf/butler/queries/tree/_query_tree.py
@@ -73,7 +73,7 @@ def make_identity_query_tree(universe: DimensionUniverse) -> QueryTree:
tree : `QueryTree`
A tree with empty dimensions.
"""
- return QueryTree(dimensions=universe.empty.as_group())
+ return QueryTree(dimensions=universe.empty)
@final
diff --git a/python/lsst/daf/butler/registry/_registry.py b/python/lsst/daf/butler/registry/_registry.py
index ff6f5b355e..f934abc898 100644
--- a/python/lsst/daf/butler/registry/_registry.py
+++ b/python/lsst/daf/butler/registry/_registry.py
@@ -40,15 +40,12 @@
from .._dataset_association import DatasetAssociation
from .._dataset_ref import DatasetId, DatasetIdGenEnum, DatasetRef
from .._dataset_type import DatasetType
-from .._named import NameLookupMapping
from .._storage_class import StorageClassFactory
from .._timespan import Timespan
from ..dimensions import (
DataCoordinate,
DataId,
- Dimension,
DimensionElement,
- DimensionGraph,
DimensionGroup,
DimensionRecord,
DimensionUniverse,
@@ -881,9 +878,8 @@ def expandDataId(
self,
dataId: DataId | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
- graph: DimensionGraph | None = None,
- records: NameLookupMapping[DimensionElement, DimensionRecord | None] | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
+ records: Mapping[str, DimensionRecord | None] | None = None,
withDefaults: bool = True,
**kwargs: Any,
) -> DataCoordinate:
@@ -893,16 +889,12 @@ def expandDataId(
----------
dataId : `DataCoordinate` or `dict`, optional
Data ID to be expanded; augmented and overridden by ``kwargs``.
- dimensions : `~collections.abc.Iterable` [ `str` ], \
- `DimensionGroup`, or `DimensionGraph`, optional
+ dimensions : `~collections.abc.Iterable` [ `str` ] or \
+ `DimensionGroup` optional
The dimensions to be identified by the new `DataCoordinate`.
- If not provided, will be inferred from the keys of ``mapping`` and
- ``**kwargs``, and ``universe`` must be provided unless ``mapping``
+ If not provided, will be inferred from the keys of ``dataId`` and
+ ``**kwargs``, and ``universe`` must be provided unless ``dataId``
is already a `DataCoordinate`.
- graph : `DimensionGraph`, optional
- Like ``dimensions``, but as a ``DimensionGraph`` instance. Ignored
- if ``dimensions`` is provided. Deprecated and will be removed
- after v27.
records : `~collections.abc.Mapping` [`str`, `DimensionRecord`], \
optional
Dimension record data to use before querying the database for that
@@ -1118,7 +1110,7 @@ def queryDatasets(
datasetType: Any,
*,
collections: CollectionArgType | None = None,
- dimensions: Iterable[Dimension | str] | None = None,
+ dimensions: Iterable[str] | None = None,
dataId: DataId | None = None,
where: str = "",
findFirst: bool = False,
@@ -1146,7 +1138,7 @@ def queryDatasets(
collections, because this will still find all datasets).
If not provided, ``self.default.collections`` is used. See
:ref:`daf_butler_collection_expressions` for more information.
- dimensions : `~collections.abc.Iterable` of `Dimension` or `str`
+ dimensions : `~collections.abc.Iterable` [ `str` ]
Dimensions to include in the query (in addition to those used
to identify the queried dataset type(s)), either to constrain
the resulting datasets to those for which a matching dimension
@@ -1229,8 +1221,7 @@ def queryDatasets(
@abstractmethod
def queryDataIds(
self,
- # TODO: Drop `Dimension` objects on DM-41326.
- dimensions: DimensionGroup | Iterable[Dimension | str] | Dimension | str,
+ dimensions: DimensionGroup | Iterable[str] | str,
*,
dataId: DataId | None = None,
datasets: Any = None,
@@ -1245,12 +1236,10 @@ def queryDataIds(
Parameters
----------
- dimensions : `DimensionGroup`, `Dimension`, or `str`, or \
- `~collections.abc.Iterable` [ `Dimension` or `str` ]
- The dimensions of the data IDs to yield, as either `Dimension`
- instances or `str`. Will be automatically expanded to a complete
- `DimensionGroup`. Support for `Dimension` instances is deprecated
- and will not be supported after v27.
+ dimensions : `DimensionGroup`, `str`, or \
+ `~collections.abc.Iterable` [ `str` ]
+ The dimensions of the data IDs to yield. Will be automatically
+ expanded to a complete `DimensionGroup`.
dataId : `dict` or `DataCoordinate`, optional
A data ID whose key-value pairs are used as equality constraints
in the query.
diff --git a/python/lsst/daf/butler/registry/datasets/byDimensions/_manager.py b/python/lsst/daf/butler/registry/datasets/byDimensions/_manager.py
index dafe862a0b..909761ea19 100644
--- a/python/lsst/daf/butler/registry/datasets/byDimensions/_manager.py
+++ b/python/lsst/daf/butler/registry/datasets/byDimensions/_manager.py
@@ -322,7 +322,7 @@ def register(self, datasetType: DatasetType) -> bool:
)
record = self._fetch_dataset_type_record(datasetType.name)
if record is None:
- dimensionsKey = self._dimensions.save_dimension_group(datasetType.dimensions.as_group())
+ dimensionsKey = self._dimensions.save_dimension_group(datasetType.dimensions)
tagTableName = makeTagTableName(datasetType, dimensionsKey)
self._db.ensureTableExists(
tagTableName,
diff --git a/python/lsst/daf/butler/registry/datasets/byDimensions/_storage.py b/python/lsst/daf/butler/registry/datasets/byDimensions/_storage.py
index 0f7b2efba4..8d409d6921 100644
--- a/python/lsst/daf/butler/registry/datasets/byDimensions/_storage.py
+++ b/python/lsst/daf/butler/registry/datasets/byDimensions/_storage.py
@@ -197,9 +197,9 @@ def _buildCalibOverlapQuery(
)
if data_ids is not None:
relation = relation.join(
- context.make_data_id_relation(
- data_ids, self.datasetType.dimensions.required.names
- ).transferred_to(context.sql_engine),
+ context.make_data_id_relation(data_ids, self.datasetType.dimensions.required).transferred_to(
+ context.sql_engine
+ ),
)
return relation
@@ -314,9 +314,7 @@ def decertify(
calib_pkey_tag = DatasetColumnTag(self.datasetType.name, "calib_pkey")
dataset_id_tag = DatasetColumnTag(self.datasetType.name, "dataset_id")
timespan_tag = DatasetColumnTag(self.datasetType.name, "timespan")
- data_id_tags = [
- (name, DimensionKeyColumnTag(name)) for name in self.datasetType.dimensions.required.names
- ]
+ data_id_tags = [(name, DimensionKeyColumnTag(name)) for name in self.datasetType.dimensions.required]
# Set up collections to populate with the rows we'll want to modify.
# The insert rows will have the same values for collection and
# dataset type.
@@ -509,7 +507,7 @@ def _finish_single_relation(
value=collection_col,
)
# Add more column definitions, starting with the data ID.
- for dimension_name in self.datasetType.dimensions.required.names:
+ for dimension_name in self.datasetType.dimensions.required:
payload.columns_available[DimensionKeyColumnTag(dimension_name)] = payload.from_clause.columns[
dimension_name
]
@@ -596,7 +594,7 @@ def make_query_joiner(self, collections: Sequence[CollectionRecord], fields: Set
# using very important indexes. At present, we don't include those
# redundant columns in the JOIN ON expression, however, because the
# FOREIGN KEY (and its index) are defined only on dataset_id.
- columns = qt.ColumnSet(self.datasetType.dimensions.as_group())
+ columns = qt.ColumnSet(self.datasetType.dimensions)
columns.drop_implied_dimension_keys()
columns.dataset_fields[self.datasetType.name].update(fields)
tags_builder: QueryBuilder | None = None
@@ -692,7 +690,7 @@ def _finish_query_builder(
collections, collection_col
)
# Add more column definitions, starting with the data ID.
- sql_projection.joiner.extract_dimensions(self.datasetType.dimensions.required.names)
+ sql_projection.joiner.extract_dimensions(self.datasetType.dimensions.required)
# We can always get the dataset_id from the tags/calibs table, even if
# could also get it from the 'static' dataset table.
if "dataset_id" in fields:
@@ -793,8 +791,8 @@ def getDataId(self, id: DatasetId) -> DataCoordinate:
row = sql_result.mappings().fetchone()
assert row is not None, "Should be guaranteed by caller and foreign key constraints."
return DataCoordinate.from_required_values(
- self.datasetType.dimensions.as_group(),
- tuple(row[dimension] for dimension in self.datasetType.dimensions.required.names),
+ self.datasetType.dimensions,
+ tuple(row[dimension] for dimension in self.datasetType.dimensions.required),
)
def refresh_collection_summaries(self) -> None:
@@ -1062,11 +1060,8 @@ def _validateImport(self, tmp_tags: sqlalchemy.schema.Table, run: RunRecord) ->
tags.columns.dataset_id,
tags.columns.dataset_type_id.label("type_id"),
tmp_tags.columns.dataset_type_id.label("new_type_id"),
- *[tags.columns[dim] for dim in self.datasetType.dimensions.required.names],
- *[
- tmp_tags.columns[dim].label(f"new_{dim}")
- for dim in self.datasetType.dimensions.required.names
- ],
+ *[tags.columns[dim] for dim in self.datasetType.dimensions.required],
+ *[tmp_tags.columns[dim].label(f"new_{dim}") for dim in self.datasetType.dimensions.required],
)
.select_from(tags.join(tmp_tags, tags.columns.dataset_id == tmp_tags.columns.dataset_id))
.where(
@@ -1074,7 +1069,7 @@ def _validateImport(self, tmp_tags: sqlalchemy.schema.Table, run: RunRecord) ->
tags.columns.dataset_type_id != tmp_tags.columns.dataset_type_id,
*[
tags.columns[dim] != tmp_tags.columns[dim]
- for dim in self.datasetType.dimensions.required.names
+ for dim in self.datasetType.dimensions.required
],
)
)
@@ -1091,7 +1086,7 @@ def _validateImport(self, tmp_tags: sqlalchemy.schema.Table, run: RunRecord) ->
# Check that matching run+dataId have the same dataset ID.
query = (
sqlalchemy.sql.select(
- *[tags.columns[dim] for dim in self.datasetType.dimensions.required.names],
+ *[tags.columns[dim] for dim in self.datasetType.dimensions.required],
tags.columns.dataset_id,
tmp_tags.columns.dataset_id.label("new_dataset_id"),
tags.columns[collFkName],
@@ -1105,7 +1100,7 @@ def _validateImport(self, tmp_tags: sqlalchemy.schema.Table, run: RunRecord) ->
tags.columns[collFkName] == tmp_tags.columns[collFkName],
*[
tags.columns[dim] == tmp_tags.columns[dim]
- for dim in self.datasetType.dimensions.required.names
+ for dim in self.datasetType.dimensions.required
],
),
)
@@ -1116,7 +1111,7 @@ def _validateImport(self, tmp_tags: sqlalchemy.schema.Table, run: RunRecord) ->
with self._db.query(query) as result:
# only include the first one in the exception message
if (row := result.first()) is not None:
- data_id = {dim: getattr(row, dim) for dim in self.datasetType.dimensions.required.names}
+ data_id = {dim: getattr(row, dim) for dim in self.datasetType.dimensions.required}
existing_collection = self._collections[getattr(row, collFkName)].name
new_collection = self._collections[getattr(row, f"new_{collFkName}")].name
raise ConflictingDefinitionError(
diff --git a/python/lsst/daf/butler/registry/datasets/byDimensions/tables.py b/python/lsst/daf/butler/registry/datasets/byDimensions/tables.py
index 706fc78d54..5729bd2b80 100644
--- a/python/lsst/daf/butler/registry/datasets/byDimensions/tables.py
+++ b/python/lsst/daf/butler/registry/datasets/byDimensions/tables.py
@@ -353,7 +353,7 @@ def makeTagTableSpec(
target=(collectionFieldSpec.name, "dataset_type_id"),
)
)
- for dimension_name in datasetType.dimensions.required.names:
+ for dimension_name in datasetType.dimensions.required:
dimension = datasetType.dimensions.universe.dimensions[dimension_name]
fieldSpec = addDimensionForeignKey(
tableSpec, dimension=dimension, nullable=False, primaryKey=False, constraint=constraints
@@ -439,7 +439,7 @@ def makeCalibTableSpec(
)
)
# Add dimension fields (part of the temporal lookup index.constraint).
- for dimension_name in datasetType.dimensions.required.names:
+ for dimension_name in datasetType.dimensions.required:
dimension = datasetType.dimensions.universe.dimensions[dimension_name]
fieldSpec = addDimensionForeignKey(tableSpec, dimension=dimension, nullable=False, primaryKey=False)
index.append(fieldSpec.name)
diff --git a/python/lsst/daf/butler/registry/dimensions/static.py b/python/lsst/daf/butler/registry/dimensions/static.py
index ae449dd9dd..6e6178fa57 100644
--- a/python/lsst/daf/butler/registry/dimensions/static.py
+++ b/python/lsst/daf/butler/registry/dimensions/static.py
@@ -176,7 +176,7 @@ def initialize(
# dimensions. We've never used these and no longer plan to, but we
# have to keep creating them to keep schema versioning consistent.
cls._make_legacy_overlap_tables(context, spatial)
- # Create tables that store DimensionGraph definitions.
+ # Create tables that store DimensionGroup definitions.
dimension_group_storage = _DimensionGroupStorage.initialize(db, context, universe=universe)
return cls(
db=db,
@@ -529,7 +529,7 @@ def _make_common_skypix_join_relation(
payload.columns_available[DimensionKeyColumnTag(self.universe.commonSkyPix.name)] = (
payload.from_clause.columns.skypix_index
)
- for dimension_name in element.graph.required.names:
+ for dimension_name in element.minimal_group.required:
payload.columns_available[DimensionKeyColumnTag(dimension_name)] = payload.from_clause.columns[
dimension_name
]
@@ -596,7 +596,7 @@ def _make_skypix_overlap_tables(
"skypix_system",
"skypix_level",
"skypix_index",
- *element.graph.required.names,
+ *element.minimal_group.required,
),
},
foreignKeys=[
@@ -843,8 +843,8 @@ def __init__(
self._idTable = idTable
self._definitionTable = definitionTable
self._universe = universe
- self._keysByGroup: dict[DimensionGroup, int] = {universe.empty.as_group(): 0}
- self._groupsByKey: dict[int, DimensionGroup] = {0: universe.empty.as_group()}
+ self._keysByGroup: dict[DimensionGroup, int] = {universe.empty: 0}
+ self._groupsByKey: dict[int, DimensionGroup] = {0: universe.empty}
def clone(self, db: Database) -> _DimensionGroupStorage:
"""Make an independent copy of this manager instance bound to a new
@@ -926,7 +926,7 @@ def initialize(
return cls(db, idTable, definitionTable, universe=universe)
def refresh(self) -> None:
- """Refresh the in-memory cache of saved DimensionGraph definitions.
+ """Refresh the in-memory cache of saved DimensionGroup definitions.
This should be done automatically whenever needed, but it can also
be called explicitly.
@@ -937,8 +937,8 @@ def refresh(self) -> None:
for row in sql_rows:
key = row[self._definitionTable.columns.dimension_graph_id]
dimensionNamesByKey[key].add(row[self._definitionTable.columns.dimension_name])
- keysByGraph: dict[DimensionGroup, int] = {self._universe.empty.as_group(): 0}
- graphsByKey: dict[int, DimensionGroup] = {0: self._universe.empty.as_group()}
+ keysByGraph: dict[DimensionGroup, int] = {self._universe.empty: 0}
+ graphsByKey: dict[int, DimensionGroup] = {0: self._universe.empty}
for key, dimensionNames in dimensionNamesByKey.items():
graph = DimensionGroup(self._universe, names=dimensionNames)
keysByGraph[graph] = key
@@ -947,7 +947,7 @@ def refresh(self) -> None:
self._keysByGroup = keysByGraph
def save(self, group: DimensionGroup) -> int:
- """Save a `DimensionGraph` definition to the database, allowing it to
+ """Save a `DimensionGroup` definition to the database, allowing it to
be retrieved later via the returned key.
Parameters
@@ -958,7 +958,7 @@ def save(self, group: DimensionGroup) -> int:
Returns
-------
key : `int`
- Integer used as the unique key for this `DimensionGraph` in the
+ Integer used as the unique key for this `DimensionGroup` in the
database.
"""
key = self._keysByGroup.get(group)
@@ -983,18 +983,18 @@ def save(self, group: DimensionGroup) -> int:
return key
def load(self, key: int) -> DimensionGroup:
- """Retrieve a `DimensionGraph` that was previously saved in the
+ """Retrieve a `DimensionGroup` that was previously saved in the
database.
Parameters
----------
key : `int`
- Integer used as the unique key for this `DimensionGraph` in the
+ Integer used as the unique key for this `DimensionGroup` in the
database.
Returns
-------
- graph : `DimensionGraph`
+ graph : `DimensionGroup`
Retrieved graph.
"""
graph = self._groupsByKey.get(key)
diff --git a/python/lsst/daf/butler/registry/obscore/_records.py b/python/lsst/daf/butler/registry/obscore/_records.py
index 3b8b1b3721..c89a1074b6 100644
--- a/python/lsst/daf/butler/registry/obscore/_records.py
+++ b/python/lsst/daf/butler/registry/obscore/_records.py
@@ -221,7 +221,7 @@ def __call__(self, ref: DatasetRef, context: SqlQueryContext) -> Record | None:
if em_range:
record["em_min"], record["em_max"] = em_range
else:
- _LOG.warning("could not find spectral range for dataId=%s", dataId.full)
+ _LOG.warning("could not find spectral range for dataId=%s", dataId)
record["em_filter_name"] = dataId["band"]
# Dictionary to use for substitutions when formatting various
diff --git a/python/lsst/daf/butler/registry/queries/_builder.py b/python/lsst/daf/butler/registry/queries/_builder.py
index 4264111b13..ff6f8c7d72 100644
--- a/python/lsst/daf/butler/registry/queries/_builder.py
+++ b/python/lsst/daf/butler/registry/queries/_builder.py
@@ -227,8 +227,8 @@ def finish(self, joinMissing: bool = True) -> Query:
for family1, family2 in itertools.combinations(self.summary.dimensions.spatial, 2):
spatial_joins.append(
(
- family1.choose(self.summary.dimensions.elements.names, self.summary.universe).name,
- family2.choose(self.summary.dimensions.elements.names, self.summary.universe).name,
+ family1.choose(self.summary.dimensions.elements, self.summary.universe).name,
+ family2.choose(self.summary.dimensions.elements, self.summary.universe).name,
)
)
self.relation = self._backend.make_dimension_relation(
diff --git a/python/lsst/daf/butler/registry/queries/_query.py b/python/lsst/daf/butler/registry/queries/_query.py
index 5d05bb73ab..6bd5474472 100644
--- a/python/lsst/daf/butler/registry/queries/_query.py
+++ b/python/lsst/daf/butler/registry/queries/_query.py
@@ -706,7 +706,7 @@ def find_datasets(
# If the dataset type has dimensions not in the current query, or we
# need a temporal join for a calibration collection, either restore
# those columns or join them in.
- full_dimensions = dataset_type.dimensions.as_group().union(self._dimensions)
+ full_dimensions = dataset_type.dimensions.union(self._dimensions)
relation = self._relation
record_caches = self._record_caches
base_columns_required: set[ColumnTag] = {
@@ -746,12 +746,8 @@ def find_datasets(
# present in each family (e.g. patch beats tract).
spatial_joins.append(
(
- lhs_spatial_family.choose(
- full_dimensions.elements.names, self.dimensions.universe
- ).name,
- rhs_spatial_family.choose(
- full_dimensions.elements.names, self.dimensions.universe
- ).name,
+ lhs_spatial_family.choose(full_dimensions.elements, self.dimensions.universe).name,
+ rhs_spatial_family.choose(full_dimensions.elements, self.dimensions.universe).name,
)
)
# Set up any temporal join between the query dimensions and CALIBRATION
@@ -759,7 +755,7 @@ def find_datasets(
temporal_join_on: set[ColumnTag] = set()
if any(r.type is CollectionType.CALIBRATION for r in collection_records):
for family in self._dimensions.temporal:
- endpoint = family.choose(self._dimensions.elements.names, self.dimensions.universe)
+ endpoint = family.choose(self._dimensions.elements, self.dimensions.universe)
temporal_join_on.add(DimensionRecordColumnTag(endpoint.name, "timespan"))
base_columns_required.update(temporal_join_on)
# Note which of the many kinds of potentially-missing columns we have
diff --git a/python/lsst/daf/butler/registry/queries/_query_backend.py b/python/lsst/daf/butler/registry/queries/_query_backend.py
index 515d5084e5..bac06c3c3c 100644
--- a/python/lsst/daf/butler/registry/queries/_query_backend.py
+++ b/python/lsst/daf/butler/registry/queries/_query_backend.py
@@ -579,9 +579,7 @@ def make_doomed_dataset_relation(
relation : `lsst.daf.relation.Relation`
Relation with the requested columns and no rows.
"""
- column_tags: set[ColumnTag] = set(
- DimensionKeyColumnTag.generate(dataset_type.dimensions.required.names)
- )
+ column_tags: set[ColumnTag] = set(DimensionKeyColumnTag.generate(dataset_type.dimensions.required))
column_tags.update(DatasetColumnTag.generate(dataset_type.name, columns))
return context.preferred_engine.make_doomed_relation(columns=column_tags, messages=list(messages))
diff --git a/python/lsst/daf/butler/registry/queries/_readers.py b/python/lsst/daf/butler/registry/queries/_readers.py
index 4b12f5be10..ccb7262497 100644
--- a/python/lsst/daf/butler/registry/queries/_readers.py
+++ b/python/lsst/daf/butler/registry/queries/_readers.py
@@ -142,7 +142,7 @@ class _BasicDataCoordinateReader(DataCoordinateReader):
def __init__(self, dimensions: DimensionGroup):
self._dimensions = dimensions
- self._tags = tuple(DimensionKeyColumnTag(name) for name in self._dimensions.required.names)
+ self._tags = tuple(DimensionKeyColumnTag(name) for name in self._dimensions.required)
__slots__ = ("_dimensions", "_tags")
@@ -169,9 +169,7 @@ class _FullDataCoordinateReader(DataCoordinateReader):
def __init__(self, dimensions: DimensionGroup):
self._dimensions = dimensions
- self._tags = tuple(
- DimensionKeyColumnTag(name) for name in self._dimensions.as_group().data_coordinate_keys
- )
+ self._tags = tuple(DimensionKeyColumnTag(name) for name in self._dimensions.data_coordinate_keys)
__slots__ = ("_dimensions", "_tags")
@@ -265,7 +263,7 @@ def __init__(
record_caches: Mapping[str, DimensionRecordSet] | None = None,
):
self._data_coordinate_reader = DataCoordinateReader.make(
- dataset_type.dimensions.as_group(), full=full, records=records, record_caches=record_caches
+ dataset_type.dimensions, full=full, records=records, record_caches=record_caches
)
self._dataset_type = dataset_type
self._translate_collection = translate_collection
diff --git a/python/lsst/daf/butler/registry/queries/_results.py b/python/lsst/daf/butler/registry/queries/_results.py
index b66f8428d2..4627d246e9 100644
--- a/python/lsst/daf/butler/registry/queries/_results.py
+++ b/python/lsst/daf/butler/registry/queries/_results.py
@@ -44,8 +44,6 @@
from contextlib import AbstractContextManager, ExitStack, contextmanager
from typing import Any, Self
-from deprecated.sphinx import deprecated
-
from ..._dataset_ref import DatasetRef
from ..._dataset_type import DatasetType
from ..._exceptions_legacy import DatasetTypeError
@@ -53,7 +51,6 @@
DataCoordinate,
DataCoordinateIterable,
DimensionElement,
- DimensionGraph,
DimensionGroup,
DimensionRecord,
)
@@ -262,7 +259,7 @@ def expanded(self) -> DataCoordinateQueryResults:
@abstractmethod
def subset(
self,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
*,
unique: bool = False,
) -> DataCoordinateQueryResults:
@@ -274,7 +271,7 @@ def subset(
Parameters
----------
- dimensions : `DimensionGroup`, `DimensionGraph`, or \
+ dimensions : `DimensionGroup` or \
`~collections.abc.Iterable` [ `str`], optional
Dimensions to include in the new results object. If `None`,
``self.dimensions`` is used.
@@ -370,7 +367,7 @@ def findRelatedDatasets(
collections: Any,
*,
findFirst: bool = True,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
) -> Iterable[tuple[DataCoordinate, DatasetRef]]:
"""Find datasets using the data IDs identified by this query, and
return them along with the original data IDs.
@@ -398,7 +395,7 @@ def findRelatedDatasets(
expressions and may not be ``...``. Note that this is not the
same as yielding one `DatasetRef` for each yielded data ID if
``dimensions`` is not `None`.
- dimensions : `DimensionGroup`, `DimensionGraph`, or \
+ dimensions : `DimensionGroup` or \
`~collections.abc.Iterable` [ `str` ], optional
The dimensions of the data IDs returned. Must be a subset of
``self.dimensions``.
@@ -443,17 +440,7 @@ def __iter__(self) -> Iterator[DataCoordinate]:
return self._query.iter_data_ids()
def __repr__(self) -> str:
- return f""
-
- @property
- @deprecated(
- "Deprecated in favor of .dimensions. Will be removed after v27.",
- version="v27",
- category=FutureWarning,
- )
- def graph(self) -> DimensionGraph:
- # Docstring inherited from DataCoordinateIterable.
- return self._query.dimensions._as_graph()
+ return f""
@property
def dimensions(self) -> DimensionGroup:
@@ -478,7 +465,7 @@ def expanded(self) -> DataCoordinateQueryResults:
def subset(
self,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
*,
unique: bool = False,
) -> DataCoordinateQueryResults:
@@ -518,7 +505,7 @@ def findRelatedDatasets(
collections: Any,
*,
findFirst: bool = True,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
) -> Iterable[tuple[DataCoordinate, DatasetRef]]:
if dimensions is None:
dimensions = self.dimensions
diff --git a/python/lsst/daf/butler/registry/queries/_sql_query_backend.py b/python/lsst/daf/butler/registry/queries/_sql_query_backend.py
index 0f0f51c930..07d0c35432 100644
--- a/python/lsst/daf/butler/registry/queries/_sql_query_backend.py
+++ b/python/lsst/daf/butler/registry/queries/_sql_query_backend.py
@@ -271,7 +271,7 @@ def make_dimension_relation(
"out if they have already been added or will be added later."
)
for element_name in missing_columns.dimension_records:
- if element_name not in dimensions.elements.names:
+ if element_name not in dimensions.elements:
raise ColumnError(
f"Cannot join dimension element {element_name} whose dimensions are not a "
f"subset of {dimensions}."
diff --git a/python/lsst/daf/butler/registry/queries/_structs.py b/python/lsst/daf/butler/registry/queries/_structs.py
index d470798337..fca11e3700 100644
--- a/python/lsst/daf/butler/registry/queries/_structs.py
+++ b/python/lsst/daf/butler/registry/queries/_structs.py
@@ -500,7 +500,7 @@ def _compute_columns_required(
missing_common_skypix = False
if region is not None:
for family in dimensions.spatial:
- element = family.choose(dimensions.elements.names, self.universe)
+ element = family.choose(dimensions.elements, self.universe)
tags.add(DimensionRecordColumnTag(element.name, "region"))
if (
not isinstance(element, SkyPixDimension)
diff --git a/python/lsst/daf/butler/registry/queries/expressions/check.py b/python/lsst/daf/butler/registry/queries/expressions/check.py
index 7e5dda1fba..179d71750f 100644
--- a/python/lsst/daf/butler/registry/queries/expressions/check.py
+++ b/python/lsst/daf/butler/registry/queries/expressions/check.py
@@ -435,7 +435,7 @@ def visitInner(self, branches: Sequence[TreeSummary], form: NormalForm) -> Inner
if missing <= self.defaults.dimensions.required:
summary.defaultsNeeded.update(missing)
elif not self._allow_orphans:
- still_missing = missing - self.defaults.names
+ still_missing = missing - self.defaults.dimensions.names
raise UserExpressionError(
f"No value(s) for governor dimensions {still_missing} in expression "
"that references dependent dimensions. 'Governor' dimensions must always be specified "
diff --git a/python/lsst/daf/butler/registry/sql_registry.py b/python/lsst/daf/butler/registry/sql_registry.py
index e20fbf41ad..08ff18519c 100644
--- a/python/lsst/daf/butler/registry/sql_registry.py
+++ b/python/lsst/daf/butler/registry/sql_registry.py
@@ -54,16 +54,13 @@
DimensionNameError,
InconsistentDataIdError,
)
-from .._named import NamedKeyMapping, NameLookupMapping
from .._storage_class import StorageClassFactory
from .._timespan import Timespan
from ..dimensions import (
DataCoordinate,
DataId,
- Dimension,
DimensionConfig,
DimensionElement,
- DimensionGraph,
DimensionGroup,
DimensionRecord,
DimensionUniverse,
@@ -1488,9 +1485,8 @@ def expandDataId(
self,
dataId: DataId | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
- graph: DimensionGraph | None = None,
- records: NameLookupMapping[DimensionElement, DimensionRecord | None] | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
+ records: Mapping[str, DimensionRecord | None] | None = None,
withDefaults: bool = True,
**kwargs: Any,
) -> DataCoordinate:
@@ -1501,15 +1497,11 @@ def expandDataId(
dataId : `DataCoordinate` or `dict`, optional
Data ID to be expanded; augmented and overridden by ``kwargs``.
dimensions : `~collections.abc.Iterable` [ `str` ], \
- `DimensionGroup`, or `DimensionGraph`, optional
+ `DimensionGroup`, optional
The dimensions to be identified by the new `DataCoordinate`.
- If not provided, will be inferred from the keys of ``mapping`` and
- ``**kwargs``, and ``universe`` must be provided unless ``mapping``
+ If not provided, will be inferred from the keys of ``dataId`` and
+ ``**kwargs``, and ``universe`` must be provided unless ``dataId``
is already a `DataCoordinate`.
- graph : `DimensionGraph`, optional
- Like ``dimensions``, but as a ``DimensionGraph`` instance. Ignored
- if ``dimensions`` is provided. Deprecated and will be removed
- after v27.
records : `~collections.abc.Mapping` [`str`, `DimensionRecord`], \
optional
Dimension record data to use before querying the database for that
@@ -1551,7 +1543,6 @@ def expandDataId(
defaults = self.defaults.dataId
standardized = DataCoordinate.standardize(
dataId,
- graph=graph,
dimensions=dimensions,
universe=self.dimensions,
defaults=defaults,
@@ -1561,8 +1552,6 @@ def expandDataId(
return standardized
if records is None:
records = {}
- elif isinstance(records, NamedKeyMapping):
- records = records.byName()
else:
records = dict(records)
if isinstance(dataId, DataCoordinate) and dataId.hasRecords():
@@ -1982,7 +1971,7 @@ def queryDatasets(
datasetType: Any,
*,
collections: CollectionArgType | None = None,
- dimensions: Iterable[Dimension | str] | None = None,
+ dimensions: Iterable[str] | None = None,
dataId: DataId | None = None,
where: str = "",
findFirst: bool = False,
@@ -2010,7 +1999,7 @@ def queryDatasets(
collections, because this will still find all datasets).
If not provided, ``self.default.collections`` is used. See
:ref:`daf_butler_collection_expressions` for more information.
- dimensions : `~collections.abc.Iterable` of `Dimension` or `str`
+ dimensions : `~collections.abc.Iterable` of `str`
Dimensions to include in the query (in addition to those used
to identify the queried dataset type(s)), either to constrain
the resulting datasets to those for which a matching dimension
@@ -2156,8 +2145,7 @@ def queryDatasets(
def queryDataIds(
self,
- # TODO: Drop Dimension support on DM-41326.
- dimensions: DimensionGroup | Iterable[Dimension | str] | Dimension | str,
+ dimensions: DimensionGroup | Iterable[str] | str,
*,
dataId: DataId | None = None,
datasets: Any = None,
@@ -2172,12 +2160,11 @@ def queryDataIds(
Parameters
----------
- dimensions : `DimensionGroup`, `Dimension`, or `str`, or \
- `~collections.abc.Iterable` [ `Dimension` or `str` ]
+ dimensions : `DimensionGroup`, `str`, or \
+ `~collections.abc.Iterable` [ `str` ]
The dimensions of the data IDs to yield, as either `Dimension`
instances or `str`. Will be automatically expanded to a complete
- `DimensionGroup`. Support for `Dimension` instances is deprecated
- and will not be supported after v27.
+ `DimensionGroup`.
dataId : `dict` or `DataCoordinate`, optional
A data ID whose key-value pairs are used as equality constraints
in the query.
diff --git a/python/lsst/daf/butler/registry/tests/_registry.py b/python/lsst/daf/butler/registry/tests/_registry.py
index 8ef96d5746..25c9bdee53 100644
--- a/python/lsst/daf/butler/registry/tests/_registry.py
+++ b/python/lsst/daf/butler/registry/tests/_registry.py
@@ -1211,9 +1211,7 @@ def testInstrumentDimensions(self):
(ref,) = registry.insertDatasets(rawType, dataIds=[dataId], run=run2)
registry.associate(tagged2, [ref])
- dimensions = registry.dimensions.conform(
- rawType.dimensions.required.names | calexpType.dimensions.required.names
- )
+ dimensions = registry.dimensions.conform(rawType.dimensions.required | calexpType.dimensions.required)
# Test that single dim string works as well as list of str
rows = registry.queryDataIds("visit", datasets=rawType, collections=run1).expanded().toSet()
rowsI = registry.queryDataIds(["visit"], datasets=rawType, collections=run1).expanded().toSet()
@@ -1337,9 +1335,7 @@ def testSkyMapDimensions(self):
registry.registerDatasetType(measType)
dimensions = registry.dimensions.conform(
- calexpType.dimensions.required.names
- | mergeType.dimensions.required.names
- | measType.dimensions.required.names
+ calexpType.dimensions.required | mergeType.dimensions.required | measType.dimensions.required
)
# add pre-existing datasets
diff --git a/python/lsst/daf/butler/remote_butler/_registry.py b/python/lsst/daf/butler/remote_butler/_registry.py
index 1c0db3d5e2..168665992c 100644
--- a/python/lsst/daf/butler/remote_butler/_registry.py
+++ b/python/lsst/daf/butler/remote_butler/_registry.py
@@ -36,15 +36,12 @@
from .._dataset_association import DatasetAssociation
from .._dataset_ref import DatasetId, DatasetIdGenEnum, DatasetRef
from .._dataset_type import DatasetType
-from .._named import NameLookupMapping
from .._storage_class import StorageClassFactory
from .._timespan import Timespan
from ..dimensions import (
DataCoordinate,
DataId,
- Dimension,
DimensionElement,
- DimensionGraph,
DimensionGroup,
DimensionRecord,
DimensionUniverse,
@@ -281,15 +278,13 @@ def expandDataId(
self,
dataId: DataId | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
- graph: DimensionGraph | None = None,
- records: NameLookupMapping[DimensionElement, DimensionRecord | None] | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
+ records: Mapping[str, DimensionRecord | None] | None = None,
withDefaults: bool = True,
**kwargs: Any,
) -> DataCoordinate:
standardized = DataCoordinate.standardize(
dataId,
- graph=graph,
dimensions=dimensions,
universe=self.dimensions,
defaults=self.defaults.dataId if withDefaults else None,
@@ -373,7 +368,7 @@ def queryDatasets(
datasetType: Any,
*,
collections: CollectionArgType | None = None,
- dimensions: Iterable[Dimension | str] | None = None,
+ dimensions: Iterable[str] | None = None,
dataId: DataId | None = None,
where: str = "",
findFirst: bool = False,
@@ -442,8 +437,7 @@ def queryDatasets(
def queryDataIds(
self,
- # TODO: Drop `Dimension` objects on DM-41326.
- dimensions: DimensionGroup | Iterable[Dimension | str] | Dimension | str,
+ dimensions: DimensionGroup | Iterable[str] | str,
*,
dataId: DataId | None = None,
datasets: Any = None,
diff --git a/python/lsst/daf/butler/remote_butler/registry/_query_data_coordinates.py b/python/lsst/daf/butler/remote_butler/registry/_query_data_coordinates.py
index 1a9dc9aab6..7daae2a68f 100644
--- a/python/lsst/daf/butler/remote_butler/registry/_query_data_coordinates.py
+++ b/python/lsst/daf/butler/remote_butler/registry/_query_data_coordinates.py
@@ -33,7 +33,7 @@
from ..._dataset_ref import DatasetRef
from ..._dataset_type import DatasetType
-from ...dimensions import DataCoordinate, DimensionGraph, DimensionGroup
+from ...dimensions import DataCoordinate, DimensionGroup
from ...queries import DataCoordinateQueryResults, Query
from ...registry.queries import DataCoordinateQueryResults as LegacyDataCoordinateQueryResults
from ...registry.queries import ParentDatasetQueryResults
@@ -106,7 +106,7 @@ def expanded(self) -> LegacyDataCoordinateQueryResults:
def subset(
self,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
*,
unique: bool = False,
) -> LegacyDataCoordinateQueryResults:
@@ -139,6 +139,6 @@ def findRelatedDatasets(
collections: Any,
*,
findFirst: bool = True,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
) -> Iterable[tuple[DataCoordinate, DatasetRef]]:
raise NotImplementedError()
diff --git a/python/lsst/daf/butler/script/queryDataIds.py b/python/lsst/daf/butler/script/queryDataIds.py
index e312d2c533..3bcd597c94 100644
--- a/python/lsst/daf/butler/script/queryDataIds.py
+++ b/python/lsst/daf/butler/script/queryDataIds.py
@@ -142,13 +142,11 @@ def queryDataIds(
for dataset_type in dataset_types:
if dataset_type_dimensions is None:
# Seed with dimensions of first dataset type.
- dataset_type_dimensions = dataset_type.dimensions.as_group()
+ dataset_type_dimensions = dataset_type.dimensions
else:
# Only retain dimensions that are in the current
# set AND the set from this dataset type.
- dataset_type_dimensions = dataset_type_dimensions.intersection(
- dataset_type.dimensions.as_group()
- )
+ dataset_type_dimensions = dataset_type_dimensions.intersection(dataset_type.dimensions)
_LOG.debug("Dimensions now %s from %s", set(dataset_type_dimensions.names), dataset_type.name)
# Break out of the loop early. No additional dimensions
diff --git a/python/lsst/daf/butler/tests/_datasetsHelper.py b/python/lsst/daf/butler/tests/_datasetsHelper.py
index 2f73c57b36..fef710a7c3 100644
--- a/python/lsst/daf/butler/tests/_datasetsHelper.py
+++ b/python/lsst/daf/butler/tests/_datasetsHelper.py
@@ -44,7 +44,7 @@
from lsst.daf.butler.formatters.yaml import YamlFormatter
if TYPE_CHECKING:
- from lsst.daf.butler import Config, DatasetId, Dimension, DimensionGraph
+ from lsst.daf.butler import Config, DatasetId
class DatasetTestHelper:
@@ -53,7 +53,7 @@ class DatasetTestHelper:
def makeDatasetRef(
self,
datasetTypeName: str,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str | Dimension],
+ dimensions: DimensionGroup | Iterable[str],
storageClass: StorageClass | str,
dataId: DataCoordinate | Mapping[str, Any],
*,
@@ -67,8 +67,7 @@ def makeDatasetRef(
----------
datasetTypeName : `str`
The name of the dataset type.
- dimensions : `DimensionGroup` or `~collections.abc.Iterable` of `str` \
- or `Dimension`
+ dimensions : `DimensionGroup` or `~collections.abc.Iterable` of `str`
The dimensions to use for this dataset type.
storageClass : `StorageClass` or `str`
The relevant storage class.
@@ -100,7 +99,7 @@ def makeDatasetRef(
def _makeDatasetRef(
self,
datasetTypeName: str,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str | Dimension],
+ dimensions: DimensionGroup | Iterable[str],
storageClass: StorageClass | str,
dataId: DataCoordinate | Mapping,
*,
diff --git a/python/lsst/daf/butler/tests/hybrid_butler_registry.py b/python/lsst/daf/butler/tests/hybrid_butler_registry.py
index cbd4b1f5c0..450e831952 100644
--- a/python/lsst/daf/butler/tests/hybrid_butler_registry.py
+++ b/python/lsst/daf/butler/tests/hybrid_butler_registry.py
@@ -34,15 +34,12 @@
from .._dataset_association import DatasetAssociation
from .._dataset_ref import DatasetId, DatasetIdGenEnum, DatasetRef
from .._dataset_type import DatasetType
-from .._named import NameLookupMapping
from .._storage_class import StorageClassFactory
from .._timespan import Timespan
from ..dimensions import (
DataCoordinate,
DataId,
- Dimension,
DimensionElement,
- DimensionGraph,
DimensionGroup,
DimensionRecord,
DimensionUniverse,
@@ -225,14 +222,13 @@ def expandDataId(
self,
dataId: DataId | None = None,
*,
- dimensions: Iterable[str] | DimensionGroup | DimensionGraph | None = None,
- graph: DimensionGraph | None = None,
- records: NameLookupMapping[DimensionElement, DimensionRecord | None] | None = None,
+ dimensions: Iterable[str] | DimensionGroup | None = None,
+ records: Mapping[str, DimensionRecord | None] | None = None,
withDefaults: bool = True,
**kwargs: Any,
) -> DataCoordinate:
return self._remote.expandDataId(
- dataId, dimensions=dimensions, graph=graph, records=records, withDefaults=withDefaults, **kwargs
+ dataId, dimensions=dimensions, records=records, withDefaults=withDefaults, **kwargs
)
def insertDimensionData(
@@ -282,7 +278,7 @@ def queryDatasets(
datasetType: Any,
*,
collections: CollectionArgType | None = None,
- dimensions: Iterable[Dimension | str] | None = None,
+ dimensions: Iterable[str] | None = None,
dataId: DataId | None = None,
where: str = "",
findFirst: bool = False,
@@ -305,8 +301,7 @@ def queryDatasets(
def queryDataIds(
self,
- # TODO: Drop `Dimension` objects on DM-41326.
- dimensions: DimensionGroup | Iterable[Dimension | str] | Dimension | str,
+ dimensions: DimensionGroup | Iterable[str] | str,
*,
dataId: DataId | None = None,
datasets: Any = None,
@@ -424,7 +419,7 @@ def expanded(self) -> _HybridDataCoordinateQueryResults:
def subset(
self,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
*,
unique: bool = False,
) -> _HybridDataCoordinateQueryResults:
@@ -449,7 +444,7 @@ def findRelatedDatasets(
collections: Any,
*,
findFirst: bool = True,
- dimensions: DimensionGroup | DimensionGraph | Iterable[str] | None = None,
+ dimensions: DimensionGroup | Iterable[str] | None = None,
) -> Iterable[tuple[DataCoordinate, DatasetRef]]:
return self._direct.findRelatedDatasets(
datasetType, collections, findFirst=findFirst, dimensions=dimensions
diff --git a/tests/test_dimensions.py b/tests/test_dimensions.py
index 091e7e70c5..09f7e133ac 100644
--- a/tests/test_dimensions.py
+++ b/tests/test_dimensions.py
@@ -257,7 +257,7 @@ def testConfigRead(self):
)
def testGraphs(self):
- self.checkGroupInvariants(self.universe.empty.as_group())
+ self.checkGroupInvariants(self.universe.empty)
for element in self.universe.getStaticElements():
self.checkGroupInvariants(element.minimal_group)
diff --git a/tests/test_query_interface.py b/tests/test_query_interface.py
index 06be5f5e2f..7ca492fbcb 100644
--- a/tests/test_query_interface.py
+++ b/tests/test_query_interface.py
@@ -430,7 +430,7 @@ def test_int_literals(self) -> None:
self.assertEqual(expr.column_type, "int")
self.assertEqual(str(expr), "5")
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), 5)
@@ -443,7 +443,7 @@ def test_string_literals(self) -> None:
self.assertEqual(expr.column_type, "string")
self.assertEqual(str(expr), "'five'")
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), "five")
@@ -456,7 +456,7 @@ def test_float_literals(self) -> None:
self.assertEqual(expr.column_type, "float")
self.assertEqual(str(expr), "0.5")
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), 0.5)
@@ -469,7 +469,7 @@ def test_hash_literals(self) -> None:
self.assertEqual(expr.column_type, "hash")
self.assertEqual(str(expr), "(bytes)")
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), b"eleven")
@@ -483,7 +483,7 @@ def test_uuid_literals(self) -> None:
self.assertEqual(expr.column_type, "uuid")
self.assertEqual(str(expr), str(value))
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), value)
@@ -497,7 +497,7 @@ def test_datetime_literals(self) -> None:
self.assertEqual(expr.column_type, "datetime")
self.assertEqual(str(expr), "2020-01-01T00:00:00")
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), value)
@@ -513,7 +513,7 @@ def test_timespan_literals(self) -> None:
self.assertEqual(expr.column_type, "timespan")
self.assertEqual(str(expr), "[2020-01-01T00:00:00, 2020-01-01T00:01:00)")
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), value)
@@ -528,7 +528,7 @@ def test_region_literals(self) -> None:
self.assertEqual(expr.column_type, "region")
self.assertEqual(str(expr), "(region)")
self.assertTrue(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertFalse(columns)
self.assertEqual(expr.visit(_TestVisitor()), value)
@@ -544,7 +544,7 @@ def test_dimension_key_reference(self) -> None:
self.assertEqual(expr.column_type, "int")
self.assertEqual(str(expr), "detector")
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["detector"]))
self.assertEqual(expr.visit(_TestVisitor(dimension_keys={"detector": 3})), 3)
@@ -556,7 +556,7 @@ def test_dimension_field_reference(self) -> None:
self.assertEqual(expr.column_type, "string")
self.assertEqual(str(expr), "detector.purpose")
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["detector"]))
self.assertEqual(columns.dimension_fields["detector"], {"purpose"})
@@ -572,9 +572,9 @@ def test_dataset_field_reference(self) -> None:
self.assertEqual(expr.expression_type, "dataset_field")
self.assertEqual(str(expr), "raw.ingest_date")
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
- self.assertEqual(columns.dimensions, self.universe.empty.as_group())
+ self.assertEqual(columns.dimensions, self.universe.empty)
self.assertEqual(columns.dataset_fields["raw"], {"ingest_date"})
self.assertEqual(qt.DatasetFieldReference(dataset_type="raw", field="dataset_id").column_type, "uuid")
self.assertEqual(
@@ -597,7 +597,7 @@ def test_unary_negation(self) -> None:
self.assertEqual(expr.column_type, "float")
self.assertEqual(str(expr), "-visit.exposure_time")
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertEqual(columns.dimension_fields["visit"], {"exposure_time"})
@@ -617,7 +617,7 @@ def test_unary_timespan_begin(self) -> None:
self.assertEqual(expr.column_type, "datetime")
self.assertEqual(str(expr), "visit.timespan.begin")
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertEqual(columns.dimension_fields["visit"], {"timespan"})
@@ -642,7 +642,7 @@ def test_unary_timespan_end(self) -> None:
self.assertEqual(expr.column_type, "datetime")
self.assertEqual(str(expr), "visit.timespan.end")
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertEqual(columns.dimension_fields["visit"], {"timespan"})
@@ -686,7 +686,7 @@ def test_binary_expression_float(self) -> None:
self.assertEqual(expr.column_type, "float")
self.assertEqual(str(expr), string)
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertEqual(columns.dimension_fields["visit"], {"exposure_time"})
@@ -706,7 +706,7 @@ def test_binary_modulus(self) -> None:
self.assertEqual(expr.column_type, "int")
self.assertEqual(str(expr), string)
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertFalse(columns.dimension_fields["visit"])
@@ -732,7 +732,7 @@ def test_reversed(self) -> None:
self.assertEqual(expr.column_type, "int")
self.assertEqual(str(expr), "detector DESC")
self.assertFalse(expr.is_literal)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
expr.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["detector"]))
self.assertFalse(columns.dimension_fields["detector"])
@@ -805,7 +805,7 @@ def test_comparison(self) -> None:
with self.subTest(string=string, detector=detector):
self.assertEqual(predicate.column_type, "bool")
self.assertEqual(str(predicate), string)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
predicate.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["detector"]))
self.assertFalse(columns.dimension_fields["detector"])
@@ -818,7 +818,7 @@ def test_comparison(self) -> None:
self.assertEqual(
inverted.visit(_TestVisitor(dimension_keys={"detector": detector})), not value
)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
inverted.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["detector"]))
self.assertFalse(columns.dimension_fields["detector"])
@@ -829,7 +829,7 @@ def test_overlap_comparison(self) -> None:
predicate = self.x.visit.region.overlaps(region1)
self.assertEqual(predicate.column_type, "bool")
self.assertEqual(str(predicate), "visit.region OVERLAPS (region)")
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
predicate.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertEqual(columns.dimension_fields["visit"], {"region"})
@@ -839,7 +839,7 @@ def test_overlap_comparison(self) -> None:
self.assertEqual(inverted.column_type, "bool")
self.assertEqual(str(inverted), "NOT visit.region OVERLAPS (region)")
self.assertTrue(inverted.visit(_TestVisitor(dimension_fields={("visit", "region"): region2})))
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
inverted.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertEqual(columns.dimension_fields["visit"], {"region"})
@@ -858,7 +858,7 @@ def test_is_null(self) -> None:
predicate = self.x.visit.region.is_null
self.assertEqual(predicate.column_type, "bool")
self.assertEqual(str(predicate), "visit.region IS NULL")
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
predicate.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertEqual(columns.dimension_fields["visit"], {"region"})
@@ -875,7 +875,7 @@ def test_in_container(self) -> None:
predicate: qt.Predicate = self.x.visit.in_iterable([3, 4, self.x.exposure.id])
self.assertEqual(predicate.column_type, "bool")
self.assertEqual(str(predicate), "visit IN [3, 4, exposure]")
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
predicate.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit", "exposure"]))
self.assertFalse(columns.dimension_fields["visit"])
@@ -887,7 +887,7 @@ def test_in_container(self) -> None:
self.assertEqual(str(inverted), "NOT visit IN [3, 4, exposure]")
self.assertFalse(inverted.visit(_TestVisitor(dimension_keys={"visit": 2, "exposure": 2})))
self.assertTrue(inverted.visit(_TestVisitor(dimension_keys={"visit": 2, "exposure": 5})))
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
inverted.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit", "exposure"]))
self.assertFalse(columns.dimension_fields["visit"])
@@ -907,7 +907,7 @@ def test_in_range(self) -> None:
predicate: qt.Predicate = self.x.visit.in_range(2, 8, 2)
self.assertEqual(predicate.column_type, "bool")
self.assertEqual(str(predicate), "visit IN 2:8:2")
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
predicate.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertFalse(columns.dimension_fields["visit"])
@@ -918,7 +918,7 @@ def test_in_range(self) -> None:
self.assertEqual(str(inverted), "NOT visit IN 2:8:2")
self.assertFalse(inverted.visit(_TestVisitor(dimension_keys={"visit": 2})))
self.assertTrue(inverted.visit(_TestVisitor(dimension_keys={"visit": 8})))
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
inverted.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["visit"]))
self.assertFalse(columns.dimension_fields["visit"])
@@ -937,7 +937,7 @@ def test_in_query(self) -> None:
predicate: qt.Predicate = self.x.exposure.in_query(self.x.visit, query)
self.assertEqual(predicate.column_type, "bool")
self.assertEqual(str(predicate), "exposure IN (query).visit")
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
predicate.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["exposure"]))
self.assertFalse(columns.dimension_fields["exposure"])
@@ -956,7 +956,7 @@ def test_in_query(self) -> None:
self.assertTrue(
inverted.visit(_TestVisitor(dimension_keys={"exposure": 8}, query_tree_items={1, 2, 3}))
)
- columns = qt.ColumnSet(self.universe.empty.as_group())
+ columns = qt.ColumnSet(self.universe.empty)
inverted.gather_required_columns(columns)
self.assertEqual(columns.dimensions, self.universe.conform(["exposure"]))
self.assertFalse(columns.dimension_fields["exposure"])
@@ -1118,7 +1118,7 @@ def test_dataset_join(self) -> None:
def check(
query: Query,
- dimensions: DimensionGroup = self.raw.dimensions.as_group(),
+ dimensions: DimensionGroup = self.raw.dimensions,
) -> None:
"""Run a battery of tests on one of a set of very similar queries
constructed in different ways (see below).
@@ -1139,16 +1139,14 @@ def check_query_tree(
Dimensions to expect in the `QueryTree`, not necessarily
including those in the test 'raw' dataset type.
"""
- self.assertEqual(tree.dimensions, dimensions | self.raw.dimensions.as_group())
+ self.assertEqual(tree.dimensions, dimensions | self.raw.dimensions)
self.assertEqual(str(tree.predicate), "raw.run == 'DummyCam/raw/all'")
self.assertFalse(tree.materializations)
self.assertFalse(tree.data_coordinate_uploads)
self.assertEqual(tree.datasets.keys(), {"raw"})
- self.assertEqual(tree.datasets["raw"].dimensions, self.raw.dimensions.as_group())
+ self.assertEqual(tree.datasets["raw"].dimensions, self.raw.dimensions)
self.assertEqual(tree.datasets["raw"].collections, ("DummyCam/defaults",))
- self.assertEqual(
- tree.get_joined_dimension_groups(), frozenset({self.raw.dimensions.as_group()})
- )
+ self.assertEqual(tree.get_joined_dimension_groups(), frozenset({self.raw.dimensions}))
def check_data_id_results(*args, query: Query, dimensions: DimensionGroup = dimensions) -> None:
"""Construct a DataCoordinateQueryResults object from the query
@@ -1197,7 +1195,7 @@ def check_dataset_results(
cm.exception.result_spec,
qrs.DatasetRefResultSpec(
dataset_type_name="raw",
- dimensions=self.raw.dimensions.as_group(),
+ dimensions=self.raw.dimensions,
storage_class_name=storage_class_name,
find_first=find_first,
),
@@ -1242,7 +1240,7 @@ def check_materialization(
# might need to re-join for some result columns in a
# derived query.
self.assertTrue(derived_tree.datasets.keys(), {"raw"})
- self.assertEqual(derived_tree.datasets["raw"].dimensions, self.raw.dimensions.as_group())
+ self.assertEqual(derived_tree.datasets["raw"].dimensions, self.raw.dimensions)
self.assertEqual(derived_tree.datasets["raw"].collections, ("DummyCam/defaults",))
else:
self.assertFalse(derived_tree.datasets)
@@ -1263,7 +1261,7 @@ def check_materialization(
# Actual logic for the check() function begins here.
self.assertEqual(query.constraint_dataset_types, {"raw"})
- self.assertEqual(query.constraint_dimensions, self.raw.dimensions.as_group())
+ self.assertEqual(query.constraint_dimensions, self.raw.dimensions)
# Adding a constraint on a field for this dataset type should work
# (this constraint will be present in all downstream tests).
@@ -1624,14 +1622,14 @@ def check(
self.assertEqual(str(tree.predicate), "instrument == 'DummyCam' AND visit == 4")
self.assertEqual(
tree.dimensions,
- self.universe.conform(["visit"]).union(results.dataset_type.dimensions.as_group()),
+ self.universe.conform(["visit"]).union(results.dataset_type.dimensions),
)
self.assertFalse(tree.materializations)
self.assertEqual(tree.datasets.keys(), {results.dataset_type.name})
self.assertEqual(tree.datasets[results.dataset_type.name].collections, ("DummyCam/defaults",))
self.assertEqual(
tree.datasets[results.dataset_type.name].dimensions,
- results.dataset_type.dimensions.as_group(),
+ results.dataset_type.dimensions,
)
self.assertFalse(tree.data_coordinate_uploads)
result_spec = cm.exception.result_spec
@@ -1650,7 +1648,7 @@ def check(
self.assertEqual(
cm.exception.result_spec,
qrs.DataCoordinateResultSpec(
- dimensions=results.dataset_type.dimensions.as_group(),
+ dimensions=results.dataset_type.dimensions,
include_dimension_records=include_dimension_records,
),
)
@@ -1755,7 +1753,7 @@ def test_invalid_models(self) -> None:
datasets={
"raw": qt.DatasetSearch(
collections=("DummyCam/raw/all",),
- dimensions=self.raw.dimensions.as_group(),
+ dimensions=self.raw.dimensions,
)
},
)
@@ -1782,10 +1780,10 @@ def test_invalid_models(self) -> None:
# ResultSpec's datasets are not a subset of the query tree's.
DatasetRefQueryResults(
_TestQueryDriver(),
- qt.QueryTree(dimensions=self.raw.dimensions.as_group()),
+ qt.QueryTree(dimensions=self.raw.dimensions),
qrs.DatasetRefResultSpec(
dataset_type_name="raw",
- dimensions=self.raw.dimensions.as_group(),
+ dimensions=self.raw.dimensions,
storage_class_name=self.raw.storageClass_name,
find_first=True,
),
diff --git a/tests/test_query_utilities.py b/tests/test_query_utilities.py
index 30bcdbef82..df52cef497 100644
--- a/tests/test_query_utilities.py
+++ b/tests/test_query_utilities.py
@@ -66,7 +66,7 @@ def test_basics(self) -> None:
+ [("detector", "purpose"), ("bias", "dataset_id")],
)
self.assertEqual(str(columns), "{instrument, detector, detector:purpose, bias:dataset_id}")
- empty = qt.ColumnSet(self.universe.empty.as_group())
+ empty = qt.ColumnSet(self.universe.empty)
self.assertFalse(empty)
self.assertFalse(columns.issubset(empty))
self.assertTrue(columns.issuperset(empty))
diff --git a/tests/test_simpleButler.py b/tests/test_simpleButler.py
index e88802d84c..098ddf06ff 100644
--- a/tests/test_simpleButler.py
+++ b/tests/test_simpleButler.py
@@ -580,7 +580,7 @@ def testRegistryDefaults(self):
butler.registry.insertDimensionData("instrument", {"name": "Cam2"})
camera = DatasetType(
"camera",
- dimensions=butler.dimensions["instrument"].graph,
+ dimensions=butler.dimensions["instrument"].minimal_group,
storageClass="Camera",
)
butler.registry.registerDatasetType(camera)