Commit be1b4881 by Sébastien Eustace Committed by Arun Babu Neelicattu

Improve dependency resolution and markers handling

parent 01624864
...@@ -67,4 +67,4 @@ jobs: ...@@ -67,4 +67,4 @@ jobs:
- name: Run pytest - name: Run pytest
shell: bash shell: bash
run: poetry run pytest -q tests run: poetry run pytest -v tests
...@@ -28,9 +28,8 @@ class DebugResolveCommand(InitCommand): ...@@ -28,9 +28,8 @@ class DebugResolveCommand(InitCommand):
loggers = ["poetry.repositories.pypi_repository"] loggers = ["poetry.repositories.pypi_repository"]
def handle(self): def handle(self):
from poetry.core.packages.project_package import ProjectPackage
from poetry.io.null_io import NullIO from poetry.io.null_io import NullIO
from poetry.core.packages import ProjectPackage
from poetry.installation.installer import Installer
from poetry.puzzle import Solver from poetry.puzzle import Solver
from poetry.repositories.pool import Pool from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository from poetry.repositories.repository import Repository
...@@ -106,7 +105,6 @@ class DebugResolveCommand(InitCommand): ...@@ -106,7 +105,6 @@ class DebugResolveCommand(InitCommand):
if self.option("install"): if self.option("install"):
env = EnvManager(self.poetry).get() env = EnvManager(self.poetry).get()
current_python_version = ".".join(str(v) for v in env.version_info)
pool = Pool() pool = Pool()
locked_repository = Repository() locked_repository = Repository()
for op in ops: for op in ops:
...@@ -114,11 +112,9 @@ class DebugResolveCommand(InitCommand): ...@@ -114,11 +112,9 @@ class DebugResolveCommand(InitCommand):
pool.add_repository(locked_repository) pool.add_repository(locked_repository)
with package.with_python_versions(current_python_version): solver = Solver(package, pool, Repository(), Repository(), NullIO())
installer = Installer(NullIO(), env, package, self.poetry.locker, pool) with solver.use_environment(env):
solver = Solver(package, pool, Repository(), Repository(), NullIO())
ops = solver.solve() ops = solver.solve()
installer._filter_operations(ops, Repository())
for op in ops: for op in ops:
if self.option("install") and op.skipped: if self.option("install") and op.skipped:
......
...@@ -3,8 +3,8 @@ from typing import Union ...@@ -3,8 +3,8 @@ from typing import Union
from clikit.api.io import IO from clikit.api.io import IO
from poetry.core.packages.package import Package from poetry.core.packages.project_package import ProjectPackage
from poetry.core.semver import parse_constraint from poetry.io.null_io import NullIO
from poetry.packages import Locker from poetry.packages import Locker
from poetry.puzzle import Solver from poetry.puzzle import Solver
from poetry.puzzle.operations import Install from poetry.puzzle.operations import Install
...@@ -26,7 +26,7 @@ class Installer: ...@@ -26,7 +26,7 @@ class Installer:
self, self,
io, # type: IO io, # type: IO
env, env,
package, # type: Package package, # type: ProjectPackage
locker, # type: Locker locker, # type: Locker
pool, # type: Pool pool, # type: Pool
installed=None, # type: (Union[InstalledRepository, None]) installed=None, # type: (Union[InstalledRepository, None])
...@@ -196,6 +196,37 @@ class Installer: ...@@ -196,6 +196,37 @@ class Installer:
root = root.clone() root = root.clone()
del root.dev_requires[:] del root.dev_requires[:]
if self._io.is_verbose():
self._io.write_line("")
self._io.write_line(
"<info>Finding the necessary packages for the current system</>"
)
# We resolve again by only using the lock file
pool = Pool(ignore_repository_names=True)
# Making a new repo containing the packages
# newly resolved and the ones from the current lock file
repo = Repository()
for package in local_repo.packages + locked_repository.packages:
if not repo.has_package(package):
repo.add_package(package)
pool.add_repository(repo)
# We whitelist all packages to be sure
# that the latest ones are picked up
whitelist = []
for pkg in locked_repository.packages:
whitelist.append(pkg.name)
solver = Solver(
root, pool, self._installed_repository, locked_repository, NullIO()
)
with solver.use_environment(self._env):
ops = solver.solve(use_latest=whitelist)
# We need to filter operations so that packages # We need to filter operations so that packages
# not compatible with the current system, # not compatible with the current system,
# or optional and not requested, are dropped # or optional and not requested, are dropped
...@@ -411,15 +442,6 @@ class Installer: ...@@ -411,15 +442,6 @@ class Installer:
if op.job_type == "uninstall": if op.job_type == "uninstall":
continue continue
current_python = parse_constraint(
".".join(str(v) for v in self._env.version_info[:3])
)
if not package.python_constraint.allows(
current_python
) or not self._env.is_valid_for_marker(package.marker):
op.skip("Not needed for the current environment")
continue
if self._update: if self._update:
extras = {} extras = {}
for extra, deps in self._package.extras.items(): for extra, deps in self._package.extras.items():
......
...@@ -440,6 +440,13 @@ class VersionSolver: ...@@ -440,6 +440,13 @@ class VersionSolver:
if dependency.extras: if dependency.extras:
locked.requires_extras = dependency.extras locked.requires_extras = dependency.extras
if not dependency.transitive_marker.without_extras().is_any():
marker_intersection = dependency.transitive_marker.without_extras().intersect(
locked.dependency.marker.without_extras()
)
if not marker_intersection.is_empty():
locked.dependency.transitive_marker = marker_intersection
return locked return locked
def _log(self, text): def _log(self, text):
......
...@@ -244,7 +244,8 @@ class Locker(object): ...@@ -244,7 +244,8 @@ class Locker(object):
if dependency.pretty_name not in dependencies: if dependency.pretty_name not in dependencies:
dependencies[dependency.pretty_name] = [] dependencies[dependency.pretty_name] = []
constraint = {"version": str(dependency.pretty_constraint)} constraint = inline_table()
constraint["version"] = str(dependency.pretty_constraint)
if dependency.extras: if dependency.extras:
constraint["extras"] = sorted(dependency.extras) constraint["extras"] = sorted(dependency.extras)
...@@ -252,8 +253,8 @@ class Locker(object): ...@@ -252,8 +253,8 @@ class Locker(object):
if dependency.is_optional(): if dependency.is_optional():
constraint["optional"] = True constraint["optional"] = True
if not dependency.python_constraint.is_any(): if not dependency.marker.is_any():
constraint["python"] = str(dependency.python_constraint) constraint["markers"] = str(dependency.marker)
dependencies[dependency.pretty_name].append(constraint) dependencies[dependency.pretty_name].append(constraint)
...@@ -274,8 +275,6 @@ class Locker(object): ...@@ -274,8 +275,6 @@ class Locker(object):
"python-versions": package.python_versions, "python-versions": package.python_versions,
"files": sorted(package.files, key=lambda x: x["file"]), "files": sorted(package.files, key=lambda x: x["file"]),
} }
if not package.marker.is_any():
data["marker"] = str(package.marker)
if package.extras: if package.extras:
extras = {} extras = {}
......
...@@ -33,6 +33,7 @@ from poetry.repositories import Pool ...@@ -33,6 +33,7 @@ from poetry.repositories import Pool
from poetry.utils._compat import OrderedDict from poetry.utils._compat import OrderedDict
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils._compat import urlparse from poetry.utils._compat import urlparse
from poetry.utils.env import Env
from poetry.utils.helpers import download_file from poetry.utils.helpers import download_file
from poetry.utils.helpers import safe_rmtree from poetry.utils.helpers import safe_rmtree
from poetry.utils.helpers import temporary_directory from poetry.utils.helpers import temporary_directory
...@@ -52,10 +53,13 @@ class Provider: ...@@ -52,10 +53,13 @@ class Provider:
UNSAFE_PACKAGES = {"setuptools", "distribute", "pip"} UNSAFE_PACKAGES = {"setuptools", "distribute", "pip"}
def __init__(self, package, pool, io): # type: (Package, Pool, Any) -> None def __init__(
self, package, pool, io, env=None
): # type: (Package, Pool, Any, Optional[Env]) -> None
self._package = package self._package = package
self._pool = pool self._pool = pool
self._io = io self._io = io
self._env = env
self._python_constraint = package.python_constraint self._python_constraint = package.python_constraint
self._search_for = {} self._search_for = {}
self._is_debugging = self._io.is_debug() or self._io.is_very_verbose() self._is_debugging = self._io.is_debug() or self._io.is_very_verbose()
...@@ -66,25 +70,21 @@ class Provider: ...@@ -66,25 +70,21 @@ class Provider:
def pool(self): # type: () -> Pool def pool(self): # type: () -> Pool
return self._pool return self._pool
@property
def name_for_explicit_dependency_source(self): # type: () -> str
return "pyproject.toml"
@property
def name_for_locking_dependency_source(self): # type: () -> str
return "poetry.lock"
def is_debugging(self): def is_debugging(self):
return self._is_debugging return self._is_debugging
def set_overrides(self, overrides): def set_overrides(self, overrides):
self._overrides = overrides self._overrides = overrides
def name_for(self, dependency): # type: (Dependency) -> str @contextmanager
""" def use_environment(self, env): # type: (Env) -> Provider
Returns the name for the given dependency. original_env = self._env
"""
return dependency.name self._env = env
yield self
self._env = original_env
def search_for(self, dependency): # type: (Dependency) -> List[Package] def search_for(self, dependency): # type: (Dependency) -> List[Package]
""" """
...@@ -371,6 +371,7 @@ class Provider: ...@@ -371,6 +371,7 @@ class Provider:
for dep in dependencies for dep in dependencies
if dep.name not in self.UNSAFE_PACKAGES if dep.name not in self.UNSAFE_PACKAGES
and self._package.python_constraint.allows_any(dep.python_constraint) and self._package.python_constraint.allows_any(dep.python_constraint)
and (not self._env or dep.marker.validate(self._env.marker_env))
] ]
overrides = self._overrides.get(package, {}) overrides = self._overrides.get(package, {})
...@@ -426,6 +427,7 @@ class Provider: ...@@ -426,6 +427,7 @@ class Provider:
for r in requires for r in requires
if self._package.python_constraint.allows_any(r.python_constraint) if self._package.python_constraint.allows_any(r.python_constraint)
and r.name not in self.UNSAFE_PACKAGES and r.name not in self.UNSAFE_PACKAGES
and (not self._env or r.marker.validate(self._env.marker_env))
] ]
overrides = self._overrides.get(package, {}) overrides = self._overrides.get(package, {})
......
import time import time
from contextlib import contextmanager
from typing import Any from typing import Any
from typing import Dict from typing import Dict
from typing import List from typing import List
from poetry.core.packages import Package from poetry.core.packages import Package
from poetry.core.version.markers import AnyMarker
from poetry.mixology import resolve_version from poetry.mixology import resolve_version
from poetry.mixology.failure import SolveFailure from poetry.mixology.failure import SolveFailure
from poetry.packages import DependencyPackage from poetry.packages import DependencyPackage
from poetry.utils.env import Env
from .exceptions import OverrideNeeded from .exceptions import OverrideNeeded
from .exceptions import SolverProblemError from .exceptions import SolverProblemError
...@@ -29,6 +30,15 @@ class Solver: ...@@ -29,6 +30,15 @@ class Solver:
self._provider = Provider(self._package, self._pool, self._io) self._provider = Provider(self._package, self._pool, self._io)
self._overrides = [] self._overrides = []
@property
def provider(self): # type: () -> Provider
return self._provider
@contextmanager
def use_environment(self, env): # type: (Env) -> None
with self.provider.use_environment(env):
yield
def solve(self, use_latest=None): # type: (...) -> List[Operation] def solve(self, use_latest=None): # type: (...) -> List[Operation]
with self._provider.progress(): with self._provider.progress():
start = time.time() start = time.time()
...@@ -157,7 +167,6 @@ class Solver: ...@@ -157,7 +167,6 @@ class Solver:
idx = packages.index(package) idx = packages.index(package)
pkg = packages[idx] pkg = packages[idx]
depths[idx] = max(depths[idx], _depths[index]) depths[idx] = max(depths[idx], _depths[index])
pkg.marker = pkg.marker.union(package.marker)
for dep in package.requires: for dep in package.requires:
if dep not in pkg.requires: if dep not in pkg.requires:
...@@ -189,18 +198,10 @@ class Solver: ...@@ -189,18 +198,10 @@ class Solver:
depths = [] depths = []
final_packages = [] final_packages = []
for package in packages: for package in packages:
category, optional, marker, depth = self._get_tags_for_package( category, optional, depth = self._get_tags_for_package(package, graph)
package, graph
)
if marker is None:
marker = AnyMarker()
if marker.is_empty():
continue
package.category = category package.category = category
package.optional = optional package.optional = optional
package.marker = marker
depths.append(depth) depths.append(depth)
final_packages.append(package) final_packages.append(package)
...@@ -213,25 +214,15 @@ class Solver: ...@@ -213,25 +214,15 @@ class Solver:
if not previous: if not previous:
category = "dev" category = "dev"
optional = True optional = True
marker = package.marker
else: else:
category = dep.category category = dep.category
optional = dep.is_optional() and not dep.is_activated() optional = dep.is_optional() and not dep.is_activated()
intersection = (
previous["marker"]
.without_extras()
.intersect(previous_dep.transitive_marker.without_extras())
)
intersection = intersection.intersect(package.marker.without_extras())
marker = intersection
childrens = [] # type: List[Dict[str, Any]] childrens = [] # type: List[Dict[str, Any]]
graph = { graph = {
"name": package.name, "name": package.name,
"category": category, "category": category,
"optional": optional, "optional": optional,
"marker": marker,
"children": childrens, "children": childrens,
} }
...@@ -290,9 +281,6 @@ class Solver: ...@@ -290,9 +281,6 @@ class Solver:
child_graph["optional"] = True child_graph["optional"] = True
if existing: if existing:
existing["marker"] = existing["marker"].union(
child_graph["marker"]
)
continue continue
childrens.append(child_graph) childrens.append(child_graph)
...@@ -302,7 +290,6 @@ class Solver: ...@@ -302,7 +290,6 @@ class Solver:
def _get_tags_for_package(self, package, graph, depth=0): def _get_tags_for_package(self, package, graph, depth=0):
categories = ["dev"] categories = ["dev"]
optionals = [True] optionals = [True]
markers = []
_depths = [0] _depths = [0]
children = graph["children"] children = graph["children"]
...@@ -310,10 +297,9 @@ class Solver: ...@@ -310,10 +297,9 @@ class Solver:
if child["name"] == package.name: if child["name"] == package.name:
category = child["category"] category = child["category"]
optional = child["optional"] optional = child["optional"]
marker = child["marker"]
_depths.append(depth) _depths.append(depth)
else: else:
(category, optional, marker, _depth) = self._get_tags_for_package( (category, optional, _depth) = self._get_tags_for_package(
package, child, depth=depth + 1 package, child, depth=depth + 1
) )
...@@ -321,8 +307,6 @@ class Solver: ...@@ -321,8 +307,6 @@ class Solver:
categories.append(category) categories.append(category)
optionals.append(optional) optionals.append(optional)
if marker is not None:
markers.append(marker)
if "main" in categories: if "main" in categories:
category = "main" category = "main"
...@@ -333,11 +317,4 @@ class Solver: ...@@ -333,11 +317,4 @@ class Solver:
depth = max(*(_depths + [0])) depth = max(*(_depths + [0]))
if not markers: return category, optional, depth
marker = None
else:
marker = markers[0]
for m in markers[1:]:
marker = marker.union(m)
return category, optional, marker, depth
...@@ -1010,7 +1010,9 @@ class SystemEnv(Env): ...@@ -1010,7 +1010,9 @@ class SystemEnv(Env):
"platform_version": platform.version(), "platform_version": platform.version(),
"python_full_version": platform.python_version(), "python_full_version": platform.python_version(),
"platform_python_implementation": platform.python_implementation(), "platform_python_implementation": platform.python_implementation(),
"python_version": platform.python_version()[:3], "python_version": ".".join(
v for v in platform.python_version().split(".")[:2]
),
"sys_platform": sys.platform, "sys_platform": sys.platform,
"version_info": sys.version_info, "version_info": sys.version_info,
} }
...@@ -1185,6 +1187,7 @@ class MockEnv(NullEnv): ...@@ -1185,6 +1187,7 @@ class MockEnv(NullEnv):
is_venv=False, is_venv=False,
pip_version="19.1", pip_version="19.1",
sys_path=None, sys_path=None,
marker_env=None,
**kwargs **kwargs
): ):
super(MockEnv, self).__init__(**kwargs) super(MockEnv, self).__init__(**kwargs)
...@@ -1196,14 +1199,7 @@ class MockEnv(NullEnv): ...@@ -1196,14 +1199,7 @@ class MockEnv(NullEnv):
self._is_venv = is_venv self._is_venv = is_venv
self._pip_version = Version.parse(pip_version) self._pip_version = Version.parse(pip_version)
self._sys_path = sys_path self._sys_path = sys_path
self._mock_marker_env = marker_env
@property
def version_info(self): # type: () -> Tuple[int]
return self._version_info
@property
def python_implementation(self): # type: () -> str
return self._python_implementation
@property @property
def platform(self): # type: () -> str def platform(self): # type: () -> str
...@@ -1224,5 +1220,17 @@ class MockEnv(NullEnv): ...@@ -1224,5 +1220,17 @@ class MockEnv(NullEnv):
return self._sys_path return self._sys_path
def get_marker_env(self): # type: () -> Dict[str, Any]
if self._mock_marker_env is not None:
return self._mock_marker_env
marker_env = super(MockEnv, self).get_marker_env()
marker_env["python_implementation"] = self._python_implementation
marker_env["version_info"] = self._version_info
marker_env["python_version"] = ".".join(str(v) for v in self._version_info[:2])
marker_env["sys_platform"] = self._platform
return marker_env
def is_venv(self): # type: () -> bool def is_venv(self): # type: () -> bool
return self._is_venv return self._is_venv
...@@ -598,8 +598,9 @@ Package operations: 1 install, 0 updates, 0 removals ...@@ -598,8 +598,9 @@ Package operations: 1 install, 0 updates, 0 removals
assert content["dependencies"]["cachy"] == {"version": "0.2.0", "python": ">=2.7"} assert content["dependencies"]["cachy"] == {"version": "0.2.0", "python": ">=2.7"}
def test_add_constraint_with_platform(app, repo, installer): def test_add_constraint_with_platform(app, repo, installer, env):
platform = sys.platform platform = sys.platform
env._platform = platform
command = app.find("add") command = app.find("add")
tester = CommandTester(command) tester = CommandTester(command)
...@@ -608,7 +609,7 @@ def test_add_constraint_with_platform(app, repo, installer): ...@@ -608,7 +609,7 @@ def test_add_constraint_with_platform(app, repo, installer):
repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.1.0"))
repo.add_package(cachy2) repo.add_package(cachy2)
tester.execute("cachy=0.2.0 --platform {}".format(platform)) tester.execute("cachy=0.2.0 --platform {} -vvv".format(platform))
expected = """\ expected = """\
......
[[package]]
name = "attrs"
version = "17.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = "*"
[package.extras]
dev = ["coverage", "hypothesis", "pympler", "pytest", "six", "zope.interface", "sphinx", "zope.interface"]
docs = ["sphinx", "zope.interface"]
tests = ["coverage", "hypothesis", "pympler", "pytest", "six", "zope.interface"]
[[package]]
name = "colorama"
version = "0.3.9"
description = "Cross-platform colored terminal text."
category = "dev"
marker = "sys_platform == \"win32\""
optional = false
python-versions = "*"
[[package]]
name = "funcsigs"
version = "1.0.2"
description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+"
category = "dev"
marker = "python_version < \"3.0\""
optional = false
python-versions = "*"
[[package]]
name = "more-itertools"
version = "4.1.0"
description = "More routines for operating on iterables, beyond itertools"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
six = ">=1.0.0,<2.0.0"
[[package]]
name = "pluggy"
version = "0.6.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "py"
version = "1.5.3"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pytest"
version = "3.5.0"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
py = ">=1.5.0"
six = ">=1.10.0"
attrs = ">=17.4.0"
more-itertools = ">=4.0.0"
pluggy = ">=0.5,<0.7"
funcsigs = {"version" = "*", "python" = "<3.0"}
colorama = "*"
[[package]]
name = "six"
version = "1.11.0"
description = "Python 2 and 3 compatibility utilities"
category = "dev"
optional = false
python-versions = "*"
[metadata]
python-versions = "*"
content-hash = "123456789"
[metadata.files]
attrs = [
{file = "attrs-17.4.0-py2.py3-none-any.whl", hash = "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"},
{file = "attrs-17.4.0.tar.gz", hash = "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"},
]
colorama = [
{file = "colorama-0.3.9-py2.py3-none-any.whl", hash = "sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda"},
{file = "colorama-0.3.9.tar.gz", hash = "sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1"},
]
funcsigs = [
{file = "funcsigs-1.0.2-py2.py3-none-any.whl", hash = "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca"},
{file = "funcsigs-1.0.2.tar.gz", hash = "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"},
]
more-itertools = [
{file = "more-itertools-4.1.0.tar.gz", hash = "sha256:c9ce7eccdcb901a2c75d326ea134e0886abfbea5f93e91cc95de9507c0816c44"},
{file = "more_itertools-4.1.0-py2-none-any.whl", hash = "sha256:11a625025954c20145b37ff6309cd54e39ca94f72f6bb9576d1195db6fa2442e"},
{file = "more_itertools-4.1.0-py3-none-any.whl", hash = "sha256:0dd8f72eeab0d2c3bd489025bb2f6a1b8342f9b198f6fc37b52d15cfa4531fea"},
]
pluggy = [
{file = "pluggy-0.6.0.tar.gz", hash = "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff"},
]
py = [
{file = "py-1.5.3-py2.py3-none-any.whl", hash = "sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a"},
{file = "py-1.5.3.tar.gz", hash = "sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881"},
]
pytest = [
{file = "pytest-3.5.0-py2.py3-none-any.whl", hash = "sha256:6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c"},
{file = "pytest-3.5.0.tar.gz", hash = "sha256:fae491d1874f199537fd5872b5e1f0e74a009b979df9d53d1553fd03da1703e1"},
]
six = [
{file = "six-1.11.0-py2.py3-none-any.whl", hash = "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"},
{file = "six-1.11.0.tar.gz", hash = "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"},
]
...@@ -8,7 +8,7 @@ python-versions = "*" ...@@ -8,7 +8,7 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
"B" = {version = "^1.0", optional = true} "B" = {version = "^1.0", optional = true}
"C" = {"version" = "^1.0", "python" = ">=2.7,<2.8"} "C" = {version = "^1.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""}
[package.extras] [package.extras]
foo = ["b"] foo = ["b"]
...@@ -26,7 +26,6 @@ name = "C" ...@@ -26,7 +26,6 @@ name = "C"
version = "1.1" version = "1.1"
description = "" description = ""
category = "main" category = "main"
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
optional = false optional = false
python-versions = "*" python-versions = "*"
......
...@@ -16,7 +16,7 @@ python-versions = "*" ...@@ -16,7 +16,7 @@ python-versions = "*"
version = "1.2.3" version = "1.2.3"
[package.dependencies] [package.dependencies]
pendulum = {version = ">=1.4.4", optional = true} pendulum = {version = ">=1.4.4", optional = true, markers = "extra == \"extras_a\""}
[package.extras] [package.extras]
extras_a = ["pendulum (>=1.4.4)"] extras_a = ["pendulum (>=1.4.4)"]
......
...@@ -23,7 +23,6 @@ C = "1.5" ...@@ -23,7 +23,6 @@ C = "1.5"
[[package]] [[package]]
name = "C" name = "C"
version = "1.5" version = "1.5"
marker = "python_version >= \"2.7\""
description = "" description = ""
category = "main" category = "main"
optional = false optional = false
......
...@@ -8,8 +8,8 @@ python-versions = "*" ...@@ -8,8 +8,8 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
B = [ B = [
{"version" = "^1.0", "python" = "<4.0"}, {version = "^1.0", markers = "python_version < \"4.0\""},
{"version" = "^2.0", "python" = ">=4.0"}, {version = "^2.0", markers = "python_version >= \"4.0\""},
] ]
[[package]] [[package]]
...@@ -17,7 +17,6 @@ name = "B" ...@@ -17,7 +17,6 @@ name = "B"
version = "1.0" version = "1.0"
description = "" description = ""
category = "main" category = "main"
marker = "python_version < \"4.0\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -29,7 +28,6 @@ name = "B" ...@@ -29,7 +28,6 @@ name = "B"
version = "2.0" version = "2.0"
description = "" description = ""
category = "main" category = "main"
marker = "python_version >= \"4.0\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -41,7 +39,6 @@ name = "C" ...@@ -41,7 +39,6 @@ name = "C"
version = "1.2" version = "1.2"
description = "" description = ""
category = "main" category = "main"
marker = "python_version < \"4.0\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -50,7 +47,6 @@ name = "C" ...@@ -50,7 +47,6 @@ name = "C"
version = "1.5" version = "1.5"
description = "" description = ""
category = "main" category = "main"
marker = "python_version >= \"4.0\""
optional = false optional = false
python-versions = "*" python-versions = "*"
......
[[package]] [[package]]
name = "A" name = "A"
version = "1.0" version = "1.1"
description = "" description = ""
category = "main" category = "main"
optional = false optional = false
...@@ -9,17 +9,17 @@ python-versions = "*" ...@@ -9,17 +9,17 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
B = ">=1.0.1" B = ">=1.0.1"
C = [ C = [
{version = "^1.0", python = ">=2.7,<2.8"}, {version = "^1.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""},
{version = "^2.0", python = ">=3.4,<4.0"}, {version = "^2.0", markers = "python_version >= \"3.4\" and python_version < \"4.0\""},
] ]
[[package]] [[package]]
name = "B" name = "B"
version = "1.0.1" version = "1.1.0"
description = "" description = ""
category = "main" category = "main"
optional = false optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" python-versions = "*"
[[package]] [[package]]
name = "C" name = "C"
...@@ -27,7 +27,6 @@ version = "1.0" ...@@ -27,7 +27,6 @@ version = "1.0"
description = "" description = ""
category = "main" category = "main"
optional = false optional = false
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
python-versions = "*" python-versions = "*"
[[package]] [[package]]
...@@ -36,7 +35,6 @@ version = "2.0" ...@@ -36,7 +35,6 @@ version = "2.0"
description = "" description = ""
category = "main" category = "main"
optional = false optional = false
marker = "python_version >= \"3.4\" and python_version < \"4.0\""
python-versions = "*" python-versions = "*"
[metadata] [metadata]
......
...@@ -11,7 +11,6 @@ name = "C" ...@@ -11,7 +11,6 @@ name = "C"
version = "1.3" version = "1.3"
description = "" description = ""
category = "main" category = "main"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"4.0\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -23,7 +22,6 @@ name = "D" ...@@ -23,7 +22,6 @@ name = "D"
version = "1.4" version = "1.4"
description = "" description = ""
category = "main" category = "main"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"4.0\""
optional = false optional = false
python-versions = "*" python-versions = "*"
......
...@@ -11,7 +11,6 @@ name = "B" ...@@ -11,7 +11,6 @@ name = "B"
version = "1.1" version = "1.1"
description = "" description = ""
category = "main" category = "main"
marker = "sys_platform == \"custom\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -20,7 +19,6 @@ name = "C" ...@@ -20,7 +19,6 @@ name = "C"
version = "1.3" version = "1.3"
description = "" description = ""
category = "main" category = "main"
marker = "sys_platform == \"darwin\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -32,7 +30,6 @@ name = "D" ...@@ -32,7 +30,6 @@ name = "D"
version = "1.4" version = "1.4"
description = "" description = ""
category = "main" category = "main"
marker = "sys_platform == \"darwin\""
optional = false optional = false
python-versions = "*" python-versions = "*"
......
...@@ -16,7 +16,6 @@ name = "colorama" ...@@ -16,7 +16,6 @@ name = "colorama"
version = "0.3.9" version = "0.3.9"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
category = "dev" category = "dev"
marker = "sys_platform == \"win32\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -25,7 +24,6 @@ name = "funcsigs" ...@@ -25,7 +24,6 @@ name = "funcsigs"
version = "1.0.2" version = "1.0.2"
description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+"
category = "dev" category = "dev"
marker = "python_version < \"3.0\""
optional = false optional = false
python-versions = "*" python-versions = "*"
...@@ -70,8 +68,8 @@ six = ">=1.10.0" ...@@ -70,8 +68,8 @@ six = ">=1.10.0"
attrs = ">=17.4.0" attrs = ">=17.4.0"
more-itertools = ">=4.0.0" more-itertools = ">=4.0.0"
pluggy = ">=0.5,<0.7" pluggy = ">=0.5,<0.7"
funcsigs = {"version" = "*", "python" = "<3.0"} funcsigs = {"version" = "*", "markers" = "python_version < \"3.0\""}
colorama = "*" colorama = {"version" = "*", "markers" = "sys_platform == \"win32\""}
[[package]] [[package]]
name = "six" name = "six"
......
...@@ -15,6 +15,7 @@ from poetry.repositories import Repository ...@@ -15,6 +15,7 @@ from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils._compat import PY2 from poetry.utils._compat import PY2
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
from poetry.utils.env import NullEnv from poetry.utils.env import NullEnv
from poetry.utils.toml_file import TomlFile from poetry.utils.toml_file import TomlFile
from tests.helpers import get_dependency from tests.helpers import get_dependency
...@@ -1461,15 +1462,15 @@ def test_update_multiple_times_with_split_dependencies_is_idempotent( ...@@ -1461,15 +1462,15 @@ def test_update_multiple_times_with_split_dependencies_is_idempotent(
package.python_versions = "~2.7 || ^3.4" package.python_versions = "~2.7 || ^3.4"
package.add_dependency("A", "^1.0") package.add_dependency("A", "^1.0")
a = get_package("A", "1.0") a10 = get_package("A", "1.0")
a.add_dependency("B", ">=1.0.1") a11 = get_package("A", "1.1")
a.add_dependency("C", {"version": "^1.0", "python": "~2.7"}) a11.add_dependency("B", ">=1.0.1")
a.add_dependency("C", {"version": "^2.0", "python": "^3.4"}) a11.add_dependency("C", {"version": "^1.0", "python": "~2.7"})
a11.add_dependency("C", {"version": "^2.0", "python": "^3.4"})
b101 = get_package("B", "1.0.1") b101 = get_package("B", "1.0.1")
b101.python_versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
b110 = get_package("B", "1.1.0") b110 = get_package("B", "1.1.0")
b110.python_versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" repo.add_package(a10)
repo.add_package(a) repo.add_package(a11)
repo.add_package(b101) repo.add_package(b101)
repo.add_package(b110) repo.add_package(b110)
repo.add_package(get_package("C", "1.0")) repo.add_package(get_package("C", "1.0"))
...@@ -1557,3 +1558,50 @@ def test_installer_uses_prereleases_if_they_are_compatible( ...@@ -1557,3 +1558,50 @@ def test_installer_uses_prereleases_if_they_are_compatible(
installer.run() installer.run()
assert len(installer.installer.installs) == 2 assert len(installer.installer.installs) == 2
def test_installer_can_handle_old_lock_files(
installer, locker, package, repo, installed
):
pool = Pool()
pool.add_repository(MockRepository())
package.add_dependency("pytest", "^3.5", category="dev")
locker.mock_lock_data(fixture("with-pypi-repository"))
installer = Installer(
NullIO(), MockEnv(), package, locker, pool, installed=installed
)
installer.run()
assert 6 == len(installer.installer.installs)
installer = Installer(
NullIO(),
MockEnv(version_info=(2, 7, 18)),
package,
locker,
pool,
installed=installed,
)
installer.run()
# funcsigs will be added
assert 7 == len(installer.installer.installs)
installer = Installer(
NullIO(),
MockEnv(version_info=(2, 7, 18), platform="win32"),
package,
locker,
pool,
installed=installed,
)
installer.run()
# colorama will be added
assert 8 == len(installer.installer.installs)
...@@ -266,10 +266,7 @@ python-versions = "*" ...@@ -266,10 +266,7 @@ python-versions = "*"
version = "1.0.0" version = "1.0.0"
[package.dependencies] [package.dependencies]
[package.dependencies.B] B = {version = "^1.0.0", extras = ["a", "b", "c"], optional = true}
extras = ["a", "b", "c"]
optional = true
version = "^1.0.0"
[metadata] [metadata]
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8" content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8"
......
...@@ -559,21 +559,6 @@ def test_solver_sub_dependencies_with_requirements_complex(solver, repo, package ...@@ -559,21 +559,6 @@ def test_solver_sub_dependencies_with_requirements_complex(solver, repo, package
], ],
) )
op = ops[3] # d
assert str(op.package.marker) == 'python_version < "4.0"'
op = ops[0] # e
assert str(op.package.marker) == (
'python_version < "4.0" and sys_platform == "win32" '
'or python_version < "5.0" and sys_platform == "win32"'
)
op = ops[1] # f
assert str(op.package.marker) == 'python_version < "5.0"'
op = ops[4] # a
assert str(op.package.marker) == 'python_version < "5.0"'
def test_solver_sub_dependencies_with_not_supported_python_version( def test_solver_sub_dependencies_with_not_supported_python_version(
solver, repo, package solver, repo, package
...@@ -773,11 +758,6 @@ def test_solver_duplicate_dependencies_same_constraint(solver, repo, package): ...@@ -773,11 +758,6 @@ def test_solver_duplicate_dependencies_same_constraint(solver, repo, package):
], ],
) )
op = ops[0]
assert (
str(op.package.marker) == 'python_version == "2.7" or python_version >= "3.4"'
)
def test_solver_duplicate_dependencies_different_constraints(solver, repo, package): def test_solver_duplicate_dependencies_different_constraints(solver, repo, package):
package.add_dependency("A") package.add_dependency("A")
...@@ -804,12 +784,6 @@ def test_solver_duplicate_dependencies_different_constraints(solver, repo, packa ...@@ -804,12 +784,6 @@ def test_solver_duplicate_dependencies_different_constraints(solver, repo, packa
], ],
) )
op = ops[0]
assert str(op.package.marker) == 'python_version < "3.4"'
op = ops[1]
assert str(op.package.marker) == 'python_version >= "3.4"'
def test_solver_duplicate_dependencies_different_constraints_same_requirements( def test_solver_duplicate_dependencies_different_constraints_same_requirements(
solver, repo, package solver, repo, package
...@@ -872,12 +846,6 @@ def test_solver_duplicate_dependencies_sub_dependencies(solver, repo, package): ...@@ -872,12 +846,6 @@ def test_solver_duplicate_dependencies_sub_dependencies(solver, repo, package):
], ],
) )
op = ops[2]
assert str(op.package.marker) == 'python_version < "3.4"'
op = ops[3]
assert str(op.package.marker) == 'python_version >= "3.4"'
def test_solver_fails_if_dependency_name_does_not_match_package(solver, repo, package): def test_solver_fails_if_dependency_name_does_not_match_package(solver, repo, package):
package.add_dependency("my-demo", {"git": "https://github.com/demo/demo.git"}) package.add_dependency("my-demo", {"git": "https://github.com/demo/demo.git"})
...@@ -1006,11 +974,6 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir ...@@ -1006,11 +974,6 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir
check_solver_result(ops, [{"job": "install", "package": package_a}]) check_solver_result(ops, [{"job": "install", "package": package_a}])
assert (
str(ops[0].package.marker)
== 'python_version >= "3.6" and python_version < "4.0"'
)
def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible_multiple( def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requirement_is_compatible_multiple(
solver, repo, package solver, repo, package
...@@ -1039,11 +1002,6 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir ...@@ -1039,11 +1002,6 @@ def test_solver_does_not_trigger_conflict_for_python_constraint_if_python_requir
], ],
) )
assert str(ops[0].package.marker) == (
'python_version >= "3.6" and python_version < "4.0" '
'or python_version >= "3.5.3" and python_version < "4.0.0"'
)
def test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_with_package_python( def test_solver_triggers_conflict_for_dependency_python_not_fully_compatible_with_package_python(
solver, repo, package solver, repo, package
...@@ -1079,44 +1037,6 @@ def test_solver_finds_compatible_package_for_dependency_python_not_fully_compati ...@@ -1079,44 +1037,6 @@ def test_solver_finds_compatible_package_for_dependency_python_not_fully_compati
check_solver_result(ops, [{"job": "install", "package": package_a100}]) check_solver_result(ops, [{"job": "install", "package": package_a100}])
assert (
str(ops[0].package.marker)
== 'python_version >= "3.5" and python_version < "4.0"'
)
def test_solver_sets_appropriate_markers_when_solving(solver, repo, package):
dep = dependency_from_pep_508(
'B (>=1.0); python_version >= "3.6" and sys_platform != "win32"'
)
package.add_dependency("A", "^1.0")
package_a = get_package("A", "1.0.0")
package_a.requires.append(dep)
package_b = get_package("B", "1.0.0")
repo.add_package(package_a)
repo.add_package(package_b)
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": package_b},
{"job": "install", "package": package_a},
],
)
assert (
str(ops[0].package.marker)
== 'python_version >= "3.6" and sys_platform != "win32"'
)
assert str(ops[1].package.marker) == ""
def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_only_extras( def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_only_extras(
solver, repo, package solver, repo, package
...@@ -1902,31 +1822,6 @@ def test_ignore_python_constraint_no_overlap_dependencies(solver, repo, package) ...@@ -1902,31 +1822,6 @@ def test_ignore_python_constraint_no_overlap_dependencies(solver, repo, package)
) )
def test_solver_properly_propagates_markers(solver, repo, package):
package.python_versions = "~2.7 || ^3.4"
package.add_dependency(
"A",
{
"version": "^1.0",
"markers": "python_version >= '3.6' and implementation_name != 'pypy'",
},
)
package_a = get_package("A", "1.0.0")
package_a.python_versions = ">=3.6"
repo.add_package(package_a)
ops = solver.solve()
check_solver_result(ops, [{"job": "install", "package": package_a}])
assert (
str(ops[0].package.marker)
== 'python_version >= "3.6" and implementation_name != "pypy"'
)
def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies( def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies(
solver, repo, package solver, repo, package
): ):
...@@ -1956,6 +1851,3 @@ def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies( ...@@ -1956,6 +1851,3 @@ def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies(
{"job": "install", "package": package_a}, {"job": "install", "package": package_a},
], ],
) )
assert 'implementation_name == "pypy"' == str(ops[0].package.marker)
assert 'implementation_name != "pypy"' == str(ops[1].package.marker)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment