@@ -670,19 +670,29 @@ def complete_package(
670
670
671
671
self .debug (f"<debug>Duplicate dependencies for { dep_name } </debug>" )
672
672
673
- non_direct_origin_deps : list [Dependency ] = []
674
- direct_origin_deps : list [Dependency ] = []
675
- for dep in deps :
676
- if dep .is_direct_origin ():
677
- direct_origin_deps .append (dep )
678
- else :
679
- non_direct_origin_deps .append (dep )
680
- deps = (
681
- self ._merge_dependencies_by_constraint (
682
- self ._merge_dependencies_by_marker (non_direct_origin_deps )
673
+ # Group dependencies for merging.
674
+ # We must not merge dependencies from different sources!
675
+ dep_groups = self ._group_by_source (deps )
676
+ deps = []
677
+ for group in dep_groups :
678
+ # In order to reduce the number of overrides we merge duplicate
679
+ # dependencies by constraint. For instance, if we have:
680
+ # - foo (>=2.0) ; python_version >= "3.6" and python_version < "3.7"
681
+ # - foo (>=2.0) ; python_version >= "3.7"
682
+ # we can avoid two overrides by merging them to:
683
+ # - foo (>=2.0) ; python_version >= "3.6"
684
+ # However, if we want to merge dependencies by constraint we have to
685
+ # merge dependencies by markers first in order to avoid unnecessary
686
+ # solver failures. For instance, if we have:
687
+ # - foo (>=2.0) ; python_version >= "3.6" and python_version < "3.7"
688
+ # - foo (>=2.0) ; python_version >= "3.7"
689
+ # - foo (<2.1) ; python_version >= "3.7"
690
+ # we must not merge the first two constraints but the last two:
691
+ # - foo (>=2.0) ; python_version >= "3.6" and python_version < "3.7"
692
+ # - foo (>=2.0,<2.1) ; python_version >= "3.7"
693
+ deps += self ._merge_dependencies_by_constraint (
694
+ self ._merge_dependencies_by_marker (group )
683
695
)
684
- + direct_origin_deps
685
- )
686
696
if len (deps ) == 1 :
687
697
self .debug (f"<debug>Merging requirements for { deps [0 ]!s} </debug>" )
688
698
dependencies .append (deps [0 ])
@@ -947,9 +957,33 @@ def debug(self, message: str, depth: int = 0) -> None:
947
957
948
958
self ._io .write (debug_info )
949
959
960
+ def _group_by_source (
961
+ self , dependencies : Iterable [Dependency ]
962
+ ) -> list [list [Dependency ]]:
963
+ """
964
+ Takes a list of dependencies and returns a list of groups of dependencies,
965
+ each group containing all dependencies from the same source.
966
+ """
967
+ groups : list [list [Dependency ]] = []
968
+ for dep in dependencies :
969
+ for group in groups :
970
+ if (
971
+ dep .is_same_source_as (group [0 ])
972
+ and dep .source_name == group [0 ].source_name
973
+ ):
974
+ group .append (dep )
975
+ break
976
+ else :
977
+ groups .append ([dep ])
978
+ return groups
979
+
950
980
def _merge_dependencies_by_constraint (
951
981
self , dependencies : Iterable [Dependency ]
952
982
) -> list [Dependency ]:
983
+ """
984
+ Merge dependencies with the same constraint
985
+ by building a union of their markers.
986
+ """
953
987
by_constraint : dict [VersionConstraint , list [Dependency ]] = defaultdict (list )
954
988
for dep in dependencies :
955
989
by_constraint [dep .constraint ].append (dep )
@@ -975,6 +1009,10 @@ def _merge_dependencies_by_constraint(
975
1009
def _merge_dependencies_by_marker (
976
1010
self , dependencies : Iterable [Dependency ]
977
1011
) -> list [Dependency ]:
1012
+ """
1013
+ Merge dependencies with the same marker
1014
+ by building the intersection of their constraints.
1015
+ """
978
1016
by_marker : dict [BaseMarker , list [Dependency ]] = defaultdict (list )
979
1017
for dep in dependencies :
980
1018
by_marker [dep .marker ].append (dep )
0 commit comments