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

Improve dependency resolution and markers handling

parent 01624864
......@@ -67,4 +67,4 @@ jobs:
- name: Run pytest
shell: bash
run: poetry run pytest -q tests
run: poetry run pytest -v tests
......@@ -28,9 +28,8 @@ class DebugResolveCommand(InitCommand):
loggers = ["poetry.repositories.pypi_repository"]
def handle(self):
from poetry.core.packages.project_package import ProjectPackage
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.repositories.pool import Pool
from poetry.repositories.repository import Repository
......@@ -106,7 +105,6 @@ class DebugResolveCommand(InitCommand):
if self.option("install"):
env = EnvManager(self.poetry).get()
current_python_version = ".".join(str(v) for v in env.version_info)
pool = Pool()
locked_repository = Repository()
for op in ops:
......@@ -114,11 +112,9 @@ class DebugResolveCommand(InitCommand):
pool.add_repository(locked_repository)
with package.with_python_versions(current_python_version):
installer = Installer(NullIO(), env, package, self.poetry.locker, pool)
solver = Solver(package, pool, Repository(), Repository(), NullIO())
solver = Solver(package, pool, Repository(), Repository(), NullIO())
with solver.use_environment(env):
ops = solver.solve()
installer._filter_operations(ops, Repository())
for op in ops:
if self.option("install") and op.skipped:
......
......@@ -3,8 +3,8 @@ from typing import Union
from clikit.api.io import IO
from poetry.core.packages.package import Package
from poetry.core.semver import parse_constraint
from poetry.core.packages.project_package import ProjectPackage
from poetry.io.null_io import NullIO
from poetry.packages import Locker
from poetry.puzzle import Solver
from poetry.puzzle.operations import Install
......@@ -26,7 +26,7 @@ class Installer:
self,
io, # type: IO
env,
package, # type: Package
package, # type: ProjectPackage
locker, # type: Locker
pool, # type: Pool
installed=None, # type: (Union[InstalledRepository, None])
......@@ -196,6 +196,37 @@ class Installer:
root = root.clone()
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
# not compatible with the current system,
# or optional and not requested, are dropped
......@@ -411,15 +442,6 @@ class Installer:
if op.job_type == "uninstall":
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:
extras = {}
for extra, deps in self._package.extras.items():
......
......@@ -440,6 +440,13 @@ class VersionSolver:
if 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
def _log(self, text):
......
......@@ -244,7 +244,8 @@ class Locker(object):
if dependency.pretty_name not in dependencies:
dependencies[dependency.pretty_name] = []
constraint = {"version": str(dependency.pretty_constraint)}
constraint = inline_table()
constraint["version"] = str(dependency.pretty_constraint)
if dependency.extras:
constraint["extras"] = sorted(dependency.extras)
......@@ -252,8 +253,8 @@ class Locker(object):
if dependency.is_optional():
constraint["optional"] = True
if not dependency.python_constraint.is_any():
constraint["python"] = str(dependency.python_constraint)
if not dependency.marker.is_any():
constraint["markers"] = str(dependency.marker)
dependencies[dependency.pretty_name].append(constraint)
......@@ -274,8 +275,6 @@ class Locker(object):
"python-versions": package.python_versions,
"files": sorted(package.files, key=lambda x: x["file"]),
}
if not package.marker.is_any():
data["marker"] = str(package.marker)
if package.extras:
extras = {}
......
......@@ -33,6 +33,7 @@ from poetry.repositories import Pool
from poetry.utils._compat import OrderedDict
from poetry.utils._compat import Path
from poetry.utils._compat import urlparse
from poetry.utils.env import Env
from poetry.utils.helpers import download_file
from poetry.utils.helpers import safe_rmtree
from poetry.utils.helpers import temporary_directory
......@@ -52,10 +53,13 @@ class Provider:
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._pool = pool
self._io = io
self._env = env
self._python_constraint = package.python_constraint
self._search_for = {}
self._is_debugging = self._io.is_debug() or self._io.is_very_verbose()
......@@ -66,25 +70,21 @@ class Provider:
def pool(self): # type: () -> 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):
return self._is_debugging
def set_overrides(self, overrides):
self._overrides = overrides
def name_for(self, dependency): # type: (Dependency) -> str
"""
Returns the name for the given dependency.
"""
return dependency.name
@contextmanager
def use_environment(self, env): # type: (Env) -> Provider
original_env = self._env
self._env = env
yield self
self._env = original_env
def search_for(self, dependency): # type: (Dependency) -> List[Package]
"""
......@@ -371,6 +371,7 @@ class Provider:
for dep in dependencies
if dep.name not in self.UNSAFE_PACKAGES
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, {})
......@@ -426,6 +427,7 @@ class Provider:
for r in requires
if self._package.python_constraint.allows_any(r.python_constraint)
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, {})
......
import time
from contextlib import contextmanager
from typing import Any
from typing import Dict
from typing import List
from poetry.core.packages import Package
from poetry.core.version.markers import AnyMarker
from poetry.mixology import resolve_version
from poetry.mixology.failure import SolveFailure
from poetry.packages import DependencyPackage
from poetry.utils.env import Env
from .exceptions import OverrideNeeded
from .exceptions import SolverProblemError
......@@ -29,6 +30,15 @@ class Solver:
self._provider = Provider(self._package, self._pool, self._io)
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]
with self._provider.progress():
start = time.time()
......@@ -157,7 +167,6 @@ class Solver:
idx = packages.index(package)
pkg = packages[idx]
depths[idx] = max(depths[idx], _depths[index])
pkg.marker = pkg.marker.union(package.marker)
for dep in package.requires:
if dep not in pkg.requires:
......@@ -189,18 +198,10 @@ class Solver:
depths = []
final_packages = []
for package in packages:
category, optional, marker, depth = self._get_tags_for_package(
package, graph
)
if marker is None:
marker = AnyMarker()
if marker.is_empty():
continue
category, optional, depth = self._get_tags_for_package(package, graph)
package.category = category
package.optional = optional
package.marker = marker
depths.append(depth)
final_packages.append(package)
......@@ -213,25 +214,15 @@ class Solver:
if not previous:
category = "dev"
optional = True
marker = package.marker
else:
category = dep.category
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]]
graph = {
"name": package.name,
"category": category,
"optional": optional,
"marker": marker,
"children": childrens,
}
......@@ -290,9 +281,6 @@ class Solver:
child_graph["optional"] = True
if existing:
existing["marker"] = existing["marker"].union(
child_graph["marker"]
)
continue
childrens.append(child_graph)
......@@ -302,7 +290,6 @@ class Solver:
def _get_tags_for_package(self, package, graph, depth=0):
categories = ["dev"]
optionals = [True]
markers = []
_depths = [0]
children = graph["children"]
......@@ -310,10 +297,9 @@ class Solver:
if child["name"] == package.name:
category = child["category"]
optional = child["optional"]
marker = child["marker"]
_depths.append(depth)
else:
(category, optional, marker, _depth) = self._get_tags_for_package(
(category, optional, _depth) = self._get_tags_for_package(
package, child, depth=depth + 1
)
......@@ -321,8 +307,6 @@ class Solver:
categories.append(category)
optionals.append(optional)
if marker is not None:
markers.append(marker)
if "main" in categories:
category = "main"
......@@ -333,11 +317,4 @@ class Solver:
depth = max(*(_depths + [0]))
if not markers:
marker = None
else:
marker = markers[0]
for m in markers[1:]:
marker = marker.union(m)
return category, optional, marker, depth
return category, optional, depth
......@@ -1010,7 +1010,9 @@ class SystemEnv(Env):
"platform_version": platform.version(),
"python_full_version": platform.python_version(),
"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,
"version_info": sys.version_info,
}
......@@ -1185,6 +1187,7 @@ class MockEnv(NullEnv):
is_venv=False,
pip_version="19.1",
sys_path=None,
marker_env=None,
**kwargs
):
super(MockEnv, self).__init__(**kwargs)
......@@ -1196,14 +1199,7 @@ class MockEnv(NullEnv):
self._is_venv = is_venv
self._pip_version = Version.parse(pip_version)
self._sys_path = sys_path
@property
def version_info(self): # type: () -> Tuple[int]
return self._version_info
@property
def python_implementation(self): # type: () -> str
return self._python_implementation
self._mock_marker_env = marker_env
@property
def platform(self): # type: () -> str
......@@ -1224,5 +1220,17 @@ class MockEnv(NullEnv):
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
return self._is_venv
......@@ -598,8 +598,9 @@ Package operations: 1 install, 0 updates, 0 removals
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
env._platform = platform
command = app.find("add")
tester = CommandTester(command)
......@@ -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(cachy2)
tester.execute("cachy=0.2.0 --platform {}".format(platform))
tester.execute("cachy=0.2.0 --platform {} -vvv".format(platform))
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 = "*"
[package.dependencies]
"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]
foo = ["b"]
......@@ -26,7 +26,6 @@ name = "C"
version = "1.1"
description = ""
category = "main"
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
optional = false
python-versions = "*"
......
......@@ -16,7 +16,7 @@ python-versions = "*"
version = "1.2.3"
[package.dependencies]
pendulum = {version = ">=1.4.4", optional = true}
pendulum = {version = ">=1.4.4", optional = true, markers = "extra == \"extras_a\""}
[package.extras]
extras_a = ["pendulum (>=1.4.4)"]
......
......@@ -23,7 +23,6 @@ C = "1.5"
[[package]]
name = "C"
version = "1.5"
marker = "python_version >= \"2.7\""
description = ""
category = "main"
optional = false
......
......@@ -8,8 +8,8 @@ python-versions = "*"
[package.dependencies]
B = [
{"version" = "^1.0", "python" = "<4.0"},
{"version" = "^2.0", "python" = ">=4.0"},
{version = "^1.0", markers = "python_version < \"4.0\""},
{version = "^2.0", markers = "python_version >= \"4.0\""},
]
[[package]]
......@@ -17,7 +17,6 @@ name = "B"
version = "1.0"
description = ""
category = "main"
marker = "python_version < \"4.0\""
optional = false
python-versions = "*"
......@@ -29,7 +28,6 @@ name = "B"
version = "2.0"
description = ""
category = "main"
marker = "python_version >= \"4.0\""
optional = false
python-versions = "*"
......@@ -41,7 +39,6 @@ name = "C"
version = "1.2"
description = ""
category = "main"
marker = "python_version < \"4.0\""
optional = false
python-versions = "*"
......@@ -50,7 +47,6 @@ name = "C"
version = "1.5"
description = ""
category = "main"
marker = "python_version >= \"4.0\""
optional = false
python-versions = "*"
......
[[package]]
name = "A"
version = "1.0"
version = "1.1"
description = ""
category = "main"
optional = false
......@@ -9,17 +9,17 @@ python-versions = "*"
[package.dependencies]
B = ">=1.0.1"
C = [
{version = "^1.0", python = ">=2.7,<2.8"},
{version = "^2.0", python = ">=3.4,<4.0"},
{version = "^1.0", markers = "python_version >= \"2.7\" and python_version < \"2.8\""},
{version = "^2.0", markers = "python_version >= \"3.4\" and python_version < \"4.0\""},
]
[[package]]
name = "B"
version = "1.0.1"
version = "1.1.0"
description = ""
category = "main"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
python-versions = "*"
[[package]]
name = "C"
......@@ -27,7 +27,6 @@ version = "1.0"
description = ""
category = "main"
optional = false
marker = "python_version >= \"2.7\" and python_version < \"2.8\""
python-versions = "*"
[[package]]
......@@ -36,7 +35,6 @@ version = "2.0"
description = ""
category = "main"
optional = false
marker = "python_version >= \"3.4\" and python_version < \"4.0\""
python-versions = "*"
[metadata]
......
......@@ -11,7 +11,6 @@ name = "C"
version = "1.3"
description = ""
category = "main"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"4.0\""
optional = false
python-versions = "*"
......@@ -23,7 +22,6 @@ name = "D"
version = "1.4"
description = ""
category = "main"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"4.0\""
optional = false
python-versions = "*"
......
......@@ -11,7 +11,6 @@ name = "B"
version = "1.1"
description = ""
category = "main"
marker = "sys_platform == \"custom\""
optional = false
python-versions = "*"
......@@ -20,7 +19,6 @@ name = "C"
version = "1.3"
description = ""
category = "main"
marker = "sys_platform == \"darwin\""
optional = false
python-versions = "*"
......@@ -32,7 +30,6 @@ name = "D"
version = "1.4"
description = ""
category = "main"
marker = "sys_platform == \"darwin\""
optional = false
python-versions = "*"
......
......@@ -16,7 +16,6 @@ name = "colorama"
version = "0.3.9"
description = "Cross-platform colored terminal text."
category = "dev"
marker = "sys_platform == \"win32\""
optional = false
python-versions = "*"
......@@ -25,7 +24,6 @@ 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 = "*"
......@@ -70,8 +68,8 @@ six = ">=1.10.0"
attrs = ">=17.4.0"
more-itertools = ">=4.0.0"
pluggy = ">=0.5,<0.7"
funcsigs = {"version" = "*", "python" = "<3.0"}
colorama = "*"
funcsigs = {"version" = "*", "markers" = "python_version < \"3.0\""}
colorama = {"version" = "*", "markers" = "sys_platform == \"win32\""}
[[package]]
name = "six"
......
......@@ -15,6 +15,7 @@ from poetry.repositories import Repository
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils._compat import PY2
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
from poetry.utils.env import NullEnv
from poetry.utils.toml_file import TomlFile
from tests.helpers import get_dependency
......@@ -1461,15 +1462,15 @@ def test_update_multiple_times_with_split_dependencies_is_idempotent(
package.python_versions = "~2.7 || ^3.4"
package.add_dependency("A", "^1.0")
a = get_package("A", "1.0")
a.add_dependency("B", ">=1.0.1")
a.add_dependency("C", {"version": "^1.0", "python": "~2.7"})
a.add_dependency("C", {"version": "^2.0", "python": "^3.4"})
a10 = get_package("A", "1.0")
a11 = get_package("A", "1.1")
a11.add_dependency("B", ">=1.0.1")
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.python_versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
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(a)
repo.add_package(a10)
repo.add_package(a11)
repo.add_package(b101)
repo.add_package(b110)
repo.add_package(get_package("C", "1.0"))
......@@ -1557,3 +1558,50 @@ def test_installer_uses_prereleases_if_they_are_compatible(
installer.run()
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 = "*"
version = "1.0.0"
[package.dependencies]
[package.dependencies.B]
extras = ["a", "b", "c"]
optional = true
version = "^1.0.0"
B = {version = "^1.0.0", extras = ["a", "b", "c"], optional = true}
[metadata]
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8"
......
......@@ -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(
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):
package.add_dependency("A")
......@@ -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(
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):
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
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(
solver, repo, package
......@@ -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(
solver, repo, package
......@@ -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}])
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(
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(
solver, repo, package
):
......@@ -1956,6 +1851,3 @@ def test_solver_should_not_go_into_an_infinite_loop_on_duplicate_dependencies(
{"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