Skip to content

Commit e0f08a7

Browse files
committed
Merge branch 'features' into conftest-exception-printing
2 parents 0171cfa + 93aae98 commit e0f08a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2752
-2740
lines changed

AUTHORS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ Alexei Kozlenok
99
Anatoly Bubenkoff
1010
Andreas Zeidler
1111
Andy Freeland
12+
Andrzej Ostrowski
1213
Anthon van der Neut
14+
Antony Lee
1315
Armin Rigo
1416
Aron Curzon
1517
Aviv Palivoda

CHANGELOG.rst

Lines changed: 220 additions & 127 deletions
Large diffs are not rendered by default.

CONTRIBUTING.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ the following:
120120

121121
- an issue tracker for bug reports and enhancement requests.
122122

123+
- a `changelog <http://keepachangelog.com/>`_
124+
123125
If no contributor strongly objects and two agree, the repository can then be
124126
transferred to the ``pytest-dev`` organisation.
125127

_pytest/_code/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
from .code import Frame # noqa
55
from .code import Traceback # noqa
66
from .code import getrawcode # noqa
7-
from .code import patch_builtins # noqa
8-
from .code import unpatch_builtins # noqa
97
from .source import Source # noqa
108
from .source import compile_ as compile # noqa
119
from .source import getfslineno # noqa
12-

_pytest/_code/code.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -179,18 +179,6 @@ def getlocals(self):
179179
return self.frame.f_locals
180180
locals = property(getlocals, None, None, "locals of underlaying frame")
181181

182-
def reinterpret(self):
183-
"""Reinterpret the failing statement and returns a detailed information
184-
about what operations are performed."""
185-
from _pytest.assertion.reinterpret import reinterpret
186-
if self.exprinfo is None:
187-
source = py.builtin._totext(self.statement).strip()
188-
x = reinterpret(source, self.frame, should_fail=True)
189-
if not py.builtin._istext(x):
190-
raise TypeError("interpret returned non-string %r" % (x,))
191-
self.exprinfo = x
192-
return self.exprinfo
193-
194182
def getfirstlinesource(self):
195183
# on Jython this firstlineno can be -1 apparently
196184
return max(self.frame.code.firstlineno, 0)
@@ -830,29 +818,6 @@ def toterminal(self, tw):
830818
tw.line("")
831819

832820

833-
834-
oldbuiltins = {}
835-
836-
def patch_builtins(assertion=True, compile=True):
837-
""" put compile and AssertionError builtins to Python's builtins. """
838-
if assertion:
839-
from _pytest.assertion import reinterpret
840-
l = oldbuiltins.setdefault('AssertionError', [])
841-
l.append(py.builtin.builtins.AssertionError)
842-
py.builtin.builtins.AssertionError = reinterpret.AssertionError
843-
if compile:
844-
import _pytest._code
845-
l = oldbuiltins.setdefault('compile', [])
846-
l.append(py.builtin.builtins.compile)
847-
py.builtin.builtins.compile = _pytest._code.compile
848-
849-
def unpatch_builtins(assertion=True, compile=True):
850-
""" remove compile and AssertionError builtins from Python builtins. """
851-
if assertion:
852-
py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
853-
if compile:
854-
py.builtin.builtins.compile = oldbuiltins['compile'].pop()
855-
856821
def getrawcode(obj, trycall=True):
857822
""" return code object for given function. """
858823
try:

_pytest/assertion/__init__.py

Lines changed: 59 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,51 @@
55
import os
66
import sys
77

8-
from _pytest.config import hookimpl
9-
from _pytest.monkeypatch import monkeypatch
108
from _pytest.assertion import util
9+
from _pytest.assertion import rewrite
1110

1211

1312
def pytest_addoption(parser):
1413
group = parser.getgroup("debugconfig")
1514
group.addoption('--assert',
1615
action="store",
1716
dest="assertmode",
18-
choices=("rewrite", "reinterp", "plain",),
17+
choices=("rewrite", "plain",),
1918
default="rewrite",
2019
metavar="MODE",
21-
help="""control assertion debugging tools. 'plain'
22-
performs no assertion debugging. 'reinterp'
23-
reinterprets assert statements after they failed
24-
to provide assertion expression information.
25-
'rewrite' (the default) rewrites assert
26-
statements in test modules on import to
27-
provide assert expression information. """)
20+
help="""Control assertion debugging tools. 'plain'
21+
performs no assertion debugging. 'rewrite'
22+
(the default) rewrites assert statements in
23+
test modules on import to provide assert
24+
expression information.""")
25+
26+
27+
def pytest_namespace():
28+
return {'register_assert_rewrite': register_assert_rewrite}
29+
30+
31+
def register_assert_rewrite(*names):
32+
"""Register a module name to be rewritten on import.
33+
34+
This function will make sure that the module will get it's assert
35+
statements rewritten when it is imported. Thus you should make
36+
sure to call this before the module is actually imported, usually
37+
in your __init__.py if you are a plugin using a package.
38+
"""
39+
for hook in sys.meta_path:
40+
if isinstance(hook, rewrite.AssertionRewritingHook):
41+
importhook = hook
42+
break
43+
else:
44+
importhook = DummyRewriteHook()
45+
importhook.mark_rewrite(*names)
46+
47+
48+
class DummyRewriteHook(object):
49+
"""A no-op import hook for when rewriting is disabled."""
50+
51+
def mark_rewrite(self, *names):
52+
pass
2853

2954

3055
class AssertionState:
@@ -33,55 +58,37 @@ class AssertionState:
3358
def __init__(self, config, mode):
3459
self.mode = mode
3560
self.trace = config.trace.root.get("assertion")
61+
self.hook = None
62+
3663

64+
def install_importhook(config):
65+
"""Try to install the rewrite hook, raise SystemError if it fails."""
66+
# Both Jython and CPython 2.6.0 have AST bugs that make the
67+
# assertion rewriting hook malfunction.
68+
if (sys.platform.startswith('java') or
69+
sys.version_info[:3] == (2, 6, 0)):
70+
raise SystemError('rewrite not supported')
3771

38-
@hookimpl(tryfirst=True)
39-
def pytest_load_initial_conftests(early_config, parser, args):
40-
ns, ns_unknown_args = parser.parse_known_and_unknown_args(args)
41-
mode = ns.assertmode
42-
if mode == "rewrite":
43-
try:
44-
import ast # noqa
45-
except ImportError:
46-
mode = "reinterp"
47-
else:
48-
# Both Jython and CPython 2.6.0 have AST bugs that make the
49-
# assertion rewriting hook malfunction.
50-
if (sys.platform.startswith('java') or
51-
sys.version_info[:3] == (2, 6, 0)):
52-
mode = "reinterp"
53-
54-
early_config._assertstate = AssertionState(early_config, mode)
55-
warn_about_missing_assertion(mode, early_config.pluginmanager)
56-
57-
if mode != "plain":
58-
_load_modules(mode)
59-
m = monkeypatch()
60-
early_config._cleanup.append(m.undo)
61-
m.setattr(py.builtin.builtins, 'AssertionError',
62-
reinterpret.AssertionError) # noqa
63-
64-
hook = None
65-
if mode == "rewrite":
66-
hook = rewrite.AssertionRewritingHook(early_config) # noqa
67-
sys.meta_path.insert(0, hook)
68-
69-
early_config._assertstate.hook = hook
70-
early_config._assertstate.trace("configured with mode set to %r" % (mode,))
72+
config._assertstate = AssertionState(config, 'rewrite')
73+
config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
74+
sys.meta_path.insert(0, hook)
75+
config._assertstate.trace('installed rewrite import hook')
7176
def undo():
72-
hook = early_config._assertstate.hook
77+
hook = config._assertstate.hook
7378
if hook is not None and hook in sys.meta_path:
7479
sys.meta_path.remove(hook)
75-
early_config.add_cleanup(undo)
80+
config.add_cleanup(undo)
81+
return hook
7682

7783

7884
def pytest_collection(session):
7985
# this hook is only called when test modules are collected
8086
# so for example not in the master process of pytest-xdist
8187
# (which does not collect test modules)
82-
hook = session.config._assertstate.hook
83-
if hook is not None:
84-
hook.set_session(session)
88+
assertstate = getattr(session.config, '_assertstate', None)
89+
if assertstate:
90+
if assertstate.hook is not None:
91+
assertstate.hook.set_session(session)
8592

8693

8794
def _running_on_ci():
@@ -138,43 +145,10 @@ def pytest_runtest_teardown(item):
138145

139146

140147
def pytest_sessionfinish(session):
141-
hook = session.config._assertstate.hook
142-
if hook is not None:
143-
hook.session = None
144-
145-
146-
def _load_modules(mode):
147-
"""Lazily import assertion related code."""
148-
global rewrite, reinterpret
149-
from _pytest.assertion import reinterpret # noqa
150-
if mode == "rewrite":
151-
from _pytest.assertion import rewrite # noqa
152-
153-
154-
def warn_about_missing_assertion(mode, pluginmanager):
155-
try:
156-
assert False
157-
except AssertionError:
158-
pass
159-
else:
160-
if mode == "rewrite":
161-
specifically = ("assertions which are not in test modules "
162-
"will be ignored")
163-
else:
164-
specifically = "failing tests may report as passing"
165-
166-
# temporarily disable capture so we can print our warning
167-
capman = pluginmanager.getplugin('capturemanager')
168-
try:
169-
out, err = capman.suspendcapture()
170-
sys.stderr.write("WARNING: " + specifically +
171-
" because assert statements are not executed "
172-
"by the underlying Python interpreter "
173-
"(are you using python -O?)\n")
174-
finally:
175-
capman.resumecapture()
176-
sys.stdout.write(out)
177-
sys.stderr.write(err)
148+
assertstate = getattr(session.config, '_assertstate', None)
149+
if assertstate:
150+
if assertstate.hook is not None:
151+
assertstate.hook.set_session(None)
178152

179153

180154
# Expose this plugin's implementation for the pytest_assertrepr_compare hook

0 commit comments

Comments
 (0)