7 Commits
0.1.2 ... 0.1.3

Author SHA1 Message Date
66f2c3c768 Bump patch version 2020-09-25 01:04:12 -04:00
fd2637113f Remove excessive bandit output from security checks 2020-09-25 01:03:31 -04:00
b10e796ca1 Standardize log message usage of 'dev-package' and 'env' terminology 2020-09-25 01:02:30 -04:00
5dfbca4ff6 Update docs to indicate dev package installation support 2020-09-25 00:56:21 -04:00
db09acd8fe Fix install of dev package dependencies from lockfile
Override default pip behavior by preemptively installing dev package dependencies
Keep support for tox default skip_install config flag
2020-09-25 00:54:45 -04:00
b339e3d6d9 Update poetry requirement to mitigate coming breaking API changes
Poetry 1.1 is due any day and when it does much of the functionality this
  module uses will be moved to poetry-core. Until this module is updated
  to use poetry-core 1.1 will be a breaking change
2020-09-25 00:38:20 -04:00
9db6838d94 Update logging calls to use the tox reporter 2020-09-24 23:56:36 -04:00
5 changed files with 66 additions and 30 deletions

View File

@@ -255,7 +255,7 @@ usage in production systems.
### Path to Beta ### Path to Beta
- [ ] Verify that primary package dependencies (from the `.package` env) are installed - [X] Verify that primary package dependencies (from the `.package` env) are installed
correctly using the Poetry backend. correctly using the Poetry backend.
- [ ] Support the [`extras`](https://tox.readthedocs.io/en/latest/config.html#conf-extras) - [ ] Support the [`extras`](https://tox.readthedocs.io/en/latest/config.html#conf-extras)
Tox configuration option Tox configuration option

11
poetry.lock generated
View File

@@ -364,7 +364,7 @@ marker = "python_version >= \"3.6.1\" and python_version < \"4.0.0\""
name = "identify" name = "identify"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "1.5.4" version = "1.5.5"
[package.extras] [package.extras]
license = ["editdistance"] license = ["editdistance"]
@@ -787,6 +787,7 @@ wcwidth = "*"
[[package]] [[package]]
category = "main" category = "main"
description = "Run a subprocess in a pseudo terminal" description = "Run a subprocess in a pseudo terminal"
marker = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform != \"win32\""
name = "ptyprocess" name = "ptyprocess"
optional = false optional = false
python-versions = "*" python-versions = "*"
@@ -1171,7 +1172,7 @@ version = "1.12.1"
[[package]] [[package]]
category = "main" category = "main"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version >= \"3.6.1\" and python_version < \"3.8\" or python_version >= \"3.5\" and python_version < \"3.8\" and (python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version >= \"3.6.1\" and python_version < \"3.8\") or python_version >= \"3.6.1\" and python_version < \"3.7\" or python_version >= \"3.6.1\" and python_version < \"3.7\" and (python_version >= \"3.6.1\" and python_version < \"3.7\" or python_version < \"3.7\") or python_version >= \"3.6.1\" and python_version < \"3.8\" and (python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version >= \"3.6.1\" and python_version < \"3.8\")" marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version >= \"3.6.1\" and python_version < \"3.7\" or python_version >= \"3.6.1\" and python_version < \"3.8\""
name = "zipp" name = "zipp"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
@@ -1182,7 +1183,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
[metadata] [metadata]
content-hash = "e4a4d7c9fd7fa351a513ea4ab590e227f7c6be042a4a8a3156509f7f5df7a523" content-hash = "8df0839a479a0483c969368ef8f61f553773f27bdc0a569603c7141b6c360680"
python-versions = "^3.6" python-versions = "^3.6"
[metadata.files] [metadata.files]
@@ -1392,8 +1393,8 @@ html5lib = [
{file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"},
] ]
identify = [ identify = [
{file = "identify-1.5.4-py2.py3-none-any.whl", hash = "sha256:d7da7de6825568daa4449858ce328ecc0e1ada2554d972a6f4f90e736aaf499a"}, {file = "identify-1.5.5-py2.py3-none-any.whl", hash = "sha256:da683bfb7669fa749fc7731f378229e2dbf29a1d1337cbde04106f02236eb29d"},
{file = "identify-1.5.4.tar.gz", hash = "sha256:e4db4796b3b0cf4f9cb921da51430abffff2d4ba7d7c521184ed5252bd90d461"}, {file = "identify-1.5.5.tar.gz", hash = "sha256:7c22c384a2c9b32c5cc891d13f923f6b2653aa83e2d75d8f79be240d6c86c4f4"},
] ]
idna = [ idna = [
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "tox-poetry-installer" name = "tox-poetry-installer"
version = "0.1.2" version = "0.1.3"
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"
@@ -27,7 +27,7 @@ poetry_installer = "tox_poetry_installer"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.6"
poetry = "^1.0.0" poetry = ">=1.0.0, <1.1.0"
tox = "^2.3.0 || ^3.0.0" tox = "^2.3.0 || ^3.0.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]

View File

@@ -57,6 +57,6 @@ deps =
allowlist_externals = allowlist_externals =
bash bash
commands = commands =
bandit --recursive {toxinidir}/tox_poetry_installer.py bandit --quiet {toxinidir}/tox_poetry_installer.py
bash -c "bandit --skip B101 {toxinidir}/tests/*.py" bash -c "bandit --quiet --skip B101 {toxinidir}/tests/*.py"
bash -c "poetry export --format requirements.txt --without-hashes --dev | safety check --stdin --bare" bash -c "poetry export --format requirements.txt --without-hashes --dev | safety check --stdin --bare"

View File

@@ -5,11 +5,11 @@ installation functionality to install dependencies from the Poetry lockfile for
does this by using ``poetry`` to read in the lockfile, identify necessary dependencies, and then does this by using ``poetry`` to read in the lockfile, identify necessary dependencies, and then
use Poetry's ``PipInstaller`` class to install those packages into the Tox environment. use Poetry's ``PipInstaller`` class to install those packages into the Tox environment.
""" """
import logging
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Sequence
from typing import Tuple from typing import Tuple
from poetry.factory import Factory as PoetryFactory from poetry.factory import Factory as PoetryFactory
@@ -20,13 +20,14 @@ from poetry.packages import Package as PoetryPackage
from poetry.puzzle.provider import Provider as PoetryProvider from poetry.puzzle.provider import Provider as PoetryProvider
from poetry.utils.env import VirtualEnv as PoetryVirtualEnv from poetry.utils.env import VirtualEnv as PoetryVirtualEnv
from tox import hookimpl from tox import hookimpl
from tox import reporter
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
__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.1.2" __version__ = "0.1.3"
__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>"]
@@ -34,6 +35,8 @@ __authors__ = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
_PEP508_VERSION_DELIMITERS: Tuple[str, ...] = ("~=", "==", "!=", ">", "<") _PEP508_VERSION_DELIMITERS: Tuple[str, ...] = ("~=", "==", "!=", ">", "<")
_REPORTER_PREFIX = f"[{__title__}]:"
class ToxPoetryInstallerException(Exception): class ToxPoetryInstallerException(Exception):
"""Error while installing locked dependencies to the test environment""" """Error while installing locked dependencies to the test environment"""
@@ -43,9 +46,24 @@ class NoLockedDependencyError(ToxPoetryInstallerException):
"""Cannot install a package that is not in the lockfile""" """Cannot install a package that is not in the lockfile"""
def _make_poetry(venv: ToxVirtualEnv) -> Poetry: def _install_to_venv(
"""Helper to make a poetry object from a toxenv""" poetry: Poetry, venv: ToxVirtualEnv, packages: Sequence[PoetryPackage]
return PoetryFactory().create_poetry(venv.envconfig.config.toxinidir) ):
"""Install a bunch of packages to a virtualenv
:param poetry: Poetry object the packages were sourced from
:param venv: Tox virtual environment to install the packages to
:param packages: List of packages to install to the virtual environment
"""
installer = PoetryPipInstaller(
env=PoetryVirtualEnv(path=Path(venv.envconfig.envdir)),
io=PoetryNullIO(),
pool=poetry.pool,
)
for dependency in packages:
reporter.verbosity1(f"{_REPORTER_PREFIX} installing {dependency}")
installer.install(dependency)
def _find_locked_dependencies( def _find_locked_dependencies(
@@ -72,6 +90,9 @@ def _find_locked_dependencies(
def find_transients(name: str) -> List[PoetryPackage]: def find_transients(name: str) -> List[PoetryPackage]:
if name in PoetryProvider.UNSAFE_PACKAGES: if name in PoetryProvider.UNSAFE_PACKAGES:
reporter.warning(
f"{_REPORTER_PREFIX} installing '{name}' using Poetry is not supported; skipping"
)
return [] return []
transients = [packages[name]] transients = [packages[name]]
for dep in packages[name].requires: for dep in packages[name].requires:
@@ -109,34 +130,48 @@ def tox_testenv_install_deps(
:param action: Tox action object :param action: Tox action object
""" """
logger = logging.getLogger(__name__)
if action.name == venv.envconfig.config.isolated_build_env: if action.name == venv.envconfig.config.isolated_build_env:
logger.debug( reporter.verbosity1(
f"Environment {action.name} is isolated build environment; skipping Poetry-based dependency installation" f"{_REPORTER_PREFIX} skipping isolated build env '{action.name}'"
) )
return None return None
poetry = _make_poetry(venv) poetry = PoetryFactory().create_poetry(venv.envconfig.config.toxinidir)
logger.debug(f"Loaded project pyproject.toml from {poetry.file}") reporter.verbosity1(
f"{_REPORTER_PREFIX} loaded project pyproject.toml from {poetry.file}"
)
dependencies: List[PoetryPackage] = [] dependencies: List[PoetryPackage] = []
for env_dependency in venv.envconfig.deps: for env_dependency in venv.envconfig.deps:
dependencies += _find_locked_dependencies(poetry, env_dependency.name) dependencies += _find_locked_dependencies(poetry, env_dependency.name)
logger.debug( reporter.verbosity1(
f"Identified {len(dependencies)} dependencies for environment {action.name}" f"{_REPORTER_PREFIX} identified {len(dependencies)} actual dependencies from {len(venv.envconfig.deps)} specified env dependencies"
) )
installer = PoetryPipInstaller( reporter.verbosity0(
env=PoetryVirtualEnv(path=Path(venv.envconfig.envdir)), f"{_REPORTER_PREFIX} ({venv.name}) installing {len(dependencies)} env dependencies from lockfile"
io=PoetryNullIO(),
pool=poetry.pool,
) )
_install_to_venv(poetry, venv, dependencies)
for dependency in dependencies: if not venv.envconfig.skip_install:
logger.info(f"Installing environment dependency: {dependency}") reporter.verbosity1(
installer.install(dependency) f"{_REPORTER_PREFIX} env specifies 'skip_install = false', performing installation of dev-package dependencies"
)
primary_dependencies = poetry.locker.locked_repository(False).packages
reporter.verbosity1(
f"{_REPORTER_PREFIX} identified {len(primary_dependencies)} dependencies of dev-package"
)
reporter.verbosity0(
f"{_REPORTER_PREFIX} ({venv.name}) installing {len(primary_dependencies)} dev-package dependencies from lockfile"
)
_install_to_venv(poetry, venv, primary_dependencies)
else:
reporter.verbosity1(
f"{_REPORTER_PREFIX} env specifies 'skip_install = true', skipping installation of dev-package package"
)
return dependencies return dependencies