Skip to content

Crude error message if using VIRTUALENV_DISCOVERY=pyenv #2896

@velle

Description

@velle

When virtualenv-pyenv is not installed, and the pyenv entry point is therefore not available.

virtualenv, with cli parameter

Using cli parameter, the response is reasonable and helpful

$ virtualenv --discovery pyenv myenv
usage: virtualenv [--version] [--with-traceback] [-v | -q] [--read-only-app-data] [--app-data APP_DATA] [--reset-app-data] [--upgrade-embed-wheels] [--discovery {builtin}]
virtualenv: error: argument --discovery: invalid choice: 'pyenv' (choose from 'builtin')
SystemExit: 2

virtualenv, with env.var.

If using the environment variable, its less helpful:

$ VIRTUALENV_DISCOVERY=pyenv virtualenv myenv
KeyError: 'pyenv'

tox, with env.var.

# tox.ini
[tox]
envlist = py39

[testenv]
commands = pytest
setenv = 
    VIRTUALENV_DISCOVERY=pyenv

The result is both messy and unhelpful. And as a newbie, I thought it was probably a bug, but in fact I just needed to install virtualenv-pyenv.

$ tox
py39: internal error
Traceback (most recent call last):
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/session/cmd/run/single.py", line 47, in _evaluate
    tox_env.setup()
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/api.py", line 282, in setup
    self._setup_env()
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/runner.py", line 98, in _setup_env
    super()._setup_env()
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/api.py", line 243, in _setup_env
    self.ensure_python_env()
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/api.py", line 247, in ensure_python_env
    conf = self.python_cache()
           ^^^^^^^^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/virtual_env/api.py", line 82, in python_cache
    base = super().python_cache()
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/api.py", line 284, in python_cache
    "version_info": list(self.base_python.version_info),
                         ^^^^^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/api.py", line 294, in base_python
    self._base_python = self._get_python(base_pythons)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/virtual_env/api.py", line 139, in _get_python
    interpreter = self.creator.interpreter
                  ^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/virtual_env/api.py", line 131, in creator
    return self.session.creator
           ^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/tox/tox_env/python/virtual_env/api.py", line 112, in session
    self._virtualenv_session = session_via_cli(env_dir, options=None, setup_logging=False, env=env)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/virtualenv/run/__init__.py", line 49, in session_via_cli
    parser, elements = build_parser(args, options, setup_logging, env)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/virtualenv/run/__init__.py", line 77, in build_parser
    discover = get_discover(parser, args)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/virtualenv/run/plugin/discovery.py", line 27, in get_discover
    discover_class = discover_types[options.discovery]
                     ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
KeyError: 'pyenv'
  py39: FAIL code 2 (0.01 seconds)
  evaluation failed :( (0.07 seconds)

Solution, maybe...

I admit I don't understand virtualenv too well, so this is only my humble suggestion.

So first of all. In the case that VIRTUALENV_DISCOVERY holds a key that is not available as entry point, virtualenv needs to provide a more helpful reponse to the cli. Not just KeyError. Perhaps it could raise an exception like this

class DiscoverNotFoundException(Exception):
    def __init__(self, key: str, discover_types: Mapping[str, type]):
        self.key = key
        self.discover_types = discover_types

    def __str__(self):
        msg = "No entrypoint in group 'virtualenv.discover' with config key '%s'. Available keys are:" % self.key
        for key,typ in self.discover_types.items():
            msg += "\n    %-20s ->   %s" % (key, typ)
        return msg

with this check in get_discover:

    options, _ = parser.parse_known_args(args)
    # ---- new code begin ----
    if options.discovery not in discover_types:
        raise DiscoverNotFoundException(options.discovery, discover_types)
    # ---- new code end ----
    discover_class = discover_types[options.discovery]
    discover_class.add_parser_arguments(discovery_parser)
    options, _ = parser.parse_known_args(args, namespace=options)
    return discover_class(options)

It would look like this:

$ VIRTUALENV_DISCOVERY=discoverychannel virtualenv myenv
DiscoverNotFoundException: No entrypoint in group 'virtualenv.discover' with config key 'discoverychannel'. Available keys are:
    pyenv                ->   <class '_virtualenv_pyenv.discovery.PyenvCompat'>
    pyenv-compat         ->   <class '_virtualenv_pyenv.discovery.PyenvCompat'>
    pyenv-fallback       ->   <class '_virtualenv_pyenv.discovery.PyenvFallback'>
    pyenv-strict         ->   <class '_virtualenv_pyenv.discovery.PyenvStrict'>
    builtin              ->   <class 'virtualenv.discovery.builtin.Builtin'>

That would make sure that the user always gets some helpful information, no matter how the tool is used.

And ideally, tox should catch this exception and print it without the stacktrace.

Environment

Ubuntu 24.04, bash, Python 3.12.3

(toxrunner) $ pip list
Package       Version
------------- ---------
cachetools    6.1.0
chardet       5.2.0
colorama      0.4.6
dbf           0.99.11a1
distlib       0.3.9
filelock      3.18.0
packaging     25.0
pip           24.0
platformdirs  4.3.8
pluggy        1.6.0
pyenv-inspect 0.4.0
pyproject-api 1.9.1
tox           4.27.0
virtualenv    20.31.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions