Skip to content

Commit 1485a3a

Browse files
Merge pull request #2566 from jmsdvl/iss2518
Detect and warn/ignore local python installations
2 parents d9aaab7 + 67fca04 commit 1485a3a

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

_pytest/main.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ def pytest_addoption(parser):
7070
group.addoption('--keepduplicates', '--keep-duplicates', action="store_true",
7171
dest="keepduplicates", default=False,
7272
help="Keep duplicate tests.")
73+
group.addoption('--collect-in-virtualenv', action='store_true',
74+
dest='collect_in_virtualenv', default=False,
75+
help="Don't ignore tests in a local virtualenv directory")
7376

7477
group = parser.getgroup("debugconfig",
7578
"test session debugging and configuration")
@@ -167,6 +170,17 @@ def pytest_runtestloop(session):
167170
return True
168171

169172

173+
def _in_venv(path):
174+
"""Attempts to detect if ``path`` is the root of a Virtual Environment by
175+
checking for the existence of the appropriate activate script"""
176+
bindir = path.join('Scripts' if sys.platform.startswith('win') else 'bin')
177+
if not bindir.exists():
178+
return False
179+
activates = ('activate', 'activate.csh', 'activate.fish',
180+
'Activate', 'Activate.bat', 'Activate.ps1')
181+
return any([fname.basename in activates for fname in bindir.listdir()])
182+
183+
170184
def pytest_ignore_collect(path, config):
171185
ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath())
172186
ignore_paths = ignore_paths or []
@@ -177,6 +191,10 @@ def pytest_ignore_collect(path, config):
177191
if py.path.local(path) in ignore_paths:
178192
return True
179193

194+
allow_in_venv = config.getoption("collect_in_virtualenv")
195+
if _in_venv(path) and not allow_in_venv:
196+
return True
197+
180198
# Skip duplicate paths.
181199
keepduplicates = config.getoption("keepduplicates")
182200
duplicate_paths = config.pluginmanager._duplicatepaths

changelog/2518.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Collection ignores local virtualenvs by default; `--collect-in-virtualenv` overrides this behavior.

doc/en/customize.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,16 @@ Builtin configuration file options
171171
norecursedirs = .svn _build tmp*
172172
173173
This would tell ``pytest`` to not look into typical subversion or
174-
sphinx-build directories or into any ``tmp`` prefixed directory.
174+
sphinx-build directories or into any ``tmp`` prefixed directory.
175+
176+
Additionally, ``pytest`` will attempt to intelligently identify and ignore a
177+
virtualenv by the presence of an activation script. Any directory deemed to
178+
be the root of a virtual environment will not be considered during test
179+
collection unless ``‑‑collect‑in‑virtualenv`` is given. Note also that
180+
``norecursedirs`` takes precedence over ``‑‑collect‑in‑virtualenv``; e.g. if
181+
you intend to run tests in a virtualenv with a base directory that matches
182+
``'.*'`` you *must* override ``norecursedirs`` in addition to using the
183+
``‑‑collect‑in‑virtualenv`` flag.
175184

176185
.. confval:: testpaths
177186

testing/test_collection.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import absolute_import, division, print_function
22
import pytest, py
33

4-
from _pytest.main import Session, EXIT_NOTESTSCOLLECTED
4+
from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv
55

66
class TestCollector(object):
77
def test_collect_versus_item(self):
@@ -121,6 +121,53 @@ def test_ignored_certain_directories(self, testdir):
121121
assert "test_notfound" not in s
122122
assert "test_found" in s
123123

124+
@pytest.mark.parametrize('fname',
125+
("activate", "activate.csh", "activate.fish",
126+
"Activate", "Activate.bat", "Activate.ps1"))
127+
def test_ignored_virtualenvs(self, testdir, fname):
128+
bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin"
129+
testdir.tmpdir.ensure("virtual", bindir, fname)
130+
testfile = testdir.tmpdir.ensure("virtual", "test_invenv.py")
131+
testfile.write("def test_hello(): pass")
132+
133+
# by default, ignore tests inside a virtualenv
134+
result = testdir.runpytest()
135+
assert "test_invenv" not in result.stdout.str()
136+
# allow test collection if user insists
137+
result = testdir.runpytest("--collect-in-virtualenv")
138+
assert "test_invenv" in result.stdout.str()
139+
# allow test collection if user directly passes in the directory
140+
result = testdir.runpytest("virtual")
141+
assert "test_invenv" in result.stdout.str()
142+
143+
@pytest.mark.parametrize('fname',
144+
("activate", "activate.csh", "activate.fish",
145+
"Activate", "Activate.bat", "Activate.ps1"))
146+
def test_ignored_virtualenvs_norecursedirs_precedence(self, testdir, fname):
147+
bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin"
148+
# norecursedirs takes priority
149+
testdir.tmpdir.ensure(".virtual", bindir, fname)
150+
testfile = testdir.tmpdir.ensure(".virtual", "test_invenv.py")
151+
testfile.write("def test_hello(): pass")
152+
result = testdir.runpytest("--collect-in-virtualenv")
153+
assert "test_invenv" not in result.stdout.str()
154+
# ...unless the virtualenv is explicitly given on the CLI
155+
result = testdir.runpytest("--collect-in-virtualenv", ".virtual")
156+
assert "test_invenv" in result.stdout.str()
157+
158+
@pytest.mark.parametrize('fname',
159+
("activate", "activate.csh", "activate.fish",
160+
"Activate", "Activate.bat", "Activate.ps1"))
161+
def test__in_venv(self, testdir, fname):
162+
"""Directly test the virtual env detection function"""
163+
bindir = "Scripts" if py.std.sys.platform.startswith("win") else "bin"
164+
# no bin/activate, not a virtualenv
165+
base_path = testdir.tmpdir.mkdir('venv')
166+
assert _in_venv(base_path) is False
167+
# with bin/activate, totally a virtualenv
168+
base_path.ensure(bindir, fname)
169+
assert _in_venv(base_path) is True
170+
124171
def test_custom_norecursedirs(self, testdir):
125172
testdir.makeini("""
126173
[pytest]

0 commit comments

Comments
 (0)