Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-use of virtual environments across envs in tox4 #2788

Closed
fdallmeier opened this issue Dec 29, 2022 · 10 comments · Fixed by #2789
Closed

Re-use of virtual environments across envs in tox4 #2788

fdallmeier opened this issue Dec 29, 2022 · 10 comments · Fixed by #2789

Comments

@fdallmeier
Copy link

Issue

In tox <4 it was possible to re-use the same python virtual environment across envs by specifying a shared envdir. Tox 4 however re-creates the virtual environment if the env name is different to the env name that was used to create the virtual environment.
It seems that tox 4 remembers the env name used to create the virtual environment in .tox-info.json and then decides to re-create the virtual environment if the env name changed.

I was not able to spot this change documented under https://tox.wiki/en/latest/faq.html#breaking-changes-in-tox-4. Also, I was not able to find an alternative way to re-use virtual environments across envs in tox 4.

Use case

A common use case for me is to run pytest and pylint with the same set of dependencies. Re-using the same virtual environment avoids the expensive creation of the multiple copies of the same virtual environment. Since dependencies change frequently this cost is significant. In practice, I also run more tools than just pytest and pylint via tox.
Also, I often run the entire test suite locally. For this I run tox which runs all envs in the envlist. If the virtual environment is re-created between every command I spend a lot of time waiting for the exact same packages to be installed.

Environment

  • OS: Ubuntu 20.04
  • Python 3.11.1
  • tox 3.28.0 and tox 4.0.19

Example

Here an example that demonsrates the behavior of tox 3 and tox 4 on the same tox.ini.

tox.ini:

[tox]
envlist = 
    pytest
    pylint
skipsdist = true

[testenv]
# reuse virtual environment across commands
envdir = {toxworkdir}/.venv

deps = 
    pytest
    pylint

commands =
    pytest: pytest main.py
    pylint: pylint main.py

The main.py under test:

# pylint: disable=missing-module-docstring,missing-function-docstring
def one():
    return 1

def test_one():
    assert one() == 1

Bash script demonstrating above described behavior that tox 4 does not re-use venvs where tox 3 does.

#!/bin/bash

# echo commands as they are executed
set -x 

# remove old virtual environments
rm -rf .tox tox3-venv tox4-venv

# create virtual environment for tox3
python -m venv tox3-venv 
tox3-venv/bin/pip --quiet install "tox==3.28.0"

# create virtual environment for tox4
python -m venv tox4-venv 
tox4-venv/bin/pip --quiet install "tox==4.0.19"

# run tox3
tox3-venv/bin/tox -e pytest
tox3-venv/bin/tox -e pylint

# remove old virtual environments
rm -rf .tox

# run tox4
# run each command twice to demonstrate that the env is reused with same env name
tox4-venv/bin/tox -e pytest
tox4-venv/bin/tox -e pytest
tox4-venv/bin/tox -e pylint
tox4-venv/bin/tox -e pylint

Output of the script:

+ rm -rf .tox tox3-venv tox4-venv
+ python -m venv tox3-venv
+ tox3-venv/bin/pip --quiet install tox==3.28.0
+ python -m venv tox4-venv
+ tox4-venv/bin/pip --quiet install tox==4.0.19
+ tox3-venv/bin/tox -e pytest
pytest create: /home/fd/.tox/.venv
pytest installdeps: pytest, pylint
pytest installed: astroid==2.12.13,attrs==22.2.0,dill==0.3.6,iniconfig==1.1.1,isort==5.11.4,lazy-object-proxy==1.8.0,mccabe==0.7.0,packaging==22.0,platformdirs==2.6.2,pluggy==1.0.0,pylint==2.15.9,pytest==7.2.0,tomlkit==0.11.6,wrapt==1.14.1
pytest run-test-pre: PYTHONHASHSEED='2731687734'
pytest run-test: commands[0] | pytest main.py
======================================================================= test session starts =======================================================================
platform linux -- Python 3.11.1, pytest-7.2.0, pluggy-1.0.0
cachedir: .tox/.venv/.pytest_cache
rootdir: /home/fd
collected 1 item                                                                                                                                                  

main.py .                                                                                                                                                   [100%]

======================================================================== 1 passed in 0.01s ========================================================================
_____________________________________________________________________________ summary _____________________________________________________________________________
  pytest: commands succeeded
  congratulations :)
+ tox3-venv/bin/tox -e pylint
pylint installed: astroid==2.12.13,attrs==22.2.0,dill==0.3.6,iniconfig==1.1.1,isort==5.11.4,lazy-object-proxy==1.8.0,mccabe==0.7.0,packaging==22.0,platformdirs==2.6.2,pluggy==1.0.0,pylint==2.15.9,pytest==7.2.0,tomlkit==0.11.6,wrapt==1.14.1
pylint run-test-pre: PYTHONHASHSEED='3576645533'
pylint run-test: commands[0] | pylint main.py

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

_____________________________________________________________________________ summary _____________________________________________________________________________
  pylint: commands succeeded
  congratulations :)
+ rm -rf .tox
+ tox4-venv/bin/tox -e pytest
pytest: install_deps> python -I -m pip install pylint pytest
pytest: commands[0]> pytest main.py
======================================================================= test session starts =======================================================================
platform linux -- Python 3.11.1, pytest-7.2.0, pluggy-1.0.0
cachedir: .tox/.venv/.pytest_cache
rootdir: /home/fd
collected 1 item                                                                                                                                                  

main.py .                                                                                                                                                   [100%]

======================================================================== 1 passed in 0.01s ========================================================================
  pytest: OK (6.58=setup[6.27]+cmd[0.31] seconds)
  congratulations :) (6.64 seconds)
+ tox4-venv/bin/tox -e pytest
pytest: commands[0]> pytest main.py
======================================================================= test session starts =======================================================================
platform linux -- Python 3.11.1, pytest-7.2.0, pluggy-1.0.0
cachedir: .tox/.venv/.pytest_cache
rootdir: /home/fd
collected 1 item                                                                                                                                                  

main.py .                                                                                                                                                   [100%]

======================================================================== 1 passed in 0.01s ========================================================================
  pytest: OK (0.36=setup[0.04]+cmd[0.31] seconds)
  congratulations :) (0.42 seconds)
+ tox4-venv/bin/tox -e pylint
pylint: recreate env because env type changed from {'name': 'pytest', 'type': 'VirtualEnvRunner'} to {'name': 'pylint', 'type': 'VirtualEnvRunner'}
pylint: remove tox env folder /home/fd/.tox/.venv
pylint: install_deps> python -I -m pip install pylint pytest
pylint: commands[0]> pylint main.py

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

  pylint: OK (6.88=setup[6.21]+cmd[0.67] seconds)
  congratulations :) (6.95 seconds)
+ tox4-venv/bin/tox -e pylint
pylint: commands[0]> pylint main.py

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

  pylint: OK (0.72=setup[0.04]+cmd[0.68] seconds)
  congratulations :) (0.79 seconds)

We see that in tox 3, execution of tox -e pytest after tox -e pytest leads to the virtual environment being re-used.

With tox 4, the virtual environment is only re-used if the same env is used, e.g. executing tox -e pytest after tox -e pytest. Executing tox -e pylint after tox -e pytest leads to tox 4 recreating the virtual environment which tox 4 tells us via

pylint: recreate env because env type changed from {'name': 'pytest', 'type': 'VirtualEnvRunner'} to {'name': 'pylint', 'type': 'VirtualEnvRunner'}

@jugmac00
Copy link
Member

Thanks for the extensive bug report.

I am pretty sure we had this discussion quite recently, but I cannot find it again.

From what I recall is that re-use of environments worked for tox v3 by chance, so was never officially supported.

Even as this is not a planned change of behavior, this should be still listed in the mentioned faq.

Waiting for @gaborbernat to confirm, and maybe he recalls the issue where this was discussed.

FWIW there is also a very old issue where it was mentioned that re-use of environments is not supported by tox core, but could be supported by a plugin, see #425

Keeping this issue open until at least the faq is updated.

@jugmac00
Copy link
Member

And FWIW.. the usual way to define what you want is...

[tox]
envlist = 
    py311
    pylint
skipsdist = true

[testenv]
deps = pytest
commands = pytest main.py

[testenv:pylint]
deps = pylint
commands = pylint main.py

This all said, you could also have a look at https://pre-commit.com/ to run your linters, which is very common in the Python community.

[tox]
envlist = py311, lint
skipsdist = true

[testenv]
deps = pytest
commands = pytest main.py

[testenv:lint]
deps = pre-commit
commands = pre-commit run --all-files

@gaborbernat
Copy link
Member

In tox \u003C4 it was possible to re-use the same python virtual environment across envs by specifying a shared envdir.

This was never actually supported, worked only by chance because our env detection logic wasn't good enough. We don't plan to support this behavior.

@gaborbernat gaborbernat closed this as not planned Won't fix, can't repro, duplicate, stale Dec 29, 2022
@jugmac00
Copy link
Member

I'll add an entry to the faq/changes list later today.

@masenf
Copy link
Collaborator

masenf commented Jan 14, 2023

I've published a plugin to support this workflow as tox-ignore-env-name-mismatch

@broken-wheel
Copy link

Thank you @masenf!!!
I was pouring through documentations and other plugins (and even ChatGPT :-P) to see how to write a tox plugin for just this purpose. You just saved me a ton of time.

@gaborbernat
Copy link
Member

gaborbernat commented Jan 15, 2023

I'll just say that the core team does not agree with the approach taken, and we don't plan to offer support for the interface used to create this, but we'll not disallow it either 👍 in practice it means if you submit a bug to tox and you're using this plugin you're on your own 🙂 we'll close the issue without addressing the issue you're running into.

@masenf
Copy link
Collaborator

masenf commented Jan 15, 2023

we don't plan to offer support for the interface used to create this

Is this referring to Info._content being accessed and modified by the plug-in? Or extending VirtualEnvRunner.cache? Or a different interface?

@gaborbernat
Copy link
Member

The former, it's private for a reason. Also note that we don't really support sidestepping a core check of the tool, even if the python language allows it. 😅

@gaborbernat
Copy link
Member

It is a smart workaround of the problem though, just not an elegant solution to the underlying problem 😎

wmfgerrit pushed a commit to wikimedia/integration-quibble that referenced this issue Oct 9, 2023
When test environments share the same basepython and requirements, might
want to optimize the build by having the environment directory shared
between the test environments. That was however never officially
supported by tox v3:
tox-dev/tox#425 (comment)
and tox v4 explicitly breaks that assumption by comparing the
environemtn based on the name of the test env (which prevents them from
being shared): tox-dev/tox#2788

There is a plugin to filter out the test environment `name` from the
configuration when doing the comparison:
https://github.com/masenf/tox-ignore-env-name-mismatch
But I don't want to require that plugin to run tox.

As part of the switch to tox v4 (6b8a8f0) I also had to add a hack
to allow tox v3 to provision tox v4 which was to set the environment
directory to `.pkg` for the special `.tox` environment. That felt like a
hack and is potentially fragile.

Unless a solution is found in tox v4 (rather than via a plugin), remove
the envdir configuration and the now useless tox v3 back compatibility
bit. This will cause multiple identical environments to be created
initially, they should be quite fast though since they reuse wheels from
the cache.

Bug: T348434
Change-Id: Idf58bd1eb370feff25cfd7aa432572015e7e0edf
wmfgerrit pushed a commit to wikimedia/operations-software-puppet-compiler that referenced this issue Oct 19, 2023
When test environments share the same basepython and requirements, might
want to optimize the build by having the environment directory shared
between the test environments. That was however never officially
supported by tox v3:
tox-dev/tox#425 (comment)
and tox v4 explicitly breaks that assumption by comparing the
environemtn based on the name of the test env (which prevents them from
being shared): tox-dev/tox#2788

There is a plugin to filter out the test environment `name` from the
configuration when doing the comparison:
https://github.com/masenf/tox-ignore-env-name-mismatch
But I don't want to require that plugin to run tox.

As part of the switch to tox v4 (6b8a8f072416) I also had to add a hack
to allow tox v3 to provision tox v4 which was to set the environment
directory to `.pkg` for the special `.tox` environment. That felt like a
hack and is potentially fragile.

Unless a solution is found in tox v4 (rather than via a plugin), remove
the envdir configuration and the now useless tox v3 back compatibility
bit. This will cause multiple identical environments to be created
initially, they should be quite fast though since they reuse wheels from
the cache.

Bug: T348434
Change-Id: I77702f5c220deff0586424aee0d08e719ad6d384
chanchiwai-ray added a commit to chanchiwai-ray/prometheus-hardware-exporter that referenced this issue Apr 25, 2024
The config option `envdir` is never intended to be used to share env
directory [1].

[1] tox-dev/tox#2788 (comment).
chanchiwai-ray added a commit to chanchiwai-ray/hardware-observer-operator that referenced this issue Apr 26, 2024
The `envdir` is not supposed to be used that way, and it's broken
"feature" (technically a bug) in tox >= 4. [1]

[1] tox-dev/tox#2788 (comment)
chanchiwai-ray added a commit to canonical/prometheus-hardware-exporter that referenced this issue Apr 26, 2024
* Remove `envdir` from `tox.ini`.

The config option `envdir` is never intended to be used to share env
directory [1].

[1] tox-dev/tox#2788 (comment).

* Update author name.

* Update the description for the cli: `prometheus-hardware-exporter`

* Update existing readme and create readme for snap.

* Update description of `snapcraft.yaml`.

* Move the "grade" in `snapcraft.yaml` back to devel.

* Fix broken snap build: move confinement back to `strict`.
chanchiwai-ray added a commit to chanchiwai-ray/hardware-observer-operator that referenced this issue Apr 29, 2024
The `envdir` is not supposed to be used that way, and it's broken
"feature" (technically a bug) in tox >= 4. [1]

[1] tox-dev/tox#2788 (comment)
chanchiwai-ray added a commit to canonical/hardware-observer-operator that referenced this issue Apr 29, 2024
The `envdir` is not supposed to be used that way, and it's broken
"feature" (technically a bug) in tox >= 4. [1]

[1] tox-dev/tox#2788 (comment)
chanchiwai-ray added a commit to chanchiwai-ray/hardware-observer-operator that referenced this issue May 2, 2024
The `envdir` is not supposed to be used that way, and it's broken
"feature" (technically a bug) in tox >= 4. [1]

[1] tox-dev/tox#2788 (comment)
chanchiwai-ray added a commit to chanchiwai-ray/hardware-observer-operator that referenced this issue May 2, 2024
The `envdir` is not supposed to be used that way, and it's broken
"feature" (technically a bug) in tox >= 4. [1]

[1] tox-dev/tox#2788 (comment)
chanchiwai-ray added a commit to canonical/hardware-observer-operator that referenced this issue May 2, 2024
* chore: remove `envdir` from `tox.ini`

The `envdir` is not supposed to be used that way, and it's broken
"feature" (technically a bug) in tox >= 4. [1]

[1] tox-dev/tox#2788 (comment)

* refactor: remove multiple relation check in charm code.

The `metadata.yaml` support `limit` for interface, this can limits how
many relation the interface can be connected to, and we can handle the
relation checks in juju level.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants