pythonpytestvirtualenvpyenvtox

Getting tox to use the Python version set by pyenv


I can't seem to wrap my head around managing Python versions. When I run tox, I can immediately see that it's using Python 3.7.9:

$ tox
py39: commands[0]> coverage run -m pytest
================================================================================== test session starts ==================================================================================
platform darwin -- Python 3.7.9, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /usr/local/bin/python3

But it's configured to use 3.9:

[tox]
envlist = py39,manifest,check-formatting,lint
skipsdist = True
usedevelop = True
indexserver =
    spotify = https://artifactory.spotify.net/artifactory/api/pypi/pypi/simple

[testenv]
basepython = python3.9
deps =
    :spotify:-r{toxinidir}/dev-requirements.txt
commands =
    coverage run -m pytest {posargs}
allowlist_externals = coverage

[testenv:manifest]
; a safety check for source distributions
basepython = python3.9
deps = check-manifest
skip_install = true
commands = check-manifest

Here's what I see with which:

$ pyenv local 3.9.10
$ which python
/Users/acheong/.pyenv/shims/python
$ which python3
/Library/Frameworks/Python.framework/Versions/3.7/bin/python3
$ pyenv which python
/Users/acheong/.pyenv/versions/3.9.10/bin/python

pytest also uses the wrong version:

$ pytest tests
================================================================================== test session starts ==================================================================================
platform darwin -- Python 3.7.9, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /Library/Frameworks/Python.framework/Versions/3.7/bin/python3
cachedir: .pytest_cache
rootdir: /Users/acheong/src/spotify/protean/ezmode-cli, configfile: tox.ini, testpaths: tests
plugins: mock-3.10.0, cov-2.10.0

But in this case I learned I can do this:

$ pyenv exec pytest tests
================================================================================== test session starts ==================================================================================
platform darwin -- Python 3.9.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /Users/acheong/.pyenv/versions/3.9.10/bin/python
cachedir: .pytest_cache
rootdir: /Users/acheong/src/spotify/protean/ezmode-cli, configfile: tox.ini, testpaths: tests
plugins: mock-3.10.0, cov-2.10.0

But when I try that with tox, I get an error:

$ pyenv exec tox
Traceback (most recent call last):
  File "/Users/acheong/.pyenv/versions/3.9.10/bin/tox", line 8, in <module>
    sys.exit(run())
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/run.py", line 19, in run
    result = main(sys.argv[1:] if args is None else args)
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/run.py", line 38, in main
    state = setup_state(args)
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/run.py", line 53, in setup_state
    options = get_options(*args)
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/config/cli/parse.py", line 38, in get_options
    guess_verbosity, log_handler, source = _get_base(args)
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/config/cli/parse.py", line 61, in _get_base
    MANAGER.load_plugins(source.path)
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/plugin/manager.py", line 90, in load_plugins
    self._register_plugins(inline)
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/plugin/manager.py", line 38, in _register_plugins
    self.manager.load_setuptools_entrypoints(NAME)
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pluggy/_manager.py", line 287, in load_setuptools_entrypoints
    plugin = ep.load()
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/importlib/metadata.py", line 77, in load
    module = import_module(match.group('module'))
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox_pyenv.py", line 48, in <module>
    from tox import hookimpl as tox_hookimpl
ImportError: cannot import name 'hookimpl' from 'tox' (/Users/acheong/.pyenv/versions/3.9.10/lib/python3.9/site-packages/tox/__init__.py)

I've tried a lot of things I found online but I'm afraid if anything I've only messed up my environment even more. What steps can I take to diagnose the problem and get tox to use my pyenv version?


Solution

  • Although this is an old question and it has already been marked as solved, I dare to give another answer since this post appears on the first page of Google search results of the "tox pyenv" query.

    As already noted, tox-pyenv is not compatible with tox version 4. Moreover, tox no longer discovers Python executables by itself, this job is now delegated to virtualenv. That being said, a special tox plugin like tox-pyenv is no longer needed, the discovery machinery is extended via virtualenv plugins, not tox plugins. Here is an explanation in the tox-pyenv issues, and here is a more detailed migration guide.

    For those who don't want to configure virtualenv and are looking for a simpler solution, there is another tox plugin — tox-pyenv-redux (not related to tox-pyenv). It is compatible with tox 4, uses virtualenv-pyenv under the hood, and does not require any configuration to work (though, it has a configuration setting).