Skip to content

Commit 5891061

Browse files
authored
Merge pull request #1675 from kvas-it/issue-1562
Add warning for assertions on tuples #1562
2 parents 48ac1a0 + 6d4cee2 commit 5891061

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@
9393
finalizer and has access to the fixture's result cache.
9494
Thanks `@d6e`_, `@sallner`_
9595

96+
* Issue a warning for asserts whose test is a tuple literal. Such asserts will
97+
never fail because tuples are always truthy and are usually a mistake
98+
(see `#1562`_). Thanks `@kvas-it`_, for the PR.
99+
96100
* New cli flag ``--override-ini`` or ``-o`` that overrides values from the ini file.
97101
Example '-o xfail_strict=True'. A complete ini-options can be viewed
98102
by py.test --help. Thanks `@blueyed`_ and `@fengxx`_ for the PR
@@ -227,6 +231,7 @@
227231
.. _#1619: https://github.com/pytest-dev/pytest/issues/1619
228232
.. _#372: https://github.com/pytest-dev/pytest/issues/372
229233
.. _#1544: https://github.com/pytest-dev/pytest/issues/1544
234+
.. _#1562: https://github.com/pytest-dev/pytest/issues/1562
230235
.. _#1616: https://github.com/pytest-dev/pytest/pull/1616
231236
.. _#1628: https://github.com/pytest-dev/pytest/pull/1628
232237
.. _#1629: https://github.com/pytest-dev/pytest/issues/1629

_pytest/assertion/rewrite.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def find_module(self, name, path=None):
125125
co = _read_pyc(fn_pypath, pyc, state.trace)
126126
if co is None:
127127
state.trace("rewriting %r" % (fn,))
128-
source_stat, co = _rewrite_test(state, fn_pypath)
128+
source_stat, co = _rewrite_test(self.config, fn_pypath)
129129
if co is None:
130130
# Probably a SyntaxError in the test.
131131
return None
@@ -252,8 +252,9 @@ def _write_pyc(state, co, source_stat, pyc):
252252
cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
253253
BOM_UTF8 = '\xef\xbb\xbf'
254254

255-
def _rewrite_test(state, fn):
255+
def _rewrite_test(config, fn):
256256
"""Try to read and rewrite *fn* and return the code object."""
257+
state = config._assertstate
257258
try:
258259
stat = fn.stat()
259260
source = fn.read("rb")
@@ -298,7 +299,7 @@ def _rewrite_test(state, fn):
298299
# Let this pop up again in the real import.
299300
state.trace("failed to parse: %r" % (fn,))
300301
return None, None
301-
rewrite_asserts(tree)
302+
rewrite_asserts(tree, fn, config)
302303
try:
303304
co = compile(tree, fn.strpath, "exec")
304305
except SyntaxError:
@@ -354,9 +355,9 @@ def _read_pyc(source, pyc, trace=lambda x: None):
354355
return co
355356

356357

357-
def rewrite_asserts(mod):
358+
def rewrite_asserts(mod, module_path=None, config=None):
358359
"""Rewrite the assert statements in mod."""
359-
AssertionRewriter().run(mod)
360+
AssertionRewriter(module_path, config).run(mod)
360361

361362

362363
def _saferepr(obj):
@@ -543,6 +544,11 @@ class AssertionRewriter(ast.NodeVisitor):
543544
544545
"""
545546

547+
def __init__(self, module_path, config):
548+
super(AssertionRewriter, self).__init__()
549+
self.module_path = module_path
550+
self.config = config
551+
546552
def run(self, mod):
547553
"""Find all assert statements in *mod* and rewrite them."""
548554
if not mod.body:
@@ -683,6 +689,10 @@ def visit_Assert(self, assert_):
683689
the expression is false.
684690
685691
"""
692+
if isinstance(assert_.test, ast.Tuple) and self.config is not None:
693+
fslocation = (self.module_path, assert_.lineno)
694+
self.config.warn('R1', 'assertion is always true, perhaps '
695+
'remove parentheses?', fslocation=fslocation)
686696
self.statements = []
687697
self.variables = []
688698
self.variable_counter = itertools.count()

testing/test_assertion.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,3 +632,21 @@ def test_diff():
632632
* + asdf
633633
* ? +
634634
""")
635+
636+
def test_assert_tuple_warning(testdir):
637+
testdir.makepyfile("""
638+
def test_tuple():
639+
assert(False, 'you shall not pass')
640+
""")
641+
result = testdir.runpytest('-rw')
642+
result.stdout.fnmatch_lines('WR1*:2 assertion is always true*')
643+
644+
def test_assert_indirect_tuple_no_warning(testdir):
645+
testdir.makepyfile("""
646+
def test_tuple():
647+
tpl = ('foo', 'bar')
648+
assert tpl
649+
""")
650+
result = testdir.runpytest('-rw')
651+
output = '\n'.join(result.stdout.lines)
652+
assert 'WR1' not in output

0 commit comments

Comments
 (0)