Skip to content

Commit

Permalink
Support venv detection on Windows with mingw Python (#12545)
Browse files Browse the repository at this point in the history
Closes #12544
  • Loading branch information
zachsnicker committed Jun 28, 2024
1 parent 0ed2d79 commit 0adcc21
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 66 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ Yusuke Kadowaki
Yutian Li
Yuval Shimon
Zac Hatfield-Dodds
Zach Snicker
Zachary Kneupper
Zachary OBrien
Zhouxin Qiu
Expand Down
4 changes: 4 additions & 0 deletions changelog/12544.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The _in_venv function now detects Python virtual environments by checking
for a pyvenv.cfg file, ensuring reliable detection on various platforms.

-- by :user:`zachsnickers`.
14 changes: 7 additions & 7 deletions doc/en/reference/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1702,13 +1702,13 @@ passed multiple times. The expected format is ``name=value``. For example::
This would tell ``pytest`` to not look into typical subversion or
sphinx-build directories or into any ``tmp`` prefixed directory.

Additionally, ``pytest`` will attempt to intelligently identify and ignore a
virtualenv by the presence of an activation script. Any directory deemed to
be the root of a virtual environment will not be considered during test
collection unless ``--collect-in-virtualenv`` is given. Note also that
``norecursedirs`` takes precedence over ``--collect-in-virtualenv``; e.g. if
you intend to run tests in a virtualenv with a base directory that matches
``'.*'`` you *must* override ``norecursedirs`` in addition to using the
Additionally, ``pytest`` will attempt to intelligently identify and ignore
a virtualenv. Any directory deemed to be the root of a virtual environment
will not be considered during test collection unless
``--collect-in-virtualenv`` is given. Note also that ``norecursedirs``
takes precedence over ``--collect-in-virtualenv``; e.g. if you intend to
run tests in a virtualenv with a base directory that matches ``'.*'`` you
*must* override ``norecursedirs`` in addition to using the
``--collect-in-virtualenv`` flag.


Expand Down
16 changes: 3 additions & 13 deletions src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,22 +369,12 @@ def pytest_runtestloop(session: Session) -> bool:

def _in_venv(path: Path) -> bool:
"""Attempt to detect if ``path`` is the root of a Virtual Environment by
checking for the existence of the appropriate activate script."""
bindir = path.joinpath("Scripts" if sys.platform.startswith("win") else "bin")
checking for the existence of the pyvenv.cfg file.
[https://peps.python.org/pep-0405/]"""
try:
if not bindir.is_dir():
return False
return path.joinpath("pyvenv.cfg").is_file()
except OSError:
return False
activates = (
"activate",
"activate.csh",
"activate.fish",
"Activate",
"Activate.bat",
"Activate.ps1",
)
return any(fname.name in activates for fname in bindir.iterdir())


def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None:
Expand Down
54 changes: 8 additions & 46 deletions testing/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,8 @@ def test_ignored_certain_directories(self, pytester: Pytester) -> None:
assert "test_notfound" not in s
assert "test_found" in s

@pytest.mark.parametrize(
"fname",
(
"activate",
"activate.csh",
"activate.fish",
"Activate",
"Activate.bat",
"Activate.ps1",
),
)
def test_ignored_virtualenvs(self, pytester: Pytester, fname: str) -> None:
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
ensure_file(pytester.path / "virtual" / bindir / fname)
def test_ignored_virtualenvs(self, pytester: Pytester) -> None:
ensure_file(pytester.path / "virtual" / "pyvenv.cfg")
testfile = ensure_file(pytester.path / "virtual" / "test_invenv.py")
testfile.write_text("def test_hello(): pass", encoding="utf-8")

Expand All @@ -179,23 +167,11 @@ def test_ignored_virtualenvs(self, pytester: Pytester, fname: str) -> None:
result = pytester.runpytest("virtual")
assert "test_invenv" in result.stdout.str()

@pytest.mark.parametrize(
"fname",
(
"activate",
"activate.csh",
"activate.fish",
"Activate",
"Activate.bat",
"Activate.ps1",
),
)
def test_ignored_virtualenvs_norecursedirs_precedence(
self, pytester: Pytester, fname: str
self, pytester: Pytester
) -> None:
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
# norecursedirs takes priority
ensure_file(pytester.path / ".virtual" / bindir / fname)
ensure_file(pytester.path / ".virtual" / "pyvenv.cfg")
testfile = ensure_file(pytester.path / ".virtual" / "test_invenv.py")
testfile.write_text("def test_hello(): pass", encoding="utf-8")
result = pytester.runpytest("--collect-in-virtualenv")
Expand All @@ -204,27 +180,13 @@ def test_ignored_virtualenvs_norecursedirs_precedence(
result = pytester.runpytest("--collect-in-virtualenv", ".virtual")
assert "test_invenv" in result.stdout.str()

@pytest.mark.parametrize(
"fname",
(
"activate",
"activate.csh",
"activate.fish",
"Activate",
"Activate.bat",
"Activate.ps1",
),
)
def test__in_venv(self, pytester: Pytester, fname: str) -> None:
def test__in_venv(self, pytester: Pytester) -> None:
"""Directly test the virtual env detection function"""
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
# no bin/activate, not a virtualenv
# no pyvenv.cfg, not a virtualenv
base_path = pytester.mkdir("venv")
assert _in_venv(base_path) is False
# with bin/activate, totally a virtualenv
bin_path = base_path.joinpath(bindir)
bin_path.mkdir()
bin_path.joinpath(fname).touch()
# with pyvenv.cfg, totally a virtualenv
base_path.joinpath("pyvenv.cfg").touch()
assert _in_venv(base_path) is True

def test_custom_norecursedirs(self, pytester: Pytester) -> None:
Expand Down

0 comments on commit 0adcc21

Please sign in to comment.