mirror of
https://github.com/enpaul/tox-poetry-installer.git
synced 2025-01-15 08:43:29 +00:00
Merge pull request #44 from enpaul/enp/resolver
Fix compatibility check failures in the core resolver
This commit is contained in:
commit
333bbe665f
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "tox-poetry-installer"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
license = "MIT"
|
||||
authors = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
|
||||
description = "Tox plugin to install Tox environment dependencies using the Poetry backend and lockfile"
|
||||
@ -22,6 +22,7 @@ classifiers = [
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
|
@ -1,7 +1,7 @@
|
||||
# pylint: disable=missing-docstring
|
||||
__title__ = "tox-poetry-installer"
|
||||
__summary__ = "Tox plugin to install Tox environment dependencies using the Poetry backend and lockfile"
|
||||
__version__ = "0.6.3"
|
||||
__version__ = "0.6.4"
|
||||
__url__ = "https://github.com/enpaul/tox-poetry-installer/"
|
||||
__license__ = "MIT"
|
||||
__authors__ = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
|
||||
|
@ -5,11 +5,8 @@ in this module.
|
||||
|
||||
All constants should be type hinted.
|
||||
"""
|
||||
import sys
|
||||
from typing import Tuple
|
||||
|
||||
from poetry.core.semver.version import Version
|
||||
|
||||
from tox_poetry_installer import __about__
|
||||
|
||||
|
||||
@ -20,12 +17,3 @@ PEP508_VERSION_DELIMITERS: Tuple[str, ...] = ("~=", "==", "!=", ">", "<")
|
||||
# Prefix all reporter messages should include to indicate that they came from this module in the
|
||||
# console output.
|
||||
REPORTER_PREFIX: str = f"{__about__.__title__}:"
|
||||
|
||||
|
||||
# Semver compatible version of the current python platform version. Used for checking
|
||||
# whether a package is compatible with the current python system version
|
||||
PLATFORM_VERSION: Version = Version(
|
||||
major=sys.version_info.major,
|
||||
minor=sys.version_info.minor,
|
||||
patch=sys.version_info.micro,
|
||||
)
|
||||
|
@ -84,6 +84,8 @@ def tox_testenv_install_deps(venv: ToxVirtualEnv, action: ToxAction) -> Optional
|
||||
f"{constants.REPORTER_PREFIX} Loaded project pyproject.toml from {poetry.file}"
|
||||
)
|
||||
|
||||
virtualenv = utilities.convert_virtualenv(venv)
|
||||
|
||||
if not poetry.locker.is_fresh():
|
||||
tox.reporter.warning(
|
||||
f"The Poetry lock file is not up to date with the latest changes in {poetry.file}"
|
||||
@ -101,7 +103,7 @@ def tox_testenv_install_deps(venv: ToxVirtualEnv, action: ToxAction) -> Optional
|
||||
}
|
||||
|
||||
if venv.envconfig.install_dev_deps:
|
||||
dev_deps = utilities.find_dev_deps(packages, poetry)
|
||||
dev_deps = utilities.find_dev_deps(packages, virtualenv, poetry)
|
||||
tox.reporter.verbosity1(
|
||||
f"{constants.REPORTER_PREFIX} Identified {len(dev_deps)} development dependencies to install to env"
|
||||
)
|
||||
@ -112,7 +114,7 @@ def tox_testenv_install_deps(venv: ToxVirtualEnv, action: ToxAction) -> Optional
|
||||
)
|
||||
|
||||
env_deps = utilities.find_additional_deps(
|
||||
packages, poetry, venv.envconfig.locked_deps
|
||||
packages, virtualenv, poetry, venv.envconfig.locked_deps
|
||||
)
|
||||
|
||||
tox.reporter.verbosity1(
|
||||
@ -121,7 +123,7 @@ def tox_testenv_install_deps(venv: ToxVirtualEnv, action: ToxAction) -> Optional
|
||||
|
||||
if not venv.envconfig.skip_install and not venv.envconfig.config.skipsdist:
|
||||
project_deps = utilities.find_project_deps(
|
||||
packages, poetry, venv.envconfig.extras
|
||||
packages, virtualenv, poetry, venv.envconfig.extras
|
||||
)
|
||||
tox.reporter.verbosity1(
|
||||
f"{constants.REPORTER_PREFIX} Identified {len(project_deps)} project dependencies to install to env"
|
||||
|
@ -3,7 +3,6 @@
|
||||
# See the docstring in 'tox_poetry_installer._poetry' for more context.
|
||||
# pylint: disable=import-outside-toplevel
|
||||
import typing
|
||||
from pathlib import Path
|
||||
from typing import Sequence
|
||||
from typing import Set
|
||||
|
||||
@ -12,6 +11,7 @@ from poetry.core.packages import Package as PoetryPackage
|
||||
from tox.venv import VirtualEnv as ToxVirtualEnv
|
||||
|
||||
from tox_poetry_installer import constants
|
||||
from tox_poetry_installer import utilities
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from tox_poetry_installer import _poetry
|
||||
@ -33,7 +33,7 @@ def install(
|
||||
)
|
||||
|
||||
pip = _poetry.PipInstaller(
|
||||
env=_poetry.VirtualEnv(path=Path(venv.envconfig.envdir)),
|
||||
env=utilities.convert_virtualenv(venv),
|
||||
io=_poetry.NullIO(),
|
||||
pool=poetry.pool,
|
||||
)
|
||||
@ -49,5 +49,5 @@ def install(
|
||||
installed.add(dependency)
|
||||
else:
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Already installed {dependency}, skipping"
|
||||
f"{constants.REPORTER_PREFIX} Skipping {dependency}, already installed"
|
||||
)
|
||||
|
@ -2,13 +2,15 @@
|
||||
# Silence this one globally to support the internal function imports for the proxied poetry module.
|
||||
# See the docstring in 'tox_poetry_installer._poetry' for more context.
|
||||
# pylint: disable=import-outside-toplevel
|
||||
import sys
|
||||
import typing
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from typing import Sequence
|
||||
from typing import Set
|
||||
from typing import Union
|
||||
|
||||
import tox
|
||||
from poetry.core.packages import Dependency as PoetryDependency
|
||||
from poetry.core.packages import Package as PoetryPackage
|
||||
from tox.action import Action as ToxAction
|
||||
from tox.venv import VirtualEnv as ToxVirtualEnv
|
||||
@ -56,81 +58,79 @@ def check_preconditions(venv: ToxVirtualEnv, action: ToxAction) -> "_poetry.Poet
|
||||
) from None
|
||||
|
||||
|
||||
def convert_virtualenv(venv: ToxVirtualEnv) -> "_poetry.VirtualEnv":
|
||||
"""Convert a Tox venv to a Poetry venv
|
||||
|
||||
:param venv: Tox ``VirtualEnv`` object representing a tox virtual environment
|
||||
:returns: Poetry ``VirtualEnv`` object representing a poetry virtual environment
|
||||
"""
|
||||
from tox_poetry_installer import _poetry
|
||||
|
||||
return _poetry.VirtualEnv(path=Path(venv.envconfig.envdir))
|
||||
|
||||
|
||||
def identify_transients(
|
||||
packages: PackageMap, dep_name: str, allow_missing: Sequence[str] = ()
|
||||
dep: Union[PoetryDependency, str],
|
||||
packages: PackageMap,
|
||||
venv: "_poetry.VirtualEnv",
|
||||
allow_missing: Sequence[str] = (),
|
||||
) -> List[PoetryPackage]:
|
||||
"""Using a pool of packages, identify all transient dependencies of a given package name
|
||||
|
||||
:param dep: Either the Poetry dependency or the dependency's bare package name to recursively
|
||||
identify the transient dependencies of
|
||||
:param packages: All packages from the lockfile to use for identifying dependency relationships.
|
||||
:param dep_name: Bare name (without version) of the dependency to fetch the transient
|
||||
dependencies of.
|
||||
:param venv: Poetry virtual environment to use for package compatibility checks
|
||||
:param allow_missing: Sequence of package names to allow to be missing from the lockfile. Any
|
||||
packages that are not found in the lockfile but their name appears in this
|
||||
list will be silently skipped from installation.
|
||||
:returns: List of packages that need to be installed for the requested dependency.
|
||||
|
||||
.. note:: The package corresponding to the dependency named by ``dep_name`` is included
|
||||
in the list of returned packages.
|
||||
.. note:: The package corresponding to the dependency specified by the ``dep`` parameter will
|
||||
be included in the returned list of packages.
|
||||
"""
|
||||
from tox_poetry_installer import _poetry
|
||||
|
||||
transients: List[PoetryPackage] = []
|
||||
searched: Set[str] = set()
|
||||
|
||||
searched: Set[PoetryPackage] = set()
|
||||
def _deps_of_dep(transient: PoetryDependency):
|
||||
searched.add(transient.name)
|
||||
|
||||
def find_deps_of_deps(name: str):
|
||||
searched.add(name)
|
||||
|
||||
if name in _poetry.Provider.UNSAFE_PACKAGES:
|
||||
tox.reporter.warning(
|
||||
f"{constants.REPORTER_PREFIX} Installing package '{name}' using Poetry is not supported and will be skipped"
|
||||
)
|
||||
if venv.is_valid_for_marker(transient.marker):
|
||||
for requirement in packages[transient.name].requires:
|
||||
if requirement.name not in searched:
|
||||
_deps_of_dep(requirement)
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skip {name}: designated unsafe by Poetry"
|
||||
f"{constants.REPORTER_PREFIX} Including {transient} for installation"
|
||||
)
|
||||
transients.append(packages[transient.name])
|
||||
else:
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skipping {transient}: package requires {transient.marker}"
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
package = packages[name]
|
||||
if isinstance(dep, str):
|
||||
dep = packages[dep].to_dependency()
|
||||
|
||||
_deps_of_dep(dep)
|
||||
except KeyError as err:
|
||||
if name in allow_missing:
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skip {name}: package is not in lockfile but designated as allowed to be missing"
|
||||
)
|
||||
return
|
||||
raise err
|
||||
dep_name = err.args[0]
|
||||
|
||||
if not package.python_constraint.allows(constants.PLATFORM_VERSION):
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skip {package}: incompatible Python requirement '{package.python_constraint}' for current version '{constants.PLATFORM_VERSION}'"
|
||||
)
|
||||
elif package.platform is not None and package.platform != sys.platform:
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skip {package}: incompatible platform requirement '{package.platform}' for current platform '{sys.platform}'"
|
||||
)
|
||||
else:
|
||||
for index, dep in enumerate(package.requires):
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Processing {package} dependency {index + 1}/{len(package.requires)}: {dep.name}"
|
||||
)
|
||||
if dep.name not in searched:
|
||||
find_deps_of_deps(dep.name)
|
||||
else:
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skip {package}: already included for installation"
|
||||
)
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Including {package} for installation"
|
||||
)
|
||||
transients.append(package)
|
||||
|
||||
try:
|
||||
find_deps_of_deps(packages[dep_name].name)
|
||||
except KeyError:
|
||||
if dep_name in _poetry.Provider.UNSAFE_PACKAGES:
|
||||
tox.reporter.warning(
|
||||
f"{constants.REPORTER_PREFIX} Installing package '{dep_name}' using Poetry is not supported and will be skipped"
|
||||
)
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skipping {dep_name}: designated unsafe by Poetry"
|
||||
)
|
||||
return []
|
||||
|
||||
if dep_name in allow_missing:
|
||||
tox.reporter.verbosity2(
|
||||
f"{constants.REPORTER_PREFIX} Skipping {dep_name}: package is allowed to be unlocked"
|
||||
)
|
||||
return []
|
||||
|
||||
if any(
|
||||
@ -148,13 +148,17 @@ def identify_transients(
|
||||
|
||||
|
||||
def find_project_deps(
|
||||
packages: PackageMap, poetry: "_poetry.Poetry", extras: Sequence[str] = ()
|
||||
packages: PackageMap,
|
||||
venv: "_poetry.VirtualEnv",
|
||||
poetry: "_poetry.Poetry",
|
||||
extras: Sequence[str] = (),
|
||||
) -> List[PoetryPackage]:
|
||||
"""Find the root project dependencies
|
||||
|
||||
Recursively identify the dependencies of the root project package
|
||||
|
||||
:param packages: Mapping of all locked package names to their corresponding package object
|
||||
:param venv: Poetry virtual environment to use for package compatibility checks
|
||||
:param poetry: Poetry object for the current project
|
||||
:param extras: Sequence of extra names to include the dependencies of
|
||||
"""
|
||||
@ -180,20 +184,24 @@ def find_project_deps(
|
||||
dependencies: List[PoetryPackage] = []
|
||||
for dep in base_deps + extra_deps:
|
||||
dependencies += identify_transients(
|
||||
packages, dep.name.lower(), allow_missing=[poetry.package.name]
|
||||
dep.name.lower(), packages, venv, allow_missing=[poetry.package.name]
|
||||
)
|
||||
|
||||
return dependencies
|
||||
|
||||
|
||||
def find_additional_deps(
|
||||
packages: PackageMap, poetry: "_poetry.Poetry", dep_names: Sequence[str]
|
||||
packages: PackageMap,
|
||||
venv: "_poetry.VirtualEnv",
|
||||
poetry: "_poetry.Poetry",
|
||||
dep_names: Sequence[str],
|
||||
) -> List[PoetryPackage]:
|
||||
"""Find additional dependencies
|
||||
|
||||
Recursively identify the dependencies of an arbitrary list of package names
|
||||
|
||||
:param packages: Mapping of all locked package names to their corresponding package object
|
||||
:param venv: Poetry virtual environment to use for package compatibility checks
|
||||
:param poetry: Poetry object for the current project
|
||||
:param dep_names: Sequence of additional dependency names to recursively find the transient
|
||||
dependencies for
|
||||
@ -201,24 +209,26 @@ def find_additional_deps(
|
||||
deps: List[PoetryPackage] = []
|
||||
for dep_name in dep_names:
|
||||
deps += identify_transients(
|
||||
packages, dep_name.lower(), allow_missing=[poetry.package.name]
|
||||
dep_name.lower(), packages, venv, allow_missing=[poetry.package.name]
|
||||
)
|
||||
|
||||
return deps
|
||||
|
||||
|
||||
def find_dev_deps(
|
||||
packages: PackageMap, poetry: "_poetry.Poetry"
|
||||
packages: PackageMap, venv: "_poetry.VirtualEnv", poetry: "_poetry.Poetry"
|
||||
) -> List[PoetryPackage]:
|
||||
"""Find the dev dependencies
|
||||
|
||||
Recursively identify the Poetry dev dependencies
|
||||
|
||||
:param packages: Mapping of all locked package names to their corresponding package object
|
||||
:param venv: Poetry virtual environment to use for package compatibility checks
|
||||
:param poetry: Poetry object for the current project
|
||||
"""
|
||||
return find_additional_deps(
|
||||
packages,
|
||||
venv,
|
||||
poetry,
|
||||
poetry.pyproject.data["tool"]["poetry"].get("dev-dependencies", {}).keys(),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user