Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify suffix_union function #531

Closed
wants to merge 2 commits into from
Closed
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
40 changes: 22 additions & 18 deletions conda_lock/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ def ordered_union(collections: Iterable[Iterable[T]]) -> List[T]:
return list({k: k for k in chain.from_iterable(collections)}.values())


def suffix_union(
collections: Iterable[Sequence["SupportsRichComparisonT"]],
) -> List["SupportsRichComparisonT"]:
def suffix_union(collections: Iterable[Sequence]) -> List:
"""Generates the union of sequence ensuring that they have a common suffix.

This is used to unify channels.
Expand All @@ -92,25 +90,31 @@ def suffix_union(
>>> suffix_union([[1], [2, 1], [4, 1]])
Traceback (most recent call last)
...
RuntimeError: [4, 1] is not a subset of [2, 1]
ValueError: [4, 1] is not an ordered subset of [2, 1]

"""
from genericpath import commonprefix
return list(reversed(prefix_union([list(reversed(s)) for s in collections])))

result: List["SupportsRichComparisonT"] = []

def prefix_union(collections: Iterable[Sequence]) -> List:
Copy link
Contributor

@jacksmith15 jacksmith15 Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems an improvement to me!

I wonder if part of the confusion has stemmed from the generic naming of these functions.

Could it be instead called unify_package_sources or merge_package_sources?

FWIW the way I reason about this logic is a bit different, not sure if this is more or less confusing to others (probably a tad less performant):

def unify_package_sources(collections: Iterable[Sequence[str]]) -> List[str]:
    result = max(collections, key=len)
    for collection in collections:
        if collection != result[-len(collection):]:
            raise ValueError(f"{collection} is not an ordered subset of {result}")
    return result

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!! I think there's an edge case when len(collection) == 0. But making sure that's in order, would you like to make a PR to supersede mine?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to do so 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""Generates the union of sequence ensuring that they have a common prefix.

>>> prefix_union([[1], [1, 2], [1, 2, 3], [1, 2], [1]])
[1, 2, 3]

>>> prefix_union([[1], [1, 2], [1, 4]])
Traceback (most recent call last)
...
ValueError: [1, 4] is not an ordered subset of [1, 2]
"""
result: List = []
for seq in collections:
if seq:
rev_priority = list(reversed(seq))
prefix = commonprefix([result, rev_priority]) # type: ignore
if len(result) == 0:
result = rev_priority
elif prefix[: len(rev_priority)] != result[: len(rev_priority)]:
raise ValueError(
f"{list(reversed(rev_priority))} is not a ordered subset of {list(reversed(result))}"
)
elif len(rev_priority) > len(result):
result = rev_priority
return list(reversed(result))
for i, item in enumerate(seq):
if i >= len(result):
result.append(item)
elif result[i] != item:
raise ValueError(f"{seq} is not an ordered subset of {result}")
return result


def relative_path(source: pathlib.Path, target: pathlib.Path) -> str:
Expand Down