Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
python-poetry
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
python-poetry
Commits
c640aaff
Unverified
Commit
c640aaff
authored
Jan 04, 2019
by
Sébastien Eustace
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed multiple constraint handling for the root package
parent
78963e69
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
136 additions
and
103 deletions
+136
-103
CHANGELOG.md
+1
-0
poetry/mixology/version_solver.py
+1
-2
poetry/packages/dependency_package.py
+3
-0
poetry/packages/package.py
+3
-0
poetry/packages/project_package.py
+10
-0
poetry/puzzle/provider.py
+100
-101
tests/puzzle/test_solver.py
+18
-0
No files found.
CHANGELOG.md
View file @
c640aaff
...
...
@@ -9,6 +9,7 @@
-
Fixed an error when resolving directory dependencies with no sub dependencies.
-
Fixed an error when locking packages with no description.
-
Fixed path resolution for transitive file dependencies.
-
Fixed multiple constraints handling for the root package.
## [0.12.10] - 2018-11-22
...
...
poetry/mixology/version_solver.py
View file @
c640aaff
...
...
@@ -374,8 +374,7 @@ class VersionSolver:
return
dependency
.
name
if
not
version
.
is_root
():
version
=
self
.
_provider
.
complete_package
(
version
)
version
=
self
.
_provider
.
complete_package
(
version
)
conflict
=
False
for
incompatibility
in
self
.
_provider
.
incompatibilities_for
(
version
):
...
...
poetry/packages/dependency_package.py
View file @
c640aaff
...
...
@@ -11,6 +11,9 @@ class DependencyPackage(object):
def
package
(
self
):
return
self
.
_package
def
clone
(
self
):
# type: () -> DependencyPackage
return
self
.
__class__
(
self
.
_dependency
,
self
.
_package
.
clone
())
def
__getattr__
(
self
,
name
):
return
getattr
(
self
.
_package
,
name
)
...
...
poetry/packages/package.py
View file @
c640aaff
...
...
@@ -369,6 +369,9 @@ class Package(object):
for
dep
in
self
.
requires
:
clone
.
requires
.
append
(
dep
)
for
dep
in
self
.
dev_requires
:
clone
.
dev_requires
.
append
(
dep
)
return
clone
def
__hash__
(
self
):
...
...
poetry/packages/project_package.py
View file @
c640aaff
...
...
@@ -43,3 +43,13 @@ class ProjectPackage(Package):
self
.
_python_marker
=
parse_marker
(
create_nested_marker
(
"python_version"
,
self
.
_python_constraint
)
)
def
clone
(
self
):
# type: () -> ProjectPackage
package
=
super
(
ProjectPackage
,
self
)
.
clone
()
package
.
build
=
self
.
build
package
.
packages
=
self
.
packages
[:]
package
.
include
=
self
.
include
[:]
package
.
exclude
=
self
.
exclude
[:]
return
package
poetry/puzzle/provider.py
View file @
c640aaff
...
...
@@ -453,9 +453,13 @@ class Provider:
self
,
package
):
# type: (DependencyPackage) -> DependencyPackage
if
package
.
is_root
():
return
package
package
=
package
.
clone
()
if
package
.
source_type
not
in
{
"directory"
,
"file"
,
"git"
}:
if
not
package
.
is_root
()
and
package
.
source_type
not
in
{
"directory"
,
"file"
,
"git"
,
}:
package
=
DependencyPackage
(
package
.
dependency
,
self
.
_pool
.
package
(
...
...
@@ -487,119 +491,114 @@ class Provider:
# An example of this is:
# - pypiwin32 (220); sys_platform == "win32" and python_version >= "3.6"
# - pypiwin32 (219); sys_platform == "win32" and python_version < "3.6"
if
not
package
.
is_root
():
duplicates
=
{}
for
dep
in
dependencies
:
if
dep
.
name
not
in
duplicates
:
duplicates
[
dep
.
name
]
=
[]
duplicates
[
dep
.
name
]
.
append
(
dep
)
dependencies
=
[]
for
dep_name
,
deps
in
duplicates
.
items
():
if
len
(
deps
)
==
1
:
dependencies
.
append
(
deps
[
0
])
continue
duplicates
=
{}
for
dep
in
dependencies
:
if
dep
.
name
not
in
duplicates
:
duplicates
[
dep
.
name
]
=
[]
self
.
debug
(
"<debug>Duplicate dependencies for {}</debug>"
.
format
(
dep_name
)
)
duplicates
[
dep
.
name
]
.
append
(
dep
)
# Regrouping by constraint
by_constraint
=
{}
for
dep
in
deps
:
if
dep
.
constraint
not
in
by_constraint
:
by_constraint
[
dep
.
constraint
]
=
[]
by_constraint
[
dep
.
constraint
]
.
append
(
dep
)
# We merge by constraint
for
constraint
,
_deps
in
by_constraint
.
items
():
new_markers
=
[]
for
dep
in
_deps
:
pep_508_dep
=
dep
.
to_pep_508
(
False
)
if
";"
not
in
pep_508_dep
:
continue
markers
=
pep_508_dep
.
split
(
";"
)[
1
]
.
strip
()
if
not
markers
:
# One of the constraint has no markers
# so this means we don't actually need to merge
new_markers
=
[]
break
new_markers
.
append
(
"({})"
.
format
(
markers
))
if
not
new_markers
:
dependencies
+=
_deps
continue
dependencies
=
[]
for
dep_name
,
deps
in
duplicates
.
items
():
if
len
(
deps
)
==
1
:
dependencies
.
append
(
deps
[
0
])
continue
dep
=
_deps
[
0
]
new_requirement
=
"{}; {}"
.
format
(
dep
.
to_pep_508
(
False
)
.
split
(
";"
)[
0
],
" or "
.
join
(
new_markers
)
)
new_dep
=
dependency_from_pep_508
(
new_requirement
)
if
dep
.
is_optional
()
and
not
dep
.
is_activated
():
new_dep
.
deactivate
()
else
:
new_dep
.
activate
()
self
.
debug
(
"<debug>Duplicate dependencies for {}</debug>"
.
format
(
dep_name
))
by_constraint
[
constraint
]
=
[
new_dep
]
# Regrouping by constraint
by_constraint
=
{}
for
dep
in
deps
:
if
dep
.
constraint
not
in
by_constraint
:
by_constraint
[
dep
.
constraint
]
=
[]
continue
if
len
(
by_constraint
)
==
1
:
self
.
debug
(
"<debug>Merging requirements for {}</debug>"
.
format
(
str
(
deps
[
0
])
)
)
dependencies
.
append
(
list
(
by_constraint
.
values
())[
0
][
0
])
continue
by_constraint
[
dep
.
constraint
]
.
append
(
dep
)
# We leave dependencies as-is if they have the same
# python/platform constraints.
# That way the resolver will pickup the conflict
# and display a proper error.
_deps
=
[
value
[
0
]
for
value
in
by_constraint
.
values
()]
seen
=
set
()
for
_dep
in
_deps
:
pep_508_dep
=
_dep
.
to_pep_508
(
False
)
# We merge by constraint
for
constraint
,
_deps
in
by_constraint
.
items
():
new_markers
=
[]
for
dep
in
_deps
:
pep_508_dep
=
dep
.
to_pep_508
(
False
)
if
";"
not
in
pep_508_dep
:
_requirements
=
""
else
:
_requirements
=
pep_508_dep
.
split
(
";"
)[
1
]
.
strip
()
continue
if
_requirements
not
in
seen
:
seen
.
add
(
_requirements
)
markers
=
pep_508_dep
.
split
(
";"
)[
1
]
.
strip
()
if
not
markers
:
# One of the constraint has no markers
# so this means we don't actually need to merge
new_markers
=
[]
break
if
len
(
_deps
)
!=
len
(
seen
):
for
_dep
in
_deps
:
dependencies
.
append
(
_dep
)
new_markers
.
append
(
"({})"
.
format
(
markers
))
if
not
new_markers
:
dependencies
+=
_deps
continue
# At this point, we raise an exception that will
# tell the solver to enter compatibility mode
# which means it will resolve for subsets
# Python constraints
#
# For instance, if our root package requires Python ~2.7 || ^3.6
# And we have one dependency that requires Python <3.6
# and the other Python >=3.6 than the solver will solve
# dependencies for Python >=2.7,<2.8 || >=3.4,<3.6
# and Python >=3.6,<4.0
python_constraints
=
[
]
for
constraint
,
_deps
in
by_constraint
.
items
():
python_constraints
.
append
(
_deps
[
0
]
.
python_versions
)
_deps
=
[
str
(
_dep
[
0
])
for
_dep
in
by_constraint
.
values
()]
dep
=
_deps
[
0
]
new_requirement
=
"{}; {}"
.
format
(
dep
.
to_pep_508
(
False
)
.
split
(
";"
)[
0
],
" or "
.
join
(
new_markers
)
)
new_dep
=
dependency_from_pep_508
(
new_requirement
)
if
dep
.
is_optional
()
and
not
dep
.
is_activated
():
new_dep
.
deactivate
()
else
:
new_dep
.
activate
()
by_constraint
[
constraint
]
=
[
new_dep
]
continue
if
len
(
by_constraint
)
==
1
:
self
.
debug
(
"<warning>Different requirements found for {}.</warning>"
.
format
(
", "
.
join
(
_deps
[:
-
1
])
+
" and "
+
_deps
[
-
1
]
)
"<debug>Merging requirements for {}</debug>"
.
format
(
str
(
deps
[
0
]))
)
raise
CompatibilityError
(
*
python_constraints
)
dependencies
.
append
(
list
(
by_constraint
.
values
())[
0
][
0
])
continue
# We leave dependencies as-is if they have the same
# python/platform constraints.
# That way the resolver will pickup the conflict
# and display a proper error.
_deps
=
[
value
[
0
]
for
value
in
by_constraint
.
values
()]
seen
=
set
()
for
_dep
in
_deps
:
pep_508_dep
=
_dep
.
to_pep_508
(
False
)
if
";"
not
in
pep_508_dep
:
_requirements
=
""
else
:
_requirements
=
pep_508_dep
.
split
(
";"
)[
1
]
.
strip
()
if
_requirements
not
in
seen
:
seen
.
add
(
_requirements
)
if
len
(
_deps
)
!=
len
(
seen
):
for
_dep
in
_deps
:
dependencies
.
append
(
_dep
)
continue
# At this point, we raise an exception that will
# tell the solver to enter compatibility mode
# which means it will resolve for subsets
# Python constraints
#
# For instance, if our root package requires Python ~2.7 || ^3.6
# And we have one dependency that requires Python <3.6
# and the other Python >=3.6 than the solver will solve
# dependencies for Python >=2.7,<2.8 || >=3.4,<3.6
# and Python >=3.6,<4.0
python_constraints
=
[]
for
constraint
,
_deps
in
by_constraint
.
items
():
python_constraints
.
append
(
_deps
[
0
]
.
python_versions
)
_deps
=
[
str
(
_dep
[
0
])
for
_dep
in
by_constraint
.
values
()]
self
.
debug
(
"<warning>Different requirements found for {}.</warning>"
.
format
(
", "
.
join
(
_deps
[:
-
1
])
+
" and "
+
_deps
[
-
1
]
)
)
raise
CompatibilityError
(
*
python_constraints
)
# Modifying dependencies as needed
for
dep
in
dependencies
:
...
...
tests/puzzle/test_solver.py
View file @
c640aaff
...
...
@@ -1552,3 +1552,21 @@ def test_solver_skips_invalid_versions(package, installed, locked, io):
check_solver_result
(
ops
,
[{
"job"
:
"install"
,
"package"
:
get_package
(
"trackpy"
,
"0.4.1"
)}]
)
def
test_multiple_constraints_on_root
(
package
,
solver
,
repo
):
package
.
add_dependency
(
"foo"
,
{
"version"
:
"^1.0"
,
"python"
:
"^2.7"
})
package
.
add_dependency
(
"foo"
,
{
"version"
:
"^2.0"
,
"python"
:
"^3.7"
})
foo15
=
get_package
(
"foo"
,
"1.5.0"
)
foo25
=
get_package
(
"foo"
,
"2.5.0"
)
repo
.
add_package
(
foo15
)
repo
.
add_package
(
foo25
)
ops
=
solver
.
solve
()
check_solver_result
(
ops
,
[{
"job"
:
"install"
,
"package"
:
foo15
},
{
"job"
:
"install"
,
"package"
:
foo25
}],
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment