diff --git a/README.md b/README.md index bb23a28..aeafcf4 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Install the package and include `pip_version` in your tox.ini ```tox [testenv] -pip_version = 19.0.1 +pip_version = pip==19.0.1 ``` Or, set the `TOX_PIP_VERSION` environment variable, @@ -44,7 +44,7 @@ export TOX_PIP_VERSION=18.1 tox ``` -The plugin will install that specific pip into the tox-created virtualenv, +The plugin will install that version of pip into the tox-created virtualenv, just after tox creates the virtualenv, but before dependencies are installed. The `pip_version` within tox.ini, if present, is always used over the @@ -53,6 +53,21 @@ environment variable. If neither `pip_version` or `TOX_PIP_VERSION` is present, the plugin does nothing. +### Version Sets + +Version sets/ranges are supported, enabling installation of a version of pip +matching a set of specifiers. There are two basic formats: a plain version +number, or the package name with optional [PEP440-compatible]( +https://www.python.org/dev/peps/pep-0440/#version-specifiers) version +specifiers. + +| tox.ini | effective pip command | +| ---------------------------- | ---------------------------- | +| `pip_version = 19.0` | `pip install -U pip==19.0` | +| `pip_version = pip==19.0` | `pip install -U pip==19.0` | +| `pip_version = pip>=19.0` | `pip install -U pip>=19.0` | +| `pip_version = pip!=19,>18` | `pip install -U pip!=19,>18` | +| `pip_version = pip` | `pip install -U pip` | ### Tests diff --git a/dev-requirements.txt b/dev-requirements.txt index 7f6c649..553d581 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,7 @@ backports.tempfile bumpversion flake8 +packaging pytest pytest-xdist readme_renderer[md] diff --git a/tests/check-pip-version.py b/tests/check-pip-version.py new file mode 100644 index 0000000..89ac340 --- /dev/null +++ b/tests/check-pip-version.py @@ -0,0 +1,29 @@ +import subprocess +import sys + +from packaging.version import Version +from packaging.specifiers import SpecifierSet + +VERSION_RANGE_OPERATORS = ('==', '!=', '<', '<=', '>', '>=', '~=', '===') + + +def to_specifer_set(raw_version): + if any(raw_version.startswith(op) for op in VERSION_RANGE_OPERATORS): + return SpecifierSet(raw_version) + return SpecifierSet('==%s' % raw_version) + + +def main(): + expected_set = to_specifer_set(sys.argv[1]) + raw_pip_version = subprocess.check_output(["pip", "--version"]).decode() + actual_version = Version(raw_pip_version.split(' ')[1]) + if actual_version not in expected_set: + print("FAIL: version '%s' not in set '%s'" + % (actual_version, expected_set)) + return 1 + print("PASS: version '%s' is in set '%s'" % (actual_version, expected_set)) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/check-pip-version.sh b/tests/check-pip-version.sh deleted file mode 100755 index 4f95821..0000000 --- a/tests/check-pip-version.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -x - -EXPECTED_PIP_VERSION="${1?Need expected pip version}" -PIP_VERSION_OUTPUT=`pip --version` - -if [[ "$PIP_VERSION_OUTPUT" == *"$EXPECTED_PIP_VERSION"* ]]; then - exit 0 -fi - -echo "Expected version ${EXPECTED_PIP_VERSION}, found ${PIP_VERSION_OUTPUT}" -exit 1 diff --git a/tests/test-env-inheritance/tox.ini b/tests/test-env-inheritance/tox.ini index 4004513..75d8fc4 100644 --- a/tests/test-env-inheritance/tox.ini +++ b/tests/test-env-inheritance/tox.ini @@ -6,12 +6,12 @@ skipsdist = True [testenv] pip_version = 10.0.1 -whitelist_externals = bash -commands = bash -c '../check-pip-version.sh 10.0.1' +deps = packaging +commands = python ../check-pip-version.py 10.0.1 [testenv:env1] -commands = bash -c '../check-pip-version.sh 10.0.1' +commands = python ../check-pip-version.py 10.0.1 [testenv:env2] pip_version = 18.1 -commands = bash -c '../check-pip-version.sh 18.1' +commands = python ../check-pip-version.py 18.1 diff --git a/tests/test-environment-variable/tox.ini b/tests/test-environment-variable/tox.ini index 930065b..bde7b15 100644 --- a/tests/test-environment-variable/tox.ini +++ b/tests/test-environment-variable/tox.ini @@ -7,16 +7,16 @@ skipsdist = True [testenv] # 18.1 comes from the TOX_PIP_VERSION env var -whitelist_externals = bash -commands = bash -c '../check-pip-version.sh 18.1' +deps = packaging +commands = python ../check-pip-version.py 18.1 [testenv:env1] # Check that setenv overrides an externally set env var setenv = TOX_PIP_VERSION = 10.0.1 -commands = bash -c '../check-pip-version.sh 10.0.1' +commands = python ../check-pip-version.py 10.0.1 [testenv:env2] # Check that pip_version wins over env vars pip_version = 19.0.1 -commands = bash -c '../check-pip-version.sh 19.0.1' +commands = python ../check-pip-version.py 19.0.1 diff --git a/tests/test-two-envs/tox.ini b/tests/test-two-envs/tox.ini index 82c11ae..2e75111 100644 --- a/tests/test-two-envs/tox.ini +++ b/tests/test-two-envs/tox.ini @@ -5,12 +5,12 @@ minversion = 2.0 skipsdist = True [testenv] -whitelist_externals = bash +deps = packaging [testenv:pip18_1] pip_version = 18.1 -commands = bash -c '../check-pip-version.sh 18.1' +commands = python ../check-pip-version.py 18.1 [testenv:pip19_0_1] pip_version = 19.0.1 -commands = bash -c '../check-pip-version.sh 19.0.1' +commands = python ../check-pip-version.py 19.0.1 diff --git a/tests/test-version-specifiers/tox.ini b/tests/test-version-specifiers/tox.ini new file mode 100644 index 0000000..a382d7d --- /dev/null +++ b/tests/test-version-specifiers/tox.ini @@ -0,0 +1,44 @@ +# Verify we can use version ranges +[tox] +envlist = equal, not_equal, lesser, greater, lesser_eq, greater_eq, tilde_eq, combo, latest +minversion = 2.0 +skipsdist = True + +[testenv] +deps = packaging + +[testenv:equal] +pip_version = pip==18.1 +commands = python ../check-pip-version.py 18.1 + +[testenv:not_equal] +pip_version = pip!=19.0 +commands = python ../check-pip-version.py '!=19.0' + +[testenv:lesser] +pip_version = pip<19.0 +commands = python ../check-pip-version.py '<19.0' + +[testenv:greater] +pip_version = pip>19.0 +commands = python ../check-pip-version.py '>19.0' + +[testenv:lesser_eq] +pip_version = pip<=19.0 +commands = python ../check-pip-version.py '<=19.0' + +[testenv:greater_eq] +pip_version = pip>=19.0 +commands = python ../check-pip-version.py '>=19.0' + +[testenv:tilde_eq] +pip_version = pip~=19.0 +commands = python ../check-pip-version.py '~=19.0' + +[testenv:combo] +pip_version = pip!=19.0,<19.1,>10.0 +commands = python ../check-pip-version.py '!=19.0,<19.1,>10.0' + +[testenv:latest] +pip_version = pip +commands = python ../check-pip-version.py '>=19.1.1' diff --git a/tests/test.py b/tests/test.py index 341decc..0118074 100644 --- a/tests/test.py +++ b/tests/test.py @@ -3,13 +3,13 @@ import subprocess import sys +import pytest + if sys.version_info.major == 2: from backports import tempfile else: import tempfile -import pytest - HERE = os.path.realpath(os.path.dirname(__file__)) PACKAGE_DIR = os.path.realpath(os.path.join(HERE, '..')) @@ -34,6 +34,9 @@ "test-environment-variable": { "env": {"TOX_PIP_VERSION": "18.1"}, }, + "test-version-specifiers": { + "env": {}, + } } PYTEST_PARAMETERS = sorted(itertools.product(TOX_VERSIONS, CASES)) diff --git a/tox.ini b/tox.ini index 8b1841a..5885fd6 100644 --- a/tox.ini +++ b/tox.ini @@ -6,4 +6,4 @@ skipsdist = True [testenv:flake8] deps = flake8 -commands = flake8 ./tox_pip_version +commands = flake8 ./tox_pip_version ./tests diff --git a/tox_pip_version/hooks.py b/tox_pip_version/hooks.py index adc28db..f1fc7b9 100644 --- a/tox_pip_version/hooks.py +++ b/tox_pip_version/hooks.py @@ -23,6 +23,15 @@ def _testenv_create(venv, action): tox_testenv_create(venv, action) +def get_pip_package_version(pip_version): + pip_version = pip_version.lower().strip() + # tox.ini: pip_version = pip==19.0 + if pip_version.startswith('pip'): + return pip_version + # tox.ini: pip_version = 19.0 + return 'pip==%s' % pip_version + + @tox.hookimpl def tox_configure(config): for env, envconfig in config.envconfigs.items(): @@ -53,10 +62,11 @@ def tox_testenv_create(venv, action): # Use `pip_version` in tox.ini over the environment variable pip_version = PER_ENV_PIP_VERSIONS.get(venvname, tox_pip_version_from_env) if pip_version: + package = get_pip_package_version(pip_version) + # Is there a way to output this better? Genuine tox commands show up # colorized (as bold white text)... - print("%s: pip_version = %s" % (venvname, pip_version)) - package = "pip==%s" % pip_version + print("%s: pip_version is %s" % (venvname, package)) # "private" _install method - unstable interface? venv._install([package], extraopts=["-U"], action=action)