Skip to content

Commit

Permalink
Merge pull request #4524 from blueyed/merge-master
Browse files Browse the repository at this point in the history
Merge master into features
  • Loading branch information
blueyed authored Dec 10, 2018
2 parents 76884c7 + 539d3dc commit 038f1f9
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 20 deletions.
1 change: 1 addition & 0 deletions changelog/1495.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Document common doctest fixture directory tree structure pitfalls
1 change: 1 addition & 0 deletions changelog/4265.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Validate arguments from the ``PYTEST_ADDOPTS`` environment variable and the ``addopts`` ini option separately.
1 change: 1 addition & 0 deletions changelog/4500.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When a fixture yields and a log call is made after the test runs, and, if the test is interrupted, capture attributes are ``None``.
3 changes: 3 additions & 0 deletions doc/en/doctest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ which can then be used in your doctests directly::
"""
pass

Note that like the normal ``conftest.py``, the fixtures are discovered in the directory tree conftest is in.
Meaning that if you put your doctest with your source code, the relevant conftest.py needs to be in the same directory tree.
Fixtures will not be discovered in a sibling directory tree!

Output format
-------------
Expand Down
2 changes: 1 addition & 1 deletion doc/en/mark.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,4 @@ More details can be found in the `original PR <https://github.com/pytest-dev/pyt
.. note::

in a future major relase of pytest we will introduce class based markers,
at which points markers will no longer be limited to instances of :py:class:`Mark`
at which point markers will no longer be limited to instances of :py:class:`Mark`
6 changes: 2 additions & 4 deletions doc/en/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
pygments-pytest>=1.0.4
# pinning sphinx to 1.4.* due to search issues with rtd:
# https://github.com/rtfd/readthedocs-sphinx-ext/issues/25
sphinx ==1.4.*
pygments-pytest>=1.1.0
sphinx>=1.8.2
sphinxcontrib-trio
5 changes: 4 additions & 1 deletion src/_pytest/capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ def stop_global_capturing(self):
self._global_capturing = None

def resume_global_capture(self):
self._global_capturing.resume_capturing()
# During teardown of the python process, and on rare occasions, capture
# attributes can be `None` while trying to resume global capture.
if self._global_capturing is not None:
self._global_capturing.resume_capturing()

def suspend_global_capture(self, in_=False):
cap = getattr(self, "_global_capturing", None)
Expand Down
13 changes: 11 additions & 2 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,12 +777,21 @@ def _mark_plugins_for_rewrite(self, hook):
for name in _iter_rewritable_modules(package_files):
hook.mark_rewrite(name)

def _validate_args(self, args):
"""Validate known args."""
self._parser.parse_known_and_unknown_args(
args, namespace=copy.copy(self.option)
)
return args

def _preparse(self, args, addopts=True):
if addopts:
args[:] = shlex.split(os.environ.get("PYTEST_ADDOPTS", "")) + args
env_addopts = os.environ.get("PYTEST_ADDOPTS", "")
if len(env_addopts):
args[:] = self._validate_args(shlex.split(env_addopts)) + args
self._initini(args)
if addopts:
args[:] = self.getini("addopts") + args
args[:] = self._validate_args(self.getini("addopts")) + args
self._checkversion()
self._consider_importhook(args)
self.pluginmanager.consider_preparse(args)
Expand Down
3 changes: 2 additions & 1 deletion testing/python/raises.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ class ClassLooksIterableException(Exception):
pass

with pytest.raises(
Failed, match="DID NOT RAISE <class 'raises.ClassLooksIterableException'>"
Failed,
match=r"DID NOT RAISE <class 'raises(\..*)*ClassLooksIterableException'>",
):
pytest.raises(ClassLooksIterableException, lambda: None)

Expand Down
56 changes: 45 additions & 11 deletions testing/test_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,14 @@ def test_logging_and_immediate_setupteardown(self, testdir):
"""\
import logging
def setup_function(function):
logging.warn("hello1")
logging.warning("hello1")
def test_logging():
logging.warn("hello2")
logging.warning("hello2")
assert 0
def teardown_function(function):
logging.warn("hello3")
logging.warning("hello3")
assert 0
"""
)
Expand All @@ -328,14 +328,14 @@ def test_logging_and_crossscope_fixtures(self, testdir):
"""\
import logging
def setup_module(function):
logging.warn("hello1")
logging.warning("hello1")
def test_logging():
logging.warn("hello2")
logging.warning("hello2")
assert 0
def teardown_module(function):
logging.warn("hello3")
logging.warning("hello3")
assert 0
"""
)
Expand All @@ -354,7 +354,7 @@ def test_conftestlogging_is_shown(self, testdir):
"""\
import logging
logging.basicConfig()
logging.warn("hello435")
logging.warning("hello435")
"""
)
# make sure that logging is still captured in tests
Expand All @@ -375,7 +375,7 @@ def test_conftestlogging_and_test_logging(self, testdir):
"""\
def test_hello():
import logging
logging.warn("hello433")
logging.warning("hello433")
assert 0
"""
)
Expand All @@ -385,6 +385,40 @@ def test_hello():
assert "something" not in result.stderr.str()
assert "operation on closed file" not in result.stderr.str()

def test_logging_after_cap_stopped(self, testdir):
testdir.makeconftest(
"""\
import pytest
import logging
log = logging.getLogger(__name__)
@pytest.fixture
def log_on_teardown():
yield
log.warning('Logging on teardown')
"""
)
# make sure that logging is still captured in tests
p = testdir.makepyfile(
"""\
def test_hello(log_on_teardown):
import logging
logging.warning("hello433")
assert 1
raise KeyboardInterrupt()
"""
)
result = testdir.runpytest_subprocess(p, "--log-cli-level", "info")
assert result.ret != 0
result.stdout.fnmatch_lines(
["*WARNING*hello433*", "*WARNING*Logging on teardown*"]
)
assert (
"AttributeError: 'NoneType' object has no attribute 'resume_capturing'"
not in result.stderr.str()
)


class TestCaptureFixture(object):
@pytest.mark.parametrize("opt", [[], ["-s"]])
Expand Down Expand Up @@ -1300,13 +1334,13 @@ def test_capturing_and_logging_fundamentals(testdir, method):
Capture=capture.%s)
cap.start_capturing()
logging.warn("hello1")
logging.warning("hello1")
outerr = cap.readouterr()
print("suspend, captured %%s" %%(outerr,))
logging.warn("hello2")
logging.warning("hello2")
cap.pop_outerr_to_orig()
logging.warn("hello3")
logging.warning("hello3")
outerr = cap.readouterr()
print("suspend2, captured %%s" %% (outerr,))
Expand Down
27 changes: 27 additions & 0 deletions testing/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,33 @@ def test_addopts_before_initini(self, monkeypatch):
config._preparse([], addopts=True)
assert config._override_ini == ["cache_dir=%s" % cache_dir]

def test_addopts_from_env_not_concatenated(self, monkeypatch):
"""PYTEST_ADDOPTS should not take values from normal args (#4265)."""
from _pytest.config import get_config

monkeypatch.setenv("PYTEST_ADDOPTS", "-o")
config = get_config()
with pytest.raises(SystemExit) as excinfo:
config._preparse(["cache_dir=ignored"], addopts=True)
assert excinfo.value.args[0] == _pytest.main.EXIT_USAGEERROR

def test_addopts_from_ini_not_concatenated(self, testdir):
"""addopts from ini should not take values from normal args (#4265)."""
testdir.makeini(
"""
[pytest]
addopts=-o
"""
)
result = testdir.runpytest("cache_dir=ignored")
result.stderr.fnmatch_lines(
[
"%s: error: argument -o/--override-ini: expected one argument"
% (testdir.request.config._parser.optparser.prog,)
]
)
assert result.ret == _pytest.main.EXIT_USAGEERROR

def test_override_ini_does_not_contain_paths(self):
"""Check that -o no longer swallows all options after it (#3103)"""
from _pytest.config import get_config
Expand Down

0 comments on commit 038f1f9

Please sign in to comment.