Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 37 additions & 15 deletions src/_pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ def __init__(self, request, tmpdir_factory):
mp.delenv("TOX_ENV_DIR", raising=False)
# Discard outer pytest options.
mp.delenv("PYTEST_ADDOPTS", raising=False)
# Do not load entrypoint plugins by default.
mp.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1")

def __repr__(self):
return "<Testdir %r>" % (self.tmpdir,)
Expand Down Expand Up @@ -790,6 +792,14 @@ def inline_genitems(self, *args):
items = [x.item for x in rec.getcalls("pytest_itemcollected")]
return items, rec

def _get_isolated_env(self):
tmpdir = str(self.tmpdir)
return (
# Do not load user config.
("HOME", tmpdir),
("USERPROFILE", tmpdir),
)

def inline_run(self, *args, **kwargs):
"""Run ``pytest.main()`` in-process, returning a HookRecorder.

Expand All @@ -811,8 +821,8 @@ def inline_run(self, *args, **kwargs):
try:
# Do not load user config (during runs only).
mp_run = MonkeyPatch()
mp_run.setenv("HOME", str(self.tmpdir))
mp_run.setenv("USERPROFILE", str(self.tmpdir))
for k, v in self._get_isolated_env():
mp_run.setenv(k, v)
finalizers.append(mp_run.undo)

# When running pytest inline any plugins active in the main test
Expand Down Expand Up @@ -1035,26 +1045,38 @@ def collect_by_name(self, modcol, name):
def popen(self, cmdargs, stdout, stderr, **kw):
"""Invoke subprocess.Popen.

This calls subprocess.Popen making sure the current working directory
is in the PYTHONPATH.
This calls subprocess.Popen, making sure the current working directory
is in the PYTHONPATH by default.

You probably want to use :py:meth:`run` instead.
Optional keyword arguments:

:param env: OS environment to be used as is (no PYTHONPATH adjustment,
nor isolation for HOME etc).
:param env_update: OS environment values to update the current
environment with.
PYTHONPATH gets adjusted if not passed in explicitly.

You probably want to use :py:meth:`run` instead.
"""
env = os.environ.copy()
env["PYTHONPATH"] = os.pathsep.join(
filter(None, [os.getcwd(), env.get("PYTHONPATH", "")])
)
# Do not load user config.
env["HOME"] = str(self.tmpdir)
env["USERPROFILE"] = env["HOME"]
kw["env"] = env
if "env" in kw:
env = kw.pop("env")
if "env_update" in kw:
raise ValueError("env and env_update are mutually exclusive")
else:
env = os.environ.copy()
env.update(self._get_isolated_env())

env_update = kw.pop("env_update", {})
if "PYTHONPATH" not in env_update:
env["PYTHONPATH"] = os.pathsep.join(
filter(None, [os.getcwd(), env.get("PYTHONPATH", "")])
)
env.update(env_update)

popen = subprocess.Popen(
cmdargs, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr, **kw
cmdargs, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr, env=env, **kw
)
popen.stdin.close()

return popen

def run(self, *cmdargs, **kwargs):
Expand Down
1 change: 1 addition & 0 deletions testing/acceptance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def my_iter(group, name=None):

monkeypatch.setattr(pkg_resources, "iter_entry_points", my_iter)
params = ("-p", "mycov") if load_cov_early else ()
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
testdir.runpytest_inprocess(*params)
if load_cov_early:
assert loaded == ["mycov", "myplugin1", "myplugin2"]
Expand Down
2 changes: 1 addition & 1 deletion testing/test_assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def test_foo(pytestconfig):
@pytest.mark.parametrize("mode", ["plain", "rewrite"])
@pytest.mark.parametrize("plugin_state", ["development", "installed"])
def test_installed_plugin_rewrite(self, testdir, mode, plugin_state, monkeypatch):
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
# Make sure the hook is installed early enough so that plugins
# installed via setuptools are rewritten.
testdir.tmpdir.join("hampkg").ensure(dir=1)
Expand Down
1 change: 1 addition & 0 deletions testing/test_helpconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


def test_version(testdir, pytestconfig):
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
result = testdir.runpytest("--version")
assert result.ret == 0
# p = py.path.local(py.__file__).dirpath()
Expand Down
38 changes: 38 additions & 0 deletions testing/test_pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
from _pytest.pytester import SysModulesSnapshot
from _pytest.pytester import SysPathsSnapshot

try:
import mock
except ImportError:
import unittest.mock as mock


def test_make_hook_recorder(testdir):
item = testdir.getitem("def test_func(): pass")
Expand Down Expand Up @@ -482,3 +487,36 @@ def test_pytester_addopts(request, monkeypatch):
testdir.finalize()

assert os.environ["PYTEST_ADDOPTS"] == "--orig-unused"


def test_popen_env(testdir, monkeypatch):
monkeypatch.delenv("PYTHONPATH", raising=False)
popen_args = (["cmd"], None, None)

with mock.patch("subprocess.Popen") as m:
testdir.popen(*popen_args)
env = m.call_args[1]["env"]
assert set(env.keys()) == set(
list(os.environ.keys()) + ["PYTHONPATH", "USERPROFILE", "HOME"]
)
assert env["PYTHONPATH"] == os.getcwd()

# Updates PYTHONPATH by default.
monkeypatch.setenv("PYTHONPATH", "custom")
testdir.popen(*popen_args)
env = m.call_args[1]["env"]
assert env["PYTHONPATH"] == os.pathsep.join((os.getcwd(), "custom"))

# Uses explicit PYTHONPATH via env_update.
testdir.popen(*popen_args, env_update={"PYTHONPATH": "mypp", "CUSTOM_ENV": "1"})
env = m.call_args[1]["env"]
assert env["PYTHONPATH"] == "mypp"
assert env["CUSTOM_ENV"] == "1"

# Uses explicit env only.
testdir.popen(*popen_args, env={"CUSTOM_ENV": "1"})
env = m.call_args[1]["env"]
assert env == {"CUSTOM_ENV": "1"}

with pytest.raises(ValueError, match="env and env_update are mutually exclusive"):
testdir.popen(*popen_args, env={}, env_update={})
2 changes: 2 additions & 0 deletions testing/test_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ def test_method(self):
assert result.ret == 0

def test_header_trailer_info(self, testdir, request):
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
testdir.makepyfile(
"""
def test_passes():
Expand Down Expand Up @@ -677,6 +678,7 @@ def test_verbose_reporting(self, verbose_testfile, testdir, pytestconfig):
def test_verbose_reporting_xdist(self, verbose_testfile, testdir, pytestconfig):
if not pytestconfig.pluginmanager.get_plugin("xdist"):
pytest.skip("xdist plugin not installed")
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")

result = testdir.runpytest(
verbose_testfile, "-v", "-n 1", "-Walways::pytest.PytestWarning"
Expand Down