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]
|
[tool.poetry]
|
||||||
name = "tox-poetry-installer"
|
name = "tox-poetry-installer"
|
||||||
version = "0.6.3"
|
version = "0.6.4"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
|
authors = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
|
||||||
description = "Tox plugin to install Tox environment dependencies using the Poetry backend and lockfile"
|
description = "Tox plugin to install Tox environment dependencies using the Poetry backend and lockfile"
|
||||||
@ -22,6 +22,7 @@ classifiers = [
|
|||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Natural Language :: English",
|
"Natural Language :: English",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# pylint: disable=missing-docstring
|
# pylint: disable=missing-docstring
|
||||||
__title__ = "tox-poetry-installer"
|
__title__ = "tox-poetry-installer"
|
||||||
__summary__ = "Tox plugin to install Tox environment dependencies using the Poetry backend and lockfile"
|
__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/"
|
__url__ = "https://github.com/enpaul/tox-poetry-installer/"
|
||||||
__license__ = "MIT"
|
__license__ = "MIT"
|
||||||
__authors__ = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
|
__authors__ = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
|
||||||
|
@ -5,11 +5,8 @@ in this module.
|
|||||||
|
|
||||||
All constants should be type hinted.
|
All constants should be type hinted.
|
||||||
"""
|
"""
|
||||||
import sys
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
from poetry.core.semver.version import Version
|
|
||||||
|
|
||||||
from tox_poetry_installer import __about__
|
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
|
# Prefix all reporter messages should include to indicate that they came from this module in the
|
||||||
# console output.
|
# console output.
|
||||||
REPORTER_PREFIX: str = f"{__about__.__title__}:"
|
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}"
|
f"{constants.REPORTER_PREFIX} Loaded project pyproject.toml from {poetry.file}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
virtualenv = utilities.convert_virtualenv(venv)
|
||||||
|
|
||||||
if not poetry.locker.is_fresh():
|
if not poetry.locker.is_fresh():
|
||||||
tox.reporter.warning(
|
tox.reporter.warning(
|
||||||
f"The Poetry lock file is not up to date with the latest changes in {poetry.file}"
|
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:
|
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(
|
tox.reporter.verbosity1(
|
||||||
f"{constants.REPORTER_PREFIX} Identified {len(dev_deps)} development dependencies to install to env"
|
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(
|
env_deps = utilities.find_additional_deps(
|
||||||
packages, poetry, venv.envconfig.locked_deps
|
packages, virtualenv, poetry, venv.envconfig.locked_deps
|
||||||
)
|
)
|
||||||
|
|
||||||
tox.reporter.verbosity1(
|
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:
|
if not venv.envconfig.skip_install and not venv.envconfig.config.skipsdist:
|
||||||
project_deps = utilities.find_project_deps(
|
project_deps = utilities.find_project_deps(
|
||||||
packages, poetry, venv.envconfig.extras
|
packages, virtualenv, poetry, venv.envconfig.extras
|
||||||
)
|
)
|
||||||
tox.reporter.verbosity1(
|
tox.reporter.verbosity1(
|
||||||
f"{constants.REPORTER_PREFIX} Identified {len(project_deps)} project dependencies to install to env"
|
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.
|
# See the docstring in 'tox_poetry_installer._poetry' for more context.
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
import typing
|
import typing
|
||||||
from pathlib import Path
|
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
from typing import Set
|
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.venv import VirtualEnv as ToxVirtualEnv
|
||||||
|
|
||||||
from tox_poetry_installer import constants
|
from tox_poetry_installer import constants
|
||||||
|
from tox_poetry_installer import utilities
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from tox_poetry_installer import _poetry
|
from tox_poetry_installer import _poetry
|
||||||
@ -33,7 +33,7 @@ def install(
|
|||||||
)
|
)
|
||||||
|
|
||||||
pip = _poetry.PipInstaller(
|
pip = _poetry.PipInstaller(
|
||||||
env=_poetry.VirtualEnv(path=Path(venv.envconfig.envdir)),
|
env=utilities.convert_virtualenv(venv),
|
||||||
io=_poetry.NullIO(),
|
io=_poetry.NullIO(),
|
||||||
pool=poetry.pool,
|
pool=poetry.pool,
|
||||||
)
|
)
|
||||||
@ -49,5 +49,5 @@ def install(
|
|||||||
installed.add(dependency)
|
installed.add(dependency)
|
||||||
else:
|
else:
|
||||||
tox.reporter.verbosity2(
|
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.
|
# 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.
|
# See the docstring in 'tox_poetry_installer._poetry' for more context.
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
import sys
|
|
||||||
import typing
|
import typing
|
||||||
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import tox
|
import tox
|
||||||
|
from poetry.core.packages import Dependency as PoetryDependency
|
||||||
from poetry.core.packages import Package as PoetryPackage
|
from poetry.core.packages import Package as PoetryPackage
|
||||||
from tox.action import Action as ToxAction
|
from tox.action import Action as ToxAction
|
||||||
from tox.venv import VirtualEnv as ToxVirtualEnv
|
from tox.venv import VirtualEnv as ToxVirtualEnv
|
||||||
@ -56,81 +58,79 @@ def check_preconditions(venv: ToxVirtualEnv, action: ToxAction) -> "_poetry.Poet
|
|||||||
) from None
|
) 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(
|
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]:
|
) -> List[PoetryPackage]:
|
||||||
"""Using a pool of packages, identify all transient dependencies of a given package name
|
"""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 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
|
:param venv: Poetry virtual environment to use for package compatibility checks
|
||||||
dependencies of.
|
|
||||||
:param allow_missing: Sequence of package names to allow to be missing from the lockfile. Any
|
: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
|
packages that are not found in the lockfile but their name appears in this
|
||||||
list will be silently skipped from installation.
|
list will be silently skipped from installation.
|
||||||
:returns: List of packages that need to be installed for the requested dependency.
|
: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
|
.. note:: The package corresponding to the dependency specified by the ``dep`` parameter will
|
||||||
in the list of returned packages.
|
be included in the returned list of packages.
|
||||||
"""
|
"""
|
||||||
from tox_poetry_installer import _poetry
|
from tox_poetry_installer import _poetry
|
||||||
|
|
||||||
transients: List[PoetryPackage] = []
|
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):
|
if venv.is_valid_for_marker(transient.marker):
|
||||||
searched.add(name)
|
for requirement in packages[transient.name].requires:
|
||||||
|
if requirement.name not in searched:
|
||||||
if name in _poetry.Provider.UNSAFE_PACKAGES:
|
_deps_of_dep(requirement)
|
||||||
tox.reporter.warning(
|
|
||||||
f"{constants.REPORTER_PREFIX} Installing package '{name}' using Poetry is not supported and will be skipped"
|
|
||||||
)
|
|
||||||
tox.reporter.verbosity2(
|
tox.reporter.verbosity2(
|
||||||
f"{constants.REPORTER_PREFIX} Skip {name}: designated unsafe by Poetry"
|
f"{constants.REPORTER_PREFIX} Including {transient} for installation"
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
package = packages[name]
|
|
||||||
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
|
|
||||||
|
|
||||||
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}'"
|
|
||||||
)
|
)
|
||||||
|
transients.append(packages[transient.name])
|
||||||
else:
|
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(
|
tox.reporter.verbosity2(
|
||||||
f"{constants.REPORTER_PREFIX} Including {package} for installation"
|
f"{constants.REPORTER_PREFIX} Skipping {transient}: package requires {transient.marker}"
|
||||||
)
|
)
|
||||||
transients.append(package)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
find_deps_of_deps(packages[dep_name].name)
|
if isinstance(dep, str):
|
||||||
except KeyError:
|
dep = packages[dep].to_dependency()
|
||||||
|
|
||||||
|
_deps_of_dep(dep)
|
||||||
|
except KeyError as err:
|
||||||
|
dep_name = err.args[0]
|
||||||
|
|
||||||
if dep_name in _poetry.Provider.UNSAFE_PACKAGES:
|
if dep_name in _poetry.Provider.UNSAFE_PACKAGES:
|
||||||
tox.reporter.warning(
|
tox.reporter.warning(
|
||||||
f"{constants.REPORTER_PREFIX} Installing package '{dep_name}' using Poetry is not supported and will be skipped"
|
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 []
|
return []
|
||||||
|
|
||||||
if any(
|
if any(
|
||||||
@ -148,13 +148,17 @@ def identify_transients(
|
|||||||
|
|
||||||
|
|
||||||
def find_project_deps(
|
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]:
|
) -> List[PoetryPackage]:
|
||||||
"""Find the root project dependencies
|
"""Find the root project dependencies
|
||||||
|
|
||||||
Recursively identify the dependencies of the root project package
|
Recursively identify the dependencies of the root project package
|
||||||
|
|
||||||
:param packages: Mapping of all locked package names to their corresponding package object
|
: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 poetry: Poetry object for the current project
|
||||||
:param extras: Sequence of extra names to include the dependencies of
|
:param extras: Sequence of extra names to include the dependencies of
|
||||||
"""
|
"""
|
||||||
@ -180,20 +184,24 @@ def find_project_deps(
|
|||||||
dependencies: List[PoetryPackage] = []
|
dependencies: List[PoetryPackage] = []
|
||||||
for dep in base_deps + extra_deps:
|
for dep in base_deps + extra_deps:
|
||||||
dependencies += identify_transients(
|
dependencies += identify_transients(
|
||||||
packages, dep.name.lower(), allow_missing=[poetry.package.name]
|
dep.name.lower(), packages, venv, allow_missing=[poetry.package.name]
|
||||||
)
|
)
|
||||||
|
|
||||||
return dependencies
|
return dependencies
|
||||||
|
|
||||||
|
|
||||||
def find_additional_deps(
|
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]:
|
) -> List[PoetryPackage]:
|
||||||
"""Find additional dependencies
|
"""Find additional dependencies
|
||||||
|
|
||||||
Recursively identify the dependencies of an arbitrary list of package names
|
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 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 poetry: Poetry object for the current project
|
||||||
:param dep_names: Sequence of additional dependency names to recursively find the transient
|
:param dep_names: Sequence of additional dependency names to recursively find the transient
|
||||||
dependencies for
|
dependencies for
|
||||||
@ -201,24 +209,26 @@ def find_additional_deps(
|
|||||||
deps: List[PoetryPackage] = []
|
deps: List[PoetryPackage] = []
|
||||||
for dep_name in dep_names:
|
for dep_name in dep_names:
|
||||||
deps += identify_transients(
|
deps += identify_transients(
|
||||||
packages, dep_name.lower(), allow_missing=[poetry.package.name]
|
dep_name.lower(), packages, venv, allow_missing=[poetry.package.name]
|
||||||
)
|
)
|
||||||
|
|
||||||
return deps
|
return deps
|
||||||
|
|
||||||
|
|
||||||
def find_dev_deps(
|
def find_dev_deps(
|
||||||
packages: PackageMap, poetry: "_poetry.Poetry"
|
packages: PackageMap, venv: "_poetry.VirtualEnv", poetry: "_poetry.Poetry"
|
||||||
) -> List[PoetryPackage]:
|
) -> List[PoetryPackage]:
|
||||||
"""Find the dev dependencies
|
"""Find the dev dependencies
|
||||||
|
|
||||||
Recursively identify the Poetry dev dependencies
|
Recursively identify the Poetry dev dependencies
|
||||||
|
|
||||||
:param packages: Mapping of all locked package names to their corresponding package object
|
: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 poetry: Poetry object for the current project
|
||||||
"""
|
"""
|
||||||
return find_additional_deps(
|
return find_additional_deps(
|
||||||
packages,
|
packages,
|
||||||
|
venv,
|
||||||
poetry,
|
poetry,
|
||||||
poetry.pyproject.data["tool"]["poetry"].get("dev-dependencies", {}).keys(),
|
poetry.pyproject.data["tool"]["poetry"].get("dev-dependencies", {}).keys(),
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user