-
Notifications
You must be signed in to change notification settings - Fork 257
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
feat: support nesting dependency groups #837
base: main
Are you sure you want to change the base?
feat: support nesting dependency groups #837
Conversation
Reviewer's Guide by SourceryThis PR introduces support for nested dependency groups. It enables a dependency group to include other groups, extends the validation logic to detect cyclic inclusions, and updates the dependency resolution to incorporate dependencies from included groups. The implementation is supported by new tests that cover various nesting scenarios, including direct, indirect, cyclic, and unknown group references. Sequence diagram for nested dependency group resolutionsequenceDiagram
participant C as Client
participant DG as DependencyGroup
participant D as Dependency
C->>DG: Access dependencies property
DG->>DG: Call _resolve_included_dependency_groups()
loop For each included DependencyGroup
DG->>DG: Iterate over included_dependency_groups
loop For each dependency in included group
DG->>D: dependency.clone()
D-->>DG: Returns cloned dependency
DG->>D: dependency.move_to_groups([current_group.name])
end
DG->>DG: Append resolved dependency
end
DG-->>C: Return combined dependencies (local + included)
Class diagram for DependencyGroup and DependencyclassDiagram
class DependencyGroup {
- list~Dependency~ _dependencies
- list~Dependency~ _poetry_dependencies
- dict~NormalizedName, DependencyGroup~ _included_dependency_groups
+ dependencies: list~Dependency~
+ dependencies_for_locking: list~Dependency~
+ include_dependency_group(dependency_group: DependencyGroup): void
+ _resolve_included_dependency_groups(): list~Dependency~
}
class Dependency {
+ clone(): Dependency
+ move_to_groups(groups: Iterable~string~): void
}
%% Relationship: DependencyGroup uses Dependency
DependencyGroup o-- Dependency : includes
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
acbfec1
to
2ee59c0
Compare
@sourcery-ai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @finswimmer - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider adding a method to
DependencyGroup
to check if a group is already included to avoid thetry-except
block in_configure_package_dependencies
. - The
move_to_groups
method inDependency
could use a more descriptive name.
Here's what I looked at during the review
- 🟡 General issues: 1 issue found
- 🟢 Security: all looks good
- 🟡 Testing: 1 issue found
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
5291fcc
to
206465f
Compare
@sourcery-ai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @finswimmer - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider adding a method to
DependencyGroup
to check if a group is already included to avoid the try/except inFactory._configure_package_dependencies
. - The
move_to_groups
method inDependency
could use a clearer name, likeadd_group_inheritance
.
Here's what I looked at during the review
- 🟢 General issues: all looks good
- 🟢 Security: all looks good
- 🟡 Testing: 3 issues found
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
||
group.include_dependency_group(group_2) | ||
|
||
assert [dep.name for dep in group.dependencies] == ["foo", "bar"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (testing): Test edge case where included group is empty.
Add a test case where group_2
is empty to ensure correct behavior when including an empty group.
Suggested implementation:
# Existing tests above...
def test_include_empty_dependency_group() -> None:
group_main = DependencyGroup(name="group")
group_main.add_poetry_dependency(
Dependency(name="foo", constraint="*", groups=["group"])
)
empty_group = DependencyGroup(name="empty")
# Include empty dependency group
group_main.include_dependency_group(empty_group)
# Assert that including an empty group does not affect group_main's dependencies.
assert [dep.name for dep in group_main.dependencies] == ["foo"]
assert [dep.name for dep in group_main.dependencies_for_locking] == ["foo"]
206465f
to
445a089
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have skipped reviewing this pull request. It looks like we've already tried to review the commit 445a089 in this pull request and failed.
2f8aabd
to
f0c551f
Compare
f0c551f
to
cb50fd3
Compare
@sourcery-ai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @finswimmer - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider adding a method to
Package
to encapsulate the logic for adding dependencies from included groups, instead of handling it directly in_configure_package_dependencies
. - The error message for cyclic dependencies could be improved to include the full cycle path for easier debugging.
Here's what I looked at during the review
- 🟢 General issues: all looks good
- 🟢 Security: all looks good
- 🟡 Testing: 1 issue found
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
) | ||
|
||
for group_name, group_config in tool_poetry["group"].items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to iterate a second time over tool_poetry["group"].items()
and not just adding included groups and dependencies in one iteration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can only include a dependency group into another one if I have an instance of this dependency group.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. In that case, it is just unlucky that this is not covered by the tests. I suppose adding a variant of test_create_poetry_with_nested_dependency_groups
with swapped dependency groups will cover this case.
|
||
@property | ||
def dependencies_for_locking(self) -> list[Dependency]: | ||
included_group_dependencies = self._resolve_included_dependency_groups() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make no difference at the moment, but I think this is wrong because _resolve_included_dependency_groups
uses dependencies
instead of dependencies_for_locking
. I think we need two versions of _resolve_included_dependency_groups
(or pass a parameter that tells which dependencies to use).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I guess I need more information about what's the difference between dependencies
and dependencies_for_locking
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you do not need to know the difference to consider it wrong: It feels clear to me that nesting groups should not violate the distinction between the two.
Nevertheless: dependencies
are used for metadata (when building), dependencies_for_locking
are used for locking. This distinction is probably not that relevant for groups because we only need dependencies_for_locking
and do not use dependencies
but that does not mean that we should not care about implementing it correctly.
@@ -1060,3 +1060,247 @@ def test_poetry_build_system_dependencies( | |||
poetry = Factory().create_poetry(temporary_directory) | |||
|
|||
assert set(poetry.build_system_dependencies) == expected | |||
|
|||
|
|||
def test_create_poetry_with_nested_dependency_groups(temporary_directory: Path) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parametrize
with different names (test normalization).
15626f4
to
912bc7a
Compare
912bc7a
to
8272314
Compare
3430202
to
61ca884
Compare
61ca884
to
c6ff173
Compare
Include dependency groups into other dependency groups was requested earlier, e.g. python-poetry/poetry#7494. Also PEP 735 describes this mechanism.
include-groups
poetry#10166Summary by Sourcery
New Features:
Summary by Sourcery
Add support for nested dependency groups.
New Features:
Tests:
Summary by Sourcery
Add support for nested dependency groups.
New Features:
Tests: