Skip to content

Commit 6a097aa

Browse files
authored
Merge branch 'master' into allow_skipping_unittests_with_pdb_active
2 parents ad56cd8 + a4fb971 commit 6a097aa

File tree

6 files changed

+63
-7
lines changed

6 files changed

+63
-7
lines changed

AUTHORS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Antony Lee
1616
Armin Rigo
1717
Aron Curzon
1818
Aviv Palivoda
19+
Barney Gale
1920
Ben Webb
2021
Benjamin Peterson
2122
Bernard Pratz
@@ -117,6 +118,7 @@ Piotr Banaszkiewicz
117118
Punyashloka Biswal
118119
Quentin Pradet
119120
Ralf Schmitt
121+
Ran Benita
120122
Raphael Pierzina
121123
Raquel Alegre
122124
Roberto Polli
@@ -141,5 +143,6 @@ Trevor Bekolay
141143
Tyler Goodlet
142144
Vasily Kuznetsov
143145
Victor Uriarte
146+
Vidar T. Fauske
144147
Wouter van Ackooy
145148
Xuecong Liao

CHANGELOG.rst

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,36 @@
44
* Fix regression, pytest now skips unittest correctly if run with ``--pdb``
55
(`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR.
66

7+
* Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_).
8+
Thanks to `@bluetech`_.
9+
10+
*
11+
712
* Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_).
813
Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR.
14+
15+
*
916

10-
*
17+
* Skipping plugin now also works with test items generated by custom collectors (`#2231`_).
18+
Thanks to `@vidartf`_.
19+
20+
*
1121

12-
*
22+
* Conditionless ``xfail`` markers no longer rely on the underlying test item
23+
being an instance of ``PyobjMixin``, and can therefore apply to tests not
24+
collected by the built-in python test collector. Thanks `@barneygale`_ for the
25+
PR.
1326

1427
*
1528

29+
.. _@bluetech: https://github.com/bluetech
1630
.. _@gst: https://github.com/gst
31+
.. _@vidartf: https://github.com/vidartf
1732

1833
.. _#2137: https://github.com/pytest-dev/pytest/issues/2137
1934
.. _#2160: https://github.com/pytest-dev/pytest/issues/2160
35+
.. _#2231: https://github.com/pytest-dev/pytest/issues/2231
36+
.. _#2234: https://github.com/pytest-dev/pytest/issues/2234
2037

2138
.. _PEP-479: https://www.python.org/dev/peps/pep-0479/
2239

@@ -53,6 +70,7 @@
5370
terminal output it relies on is missing. Thanks to `@eli-b`_ for the PR.
5471

5572

73+
.. _@barneygale: https://github.com/barneygale
5674
.. _@lesteve: https://github.com/lesteve
5775
.. _@malinoff: https://github.com/malinoff
5876
.. _@pelme: https://github.com/pelme

_pytest/fixtures.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
getfslineno, get_real_func,
1515
is_generator, isclass, getimfunc,
1616
getlocation, getfuncargnames,
17+
safe_getattr,
1718
)
1819

1920
def pytest_sessionstart(session):
@@ -124,8 +125,6 @@ def getfixturemarker(obj):
124125
exceptions."""
125126
try:
126127
return getattr(obj, "_pytestfixturefunction", None)
127-
except KeyboardInterrupt:
128-
raise
129128
except Exception:
130129
# some objects raise errors like request (from flask import request)
131130
# we don't expect them to be fixture functions
@@ -1068,7 +1067,9 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
10681067
self._holderobjseen.add(holderobj)
10691068
autousenames = []
10701069
for name in dir(holderobj):
1071-
obj = getattr(holderobj, name, None)
1070+
# The attribute can be an arbitrary descriptor, so the attribute
1071+
# access below can raise. safe_getatt() ignores such exceptions.
1072+
obj = safe_getattr(holderobj, name, None)
10721073
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
10731074
# or are "@pytest.fixture" marked
10741075
marker = getfixturemarker(obj)

_pytest/skipping.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ def istrue(self):
112112

113113
def _getglobals(self):
114114
d = {'os': os, 'sys': sys, 'config': self.item.config}
115-
d.update(self.item.obj.__globals__)
115+
if hasattr(self.item, 'obj'):
116+
d.update(self.item.obj.__globals__)
116117
return d
117118

118119
def _istrue(self):
119120
if hasattr(self, 'result'):
120121
return self.result
121122
if self.holder:
122-
d = self._getglobals()
123123
if self.holder.args or 'condition' in self.holder.kwargs:
124124
self.result = False
125125
# "holder" might be a MarkInfo or a MarkDecorator; only
@@ -135,6 +135,7 @@ def _istrue(self):
135135
for expr in args:
136136
self.expr = expr
137137
if isinstance(expr, py.builtin._basestring):
138+
d = self._getglobals()
138139
result = cached_eval(self.item.config, expr, d)
139140
else:
140141
if "reason" not in kwargs:

testing/python/collect.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,16 @@ def test_issue1579_namedtuple(self, testdir):
166166
"because it has a __new__ constructor*"
167167
)
168168

169+
def test_issue2234_property(self, testdir):
170+
testdir.makepyfile("""
171+
class TestCase(object):
172+
@property
173+
def prop(self):
174+
raise NotImplementedError()
175+
""")
176+
result = testdir.runpytest()
177+
assert result.ret == EXIT_NOTESTSCOLLECTED
178+
169179

170180
class TestGenerator:
171181
def test_generative_functions(self, testdir):

testing/test_skipping.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,3 +969,26 @@ def test_func():
969969
result.stdout.fnmatch_lines(
970970
"*Using pytest.skip outside of a test is not allowed*"
971971
)
972+
973+
974+
def test_mark_xfail_item(testdir):
975+
# Ensure pytest.mark.xfail works with non-Python Item
976+
testdir.makeconftest("""
977+
import pytest
978+
979+
class MyItem(pytest.Item):
980+
nodeid = 'foo'
981+
def setup(self):
982+
marker = pytest.mark.xfail(True, reason="Expected failure")
983+
self.add_marker(marker)
984+
def runtest(self):
985+
assert False
986+
987+
def pytest_collect_file(path, parent):
988+
return MyItem("foo", parent)
989+
""")
990+
result = testdir.inline_run()
991+
passed, skipped, failed = result.listoutcomes()
992+
assert not failed
993+
xfailed = [r for r in skipped if hasattr(r, 'wasxfail')]
994+
assert xfailed

0 commit comments

Comments
 (0)