Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/lsst/daf/butler/_butler_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ def query_info(
include_summary: bool = False,
include_doc: bool = False,
summary_datasets: Iterable[DatasetType] | Iterable[str] | None = None,
ignore_missing: bool = False,
) -> Sequence[CollectionInfo]:
"""Query the butler for collections matching an expression and
return detailed information about those collections.
Expand Down
40 changes: 40 additions & 0 deletions python/lsst/daf/butler/_rubin/synchronize_collection_structure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from collections.abc import Iterable

from .._butler import Butler
from .._butler_collections import CollectionInfo
from .._collection_type import CollectionType
from .._exceptions import CollectionTypeError


def synchronize_collection_structure(
source_butler: Butler, target_butler: Butler, collections: Iterable[str]
) -> None:
source_info = source_butler.collections.query_info(
collections, include_doc=True, include_chains=True, flatten_chains=True
)
target_info = target_butler.collections.query_info([c.name for c in source_info], ignore_missing=True)
target_map = {c.name: c for c in target_info}

missing_collections: list[CollectionInfo] = []
mismatched_children: dict[str, tuple[str, ...]] = {}
for source_collection in source_info:
target_collection = target_map.get(source_collection.name)
if not target_collection:
missing_collections.append(source_collection)
if source_collection.type == CollectionType.CHAINED:
mismatched_children[source_collection.name] = source_collection.children
elif source_collection.type != target_collection.type:
raise CollectionTypeError(
f"Collection '{source_collection.name}' has different types in source and target repos."
f" Source: {source_collection.type.name} Target: {target_collection.type.name}"
)
elif source_collection.children != target_collection.children:
mismatched_children[source_collection.name] = source_collection.children

for collection in missing_collections:
print(f"register {collection.name}")
target_butler.collections.register(collection.name, type=collection.type, doc=collection.doc)

for parent, children in mismatched_children.items():
print(f"define collection chain {parent}: {children}")
target_butler.collections.redefine_chain(parent, children)
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def query_info(
include_summary: bool = False,
include_doc: bool = False,
summary_datasets: Iterable[DatasetType] | Iterable[str] | None = None,
ignore_missing: bool = False,
) -> Sequence[CollectionInfo]:
info = []
if collection_types is None:
Expand All @@ -128,6 +129,7 @@ def query_info(
collection_types=collection_types,
flatten_chains=flatten_chains,
include_chains=include_chains,
ignore_missing=ignore_missing,
)

summaries: Mapping[Any, CollectionSummary] = {}
Expand Down
24 changes: 17 additions & 7 deletions python/lsst/daf/butler/registry/collections/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,11 @@ def find(self, name: str) -> CollectionRecord[K]:
return result

def _find_many(
self, names: Iterable[str], flatten_chains: bool, collection_cache: CollectionRecordCache | None
self,
names: Iterable[str],
flatten_chains: bool,
collection_cache: CollectionRecordCache | None,
ignore_missing: bool = False,
) -> list[CollectionRecord[K]]:
"""Return multiple records given their names.

Expand Down Expand Up @@ -373,11 +377,14 @@ def check_cache(
records[record.name] = record
self._addCachedRecord(record, collection_cache)

missing_names = [name for name in names if name not in records]
if len(missing_names) == 1:
raise MissingCollectionError(f"No collection with name '{missing_names[0]}' found.")
elif len(missing_names) > 1:
raise MissingCollectionError(f"No collections with names '{' '.join(missing_names)}' found.")
if ignore_missing:
names = [name for name in names if name in records]
else:
missing_names = [name for name in names if name not in records]
if len(missing_names) == 1:
raise MissingCollectionError(f"No collection with name '{missing_names[0]}' found.")
elif len(missing_names) > 1:
raise MissingCollectionError(f"No collections with names '{' '.join(missing_names)}' found.")

def order(names: Iterable[str]) -> Iterator[CollectionRecord[K]]:
for name in names:
Expand Down Expand Up @@ -409,6 +416,7 @@ def resolve_wildcard(
collection_types: Set[CollectionType] = CollectionType.all(),
flatten_chains: bool = True,
include_chains: bool | None = None,
ignore_missing: bool = False,
) -> list[CollectionRecord[K]]:
# Docstring inherited
include_chains = include_chains if include_chains is not None else not flatten_chains
Expand Down Expand Up @@ -451,7 +459,9 @@ def filter_types(records: Iterable[CollectionRecord[K]]) -> Iterator[CollectionR
if explicit_names:
# _find_many() returns correctly ordered records, but there may be
# duplicates.
for record in filter_types(self._find_many(explicit_names, flatten_chains, cache)):
for record in filter_types(
self._find_many(explicit_names, flatten_chains, cache, ignore_missing=ignore_missing)
):
if record.key not in done_keys:
result.append(record)
done_keys.add(record.key)
Expand Down
1 change: 1 addition & 0 deletions python/lsst/daf/butler/registry/interfaces/_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ def resolve_wildcard(
collection_types: Set[CollectionType] = CollectionType.all(),
flatten_chains: bool = True,
include_chains: bool | None = None,
ignore_missing: bool = False,
) -> list[CollectionRecord[_Key]]:
"""Iterate over collection records that match a wildcard.

Expand Down
Loading