Skip to content

Commit 038f1f9

Browse files
authored
Merge pull request #4524 from blueyed/merge-master
Merge master into features
2 parents 76884c7 + 539d3dc commit 038f1f9

File tree

11 files changed

+98
-20
lines changed

11 files changed

+98
-20
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/python/raises.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ class ClassLooksIterableException(Exception):
197197
pass
198198

199199
with pytest.raises(
200-
Failed, match="DID NOT RAISE <class 'raises.ClassLooksIterableException'>"
200+
Failed,
201+
match=r"DID NOT RAISE <class 'raises(\..*)*ClassLooksIterableException'>",
201202
):
202203
pytest.raises(ClassLooksIterableException, lambda: None)
203204

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,))

0 commit comments

Comments
 (0)