Skip to content

Commit 0db5ccb

Browse files
committed
Merge master into features
2 parents 76884c7 + abb0dfc commit 0db5ccb

File tree

10 files changed

+96
-19
lines changed

10 files changed

+96
-19
lines changed

changelog/1495.doc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Document common doctest fixture directory tree structure pitfalls

changelog/4265.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Validate arguments from the ``PYTEST_ADDOPTS`` environment variable and the ``addopts`` ini option separately.

changelog/4500.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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``.

doc/en/doctest.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ which can then be used in your doctests directly::
154154
"""
155155
pass
156156

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

158161
Output format
159162
-------------

doc/en/mark.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,4 @@ More details can be found in the `original PR <https://github.com/pytest-dev/pyt
156156
.. note::
157157

158158
in a future major relase of pytest we will introduce class based markers,
159-
at which points markers will no longer be limited to instances of :py:class:`Mark`
159+
at which point markers will no longer be limited to instances of :py:class:`Mark`

doc/en/requirements.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
pygments-pytest>=1.0.4
2-
# pinning sphinx to 1.4.* due to search issues with rtd:
3-
# https://github.com/rtfd/readthedocs-sphinx-ext/issues/25
4-
sphinx ==1.4.*
1+
pygments-pytest>=1.1.0
2+
sphinx>=1.8.2
53
sphinxcontrib-trio

src/_pytest/capture.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ def stop_global_capturing(self):
117117
self._global_capturing = None
118118

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

122125
def suspend_global_capture(self, in_=False):
123126
cap = getattr(self, "_global_capturing", None)

src/_pytest/config/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,12 +777,21 @@ def _mark_plugins_for_rewrite(self, hook):
777777
for name in _iter_rewritable_modules(package_files):
778778
hook.mark_rewrite(name)
779779

780+
def _validate_args(self, args):
781+
"""Validate known args."""
782+
self._parser.parse_known_and_unknown_args(
783+
args, namespace=copy.copy(self.option)
784+
)
785+
return args
786+
780787
def _preparse(self, args, addopts=True):
781788
if addopts:
782-
args[:] = shlex.split(os.environ.get("PYTEST_ADDOPTS", "")) + args
789+
env_addopts = os.environ.get("PYTEST_ADDOPTS", "")
790+
if len(env_addopts):
791+
args[:] = self._validate_args(shlex.split(env_addopts)) + args
783792
self._initini(args)
784793
if addopts:
785-
args[:] = self.getini("addopts") + args
794+
args[:] = self._validate_args(self.getini("addopts")) + args
786795
self._checkversion()
787796
self._consider_importhook(args)
788797
self.pluginmanager.consider_preparse(args)

testing/test_capture.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,14 @@ def test_logging_and_immediate_setupteardown(self, testdir):
302302
"""\
303303
import logging
304304
def setup_function(function):
305-
logging.warn("hello1")
305+
logging.warning("hello1")
306306
307307
def test_logging():
308-
logging.warn("hello2")
308+
logging.warning("hello2")
309309
assert 0
310310
311311
def teardown_function(function):
312-
logging.warn("hello3")
312+
logging.warning("hello3")
313313
assert 0
314314
"""
315315
)
@@ -328,14 +328,14 @@ def test_logging_and_crossscope_fixtures(self, testdir):
328328
"""\
329329
import logging
330330
def setup_module(function):
331-
logging.warn("hello1")
331+
logging.warning("hello1")
332332
333333
def test_logging():
334-
logging.warn("hello2")
334+
logging.warning("hello2")
335335
assert 0
336336
337337
def teardown_module(function):
338-
logging.warn("hello3")
338+
logging.warning("hello3")
339339
assert 0
340340
"""
341341
)
@@ -354,7 +354,7 @@ def test_conftestlogging_is_shown(self, testdir):
354354
"""\
355355
import logging
356356
logging.basicConfig()
357-
logging.warn("hello435")
357+
logging.warning("hello435")
358358
"""
359359
)
360360
# make sure that logging is still captured in tests
@@ -375,7 +375,7 @@ def test_conftestlogging_and_test_logging(self, testdir):
375375
"""\
376376
def test_hello():
377377
import logging
378-
logging.warn("hello433")
378+
logging.warning("hello433")
379379
assert 0
380380
"""
381381
)
@@ -385,6 +385,40 @@ def test_hello():
385385
assert "something" not in result.stderr.str()
386386
assert "operation on closed file" not in result.stderr.str()
387387

388+
def test_logging_after_cap_stopped(self, testdir):
389+
testdir.makeconftest(
390+
"""\
391+
import pytest
392+
import logging
393+
394+
log = logging.getLogger(__name__)
395+
396+
@pytest.fixture
397+
def log_on_teardown():
398+
yield
399+
log.warning('Logging on teardown')
400+
"""
401+
)
402+
# make sure that logging is still captured in tests
403+
p = testdir.makepyfile(
404+
"""\
405+
def test_hello(log_on_teardown):
406+
import logging
407+
logging.warning("hello433")
408+
assert 1
409+
raise KeyboardInterrupt()
410+
"""
411+
)
412+
result = testdir.runpytest_subprocess(p, "--log-cli-level", "info")
413+
assert result.ret != 0
414+
result.stdout.fnmatch_lines(
415+
["*WARNING*hello433*", "*WARNING*Logging on teardown*"]
416+
)
417+
assert (
418+
"AttributeError: 'NoneType' object has no attribute 'resume_capturing'"
419+
not in result.stderr.str()
420+
)
421+
388422

389423
class TestCaptureFixture(object):
390424
@pytest.mark.parametrize("opt", [[], ["-s"]])
@@ -1300,13 +1334,13 @@ def test_capturing_and_logging_fundamentals(testdir, method):
13001334
Capture=capture.%s)
13011335
cap.start_capturing()
13021336
1303-
logging.warn("hello1")
1337+
logging.warning("hello1")
13041338
outerr = cap.readouterr()
13051339
print("suspend, captured %%s" %%(outerr,))
1306-
logging.warn("hello2")
1340+
logging.warning("hello2")
13071341
13081342
cap.pop_outerr_to_orig()
1309-
logging.warn("hello3")
1343+
logging.warning("hello3")
13101344
13111345
outerr = cap.readouterr()
13121346
print("suspend2, captured %%s" %% (outerr,))

testing/test_config.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,33 @@ def test_addopts_before_initini(self, monkeypatch):
10831083
config._preparse([], addopts=True)
10841084
assert config._override_ini == ["cache_dir=%s" % cache_dir]
10851085

1086+
def test_addopts_from_env_not_concatenated(self, monkeypatch):
1087+
"""PYTEST_ADDOPTS should not take values from normal args (#4265)."""
1088+
from _pytest.config import get_config
1089+
1090+
monkeypatch.setenv("PYTEST_ADDOPTS", "-o")
1091+
config = get_config()
1092+
with pytest.raises(SystemExit) as excinfo:
1093+
config._preparse(["cache_dir=ignored"], addopts=True)
1094+
assert excinfo.value.args[0] == _pytest.main.EXIT_USAGEERROR
1095+
1096+
def test_addopts_from_ini_not_concatenated(self, testdir):
1097+
"""addopts from ini should not take values from normal args (#4265)."""
1098+
testdir.makeini(
1099+
"""
1100+
[pytest]
1101+
addopts=-o
1102+
"""
1103+
)
1104+
result = testdir.runpytest("cache_dir=ignored")
1105+
result.stderr.fnmatch_lines(
1106+
[
1107+
"%s: error: argument -o/--override-ini: expected one argument"
1108+
% (testdir.request.config._parser.optparser.prog,)
1109+
]
1110+
)
1111+
assert result.ret == _pytest.main.EXIT_USAGEERROR
1112+
10861113
def test_override_ini_does_not_contain_paths(self):
10871114
"""Check that -o no longer swallows all options after it (#3103)"""
10881115
from _pytest.config import get_config

0 commit comments

Comments
 (0)