Coverage for python/lsst/daf/butler/core/dimensions/_dataCoordinateIterable.py: 31%
204 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-07 00:58 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2023-04-07 00:58 -0700
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22from __future__ import annotations
24__all__ = (
25 "DataCoordinateIterable",
26 "DataCoordinateSet",
27 "DataCoordinateSequence",
28)
30from abc import abstractmethod
31from typing import AbstractSet, Any, Collection, Dict, Iterable, Iterator, Optional, Sequence, overload
33from ._coordinate import DataCoordinate
34from ._graph import DimensionGraph
35from ._universe import DimensionUniverse
38class DataCoordinateIterable(Iterable[DataCoordinate]):
39 """An abstract base class for homogeneous iterables of data IDs.
41 All elements of a `DataCoordinateIterable` identify the same set of
42 dimensions (given by the `graph` property) and generally have the same
43 `DataCoordinate.hasFull` and `DataCoordinate.hasRecords` flag values.
44 """
46 __slots__ = ()
48 @staticmethod
49 def fromScalar(dataId: DataCoordinate) -> _ScalarDataCoordinateIterable:
50 """Return a `DataCoordinateIterable` containing the single data ID.
52 Parameters
53 ----------
54 dataId : `DataCoordinate`
55 Data ID to adapt. Must be a true `DataCoordinate` instance, not
56 an arbitrary mapping. No runtime checking is performed.
58 Returns
59 -------
60 iterable : `DataCoordinateIterable`
61 A `DataCoordinateIterable` instance of unspecified (i.e.
62 implementation-detail) subclass. Guaranteed to implement
63 the `collections.abc.Sized` (i.e. `__len__`) and
64 `collections.abc.Container` (i.e. `__contains__`) interfaces as
65 well as that of `DataCoordinateIterable`.
66 """
67 return _ScalarDataCoordinateIterable(dataId)
69 @property
70 @abstractmethod
71 def graph(self) -> DimensionGraph:
72 """Dimensions identified by these data IDs (`DimensionGraph`)."""
73 raise NotImplementedError()
75 @property
76 def universe(self) -> DimensionUniverse:
77 """Universe that defines all known compatible dimensions.
79 (`DimensionUniverse`).
80 """
81 return self.graph.universe
83 @abstractmethod
84 def hasFull(self) -> bool:
85 """Indicate if all data IDs in this iterable identify all dimensions.
87 Not just required dimensions.
89 Returns
90 -------
91 state : `bool`
92 If `True`, ``all(d.hasFull() for d in iterable)`` is guaranteed.
93 If `False`, no guarantees are made.
94 """
95 raise NotImplementedError()
97 @abstractmethod
98 def hasRecords(self) -> bool:
99 """Return whether all data IDs in this iterable contain records.
101 Returns
102 -------
103 state : `bool`
104 If `True`, ``all(d.hasRecords() for d in iterable)`` is guaranteed.
105 If `False`, no guarantees are made.
106 """
107 raise NotImplementedError()
109 def toSet(self) -> DataCoordinateSet:
110 """Transform this iterable into a `DataCoordinateSet`.
112 Returns
113 -------
114 set : `DataCoordinateSet`
115 A `DatasetCoordinateSet` instance with the same elements as
116 ``self``, after removing any duplicates. May be ``self`` if it is
117 already a `DataCoordinateSet`.
118 """
119 return DataCoordinateSet(
120 frozenset(self),
121 graph=self.graph,
122 hasFull=self.hasFull(),
123 hasRecords=self.hasRecords(),
124 check=False,
125 )
127 def toSequence(self) -> DataCoordinateSequence:
128 """Transform this iterable into a `DataCoordinateSequence`.
130 Returns
131 -------
132 seq : `DataCoordinateSequence`
133 A new `DatasetCoordinateSequence` with the same elements as
134 ``self``, in the same order. May be ``self`` if it is already a
135 `DataCoordinateSequence`.
136 """
137 return DataCoordinateSequence(
138 tuple(self), graph=self.graph, hasFull=self.hasFull(), hasRecords=self.hasRecords(), check=False
139 )
141 @abstractmethod
142 def subset(self, graph: DimensionGraph) -> DataCoordinateIterable:
143 """Return a subset iterable.
145 This subset iterable returns data IDs that identify a subset of the
146 dimensions that this one's do.
148 Parameters
149 ----------
150 graph : `DimensionGraph`
151 Dimensions to be identified by the data IDs in the returned
152 iterable. Must be a subset of ``self.graph``.
154 Returns
155 -------
156 iterable : `DataCoordinateIterable`
157 A `DataCoordinateIterable` with ``iterable.graph == graph``.
158 May be ``self`` if ``graph == self.graph``. Elements are
159 equivalent to those that would be created by calling
160 `DataCoordinate.subset` on all elements in ``self``, possibly
161 with deduplication and/or reordering (depending on the subclass,
162 which may make more specific guarantees).
163 """
164 raise NotImplementedError()
167class _ScalarDataCoordinateIterable(DataCoordinateIterable):
168 """An iterable for a single `DataCoordinate`.
170 A `DataCoordinateIterable` implementation that adapts a single
171 `DataCoordinate` instance.
173 This class should only be used directly by other code in the module in
174 which it is defined; all other code should interact with it only through
175 the `DataCoordinateIterable` interface.
177 Parameters
178 ----------
179 dataId : `DataCoordinate`
180 The data ID to adapt.
181 """
183 def __init__(self, dataId: DataCoordinate):
184 self._dataId = dataId
186 __slots__ = ("_dataId",)
188 def __iter__(self) -> Iterator[DataCoordinate]:
189 yield self._dataId
191 def __len__(self) -> int:
192 return 1
194 def __contains__(self, key: Any) -> bool:
195 if isinstance(key, DataCoordinate):
196 return key == self._dataId
197 else:
198 return False
200 @property
201 def graph(self) -> DimensionGraph:
202 # Docstring inherited from DataCoordinateIterable.
203 return self._dataId.graph
205 def hasFull(self) -> bool:
206 # Docstring inherited from DataCoordinateIterable.
207 return self._dataId.hasFull()
209 def hasRecords(self) -> bool:
210 # Docstring inherited from DataCoordinateIterable.
211 return self._dataId.hasRecords()
213 def subset(self, graph: DimensionGraph) -> _ScalarDataCoordinateIterable:
214 # Docstring inherited from DataCoordinateIterable.
215 return _ScalarDataCoordinateIterable(self._dataId.subset(graph))
218class _DataCoordinateCollectionBase(DataCoordinateIterable):
219 """A partial iterable implementation backed by native Python collection.
221 A partial `DataCoordinateIterable` implementation that is backed by a
222 native Python collection.
224 This class is intended only to be used as an intermediate base class for
225 `DataCoordinateIterables` that assume a more specific type of collection
226 and can hence make more informed choices for how to implement some methods.
228 Parameters
229 ----------
230 dataIds : `collections.abc.Collection` [ `DataCoordinate` ]
231 A collection of `DataCoordinate` instances, with dimensions equal to
232 ``graph``.
233 graph : `DimensionGraph`
234 Dimensions identified by all data IDs in the set.
235 hasFull : `bool`, optional
236 If `True`, the caller guarantees that `DataCoordinate.hasFull` returns
237 `True` for all given data IDs. If `False`, no such guarantee is made,
238 and `hasFull` will always return `False`. If `None` (default),
239 `hasFull` will be computed from the given data IDs, immediately if
240 ``check`` is `True`, or on first use if ``check`` is `False`.
241 hasRecords : `bool`, optional
242 If `True`, the caller guarantees that `DataCoordinate.hasRecords`
243 returns `True` for all given data IDs. If `False`, no such guarantee
244 is made and `hasRecords` will always return `False`. If `None`
245 (default), `hasRecords` will be computed from the given data IDs,
246 immediately if ``check`` is `True`, or on first use if ``check`` is
247 `False`.
248 check: `bool`, optional
249 If `True` (default) check that all data IDs are consistent with the
250 given ``graph`` and state flags at construction. If `False`, no
251 checking will occur.
252 """
254 def __init__(
255 self,
256 dataIds: Collection[DataCoordinate],
257 graph: DimensionGraph,
258 *,
259 hasFull: Optional[bool] = None,
260 hasRecords: Optional[bool] = None,
261 check: bool = True,
262 ):
263 self._dataIds = dataIds
264 self._graph = graph
265 if check:
266 for dataId in self._dataIds:
267 if hasFull and not dataId.hasFull():
268 raise ValueError(f"{dataId} is not complete, but is required to be.")
269 if hasRecords and not dataId.hasRecords():
270 raise ValueError(f"{dataId} has no records, but is required to.")
271 if dataId.graph != self._graph:
272 raise ValueError(f"Bad dimensions {dataId.graph}; expected {self._graph}.")
273 if hasFull is None:
274 hasFull = all(dataId.hasFull() for dataId in self._dataIds)
275 if hasRecords is None:
276 hasRecords = all(dataId.hasRecords() for dataId in self._dataIds)
277 self._hasFull = hasFull
278 self._hasRecords = hasRecords
280 __slots__ = ("_graph", "_dataIds", "_hasFull", "_hasRecords")
282 @property
283 def graph(self) -> DimensionGraph:
284 # Docstring inherited from DataCoordinateIterable.
285 return self._graph
287 def hasFull(self) -> bool:
288 # Docstring inherited from DataCoordinateIterable.
289 if self._hasFull is None:
290 self._hasFull = all(dataId.hasFull() for dataId in self._dataIds)
291 return self._hasFull
293 def hasRecords(self) -> bool:
294 # Docstring inherited from DataCoordinateIterable.
295 if self._hasRecords is None:
296 self._hasRecords = all(dataId.hasRecords() for dataId in self._dataIds)
297 return self._hasRecords
299 def toSet(self) -> DataCoordinateSet:
300 # Docstring inherited from DataCoordinateIterable.
301 # Override base class to pass in attributes instead of results of
302 # method calls for _hasFull and _hasRecords - those can be None,
303 # and hence defer checking if that's what the user originally wanted.
304 return DataCoordinateSet(
305 frozenset(self._dataIds),
306 graph=self._graph,
307 hasFull=self._hasFull,
308 hasRecords=self._hasRecords,
309 check=False,
310 )
312 def toSequence(self) -> DataCoordinateSequence:
313 # Docstring inherited from DataCoordinateIterable.
314 # Override base class to pass in attributes instead of results of
315 # method calls for _hasFull and _hasRecords - those can be None,
316 # and hence defer checking if that's what the user originally wanted.
317 return DataCoordinateSequence(
318 tuple(self._dataIds),
319 graph=self._graph,
320 hasFull=self._hasFull,
321 hasRecords=self._hasRecords,
322 check=False,
323 )
325 def __iter__(self) -> Iterator[DataCoordinate]:
326 return iter(self._dataIds)
328 def __len__(self) -> int:
329 return len(self._dataIds)
331 def __contains__(self, key: Any) -> bool:
332 key = DataCoordinate.standardize(key, universe=self.universe)
333 return key in self._dataIds
335 def _subsetKwargs(self, graph: DimensionGraph) -> Dict[str, Any]:
336 """Return constructor kwargs useful for subclasses implementing subset.
338 Parameters
339 ----------
340 graph : `DimensionGraph`
341 Dimensions passed to `subset`.
343 Returns
344 -------
345 **kwargs
346 A dict with `hasFull`, `hasRecords`, and `check` keys, associated
347 with the appropriate values for a `subset` operation with the given
348 dimensions.
349 """
350 hasFull: Optional[bool]
351 if graph.dimensions <= self.graph.required:
352 hasFull = True
353 else:
354 hasFull = self._hasFull
355 return dict(hasFull=hasFull, hasRecords=self._hasRecords, check=False)
358class DataCoordinateSet(_DataCoordinateCollectionBase):
359 """Iterable iteration that is set-like.
361 A `DataCoordinateIterable` implementation that adds some set-like
362 functionality, and is backed by a true set-like object.
364 Parameters
365 ----------
366 dataIds : `collections.abc.Set` [ `DataCoordinate` ]
367 A set of `DataCoordinate` instances, with dimensions equal to
368 ``graph``. If this is a mutable object, the caller must be able to
369 guarantee that it will not be modified by any other holders.
370 graph : `DimensionGraph`
371 Dimensions identified by all data IDs in the set.
372 hasFull : `bool`, optional
373 If `True`, the caller guarantees that `DataCoordinate.hasFull` returns
374 `True` for all given data IDs. If `False`, no such guarantee is made,
375 and `DataCoordinateSet.hasFull` will always return `False`. If `None`
376 (default), `DataCoordinateSet.hasFull` will be computed from the given
377 data IDs, immediately if ``check`` is `True`, or on first use if
378 ``check`` is `False`.
379 hasRecords : `bool`, optional
380 If `True`, the caller guarantees that `DataCoordinate.hasRecords`
381 returns `True` for all given data IDs. If `False`, no such guarantee
382 is made and `DataCoordinateSet.hasRecords` will always return `False`.
383 If `None` (default), `DataCoordinateSet.hasRecords` will be computed
384 from the given data IDs, immediately if ``check`` is `True`, or on
385 first use if ``check`` is `False`.
386 check: `bool`, optional
387 If `True` (default) check that all data IDs are consistent with the
388 given ``graph`` and state flags at construction. If `False`, no
389 checking will occur.
391 Notes
392 -----
393 `DataCoordinateSet` does not formally implement the `collections.abc.Set`
394 interface, because that requires many binary operations to accept any
395 set-like object as the other argument (regardless of what its elements
396 might be), and it's much easier to ensure those operations never behave
397 surprisingly if we restrict them to `DataCoordinateSet` or (sometimes)
398 `DataCoordinateIterable`, and in most cases restrict that they identify
399 the same dimensions. In particular:
401 - a `DataCoordinateSet` will compare as not equal to any object that is
402 not a `DataCoordinateSet`, even native Python sets containing the exact
403 same elements;
405 - subset/superset comparison _operators_ (``<``, ``>``, ``<=``, ``>=``)
406 require both operands to be `DataCoordinateSet` instances that have the
407 same dimensions (i.e. ``graph`` attribute);
409 - `issubset`, `issuperset`, and `isdisjoint` require the other argument to
410 be a `DataCoordinateIterable` with the same dimensions;
412 - operators that create new sets (``&``, ``|``, ``^``, ``-``) require both
413 operands to be `DataCoordinateSet` instances that have the same
414 dimensions _and_ the same ``dtype``;
416 - named methods that create new sets (`intersection`, `union`,
417 `symmetric_difference`, `difference`) require the other operand to be a
418 `DataCoordinateIterable` with the same dimensions _and_ the same
419 ``dtype``.
421 In addition, when the two operands differ in the return values of `hasFull`
422 and/or `hasRecords`, we make no guarantees about what those methods will
423 return on the new `DataCoordinateSet` (other than that they will accurately
424 reflect what elements are in the new set - we just don't control which
425 elements are contributed by each operand).
426 """
428 def __init__(
429 self,
430 dataIds: AbstractSet[DataCoordinate],
431 graph: DimensionGraph,
432 *,
433 hasFull: Optional[bool] = None,
434 hasRecords: Optional[bool] = None,
435 check: bool = True,
436 ):
437 super().__init__(dataIds, graph, hasFull=hasFull, hasRecords=hasRecords, check=check)
439 _dataIds: AbstractSet[DataCoordinate]
441 __slots__ = ()
443 def __str__(self) -> str:
444 return str(set(self._dataIds))
446 def __repr__(self) -> str:
447 return (
448 f"DataCoordinateSet({set(self._dataIds)}, {self._graph!r}, "
449 f"hasFull={self._hasFull}, hasRecords={self._hasRecords})"
450 )
452 def __eq__(self, other: Any) -> bool:
453 if isinstance(other, DataCoordinateSet):
454 return self._graph == other._graph and self._dataIds == other._dataIds
455 return False
457 def __le__(self, other: DataCoordinateSet) -> bool:
458 if self.graph != other.graph:
459 raise ValueError(f"Inconsistent dimensions in set comparision: {self.graph} != {other.graph}.")
460 return self._dataIds <= other._dataIds
462 def __ge__(self, other: DataCoordinateSet) -> bool:
463 if self.graph != other.graph:
464 raise ValueError(f"Inconsistent dimensions in set comparision: {self.graph} != {other.graph}.")
465 return self._dataIds >= other._dataIds
467 def __lt__(self, other: DataCoordinateSet) -> bool:
468 if self.graph != other.graph:
469 raise ValueError(f"Inconsistent dimensions in set comparision: {self.graph} != {other.graph}.")
470 return self._dataIds < other._dataIds
472 def __gt__(self, other: DataCoordinateSet) -> bool:
473 if self.graph != other.graph:
474 raise ValueError(f"Inconsistent dimensions in set comparision: {self.graph} != {other.graph}.")
475 return self._dataIds > other._dataIds
477 def issubset(self, other: DataCoordinateIterable) -> bool:
478 """Test whether ``self`` contains all data IDs in ``other``.
480 Parameters
481 ----------
482 other : `DataCoordinateIterable`
483 An iterable of data IDs with ``other.graph == self.graph``.
485 Returns
486 -------
487 issubset : `bool`
488 `True` if all data IDs in ``self`` are also in ``other``, and
489 `False` otherwise.
490 """
491 if self.graph != other.graph:
492 raise ValueError(f"Inconsistent dimensions in set comparision: {self.graph} != {other.graph}.")
493 return self._dataIds <= other.toSet()._dataIds
495 def issuperset(self, other: DataCoordinateIterable) -> bool:
496 """Test whether ``other`` contains all data IDs in ``self``.
498 Parameters
499 ----------
500 other : `DataCoordinateIterable`
501 An iterable of data IDs with ``other.graph == self.graph``.
503 Returns
504 -------
505 issuperset : `bool`
506 `True` if all data IDs in ``other`` are also in ``self``, and
507 `False` otherwise.
508 """
509 if self.graph != other.graph:
510 raise ValueError(f"Inconsistent dimensions in set comparision: {self.graph} != {other.graph}.")
511 return self._dataIds >= other.toSet()._dataIds
513 def isdisjoint(self, other: DataCoordinateIterable) -> bool:
514 """Test whether there are no data IDs in both ``self`` and ``other``.
516 Parameters
517 ----------
518 other : `DataCoordinateIterable`
519 An iterable of data IDs with ``other.graph == self.graph``.
521 Returns
522 -------
523 isdisjoint : `bool`
524 `True` if there are no data IDs in both ``self`` and ``other``, and
525 `False` otherwise.
526 """
527 if self.graph != other.graph:
528 raise ValueError(f"Inconsistent dimensions in set comparision: {self.graph} != {other.graph}.")
529 return self._dataIds.isdisjoint(other.toSet()._dataIds)
531 def __and__(self, other: DataCoordinateSet) -> DataCoordinateSet:
532 if self.graph != other.graph:
533 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
534 return DataCoordinateSet(self._dataIds & other._dataIds, self.graph, check=False)
536 def __or__(self, other: DataCoordinateSet) -> DataCoordinateSet:
537 if self.graph != other.graph:
538 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
539 return DataCoordinateSet(self._dataIds | other._dataIds, self.graph, check=False)
541 def __xor__(self, other: DataCoordinateSet) -> DataCoordinateSet:
542 if self.graph != other.graph:
543 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
544 return DataCoordinateSet(self._dataIds ^ other._dataIds, self.graph, check=False)
546 def __sub__(self, other: DataCoordinateSet) -> DataCoordinateSet:
547 if self.graph != other.graph:
548 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
549 return DataCoordinateSet(self._dataIds - other._dataIds, self.graph, check=False)
551 def intersection(self, other: DataCoordinateIterable) -> DataCoordinateSet:
552 """Return a new set that contains all data IDs from parameters.
554 Parameters
555 ----------
556 other : `DataCoordinateIterable`
557 An iterable of data IDs with ``other.graph == self.graph``.
559 Returns
560 -------
561 intersection : `DataCoordinateSet`
562 A new `DataCoordinateSet` instance.
563 """
564 if self.graph != other.graph:
565 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
566 return DataCoordinateSet(self._dataIds & other.toSet()._dataIds, self.graph, check=False)
568 def union(self, other: DataCoordinateIterable) -> DataCoordinateSet:
569 """Return a new set that contains all data IDs in either parameters.
571 Parameters
572 ----------
573 other : `DataCoordinateIterable`
574 An iterable of data IDs with ``other.graph == self.graph``.
576 Returns
577 -------
578 intersection : `DataCoordinateSet`
579 A new `DataCoordinateSet` instance.
580 """
581 if self.graph != other.graph:
582 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
583 return DataCoordinateSet(self._dataIds | other.toSet()._dataIds, self.graph, check=False)
585 def symmetric_difference(self, other: DataCoordinateIterable) -> DataCoordinateSet:
586 """Return a new set with all data IDs in either parameters, not both.
588 Parameters
589 ----------
590 other : `DataCoordinateIterable`
591 An iterable of data IDs with ``other.graph == self.graph``.
593 Returns
594 -------
595 intersection : `DataCoordinateSet`
596 A new `DataCoordinateSet` instance.
597 """
598 if self.graph != other.graph:
599 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
600 return DataCoordinateSet(self._dataIds ^ other.toSet()._dataIds, self.graph, check=False)
602 def difference(self, other: DataCoordinateIterable) -> DataCoordinateSet:
603 """Return a new set with all data IDs in this that are not in other.
605 Parameters
606 ----------
607 other : `DataCoordinateIterable`
608 An iterable of data IDs with ``other.graph == self.graph``.
610 Returns
611 -------
612 intersection : `DataCoordinateSet`
613 A new `DataCoordinateSet` instance.
614 """
615 if self.graph != other.graph:
616 raise ValueError(f"Inconsistent dimensions in set operation: {self.graph} != {other.graph}.")
617 return DataCoordinateSet(self._dataIds - other.toSet()._dataIds, self.graph, check=False)
619 def toSet(self) -> DataCoordinateSet:
620 # Docstring inherited from DataCoordinateIterable.
621 return self
623 def subset(self, graph: DimensionGraph) -> DataCoordinateSet:
624 """Return a set whose data IDs identify a subset.
626 Parameters
627 ----------
628 graph : `DimensionGraph`
629 Dimensions to be identified by the data IDs in the returned
630 iterable. Must be a subset of ``self.graph``.
632 Returns
633 -------
634 set : `DataCoordinateSet`
635 A `DataCoordinateSet` with ``set.graph == graph``.
636 Will be ``self`` if ``graph == self.graph``. Elements are
637 equivalent to those that would be created by calling
638 `DataCoordinate.subset` on all elements in ``self``, with
639 deduplication but and in arbitrary order.
640 """
641 if graph == self.graph:
642 return self
643 return DataCoordinateSet(
644 {dataId.subset(graph) for dataId in self._dataIds}, graph, **self._subsetKwargs(graph)
645 )
648class DataCoordinateSequence(_DataCoordinateCollectionBase, Sequence[DataCoordinate]):
649 """Iterable supporting the full Sequence interface.
651 A `DataCoordinateIterable` implementation that supports the full
652 `collections.abc.Sequence` interface.
654 Parameters
655 ----------
656 dataIds : `collections.abc.Sequence` [ `DataCoordinate` ]
657 A sequence of `DataCoordinate` instances, with dimensions equal to
658 ``graph``.
659 graph : `DimensionGraph`
660 Dimensions identified by all data IDs in the set.
661 hasFull : `bool`, optional
662 If `True`, the caller guarantees that `DataCoordinate.hasFull` returns
663 `True` for all given data IDs. If `False`, no such guarantee is made,
664 and `DataCoordinateSet.hasFull` will always return `False`. If `None`
665 (default), `DataCoordinateSet.hasFull` will be computed from the given
666 data IDs, immediately if ``check`` is `True`, or on first use if
667 ``check`` is `False`.
668 hasRecords : `bool`, optional
669 If `True`, the caller guarantees that `DataCoordinate.hasRecords`
670 returns `True` for all given data IDs. If `False`, no such guarantee
671 is made and `DataCoordinateSet.hasRecords` will always return `False`.
672 If `None` (default), `DataCoordinateSet.hasRecords` will be computed
673 from the given data IDs, immediately if ``check`` is `True`, or on
674 first use if ``check`` is `False`.
675 check: `bool`, optional
676 If `True` (default) check that all data IDs are consistent with the
677 given ``graph`` and state flags at construction. If `False`, no
678 checking will occur.
679 """
681 def __init__(
682 self,
683 dataIds: Sequence[DataCoordinate],
684 graph: DimensionGraph,
685 *,
686 hasFull: Optional[bool] = None,
687 hasRecords: Optional[bool] = None,
688 check: bool = True,
689 ):
690 super().__init__(tuple(dataIds), graph, hasFull=hasFull, hasRecords=hasRecords, check=check)
692 _dataIds: Sequence[DataCoordinate]
694 __slots__ = ()
696 def __str__(self) -> str:
697 return str(tuple(self._dataIds))
699 def __repr__(self) -> str:
700 return (
701 f"DataCoordinateSequence({tuple(self._dataIds)}, {self._graph!r}, "
702 f"hasFull={self._hasFull}, hasRecords={self._hasRecords})"
703 )
705 def __eq__(self, other: Any) -> bool:
706 if isinstance(other, DataCoordinateSequence):
707 return self._graph == other._graph and self._dataIds == other._dataIds
708 return False
710 @overload
711 def __getitem__(self, index: int) -> DataCoordinate:
712 pass
714 @overload # noqa: F811 (FIXME: remove for py 3.8+)
715 def __getitem__(self, index: slice) -> DataCoordinateSequence: # noqa: F811
716 pass
718 def __getitem__(self, index: Any) -> Any: # noqa: F811
719 r = self._dataIds[index]
720 if isinstance(index, slice):
721 return DataCoordinateSequence(
722 r, self._graph, hasFull=self._hasFull, hasRecords=self._hasRecords, check=False
723 )
724 return r
726 def toSequence(self) -> DataCoordinateSequence:
727 # Docstring inherited from DataCoordinateIterable.
728 return self
730 def subset(self, graph: DimensionGraph) -> DataCoordinateSequence:
731 """Return a sequence whose data IDs identify a subset.
733 Parameters
734 ----------
735 graph : `DimensionGraph`
736 Dimensions to be identified by the data IDs in the returned
737 iterable. Must be a subset of ``self.graph``.
739 Returns
740 -------
741 set : `DataCoordinateSequence`
742 A `DataCoordinateSequence` with ``set.graph == graph``.
743 Will be ``self`` if ``graph == self.graph``. Elements are
744 equivalent to those that would be created by calling
745 `DataCoordinate.subset` on all elements in ``self``, in the same
746 order and with no deduplication.
747 """
748 if graph == self.graph:
749 return self
750 return DataCoordinateSequence(
751 tuple(dataId.subset(graph) for dataId in self._dataIds), graph, **self._subsetKwargs(graph)
752 )