Merge pull request #1199 from nicoddemus/move-pycode-to-pytest
Copy py.code code to py.test
This commit is contained in:
commit
52ac6cd7a9
|
@ -19,6 +19,21 @@
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
|
||||||
|
* **Important**: `py.code <http://pylib.readthedocs.org/en/latest/code.html>`_ has been
|
||||||
|
merged into the ``pytest`` repository as ``pytest._code``. This decision
|
||||||
|
was made because ``py.code`` had very few uses outside ``pytest`` and the
|
||||||
|
fact that it was in a different repository made it difficult to fix bugs on
|
||||||
|
its code in a timely manner. The team hopes with this to be able to better
|
||||||
|
refactor out and improve that code.
|
||||||
|
This change shouldn't affect users, but it is useful to let users aware
|
||||||
|
if they encounter any strange behavior.
|
||||||
|
|
||||||
|
Keep in mind that the code for ``pytest._code`` is **private** and
|
||||||
|
**experimental**, so you definitely should not import it explicitly!
|
||||||
|
|
||||||
|
Please note that the original ``py.code`` is still available in
|
||||||
|
`pylib <http://pylib.readthedocs.org>`_.
|
||||||
|
|
||||||
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
|
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
|
||||||
Thanks `@nicoddemus`_ for the PR.
|
Thanks `@nicoddemus`_ for the PR.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
""" python inspection/code generation API """
|
||||||
|
from .code import Code # noqa
|
||||||
|
from .code import ExceptionInfo # noqa
|
||||||
|
from .code import Frame # noqa
|
||||||
|
from .code import Traceback # noqa
|
||||||
|
from .code import getrawcode # noqa
|
||||||
|
from .code import patch_builtins # noqa
|
||||||
|
from .code import unpatch_builtins # noqa
|
||||||
|
from .source import Source # noqa
|
||||||
|
from .source import compile_ as compile # noqa
|
||||||
|
from .source import getfslineno # noqa
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
# copied from python-2.7.3's traceback.py
|
||||||
|
# CHANGES:
|
||||||
|
# - some_str is replaced, trying to create unicode strings
|
||||||
|
#
|
||||||
|
import types
|
||||||
|
|
||||||
|
def format_exception_only(etype, value):
|
||||||
|
"""Format the exception part of a traceback.
|
||||||
|
|
||||||
|
The arguments are the exception type and value such as given by
|
||||||
|
sys.last_type and sys.last_value. The return value is a list of
|
||||||
|
strings, each ending in a newline.
|
||||||
|
|
||||||
|
Normally, the list contains a single string; however, for
|
||||||
|
SyntaxError exceptions, it contains several lines that (when
|
||||||
|
printed) display detailed information about where the syntax
|
||||||
|
error occurred.
|
||||||
|
|
||||||
|
The message indicating which exception occurred is always the last
|
||||||
|
string in the list.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# An instance should not have a meaningful value parameter, but
|
||||||
|
# sometimes does, particularly for string exceptions, such as
|
||||||
|
# >>> raise string1, string2 # deprecated
|
||||||
|
#
|
||||||
|
# Clear these out first because issubtype(string1, SyntaxError)
|
||||||
|
# would throw another exception and mask the original problem.
|
||||||
|
if (isinstance(etype, BaseException) or
|
||||||
|
isinstance(etype, types.InstanceType) or
|
||||||
|
etype is None or type(etype) is str):
|
||||||
|
return [_format_final_exc_line(etype, value)]
|
||||||
|
|
||||||
|
stype = etype.__name__
|
||||||
|
|
||||||
|
if not issubclass(etype, SyntaxError):
|
||||||
|
return [_format_final_exc_line(stype, value)]
|
||||||
|
|
||||||
|
# It was a syntax error; show exactly where the problem was found.
|
||||||
|
lines = []
|
||||||
|
try:
|
||||||
|
msg, (filename, lineno, offset, badline) = value.args
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
filename = filename or "<string>"
|
||||||
|
lines.append(' File "%s", line %d\n' % (filename, lineno))
|
||||||
|
if badline is not None:
|
||||||
|
lines.append(' %s\n' % badline.strip())
|
||||||
|
if offset is not None:
|
||||||
|
caretspace = badline.rstrip('\n')[:offset].lstrip()
|
||||||
|
# non-space whitespace (likes tabs) must be kept for alignment
|
||||||
|
caretspace = ((c.isspace() and c or ' ') for c in caretspace)
|
||||||
|
# only three spaces to account for offset1 == pos 0
|
||||||
|
lines.append(' %s^\n' % ''.join(caretspace))
|
||||||
|
value = msg
|
||||||
|
|
||||||
|
lines.append(_format_final_exc_line(stype, value))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def _format_final_exc_line(etype, value):
|
||||||
|
"""Return a list of a single line -- normal case for format_exception_only"""
|
||||||
|
valuestr = _some_str(value)
|
||||||
|
if value is None or not valuestr:
|
||||||
|
line = "%s\n" % etype
|
||||||
|
else:
|
||||||
|
line = "%s: %s\n" % (etype, valuestr)
|
||||||
|
return line
|
||||||
|
|
||||||
|
def _some_str(value):
|
||||||
|
try:
|
||||||
|
return unicode(value)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
return str(value)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return '<unprintable %s object>' % type(value).__name__
|
|
@ -0,0 +1,795 @@
|
||||||
|
import sys
|
||||||
|
from inspect import CO_VARARGS, CO_VARKEYWORDS
|
||||||
|
|
||||||
|
import py
|
||||||
|
|
||||||
|
builtin_repr = repr
|
||||||
|
|
||||||
|
reprlib = py.builtin._tryimport('repr', 'reprlib')
|
||||||
|
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
from traceback import format_exception_only
|
||||||
|
else:
|
||||||
|
from ._py2traceback import format_exception_only
|
||||||
|
|
||||||
|
class Code(object):
|
||||||
|
""" wrapper around Python code objects """
|
||||||
|
def __init__(self, rawcode):
|
||||||
|
if not hasattr(rawcode, "co_filename"):
|
||||||
|
rawcode = getrawcode(rawcode)
|
||||||
|
try:
|
||||||
|
self.filename = rawcode.co_filename
|
||||||
|
self.firstlineno = rawcode.co_firstlineno - 1
|
||||||
|
self.name = rawcode.co_name
|
||||||
|
except AttributeError:
|
||||||
|
raise TypeError("not a code object: %r" %(rawcode,))
|
||||||
|
self.raw = rawcode
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.raw == other.raw
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self == other
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
""" return a path object pointing to source code (note that it
|
||||||
|
might not point to an actually existing file). """
|
||||||
|
p = py.path.local(self.raw.co_filename)
|
||||||
|
# maybe don't try this checking
|
||||||
|
if not p.check():
|
||||||
|
# XXX maybe try harder like the weird logic
|
||||||
|
# in the standard lib [linecache.updatecache] does?
|
||||||
|
p = self.raw.co_filename
|
||||||
|
return p
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fullsource(self):
|
||||||
|
""" return a _pytest._code.Source object for the full source file of the code
|
||||||
|
"""
|
||||||
|
from _pytest._code import source
|
||||||
|
full, _ = source.findsource(self.raw)
|
||||||
|
return full
|
||||||
|
|
||||||
|
def source(self):
|
||||||
|
""" return a _pytest._code.Source object for the code object's source only
|
||||||
|
"""
|
||||||
|
# return source only for that part of code
|
||||||
|
import _pytest._code
|
||||||
|
return _pytest._code.Source(self.raw)
|
||||||
|
|
||||||
|
def getargs(self, var=False):
|
||||||
|
""" return a tuple with the argument names for the code object
|
||||||
|
|
||||||
|
if 'var' is set True also return the names of the variable and
|
||||||
|
keyword arguments when present
|
||||||
|
"""
|
||||||
|
# handfull shortcut for getting args
|
||||||
|
raw = self.raw
|
||||||
|
argcount = raw.co_argcount
|
||||||
|
if var:
|
||||||
|
argcount += raw.co_flags & CO_VARARGS
|
||||||
|
argcount += raw.co_flags & CO_VARKEYWORDS
|
||||||
|
return raw.co_varnames[:argcount]
|
||||||
|
|
||||||
|
class Frame(object):
|
||||||
|
"""Wrapper around a Python frame holding f_locals and f_globals
|
||||||
|
in which expressions can be evaluated."""
|
||||||
|
|
||||||
|
def __init__(self, frame):
|
||||||
|
self.lineno = frame.f_lineno - 1
|
||||||
|
self.f_globals = frame.f_globals
|
||||||
|
self.f_locals = frame.f_locals
|
||||||
|
self.raw = frame
|
||||||
|
self.code = Code(frame.f_code)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def statement(self):
|
||||||
|
""" statement this frame is at """
|
||||||
|
import _pytest._code
|
||||||
|
if self.code.fullsource is None:
|
||||||
|
return _pytest._code.Source("")
|
||||||
|
return self.code.fullsource.getstatement(self.lineno)
|
||||||
|
|
||||||
|
def eval(self, code, **vars):
|
||||||
|
""" evaluate 'code' in the frame
|
||||||
|
|
||||||
|
'vars' are optional additional local variables
|
||||||
|
|
||||||
|
returns the result of the evaluation
|
||||||
|
"""
|
||||||
|
f_locals = self.f_locals.copy()
|
||||||
|
f_locals.update(vars)
|
||||||
|
return eval(code, self.f_globals, f_locals)
|
||||||
|
|
||||||
|
def exec_(self, code, **vars):
|
||||||
|
""" exec 'code' in the frame
|
||||||
|
|
||||||
|
'vars' are optiona; additional local variables
|
||||||
|
"""
|
||||||
|
f_locals = self.f_locals.copy()
|
||||||
|
f_locals.update(vars)
|
||||||
|
py.builtin.exec_(code, self.f_globals, f_locals )
|
||||||
|
|
||||||
|
def repr(self, object):
|
||||||
|
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
|
||||||
|
"""
|
||||||
|
return py.io.saferepr(object)
|
||||||
|
|
||||||
|
def is_true(self, object):
|
||||||
|
return object
|
||||||
|
|
||||||
|
def getargs(self, var=False):
|
||||||
|
""" return a list of tuples (name, value) for all arguments
|
||||||
|
|
||||||
|
if 'var' is set True also include the variable and keyword
|
||||||
|
arguments when present
|
||||||
|
"""
|
||||||
|
retval = []
|
||||||
|
for arg in self.code.getargs(var):
|
||||||
|
try:
|
||||||
|
retval.append((arg, self.f_locals[arg]))
|
||||||
|
except KeyError:
|
||||||
|
pass # this can occur when using Psyco
|
||||||
|
return retval
|
||||||
|
|
||||||
|
class TracebackEntry(object):
|
||||||
|
""" a single entry in a traceback """
|
||||||
|
|
||||||
|
_repr_style = None
|
||||||
|
exprinfo = None
|
||||||
|
|
||||||
|
def __init__(self, rawentry):
|
||||||
|
self._rawentry = rawentry
|
||||||
|
self.lineno = rawentry.tb_lineno - 1
|
||||||
|
|
||||||
|
def set_repr_style(self, mode):
|
||||||
|
assert mode in ("short", "long")
|
||||||
|
self._repr_style = mode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frame(self):
|
||||||
|
import _pytest._code
|
||||||
|
return _pytest._code.Frame(self._rawentry.tb_frame)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def relline(self):
|
||||||
|
return self.lineno - self.frame.code.firstlineno
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def statement(self):
|
||||||
|
""" _pytest._code.Source object for the current statement """
|
||||||
|
source = self.frame.code.fullsource
|
||||||
|
return source.getstatement(self.lineno)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
""" path to the source code """
|
||||||
|
return self.frame.code.path
|
||||||
|
|
||||||
|
def getlocals(self):
|
||||||
|
return self.frame.f_locals
|
||||||
|
locals = property(getlocals, None, None, "locals of underlaying frame")
|
||||||
|
|
||||||
|
def reinterpret(self):
|
||||||
|
"""Reinterpret the failing statement and returns a detailed information
|
||||||
|
about what operations are performed."""
|
||||||
|
from _pytest.assertion.reinterpret import reinterpret
|
||||||
|
if self.exprinfo is None:
|
||||||
|
source = py.builtin._totext(self.statement).strip()
|
||||||
|
x = reinterpret(source, self.frame, should_fail=True)
|
||||||
|
if not py.builtin._istext(x):
|
||||||
|
raise TypeError("interpret returned non-string %r" % (x,))
|
||||||
|
self.exprinfo = x
|
||||||
|
return self.exprinfo
|
||||||
|
|
||||||
|
def getfirstlinesource(self):
|
||||||
|
# on Jython this firstlineno can be -1 apparently
|
||||||
|
return max(self.frame.code.firstlineno, 0)
|
||||||
|
|
||||||
|
def getsource(self, astcache=None):
|
||||||
|
""" return failing source code. """
|
||||||
|
# we use the passed in astcache to not reparse asttrees
|
||||||
|
# within exception info printing
|
||||||
|
from _pytest._code.source import getstatementrange_ast
|
||||||
|
source = self.frame.code.fullsource
|
||||||
|
if source is None:
|
||||||
|
return None
|
||||||
|
key = astnode = None
|
||||||
|
if astcache is not None:
|
||||||
|
key = self.frame.code.path
|
||||||
|
if key is not None:
|
||||||
|
astnode = astcache.get(key, None)
|
||||||
|
start = self.getfirstlinesource()
|
||||||
|
try:
|
||||||
|
astnode, _, end = getstatementrange_ast(self.lineno, source,
|
||||||
|
astnode=astnode)
|
||||||
|
except SyntaxError:
|
||||||
|
end = self.lineno + 1
|
||||||
|
else:
|
||||||
|
if key is not None:
|
||||||
|
astcache[key] = astnode
|
||||||
|
return source[start:end]
|
||||||
|
|
||||||
|
source = property(getsource)
|
||||||
|
|
||||||
|
def ishidden(self):
|
||||||
|
""" return True if the current frame has a var __tracebackhide__
|
||||||
|
resolving to True
|
||||||
|
|
||||||
|
mostly for internal use
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self.frame.f_locals['__tracebackhide__']
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
return self.frame.f_globals['__tracebackhide__']
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
try:
|
||||||
|
fn = str(self.path)
|
||||||
|
except py.error.Error:
|
||||||
|
fn = '???'
|
||||||
|
name = self.frame.code.name
|
||||||
|
try:
|
||||||
|
line = str(self.statement).lstrip()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
line = "???"
|
||||||
|
return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line)
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return self.frame.code.raw.co_name
|
||||||
|
name = property(name, None, None, "co_name of underlaying code")
|
||||||
|
|
||||||
|
class Traceback(list):
|
||||||
|
""" Traceback objects encapsulate and offer higher level
|
||||||
|
access to Traceback entries.
|
||||||
|
"""
|
||||||
|
Entry = TracebackEntry
|
||||||
|
def __init__(self, tb):
|
||||||
|
""" initialize from given python traceback object. """
|
||||||
|
if hasattr(tb, 'tb_next'):
|
||||||
|
def f(cur):
|
||||||
|
while cur is not None:
|
||||||
|
yield self.Entry(cur)
|
||||||
|
cur = cur.tb_next
|
||||||
|
list.__init__(self, f(tb))
|
||||||
|
else:
|
||||||
|
list.__init__(self, tb)
|
||||||
|
|
||||||
|
def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
|
||||||
|
""" return a Traceback instance wrapping part of this Traceback
|
||||||
|
|
||||||
|
by provding any combination of path, lineno and firstlineno, the
|
||||||
|
first frame to start the to-be-returned traceback is determined
|
||||||
|
|
||||||
|
this allows cutting the first part of a Traceback instance e.g.
|
||||||
|
for formatting reasons (removing some uninteresting bits that deal
|
||||||
|
with handling of the exception/traceback)
|
||||||
|
"""
|
||||||
|
for x in self:
|
||||||
|
code = x.frame.code
|
||||||
|
codepath = code.path
|
||||||
|
if ((path is None or codepath == path) and
|
||||||
|
(excludepath is None or not hasattr(codepath, 'relto') or
|
||||||
|
not codepath.relto(excludepath)) and
|
||||||
|
(lineno is None or x.lineno == lineno) and
|
||||||
|
(firstlineno is None or x.frame.code.firstlineno == firstlineno)):
|
||||||
|
return Traceback(x._rawentry)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
val = super(Traceback, self).__getitem__(key)
|
||||||
|
if isinstance(key, type(slice(0))):
|
||||||
|
val = self.__class__(val)
|
||||||
|
return val
|
||||||
|
|
||||||
|
def filter(self, fn=lambda x: not x.ishidden()):
|
||||||
|
""" return a Traceback instance with certain items removed
|
||||||
|
|
||||||
|
fn is a function that gets a single argument, a TracebackItem
|
||||||
|
instance, and should return True when the item should be added
|
||||||
|
to the Traceback, False when not
|
||||||
|
|
||||||
|
by default this removes all the TracebackItems which are hidden
|
||||||
|
(see ishidden() above)
|
||||||
|
"""
|
||||||
|
return Traceback(filter(fn, self))
|
||||||
|
|
||||||
|
def getcrashentry(self):
|
||||||
|
""" return last non-hidden traceback entry that lead
|
||||||
|
to the exception of a traceback.
|
||||||
|
"""
|
||||||
|
for i in range(-1, -len(self)-1, -1):
|
||||||
|
entry = self[i]
|
||||||
|
if not entry.ishidden():
|
||||||
|
return entry
|
||||||
|
return self[-1]
|
||||||
|
|
||||||
|
def recursionindex(self):
|
||||||
|
""" return the index of the frame/TracebackItem where recursion
|
||||||
|
originates if appropriate, None if no recursion occurred
|
||||||
|
"""
|
||||||
|
cache = {}
|
||||||
|
for i, entry in enumerate(self):
|
||||||
|
# id for the code.raw is needed to work around
|
||||||
|
# the strange metaprogramming in the decorator lib from pypi
|
||||||
|
# which generates code objects that have hash/value equality
|
||||||
|
#XXX needs a test
|
||||||
|
key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
|
||||||
|
#print "checking for recursion at", key
|
||||||
|
l = cache.setdefault(key, [])
|
||||||
|
if l:
|
||||||
|
f = entry.frame
|
||||||
|
loc = f.f_locals
|
||||||
|
for otherloc in l:
|
||||||
|
if f.is_true(f.eval(co_equal,
|
||||||
|
__recursioncache_locals_1=loc,
|
||||||
|
__recursioncache_locals_2=otherloc)):
|
||||||
|
return i
|
||||||
|
l.append(entry.frame.f_locals)
|
||||||
|
return None
|
||||||
|
|
||||||
|
co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
|
||||||
|
'?', 'eval')
|
||||||
|
|
||||||
|
class ExceptionInfo(object):
|
||||||
|
""" wraps sys.exc_info() objects and offers
|
||||||
|
help for navigating the traceback.
|
||||||
|
"""
|
||||||
|
_striptext = ''
|
||||||
|
def __init__(self, tup=None, exprinfo=None):
|
||||||
|
import _pytest._code
|
||||||
|
if tup is None:
|
||||||
|
tup = sys.exc_info()
|
||||||
|
if exprinfo is None and isinstance(tup[1], AssertionError):
|
||||||
|
exprinfo = getattr(tup[1], 'msg', None)
|
||||||
|
if exprinfo is None:
|
||||||
|
exprinfo = str(tup[1])
|
||||||
|
if exprinfo and exprinfo.startswith('assert '):
|
||||||
|
self._striptext = 'AssertionError: '
|
||||||
|
self._excinfo = tup
|
||||||
|
#: the exception class
|
||||||
|
self.type = tup[0]
|
||||||
|
#: the exception instance
|
||||||
|
self.value = tup[1]
|
||||||
|
#: the exception raw traceback
|
||||||
|
self.tb = tup[2]
|
||||||
|
#: the exception type name
|
||||||
|
self.typename = self.type.__name__
|
||||||
|
#: the exception traceback (_pytest._code.Traceback instance)
|
||||||
|
self.traceback = _pytest._code.Traceback(self.tb)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
|
||||||
|
|
||||||
|
def exconly(self, tryshort=False):
|
||||||
|
""" return the exception as a string
|
||||||
|
|
||||||
|
when 'tryshort' resolves to True, and the exception is a
|
||||||
|
_pytest._code._AssertionError, only the actual exception part of
|
||||||
|
the exception representation is returned (so 'AssertionError: ' is
|
||||||
|
removed from the beginning)
|
||||||
|
"""
|
||||||
|
lines = format_exception_only(self.type, self.value)
|
||||||
|
text = ''.join(lines)
|
||||||
|
text = text.rstrip()
|
||||||
|
if tryshort:
|
||||||
|
if text.startswith(self._striptext):
|
||||||
|
text = text[len(self._striptext):]
|
||||||
|
return text
|
||||||
|
|
||||||
|
def errisinstance(self, exc):
|
||||||
|
""" return True if the exception is an instance of exc """
|
||||||
|
return isinstance(self.value, exc)
|
||||||
|
|
||||||
|
def _getreprcrash(self):
|
||||||
|
exconly = self.exconly(tryshort=True)
|
||||||
|
entry = self.traceback.getcrashentry()
|
||||||
|
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
|
||||||
|
return ReprFileLocation(path, lineno+1, exconly)
|
||||||
|
|
||||||
|
def getrepr(self, showlocals=False, style="long",
|
||||||
|
abspath=False, tbfilter=True, funcargs=False):
|
||||||
|
""" return str()able representation of this exception info.
|
||||||
|
showlocals: show locals per traceback entry
|
||||||
|
style: long|short|no|native traceback style
|
||||||
|
tbfilter: hide entries (where __tracebackhide__ is true)
|
||||||
|
|
||||||
|
in case of style==native, tbfilter and showlocals is ignored.
|
||||||
|
"""
|
||||||
|
if style == 'native':
|
||||||
|
return ReprExceptionInfo(ReprTracebackNative(
|
||||||
|
py.std.traceback.format_exception(
|
||||||
|
self.type,
|
||||||
|
self.value,
|
||||||
|
self.traceback[0]._rawentry,
|
||||||
|
)), self._getreprcrash())
|
||||||
|
|
||||||
|
fmt = FormattedExcinfo(showlocals=showlocals, style=style,
|
||||||
|
abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
|
||||||
|
return fmt.repr_excinfo(self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
entry = self.traceback[-1]
|
||||||
|
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
|
||||||
|
return str(loc)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
entry = self.traceback[-1]
|
||||||
|
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
|
||||||
|
return unicode(loc)
|
||||||
|
|
||||||
|
|
||||||
|
class FormattedExcinfo(object):
|
||||||
|
""" presenting information about failing Functions and Generators. """
|
||||||
|
# for traceback entries
|
||||||
|
flow_marker = ">"
|
||||||
|
fail_marker = "E"
|
||||||
|
|
||||||
|
def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
|
||||||
|
self.showlocals = showlocals
|
||||||
|
self.style = style
|
||||||
|
self.tbfilter = tbfilter
|
||||||
|
self.funcargs = funcargs
|
||||||
|
self.abspath = abspath
|
||||||
|
self.astcache = {}
|
||||||
|
|
||||||
|
def _getindent(self, source):
|
||||||
|
# figure out indent for given source
|
||||||
|
try:
|
||||||
|
s = str(source.getstatement(len(source)-1))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
s = str(source[-1])
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
return 0
|
||||||
|
return 4 + (len(s) - len(s.lstrip()))
|
||||||
|
|
||||||
|
def _getentrysource(self, entry):
|
||||||
|
source = entry.getsource(self.astcache)
|
||||||
|
if source is not None:
|
||||||
|
source = source.deindent()
|
||||||
|
return source
|
||||||
|
|
||||||
|
def _saferepr(self, obj):
|
||||||
|
return py.io.saferepr(obj)
|
||||||
|
|
||||||
|
def repr_args(self, entry):
|
||||||
|
if self.funcargs:
|
||||||
|
args = []
|
||||||
|
for argname, argvalue in entry.frame.getargs(var=True):
|
||||||
|
args.append((argname, self._saferepr(argvalue)))
|
||||||
|
return ReprFuncArgs(args)
|
||||||
|
|
||||||
|
def get_source(self, source, line_index=-1, excinfo=None, short=False):
|
||||||
|
""" return formatted and marked up source lines. """
|
||||||
|
import _pytest._code
|
||||||
|
lines = []
|
||||||
|
if source is None or line_index >= len(source.lines):
|
||||||
|
source = _pytest._code.Source("???")
|
||||||
|
line_index = 0
|
||||||
|
if line_index < 0:
|
||||||
|
line_index += len(source)
|
||||||
|
space_prefix = " "
|
||||||
|
if short:
|
||||||
|
lines.append(space_prefix + source.lines[line_index].strip())
|
||||||
|
else:
|
||||||
|
for line in source.lines[:line_index]:
|
||||||
|
lines.append(space_prefix + line)
|
||||||
|
lines.append(self.flow_marker + " " + source.lines[line_index])
|
||||||
|
for line in source.lines[line_index+1:]:
|
||||||
|
lines.append(space_prefix + line)
|
||||||
|
if excinfo is not None:
|
||||||
|
indent = 4 if short else self._getindent(source)
|
||||||
|
lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def get_exconly(self, excinfo, indent=4, markall=False):
|
||||||
|
lines = []
|
||||||
|
indent = " " * indent
|
||||||
|
# get the real exception information out
|
||||||
|
exlines = excinfo.exconly(tryshort=True).split('\n')
|
||||||
|
failindent = self.fail_marker + indent[1:]
|
||||||
|
for line in exlines:
|
||||||
|
lines.append(failindent + line)
|
||||||
|
if not markall:
|
||||||
|
failindent = indent
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def repr_locals(self, locals):
|
||||||
|
if self.showlocals:
|
||||||
|
lines = []
|
||||||
|
keys = [loc for loc in locals if loc[0] != "@"]
|
||||||
|
keys.sort()
|
||||||
|
for name in keys:
|
||||||
|
value = locals[name]
|
||||||
|
if name == '__builtins__':
|
||||||
|
lines.append("__builtins__ = <builtins>")
|
||||||
|
else:
|
||||||
|
# This formatting could all be handled by the
|
||||||
|
# _repr() function, which is only reprlib.Repr in
|
||||||
|
# disguise, so is very configurable.
|
||||||
|
str_repr = self._saferepr(value)
|
||||||
|
#if len(str_repr) < 70 or not isinstance(value,
|
||||||
|
# (list, tuple, dict)):
|
||||||
|
lines.append("%-10s = %s" %(name, str_repr))
|
||||||
|
#else:
|
||||||
|
# self._line("%-10s =\\" % (name,))
|
||||||
|
# # XXX
|
||||||
|
# py.std.pprint.pprint(value, stream=self.excinfowriter)
|
||||||
|
return ReprLocals(lines)
|
||||||
|
|
||||||
|
def repr_traceback_entry(self, entry, excinfo=None):
|
||||||
|
import _pytest._code
|
||||||
|
source = self._getentrysource(entry)
|
||||||
|
if source is None:
|
||||||
|
source = _pytest._code.Source("???")
|
||||||
|
line_index = 0
|
||||||
|
else:
|
||||||
|
# entry.getfirstlinesource() can be -1, should be 0 on jython
|
||||||
|
line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
style = entry._repr_style
|
||||||
|
if style is None:
|
||||||
|
style = self.style
|
||||||
|
if style in ("short", "long"):
|
||||||
|
short = style == "short"
|
||||||
|
reprargs = self.repr_args(entry) if not short else None
|
||||||
|
s = self.get_source(source, line_index, excinfo, short=short)
|
||||||
|
lines.extend(s)
|
||||||
|
if short:
|
||||||
|
message = "in %s" %(entry.name)
|
||||||
|
else:
|
||||||
|
message = excinfo and excinfo.typename or ""
|
||||||
|
path = self._makepath(entry.path)
|
||||||
|
filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
|
||||||
|
localsrepr = None
|
||||||
|
if not short:
|
||||||
|
localsrepr = self.repr_locals(entry.locals)
|
||||||
|
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
|
||||||
|
if excinfo:
|
||||||
|
lines.extend(self.get_exconly(excinfo, indent=4))
|
||||||
|
return ReprEntry(lines, None, None, None, style)
|
||||||
|
|
||||||
|
def _makepath(self, path):
|
||||||
|
if not self.abspath:
|
||||||
|
try:
|
||||||
|
np = py.path.local().bestrelpath(path)
|
||||||
|
except OSError:
|
||||||
|
return path
|
||||||
|
if len(np) < len(str(path)):
|
||||||
|
path = np
|
||||||
|
return path
|
||||||
|
|
||||||
|
def repr_traceback(self, excinfo):
|
||||||
|
traceback = excinfo.traceback
|
||||||
|
if self.tbfilter:
|
||||||
|
traceback = traceback.filter()
|
||||||
|
recursionindex = None
|
||||||
|
if excinfo.errisinstance(RuntimeError):
|
||||||
|
if "maximum recursion depth exceeded" in str(excinfo.value):
|
||||||
|
recursionindex = traceback.recursionindex()
|
||||||
|
last = traceback[-1]
|
||||||
|
entries = []
|
||||||
|
extraline = None
|
||||||
|
for index, entry in enumerate(traceback):
|
||||||
|
einfo = (last == entry) and excinfo or None
|
||||||
|
reprentry = self.repr_traceback_entry(entry, einfo)
|
||||||
|
entries.append(reprentry)
|
||||||
|
if index == recursionindex:
|
||||||
|
extraline = "!!! Recursion detected (same locals & position)"
|
||||||
|
break
|
||||||
|
return ReprTraceback(entries, extraline, style=self.style)
|
||||||
|
|
||||||
|
def repr_excinfo(self, excinfo):
|
||||||
|
reprtraceback = self.repr_traceback(excinfo)
|
||||||
|
reprcrash = excinfo._getreprcrash()
|
||||||
|
return ReprExceptionInfo(reprtraceback, reprcrash)
|
||||||
|
|
||||||
|
class TerminalRepr:
|
||||||
|
def __str__(self):
|
||||||
|
s = self.__unicode__()
|
||||||
|
if sys.version_info[0] < 3:
|
||||||
|
s = s.encode('utf-8')
|
||||||
|
return s
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
# FYI this is called from pytest-xdist's serialization of exception
|
||||||
|
# information.
|
||||||
|
io = py.io.TextIO()
|
||||||
|
tw = py.io.TerminalWriter(file=io)
|
||||||
|
self.toterminal(tw)
|
||||||
|
return io.getvalue().strip()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s instance at %0x>" %(self.__class__, id(self))
|
||||||
|
|
||||||
|
|
||||||
|
class ReprExceptionInfo(TerminalRepr):
|
||||||
|
def __init__(self, reprtraceback, reprcrash):
|
||||||
|
self.reprtraceback = reprtraceback
|
||||||
|
self.reprcrash = reprcrash
|
||||||
|
self.sections = []
|
||||||
|
|
||||||
|
def addsection(self, name, content, sep="-"):
|
||||||
|
self.sections.append((name, content, sep))
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
self.reprtraceback.toterminal(tw)
|
||||||
|
for name, content, sep in self.sections:
|
||||||
|
tw.sep(sep, name)
|
||||||
|
tw.line(content)
|
||||||
|
|
||||||
|
class ReprTraceback(TerminalRepr):
|
||||||
|
entrysep = "_ "
|
||||||
|
|
||||||
|
def __init__(self, reprentries, extraline, style):
|
||||||
|
self.reprentries = reprentries
|
||||||
|
self.extraline = extraline
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
# the entries might have different styles
|
||||||
|
for i, entry in enumerate(self.reprentries):
|
||||||
|
if entry.style == "long":
|
||||||
|
tw.line("")
|
||||||
|
entry.toterminal(tw)
|
||||||
|
if i < len(self.reprentries) - 1:
|
||||||
|
next_entry = self.reprentries[i+1]
|
||||||
|
if entry.style == "long" or \
|
||||||
|
entry.style == "short" and next_entry.style == "long":
|
||||||
|
tw.sep(self.entrysep)
|
||||||
|
|
||||||
|
if self.extraline:
|
||||||
|
tw.line(self.extraline)
|
||||||
|
|
||||||
|
class ReprTracebackNative(ReprTraceback):
|
||||||
|
def __init__(self, tblines):
|
||||||
|
self.style = "native"
|
||||||
|
self.reprentries = [ReprEntryNative(tblines)]
|
||||||
|
self.extraline = None
|
||||||
|
|
||||||
|
class ReprEntryNative(TerminalRepr):
|
||||||
|
style = "native"
|
||||||
|
|
||||||
|
def __init__(self, tblines):
|
||||||
|
self.lines = tblines
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
tw.write("".join(self.lines))
|
||||||
|
|
||||||
|
class ReprEntry(TerminalRepr):
|
||||||
|
localssep = "_ "
|
||||||
|
|
||||||
|
def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
|
||||||
|
self.lines = lines
|
||||||
|
self.reprfuncargs = reprfuncargs
|
||||||
|
self.reprlocals = reprlocals
|
||||||
|
self.reprfileloc = filelocrepr
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
if self.style == "short":
|
||||||
|
self.reprfileloc.toterminal(tw)
|
||||||
|
for line in self.lines:
|
||||||
|
red = line.startswith("E ")
|
||||||
|
tw.line(line, bold=True, red=red)
|
||||||
|
#tw.line("")
|
||||||
|
return
|
||||||
|
if self.reprfuncargs:
|
||||||
|
self.reprfuncargs.toterminal(tw)
|
||||||
|
for line in self.lines:
|
||||||
|
red = line.startswith("E ")
|
||||||
|
tw.line(line, bold=True, red=red)
|
||||||
|
if self.reprlocals:
|
||||||
|
#tw.sep(self.localssep, "Locals")
|
||||||
|
tw.line("")
|
||||||
|
self.reprlocals.toterminal(tw)
|
||||||
|
if self.reprfileloc:
|
||||||
|
if self.lines:
|
||||||
|
tw.line("")
|
||||||
|
self.reprfileloc.toterminal(tw)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s\n%s\n%s" % ("\n".join(self.lines),
|
||||||
|
self.reprlocals,
|
||||||
|
self.reprfileloc)
|
||||||
|
|
||||||
|
class ReprFileLocation(TerminalRepr):
|
||||||
|
def __init__(self, path, lineno, message):
|
||||||
|
self.path = str(path)
|
||||||
|
self.lineno = lineno
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
# filename and lineno output for each entry,
|
||||||
|
# using an output format that most editors unterstand
|
||||||
|
msg = self.message
|
||||||
|
i = msg.find("\n")
|
||||||
|
if i != -1:
|
||||||
|
msg = msg[:i]
|
||||||
|
tw.line("%s:%s: %s" %(self.path, self.lineno, msg))
|
||||||
|
|
||||||
|
class ReprLocals(TerminalRepr):
|
||||||
|
def __init__(self, lines):
|
||||||
|
self.lines = lines
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
for line in self.lines:
|
||||||
|
tw.line(line)
|
||||||
|
|
||||||
|
class ReprFuncArgs(TerminalRepr):
|
||||||
|
def __init__(self, args):
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def toterminal(self, tw):
|
||||||
|
if self.args:
|
||||||
|
linesofar = ""
|
||||||
|
for name, value in self.args:
|
||||||
|
ns = "%s = %s" %(name, value)
|
||||||
|
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
|
||||||
|
if linesofar:
|
||||||
|
tw.line(linesofar)
|
||||||
|
linesofar = ns
|
||||||
|
else:
|
||||||
|
if linesofar:
|
||||||
|
linesofar += ", " + ns
|
||||||
|
else:
|
||||||
|
linesofar = ns
|
||||||
|
if linesofar:
|
||||||
|
tw.line(linesofar)
|
||||||
|
tw.line("")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
oldbuiltins = {}
|
||||||
|
|
||||||
|
def patch_builtins(assertion=True, compile=True):
|
||||||
|
""" put compile and AssertionError builtins to Python's builtins. """
|
||||||
|
if assertion:
|
||||||
|
from _pytest.assertion import reinterpret
|
||||||
|
l = oldbuiltins.setdefault('AssertionError', [])
|
||||||
|
l.append(py.builtin.builtins.AssertionError)
|
||||||
|
py.builtin.builtins.AssertionError = reinterpret.AssertionError
|
||||||
|
if compile:
|
||||||
|
import _pytest._code
|
||||||
|
l = oldbuiltins.setdefault('compile', [])
|
||||||
|
l.append(py.builtin.builtins.compile)
|
||||||
|
py.builtin.builtins.compile = _pytest._code.compile
|
||||||
|
|
||||||
|
def unpatch_builtins(assertion=True, compile=True):
|
||||||
|
""" remove compile and AssertionError builtins from Python builtins. """
|
||||||
|
if assertion:
|
||||||
|
py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
|
||||||
|
if compile:
|
||||||
|
py.builtin.builtins.compile = oldbuiltins['compile'].pop()
|
||||||
|
|
||||||
|
def getrawcode(obj, trycall=True):
|
||||||
|
""" return code object for given function. """
|
||||||
|
try:
|
||||||
|
return obj.__code__
|
||||||
|
except AttributeError:
|
||||||
|
obj = getattr(obj, 'im_func', obj)
|
||||||
|
obj = getattr(obj, 'func_code', obj)
|
||||||
|
obj = getattr(obj, 'f_code', obj)
|
||||||
|
obj = getattr(obj, '__code__', obj)
|
||||||
|
if trycall and not hasattr(obj, 'co_firstlineno'):
|
||||||
|
if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj):
|
||||||
|
x = getrawcode(obj.__call__, trycall=False)
|
||||||
|
if hasattr(x, 'co_firstlineno'):
|
||||||
|
return x
|
||||||
|
return obj
|
||||||
|
|
|
@ -0,0 +1,421 @@
|
||||||
|
from __future__ import generators
|
||||||
|
|
||||||
|
from bisect import bisect_right
|
||||||
|
import sys
|
||||||
|
import inspect, tokenize
|
||||||
|
import py
|
||||||
|
from types import ModuleType
|
||||||
|
cpy_compile = compile
|
||||||
|
|
||||||
|
try:
|
||||||
|
import _ast
|
||||||
|
from _ast import PyCF_ONLY_AST as _AST_FLAG
|
||||||
|
except ImportError:
|
||||||
|
_AST_FLAG = 0
|
||||||
|
_ast = None
|
||||||
|
|
||||||
|
|
||||||
|
class Source(object):
|
||||||
|
""" a immutable object holding a source code fragment,
|
||||||
|
possibly deindenting it.
|
||||||
|
"""
|
||||||
|
_compilecounter = 0
|
||||||
|
def __init__(self, *parts, **kwargs):
|
||||||
|
self.lines = lines = []
|
||||||
|
de = kwargs.get('deindent', True)
|
||||||
|
rstrip = kwargs.get('rstrip', True)
|
||||||
|
for part in parts:
|
||||||
|
if not part:
|
||||||
|
partlines = []
|
||||||
|
if isinstance(part, Source):
|
||||||
|
partlines = part.lines
|
||||||
|
elif isinstance(part, (tuple, list)):
|
||||||
|
partlines = [x.rstrip("\n") for x in part]
|
||||||
|
elif isinstance(part, py.builtin._basestring):
|
||||||
|
partlines = part.split('\n')
|
||||||
|
if rstrip:
|
||||||
|
while partlines:
|
||||||
|
if partlines[-1].strip():
|
||||||
|
break
|
||||||
|
partlines.pop()
|
||||||
|
else:
|
||||||
|
partlines = getsource(part, deindent=de).lines
|
||||||
|
if de:
|
||||||
|
partlines = deindent(partlines)
|
||||||
|
lines.extend(partlines)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
try:
|
||||||
|
return self.lines == other.lines
|
||||||
|
except AttributeError:
|
||||||
|
if isinstance(other, str):
|
||||||
|
return str(self) == other
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
if isinstance(key, int):
|
||||||
|
return self.lines[key]
|
||||||
|
else:
|
||||||
|
if key.step not in (None, 1):
|
||||||
|
raise IndexError("cannot slice a Source with a step")
|
||||||
|
return self.__getslice__(key.start, key.stop)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.lines)
|
||||||
|
|
||||||
|
def __getslice__(self, start, end):
|
||||||
|
newsource = Source()
|
||||||
|
newsource.lines = self.lines[start:end]
|
||||||
|
return newsource
|
||||||
|
|
||||||
|
def strip(self):
|
||||||
|
""" return new source object with trailing
|
||||||
|
and leading blank lines removed.
|
||||||
|
"""
|
||||||
|
start, end = 0, len(self)
|
||||||
|
while start < end and not self.lines[start].strip():
|
||||||
|
start += 1
|
||||||
|
while end > start and not self.lines[end-1].strip():
|
||||||
|
end -= 1
|
||||||
|
source = Source()
|
||||||
|
source.lines[:] = self.lines[start:end]
|
||||||
|
return source
|
||||||
|
|
||||||
|
def putaround(self, before='', after='', indent=' ' * 4):
|
||||||
|
""" return a copy of the source object with
|
||||||
|
'before' and 'after' wrapped around it.
|
||||||
|
"""
|
||||||
|
before = Source(before)
|
||||||
|
after = Source(after)
|
||||||
|
newsource = Source()
|
||||||
|
lines = [ (indent + line) for line in self.lines]
|
||||||
|
newsource.lines = before.lines + lines + after.lines
|
||||||
|
return newsource
|
||||||
|
|
||||||
|
def indent(self, indent=' ' * 4):
|
||||||
|
""" return a copy of the source object with
|
||||||
|
all lines indented by the given indent-string.
|
||||||
|
"""
|
||||||
|
newsource = Source()
|
||||||
|
newsource.lines = [(indent+line) for line in self.lines]
|
||||||
|
return newsource
|
||||||
|
|
||||||
|
def getstatement(self, lineno, assertion=False):
|
||||||
|
""" return Source statement which contains the
|
||||||
|
given linenumber (counted from 0).
|
||||||
|
"""
|
||||||
|
start, end = self.getstatementrange(lineno, assertion)
|
||||||
|
return self[start:end]
|
||||||
|
|
||||||
|
def getstatementrange(self, lineno, assertion=False):
|
||||||
|
""" return (start, end) tuple which spans the minimal
|
||||||
|
statement region which containing the given lineno.
|
||||||
|
"""
|
||||||
|
if not (0 <= lineno < len(self)):
|
||||||
|
raise IndexError("lineno out of range")
|
||||||
|
ast, start, end = getstatementrange_ast(lineno, self)
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
def deindent(self, offset=None):
|
||||||
|
""" return a new source object deindented by offset.
|
||||||
|
If offset is None then guess an indentation offset from
|
||||||
|
the first non-blank line. Subsequent lines which have a
|
||||||
|
lower indentation offset will be copied verbatim as
|
||||||
|
they are assumed to be part of multilines.
|
||||||
|
"""
|
||||||
|
# XXX maybe use the tokenizer to properly handle multiline
|
||||||
|
# strings etc.pp?
|
||||||
|
newsource = Source()
|
||||||
|
newsource.lines[:] = deindent(self.lines, offset)
|
||||||
|
return newsource
|
||||||
|
|
||||||
|
def isparseable(self, deindent=True):
|
||||||
|
""" return True if source is parseable, heuristically
|
||||||
|
deindenting it by default.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import parser
|
||||||
|
except ImportError:
|
||||||
|
syntax_checker = lambda x: compile(x, 'asd', 'exec')
|
||||||
|
else:
|
||||||
|
syntax_checker = parser.suite
|
||||||
|
|
||||||
|
if deindent:
|
||||||
|
source = str(self.deindent())
|
||||||
|
else:
|
||||||
|
source = str(self)
|
||||||
|
try:
|
||||||
|
#compile(source+'\n', "x", "exec")
|
||||||
|
syntax_checker(source+'\n')
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "\n".join(self.lines)
|
||||||
|
|
||||||
|
def compile(self, filename=None, mode='exec',
|
||||||
|
flag=generators.compiler_flag,
|
||||||
|
dont_inherit=0, _genframe=None):
|
||||||
|
""" return compiled code object. if filename is None
|
||||||
|
invent an artificial filename which displays
|
||||||
|
the source/line position of the caller frame.
|
||||||
|
"""
|
||||||
|
if not filename or py.path.local(filename).check(file=0):
|
||||||
|
if _genframe is None:
|
||||||
|
_genframe = sys._getframe(1) # the caller
|
||||||
|
fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno
|
||||||
|
base = "<%d-codegen " % self._compilecounter
|
||||||
|
self.__class__._compilecounter += 1
|
||||||
|
if not filename:
|
||||||
|
filename = base + '%s:%d>' % (fn, lineno)
|
||||||
|
else:
|
||||||
|
filename = base + '%r %s:%d>' % (filename, fn, lineno)
|
||||||
|
source = "\n".join(self.lines) + '\n'
|
||||||
|
try:
|
||||||
|
co = cpy_compile(source, filename, mode, flag)
|
||||||
|
except SyntaxError:
|
||||||
|
ex = sys.exc_info()[1]
|
||||||
|
# re-represent syntax errors from parsing python strings
|
||||||
|
msglines = self.lines[:ex.lineno]
|
||||||
|
if ex.offset:
|
||||||
|
msglines.append(" "*ex.offset + '^')
|
||||||
|
msglines.append("(code was compiled probably from here: %s)" % filename)
|
||||||
|
newex = SyntaxError('\n'.join(msglines))
|
||||||
|
newex.offset = ex.offset
|
||||||
|
newex.lineno = ex.lineno
|
||||||
|
newex.text = ex.text
|
||||||
|
raise newex
|
||||||
|
else:
|
||||||
|
if flag & _AST_FLAG:
|
||||||
|
return co
|
||||||
|
lines = [(x + "\n") for x in self.lines]
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
# XXX py3's inspect.getsourcefile() checks for a module
|
||||||
|
# and a pep302 __loader__ ... we don't have a module
|
||||||
|
# at code compile-time so we need to fake it here
|
||||||
|
m = ModuleType("_pycodecompile_pseudo_module")
|
||||||
|
py.std.inspect.modulesbyfile[filename] = None
|
||||||
|
py.std.sys.modules[None] = m
|
||||||
|
m.__loader__ = 1
|
||||||
|
py.std.linecache.cache[filename] = (1, None, lines, filename)
|
||||||
|
return co
|
||||||
|
|
||||||
|
#
|
||||||
|
# public API shortcut functions
|
||||||
|
#
|
||||||
|
|
||||||
|
def compile_(source, filename=None, mode='exec', flags=
|
||||||
|
generators.compiler_flag, dont_inherit=0):
|
||||||
|
""" compile the given source to a raw code object,
|
||||||
|
and maintain an internal cache which allows later
|
||||||
|
retrieval of the source code for the code object
|
||||||
|
and any recursively created code objects.
|
||||||
|
"""
|
||||||
|
if _ast is not None and isinstance(source, _ast.AST):
|
||||||
|
# XXX should Source support having AST?
|
||||||
|
return cpy_compile(source, filename, mode, flags, dont_inherit)
|
||||||
|
_genframe = sys._getframe(1) # the caller
|
||||||
|
s = Source(source)
|
||||||
|
co = s.compile(filename, mode, flags, _genframe=_genframe)
|
||||||
|
return co
|
||||||
|
|
||||||
|
|
||||||
|
def getfslineno(obj):
|
||||||
|
""" Return source location (path, lineno) for the given object.
|
||||||
|
If the source cannot be determined return ("", -1)
|
||||||
|
"""
|
||||||
|
import _pytest._code
|
||||||
|
try:
|
||||||
|
code = _pytest._code.Code(obj)
|
||||||
|
except TypeError:
|
||||||
|
try:
|
||||||
|
fn = (py.std.inspect.getsourcefile(obj) or
|
||||||
|
py.std.inspect.getfile(obj))
|
||||||
|
except TypeError:
|
||||||
|
return "", -1
|
||||||
|
|
||||||
|
fspath = fn and py.path.local(fn) or None
|
||||||
|
lineno = -1
|
||||||
|
if fspath:
|
||||||
|
try:
|
||||||
|
_, lineno = findsource(obj)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
fspath = code.path
|
||||||
|
lineno = code.firstlineno
|
||||||
|
assert isinstance(lineno, int)
|
||||||
|
return fspath, lineno
|
||||||
|
|
||||||
|
#
|
||||||
|
# helper functions
|
||||||
|
#
|
||||||
|
|
||||||
|
def findsource(obj):
|
||||||
|
try:
|
||||||
|
sourcelines, lineno = py.std.inspect.findsource(obj)
|
||||||
|
except py.builtin._sysex:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
return None, -1
|
||||||
|
source = Source()
|
||||||
|
source.lines = [line.rstrip() for line in sourcelines]
|
||||||
|
return source, lineno
|
||||||
|
|
||||||
|
def getsource(obj, **kwargs):
|
||||||
|
import _pytest._code
|
||||||
|
obj = _pytest._code.getrawcode(obj)
|
||||||
|
try:
|
||||||
|
strsrc = inspect.getsource(obj)
|
||||||
|
except IndentationError:
|
||||||
|
strsrc = "\"Buggy python version consider upgrading, cannot get source\""
|
||||||
|
assert isinstance(strsrc, str)
|
||||||
|
return Source(strsrc, **kwargs)
|
||||||
|
|
||||||
|
def deindent(lines, offset=None):
|
||||||
|
if offset is None:
|
||||||
|
for line in lines:
|
||||||
|
line = line.expandtabs()
|
||||||
|
s = line.lstrip()
|
||||||
|
if s:
|
||||||
|
offset = len(line)-len(s)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
offset = 0
|
||||||
|
if offset == 0:
|
||||||
|
return list(lines)
|
||||||
|
newlines = []
|
||||||
|
def readline_generator(lines):
|
||||||
|
for line in lines:
|
||||||
|
yield line + '\n'
|
||||||
|
while True:
|
||||||
|
yield ''
|
||||||
|
|
||||||
|
it = readline_generator(lines)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
|
||||||
|
if sline > len(lines):
|
||||||
|
break # End of input reached
|
||||||
|
if sline > len(newlines):
|
||||||
|
line = lines[sline - 1].expandtabs()
|
||||||
|
if line.lstrip() and line[:offset].isspace():
|
||||||
|
line = line[offset:] # Deindent
|
||||||
|
newlines.append(line)
|
||||||
|
|
||||||
|
for i in range(sline, eline):
|
||||||
|
# Don't deindent continuing lines of
|
||||||
|
# multiline tokens (i.e. multiline strings)
|
||||||
|
newlines.append(lines[i])
|
||||||
|
except (IndentationError, tokenize.TokenError):
|
||||||
|
pass
|
||||||
|
# Add any lines we didn't see. E.g. if an exception was raised.
|
||||||
|
newlines.extend(lines[len(newlines):])
|
||||||
|
return newlines
|
||||||
|
|
||||||
|
|
||||||
|
def get_statement_startend2(lineno, node):
|
||||||
|
import ast
|
||||||
|
# flatten all statements and except handlers into one lineno-list
|
||||||
|
# AST's line numbers start indexing at 1
|
||||||
|
l = []
|
||||||
|
for x in ast.walk(node):
|
||||||
|
if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
|
||||||
|
l.append(x.lineno - 1)
|
||||||
|
for name in "finalbody", "orelse":
|
||||||
|
val = getattr(x, name, None)
|
||||||
|
if val:
|
||||||
|
# treat the finally/orelse part as its own statement
|
||||||
|
l.append(val[0].lineno - 1 - 1)
|
||||||
|
l.sort()
|
||||||
|
insert_index = bisect_right(l, lineno)
|
||||||
|
start = l[insert_index - 1]
|
||||||
|
if insert_index >= len(l):
|
||||||
|
end = None
|
||||||
|
else:
|
||||||
|
end = l[insert_index]
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
|
||||||
|
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
|
||||||
|
if astnode is None:
|
||||||
|
content = str(source)
|
||||||
|
if sys.version_info < (2,7):
|
||||||
|
content += "\n"
|
||||||
|
try:
|
||||||
|
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
|
||||||
|
except ValueError:
|
||||||
|
start, end = getstatementrange_old(lineno, source, assertion)
|
||||||
|
return None, start, end
|
||||||
|
start, end = get_statement_startend2(lineno, astnode)
|
||||||
|
# we need to correct the end:
|
||||||
|
# - ast-parsing strips comments
|
||||||
|
# - there might be empty lines
|
||||||
|
# - we might have lesser indented code blocks at the end
|
||||||
|
if end is None:
|
||||||
|
end = len(source.lines)
|
||||||
|
|
||||||
|
if end > start + 1:
|
||||||
|
# make sure we don't span differently indented code blocks
|
||||||
|
# by using the BlockFinder helper used which inspect.getsource() uses itself
|
||||||
|
block_finder = inspect.BlockFinder()
|
||||||
|
# if we start with an indented line, put blockfinder to "started" mode
|
||||||
|
block_finder.started = source.lines[start][0].isspace()
|
||||||
|
it = ((x + "\n") for x in source.lines[start:end])
|
||||||
|
try:
|
||||||
|
for tok in tokenize.generate_tokens(lambda: next(it)):
|
||||||
|
block_finder.tokeneater(*tok)
|
||||||
|
except (inspect.EndOfBlock, IndentationError):
|
||||||
|
end = block_finder.last + start
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# the end might still point to a comment or empty line, correct it
|
||||||
|
while end:
|
||||||
|
line = source.lines[end - 1].lstrip()
|
||||||
|
if line.startswith("#") or not line:
|
||||||
|
end -= 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return astnode, start, end
|
||||||
|
|
||||||
|
|
||||||
|
def getstatementrange_old(lineno, source, assertion=False):
|
||||||
|
""" return (start, end) tuple which spans the minimal
|
||||||
|
statement region which containing the given lineno.
|
||||||
|
raise an IndexError if no such statementrange can be found.
|
||||||
|
"""
|
||||||
|
# XXX this logic is only used on python2.4 and below
|
||||||
|
# 1. find the start of the statement
|
||||||
|
from codeop import compile_command
|
||||||
|
for start in range(lineno, -1, -1):
|
||||||
|
if assertion:
|
||||||
|
line = source.lines[start]
|
||||||
|
# the following lines are not fully tested, change with care
|
||||||
|
if 'super' in line and 'self' in line and '__init__' in line:
|
||||||
|
raise IndexError("likely a subclass")
|
||||||
|
if "assert" not in line and "raise" not in line:
|
||||||
|
continue
|
||||||
|
trylines = source.lines[start:lineno+1]
|
||||||
|
# quick hack to prepare parsing an indented line with
|
||||||
|
# compile_command() (which errors on "return" outside defs)
|
||||||
|
trylines.insert(0, 'def xxx():')
|
||||||
|
trysource = '\n '.join(trylines)
|
||||||
|
# ^ space here
|
||||||
|
try:
|
||||||
|
compile_command(trysource)
|
||||||
|
except (SyntaxError, OverflowError, ValueError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 2. find the end of the statement
|
||||||
|
for end in range(lineno+1, len(source)+1):
|
||||||
|
trysource = source[start:end]
|
||||||
|
if trysource.isparseable():
|
||||||
|
return start, end
|
||||||
|
raise SyntaxError("no valid source range around line %d " % (lineno,))
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ Find intermediate evalutation results in assert statements through builtin AST.
|
||||||
"""
|
"""
|
||||||
import ast
|
import ast
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
u = py.builtin._totext
|
u = py.builtin._totext
|
||||||
|
@ -26,7 +28,7 @@ class AssertionError(util.BuiltinAssertionError):
|
||||||
"<[broken __repr__] %s at %0xd>"
|
"<[broken __repr__] %s at %0xd>"
|
||||||
% (toprint.__class__, id(toprint)))
|
% (toprint.__class__, id(toprint)))
|
||||||
else:
|
else:
|
||||||
f = py.code.Frame(sys._getframe(1))
|
f = _pytest._code.Frame(sys._getframe(1))
|
||||||
try:
|
try:
|
||||||
source = f.code.fullsource
|
source = f.code.fullsource
|
||||||
if source is not None:
|
if source is not None:
|
||||||
|
@ -102,7 +104,7 @@ def reinterpret(source, frame, should_fail=False):
|
||||||
|
|
||||||
def run(offending_line, frame=None):
|
def run(offending_line, frame=None):
|
||||||
if frame is None:
|
if frame is None:
|
||||||
frame = py.code.Frame(sys._getframe(1))
|
frame = _pytest._code.Frame(sys._getframe(1))
|
||||||
return reinterpret(offending_line, frame)
|
return reinterpret(offending_line, frame)
|
||||||
|
|
||||||
def getfailure(e):
|
def getfailure(e):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Utilities for assertion debugging"""
|
"""Utilities for assertion debugging"""
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
try:
|
try:
|
||||||
from collections import Sequence
|
from collections import Sequence
|
||||||
|
@ -179,7 +180,7 @@ def assertrepr_compare(config, op, left, right):
|
||||||
explanation = [
|
explanation = [
|
||||||
u('(pytest_assertion plugin: representation of details failed. '
|
u('(pytest_assertion plugin: representation of details failed. '
|
||||||
'Probably an object has a faulty __repr__.)'),
|
'Probably an object has a faulty __repr__.)'),
|
||||||
u(py.code.ExceptionInfo())]
|
u(_pytest._code.ExceptionInfo())]
|
||||||
|
|
||||||
if not explanation:
|
if not explanation:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -8,6 +8,7 @@ import warnings
|
||||||
import py
|
import py
|
||||||
# DON't import pytest here because it causes import cycle troubles
|
# DON't import pytest here because it causes import cycle troubles
|
||||||
import sys, os
|
import sys, os
|
||||||
|
import _pytest._code
|
||||||
import _pytest.hookspec # the extension point definitions
|
import _pytest.hookspec # the extension point definitions
|
||||||
from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker
|
from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker
|
||||||
|
|
||||||
|
@ -158,7 +159,7 @@ class PytestPluginManager(PluginManager):
|
||||||
Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
|
Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
|
||||||
"""
|
"""
|
||||||
warning = dict(code="I2",
|
warning = dict(code="I2",
|
||||||
fslocation=py.code.getfslineno(sys._getframe(1)),
|
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
|
||||||
nodeid=None,
|
nodeid=None,
|
||||||
message="use pluginmanager.add_hookspecs instead of "
|
message="use pluginmanager.add_hookspecs instead of "
|
||||||
"deprecated addhooks() method.")
|
"deprecated addhooks() method.")
|
||||||
|
@ -195,7 +196,7 @@ class PytestPluginManager(PluginManager):
|
||||||
def _verify_hook(self, hook, hookmethod):
|
def _verify_hook(self, hook, hookmethod):
|
||||||
super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
|
super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
|
||||||
if "__multicall__" in hookmethod.argnames:
|
if "__multicall__" in hookmethod.argnames:
|
||||||
fslineno = py.code.getfslineno(hookmethod.function)
|
fslineno = _pytest._code.getfslineno(hookmethod.function)
|
||||||
warning = dict(code="I1",
|
warning = dict(code="I1",
|
||||||
fslocation=fslineno,
|
fslocation=fslineno,
|
||||||
nodeid=None,
|
nodeid=None,
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
""" discover and run doctests in modules and test files."""
|
""" discover and run doctests in modules and test files."""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
import pytest, py
|
|
||||||
|
import pytest
|
||||||
|
from _pytest._code.code import TerminalRepr, ReprFileLocation, ExceptionInfo
|
||||||
from _pytest.python import FixtureRequest
|
from _pytest.python import FixtureRequest
|
||||||
from py._code.code import TerminalRepr, ReprFileLocation
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
@ -107,7 +110,7 @@ class DoctestItem(pytest.Item):
|
||||||
lines += checker.output_difference(example,
|
lines += checker.output_difference(example,
|
||||||
doctestfailure.got, REPORT_UDIFF).split("\n")
|
doctestfailure.got, REPORT_UDIFF).split("\n")
|
||||||
else:
|
else:
|
||||||
inner_excinfo = py.code.ExceptionInfo(excinfo.value.exc_info)
|
inner_excinfo = ExceptionInfo(excinfo.value.exc_info)
|
||||||
lines += ["UNEXPECTED EXCEPTION: %s" %
|
lines += ["UNEXPECTED EXCEPTION: %s" %
|
||||||
repr(inner_excinfo.value)]
|
repr(inner_excinfo.value)]
|
||||||
lines += traceback.format_exception(*excinfo.value.exc_info)
|
lines += traceback.format_exception(*excinfo.value.exc_info)
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
""" core implementation of testing process: init, session, runtest loop. """
|
""" core implementation of testing process: init, session, runtest loop. """
|
||||||
|
import imp
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import _pytest
|
||||||
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
import pytest, _pytest
|
import pytest
|
||||||
import os, sys, imp
|
|
||||||
try:
|
try:
|
||||||
from collections import MutableMapping as MappingMixin
|
from collections import MutableMapping as MappingMixin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -91,11 +95,11 @@ def wrap_session(config, doit):
|
||||||
except pytest.UsageError:
|
except pytest.UsageError:
|
||||||
raise
|
raise
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
|
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
|
||||||
session.exitstatus = EXIT_INTERRUPTED
|
session.exitstatus = EXIT_INTERRUPTED
|
||||||
except:
|
except:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
config.notify_exception(excinfo, config.option)
|
config.notify_exception(excinfo, config.option)
|
||||||
session.exitstatus = EXIT_INTERNALERROR
|
session.exitstatus = EXIT_INTERNALERROR
|
||||||
if excinfo.errisinstance(SystemExit):
|
if excinfo.errisinstance(SystemExit):
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
""" (disabled by default) support for testing pytest and pytest plugins. """
|
""" (disabled by default) support for testing pytest and pytest plugins. """
|
||||||
import gc
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
import os
|
|
||||||
import codecs
|
import codecs
|
||||||
import re
|
import gc
|
||||||
import time
|
import os
|
||||||
import platform
|
import platform
|
||||||
from fnmatch import fnmatch
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
from fnmatch import fnmatch
|
||||||
|
|
||||||
import py
|
|
||||||
import pytest
|
|
||||||
from py.builtin import print_
|
from py.builtin import print_
|
||||||
|
|
||||||
|
from _pytest._code import Source
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
from _pytest.main import Session, EXIT_OK
|
from _pytest.main import Session, EXIT_OK
|
||||||
|
|
||||||
|
|
||||||
|
@ -472,7 +473,7 @@ class Testdir:
|
||||||
ret = None
|
ret = None
|
||||||
for name, value in items:
|
for name, value in items:
|
||||||
p = self.tmpdir.join(name).new(ext=ext)
|
p = self.tmpdir.join(name).new(ext=ext)
|
||||||
source = py.code.Source(value)
|
source = Source(value)
|
||||||
def my_totext(s, encoding="utf-8"):
|
def my_totext(s, encoding="utf-8"):
|
||||||
if py.builtin._isbytes(s):
|
if py.builtin._isbytes(s):
|
||||||
s = py.builtin._totext(s, encoding=encoding)
|
s = py.builtin._totext(s, encoding=encoding)
|
||||||
|
@ -835,7 +836,7 @@ class Testdir:
|
||||||
to the temporarly directory to ensure it is a package.
|
to the temporarly directory to ensure it is a package.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
kw = {self.request.function.__name__: py.code.Source(source).strip()}
|
kw = {self.request.function.__name__: Source(source).strip()}
|
||||||
path = self.makepyfile(**kw)
|
path = self.makepyfile(**kw)
|
||||||
if withinit:
|
if withinit:
|
||||||
self.makepyfile(__init__ = "#")
|
self.makepyfile(__init__ = "#")
|
||||||
|
@ -1041,8 +1042,8 @@ class LineMatcher:
|
||||||
|
|
||||||
def _getlines(self, lines2):
|
def _getlines(self, lines2):
|
||||||
if isinstance(lines2, str):
|
if isinstance(lines2, str):
|
||||||
lines2 = py.code.Source(lines2)
|
lines2 = Source(lines2)
|
||||||
if isinstance(lines2, py.code.Source):
|
if isinstance(lines2, Source):
|
||||||
lines2 = lines2.strip().lines
|
lines2 = lines2.strip().lines
|
||||||
return lines2
|
return lines2
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
""" Python test discovery, setup and run of test functions. """
|
""" Python test discovery, setup and run of test functions. """
|
||||||
import re
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import functools
|
import functools
|
||||||
import py
|
|
||||||
import inspect
|
import inspect
|
||||||
|
import re
|
||||||
import types
|
import types
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
|
from _pytest._code.code import TerminalRepr
|
||||||
from _pytest.mark import MarkDecorator, MarkerError
|
from _pytest.mark import MarkDecorator, MarkerError
|
||||||
from py._code.code import TerminalRepr
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import enum
|
import enum
|
||||||
|
@ -86,7 +87,7 @@ def getfslineno(obj):
|
||||||
obj = get_real_func(obj)
|
obj = get_real_func(obj)
|
||||||
if hasattr(obj, 'place_as'):
|
if hasattr(obj, 'place_as'):
|
||||||
obj = obj.place_as
|
obj = obj.place_as
|
||||||
fslineno = py.code.getfslineno(obj)
|
fslineno = _pytest._code.getfslineno(obj)
|
||||||
assert isinstance(fslineno[1], int), obj
|
assert isinstance(fslineno[1], int), obj
|
||||||
return fslineno
|
return fslineno
|
||||||
|
|
||||||
|
@ -331,7 +332,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
|
||||||
|
|
||||||
def is_generator(func):
|
def is_generator(func):
|
||||||
try:
|
try:
|
||||||
return py.code.getrawcode(func).co_flags & 32 # generator function
|
return _pytest._code.getrawcode(func).co_flags & 32 # generator function
|
||||||
except AttributeError: # builtin functions have no bytecode
|
except AttributeError: # builtin functions have no bytecode
|
||||||
# assume them to not be generators
|
# assume them to not be generators
|
||||||
return False
|
return False
|
||||||
|
@ -610,7 +611,7 @@ class Module(pytest.File, PyCollector):
|
||||||
mod = self.fspath.pyimport(ensuresyspath=importmode)
|
mod = self.fspath.pyimport(ensuresyspath=importmode)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
raise self.CollectError(
|
raise self.CollectError(
|
||||||
py.code.ExceptionInfo().getrepr(style="short"))
|
_pytest._code.ExceptionInfo().getrepr(style="short"))
|
||||||
except self.fspath.ImportMismatchError:
|
except self.fspath.ImportMismatchError:
|
||||||
e = sys.exc_info()[1]
|
e = sys.exc_info()[1]
|
||||||
raise self.CollectError(
|
raise self.CollectError(
|
||||||
|
@ -716,7 +717,7 @@ class FunctionMixin(PyobjMixin):
|
||||||
|
|
||||||
def _prunetraceback(self, excinfo):
|
def _prunetraceback(self, excinfo):
|
||||||
if hasattr(self, '_obj') and not self.config.option.fulltrace:
|
if hasattr(self, '_obj') and not self.config.option.fulltrace:
|
||||||
code = py.code.Code(get_real_func(self.obj))
|
code = _pytest._code.Code(get_real_func(self.obj))
|
||||||
path, firstlineno = code.path, code.firstlineno
|
path, firstlineno = code.path, code.firstlineno
|
||||||
traceback = excinfo.traceback
|
traceback = excinfo.traceback
|
||||||
ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
||||||
|
@ -1202,10 +1203,10 @@ def getlocation(function, curdir):
|
||||||
# builtin pytest.raises helper
|
# builtin pytest.raises helper
|
||||||
|
|
||||||
def raises(expected_exception, *args, **kwargs):
|
def raises(expected_exception, *args, **kwargs):
|
||||||
""" assert that a code block/function call raises @expected_exception
|
""" assert that a code block/function call raises ``expected_exception``
|
||||||
and raise a failure exception otherwise.
|
and raise a failure exception otherwise.
|
||||||
|
|
||||||
This helper produces a ``py.code.ExceptionInfo()`` object.
|
This helper produces a ``ExceptionInfo()`` object (see below).
|
||||||
|
|
||||||
If using Python 2.5 or above, you may use this function as a
|
If using Python 2.5 or above, you may use this function as a
|
||||||
context manager::
|
context manager::
|
||||||
|
@ -1221,19 +1222,19 @@ def raises(expected_exception, *args, **kwargs):
|
||||||
Lines of code after that, within the scope of the context manager will
|
Lines of code after that, within the scope of the context manager will
|
||||||
not be executed. For example::
|
not be executed. For example::
|
||||||
|
|
||||||
>>> with raises(OSError) as err:
|
>>> with raises(OSError) as exc_info:
|
||||||
assert 1 == 1 # this will execute as expected
|
assert 1 == 1 # this will execute as expected
|
||||||
raise OSError(errno.EEXISTS, 'directory exists')
|
raise OSError(errno.EEXISTS, 'directory exists')
|
||||||
assert err.errno == errno.EEXISTS # this will not execute
|
assert exc_info.value.errno == errno.EEXISTS # this will not execute
|
||||||
|
|
||||||
Instead, the following approach must be taken (note the difference in
|
Instead, the following approach must be taken (note the difference in
|
||||||
scope)::
|
scope)::
|
||||||
|
|
||||||
>>> with raises(OSError) as err:
|
>>> with raises(OSError) as exc_info:
|
||||||
assert 1 == 1 # this will execute as expected
|
assert 1 == 1 # this will execute as expected
|
||||||
raise OSError(errno.EEXISTS, 'directory exists')
|
raise OSError(errno.EEXISTS, 'directory exists')
|
||||||
|
|
||||||
assert err.errno == errno.EEXISTS # this will now execute
|
assert exc_info.value.errno == errno.EEXISTS # this will now execute
|
||||||
|
|
||||||
Or you can specify a callable by passing a to-be-called lambda::
|
Or you can specify a callable by passing a to-be-called lambda::
|
||||||
|
|
||||||
|
@ -1254,21 +1255,22 @@ def raises(expected_exception, *args, **kwargs):
|
||||||
>>> raises(ZeroDivisionError, "f(0)")
|
>>> raises(ZeroDivisionError, "f(0)")
|
||||||
<ExceptionInfo ...>
|
<ExceptionInfo ...>
|
||||||
|
|
||||||
Performance note:
|
.. autoclass:: _pytest._code.ExceptionInfo
|
||||||
-----------------
|
:members:
|
||||||
|
|
||||||
Similar to caught exception objects in Python, explicitly clearing
|
.. note::
|
||||||
local references to returned ``py.code.ExceptionInfo`` objects can
|
Similar to caught exception objects in Python, explicitly clearing
|
||||||
help the Python interpreter speed up its garbage collection.
|
local references to returned ``ExceptionInfo`` objects can
|
||||||
|
help the Python interpreter speed up its garbage collection.
|
||||||
|
|
||||||
Clearing those references breaks a reference cycle
|
Clearing those references breaks a reference cycle
|
||||||
(``ExceptionInfo`` --> caught exception --> frame stack raising
|
(``ExceptionInfo`` --> caught exception --> frame stack raising
|
||||||
the exception --> current frame stack --> local variables -->
|
the exception --> current frame stack --> local variables -->
|
||||||
``ExceptionInfo``) which makes Python keep all objects referenced
|
``ExceptionInfo``) which makes Python keep all objects referenced
|
||||||
from that cycle (including all local variables in the current
|
from that cycle (including all local variables in the current
|
||||||
frame) alive until the next cyclic garbage collection run. See the
|
frame) alive until the next cyclic garbage collection run. See the
|
||||||
official Python ``try`` statement documentation for more detailed
|
official Python ``try`` statement documentation for more detailed
|
||||||
information.
|
information.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
|
@ -1297,18 +1299,18 @@ def raises(expected_exception, *args, **kwargs):
|
||||||
loc.update(kwargs)
|
loc.update(kwargs)
|
||||||
#print "raises frame scope: %r" % frame.f_locals
|
#print "raises frame scope: %r" % frame.f_locals
|
||||||
try:
|
try:
|
||||||
code = py.code.Source(code).compile()
|
code = _pytest._code.Source(code).compile()
|
||||||
py.builtin.exec_(code, frame.f_globals, loc)
|
py.builtin.exec_(code, frame.f_globals, loc)
|
||||||
# XXX didn'T mean f_globals == f_locals something special?
|
# XXX didn'T mean f_globals == f_locals something special?
|
||||||
# this is destroyed here ...
|
# this is destroyed here ...
|
||||||
except expected_exception:
|
except expected_exception:
|
||||||
return py.code.ExceptionInfo()
|
return _pytest._code.ExceptionInfo()
|
||||||
else:
|
else:
|
||||||
func = args[0]
|
func = args[0]
|
||||||
try:
|
try:
|
||||||
func(*args[1:], **kwargs)
|
func(*args[1:], **kwargs)
|
||||||
except expected_exception:
|
except expected_exception:
|
||||||
return py.code.ExceptionInfo()
|
return _pytest._code.ExceptionInfo()
|
||||||
pytest.fail("DID NOT RAISE")
|
pytest.fail("DID NOT RAISE")
|
||||||
|
|
||||||
class RaisesContext(object):
|
class RaisesContext(object):
|
||||||
|
@ -1317,7 +1319,7 @@ class RaisesContext(object):
|
||||||
self.excinfo = None
|
self.excinfo = None
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.excinfo = object.__new__(py.code.ExceptionInfo)
|
self.excinfo = object.__new__(_pytest._code.ExceptionInfo)
|
||||||
return self.excinfo
|
return self.excinfo
|
||||||
|
|
||||||
def __exit__(self, *tp):
|
def __exit__(self, *tp):
|
||||||
|
@ -2025,7 +2027,7 @@ class FixtureManager:
|
||||||
def fail_fixturefunc(fixturefunc, msg):
|
def fail_fixturefunc(fixturefunc, msg):
|
||||||
fs, lineno = getfslineno(fixturefunc)
|
fs, lineno = getfslineno(fixturefunc)
|
||||||
location = "%s:%s" % (fs, lineno+1)
|
location = "%s:%s" % (fs, lineno+1)
|
||||||
source = py.code.Source(fixturefunc)
|
source = _pytest._code.Source(fixturefunc)
|
||||||
pytest.fail(msg + ":\n\n" + str(source.indent()) + "\n" + location,
|
pytest.fail(msg + ":\n\n" + str(source.indent()) + "\n" + location,
|
||||||
pytrace=False)
|
pytrace=False)
|
||||||
|
|
||||||
|
@ -2168,14 +2170,14 @@ def getfuncargnames(function, startindex=None):
|
||||||
startindex += num_mock_patch_args(function)
|
startindex += num_mock_patch_args(function)
|
||||||
function = realfunction
|
function = realfunction
|
||||||
if isinstance(function, functools.partial):
|
if isinstance(function, functools.partial):
|
||||||
argnames = inspect.getargs(py.code.getrawcode(function.func))[0]
|
argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0]
|
||||||
partial = function
|
partial = function
|
||||||
argnames = argnames[len(partial.args):]
|
argnames = argnames[len(partial.args):]
|
||||||
if partial.keywords:
|
if partial.keywords:
|
||||||
for kw in partial.keywords:
|
for kw in partial.keywords:
|
||||||
argnames.remove(kw)
|
argnames.remove(kw)
|
||||||
else:
|
else:
|
||||||
argnames = inspect.getargs(py.code.getrawcode(function))[0]
|
argnames = inspect.getargs(_pytest._code.getrawcode(function))[0]
|
||||||
defaults = getattr(function, 'func_defaults',
|
defaults = getattr(function, 'func_defaults',
|
||||||
getattr(function, '__defaults__', None)) or ()
|
getattr(function, '__defaults__', None)) or ()
|
||||||
numdefaults = len(defaults)
|
numdefaults = len(defaults)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
""" recording warnings during test function execution. """
|
""" recording warnings during test function execution. """
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -100,7 +102,7 @@ def warns(expected_warning, *args, **kwargs):
|
||||||
loc.update(kwargs)
|
loc.update(kwargs)
|
||||||
|
|
||||||
with wcheck:
|
with wcheck:
|
||||||
code = py.code.Source(code).compile()
|
code = _pytest._code.Source(code).compile()
|
||||||
py.builtin.exec_(code, frame.f_globals, loc)
|
py.builtin.exec_(code, frame.f_globals, loc)
|
||||||
else:
|
else:
|
||||||
func = args[0]
|
func = args[0]
|
||||||
|
|
|
@ -5,7 +5,8 @@ from time import time
|
||||||
|
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
from py._code.code import TerminalRepr
|
from _pytest._code.code import TerminalRepr, ExceptionInfo
|
||||||
|
|
||||||
|
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
return {
|
return {
|
||||||
|
@ -151,7 +152,7 @@ class CallInfo:
|
||||||
self.stop = time()
|
self.stop = time()
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
self.excinfo = py.code.ExceptionInfo()
|
self.excinfo = ExceptionInfo()
|
||||||
self.stop = time()
|
self.stop = time()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -215,7 +216,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
outcome = "passed"
|
outcome = "passed"
|
||||||
longrepr = None
|
longrepr = None
|
||||||
else:
|
else:
|
||||||
if not isinstance(excinfo, py.code.ExceptionInfo):
|
if not isinstance(excinfo, ExceptionInfo):
|
||||||
outcome = "failed"
|
outcome = "failed"
|
||||||
longrepr = excinfo
|
longrepr = excinfo
|
||||||
elif excinfo.errisinstance(pytest.skip.Exception):
|
elif excinfo.errisinstance(pytest.skip.Exception):
|
||||||
|
|
|
@ -291,9 +291,8 @@ def cached_eval(config, expr, d):
|
||||||
try:
|
try:
|
||||||
return config._evalcache[expr]
|
return config._evalcache[expr]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
#import sys
|
import _pytest._code
|
||||||
#print >>sys.stderr, ("cache-miss: %r" % expr)
|
exprcode = _pytest._code.compile(expr, mode="eval")
|
||||||
exprcode = py.code.compile(expr, mode="eval")
|
|
||||||
config._evalcache[expr] = x = eval(exprcode, d)
|
config._evalcache[expr] = x = eval(exprcode, d)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
""" discovery and running of std-library "unittest" style tests. """
|
""" discovery and running of std-library "unittest" style tests. """
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import traceback
|
|
||||||
import sys
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import py
|
|
||||||
|
|
||||||
|
|
||||||
# for transfering markers
|
# for transfering markers
|
||||||
|
import _pytest._code
|
||||||
from _pytest.python import transfer_markers
|
from _pytest.python import transfer_markers
|
||||||
from _pytest.skipping import MarkEvaluator
|
from _pytest.skipping import MarkEvaluator
|
||||||
|
|
||||||
|
@ -101,7 +100,7 @@ class TestCaseFunction(pytest.Function):
|
||||||
# unwrap potential exception info (see twisted trial support below)
|
# unwrap potential exception info (see twisted trial support below)
|
||||||
rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo)
|
rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo)
|
||||||
try:
|
try:
|
||||||
excinfo = py.code.ExceptionInfo(rawexcinfo)
|
excinfo = _pytest._code.ExceptionInfo(rawexcinfo)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
@ -117,7 +116,7 @@ class TestCaseFunction(pytest.Function):
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except pytest.fail.Exception:
|
except pytest.fail.Exception:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
self.__dict__.setdefault('_excinfo', []).append(excinfo)
|
self.__dict__.setdefault('_excinfo', []).append(excinfo)
|
||||||
|
|
||||||
def addError(self, testcase, rawexcinfo):
|
def addError(self, testcase, rawexcinfo):
|
||||||
|
|
|
@ -81,13 +81,10 @@ and if you need to have access to the actual exception info you may use::
|
||||||
f()
|
f()
|
||||||
assert 'maximum recursion' in str(excinfo.value)
|
assert 'maximum recursion' in str(excinfo.value)
|
||||||
|
|
||||||
``excinfo`` is a `py.code.ExceptionInfo`_ instance, which is a wrapper around
|
``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around
|
||||||
the actual exception raised. The main attributes of interest are
|
the actual exception raised. The main attributes of interest are
|
||||||
``.type``, ``.value`` and ``.traceback``.
|
``.type``, ``.value`` and ``.traceback``.
|
||||||
|
|
||||||
.. _py.code.ExceptionInfo:
|
|
||||||
http://pylib.readthedocs.org/en/latest/code.html#py-code-exceptioninfo
|
|
||||||
|
|
||||||
If you want to write test code that works on Python 2.4 as well,
|
If you want to write test code that works on Python 2.4 as well,
|
||||||
you may also use two other ways to test for an expected exception::
|
you may also use two other ways to test for an expected exception::
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
|
|
||||||
def otherfunc(a,b):
|
def otherfunc(a,b):
|
||||||
|
@ -159,7 +160,7 @@ def test_dynamic_compile_shows_nicely():
|
||||||
src = 'def foo():\n assert 1 == 0\n'
|
src = 'def foo():\n assert 1 == 0\n'
|
||||||
name = 'abc-123'
|
name = 'abc-123'
|
||||||
module = py.std.imp.new_module(name)
|
module = py.std.imp.new_module(name)
|
||||||
code = py.code.compile(src, name, 'exec')
|
code = _pytest._code.compile(src, name, 'exec')
|
||||||
py.builtin.exec_(code, module.__dict__)
|
py.builtin.exec_(code, module.__dict__)
|
||||||
py.std.sys.modules[name] = module
|
py.std.sys.modules[name] = module
|
||||||
module.foo()
|
module.foo()
|
||||||
|
|
|
@ -4,6 +4,7 @@ serialization via the pickle module.
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
|
import _pytest._code
|
||||||
|
|
||||||
pythonlist = ['python2.6', 'python2.7', 'python3.3']
|
pythonlist = ['python2.6', 'python2.7', 'python3.3']
|
||||||
@pytest.fixture(params=pythonlist)
|
@pytest.fixture(params=pythonlist)
|
||||||
|
@ -23,7 +24,7 @@ class Python:
|
||||||
self.picklefile = picklefile
|
self.picklefile = picklefile
|
||||||
def dumps(self, obj):
|
def dumps(self, obj):
|
||||||
dumpfile = self.picklefile.dirpath("dump.py")
|
dumpfile = self.picklefile.dirpath("dump.py")
|
||||||
dumpfile.write(py.code.Source("""
|
dumpfile.write(_pytest._code.Source("""
|
||||||
import pickle
|
import pickle
|
||||||
f = open(%r, 'wb')
|
f = open(%r, 'wb')
|
||||||
s = pickle.dump(%r, f, protocol=2)
|
s = pickle.dump(%r, f, protocol=2)
|
||||||
|
@ -33,7 +34,7 @@ class Python:
|
||||||
|
|
||||||
def load_and_is_true(self, expression):
|
def load_and_is_true(self, expression):
|
||||||
loadfile = self.picklefile.dirpath("load.py")
|
loadfile = self.picklefile.dirpath("load.py")
|
||||||
loadfile.write(py.code.Source("""
|
loadfile.write(_pytest._code.Source("""
|
||||||
import pickle
|
import pickle
|
||||||
f = open(%r, 'rb')
|
f = open(%r, 'rb')
|
||||||
obj = pickle.load(f)
|
obj = pickle.load(f)
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -75,7 +75,7 @@ def main():
|
||||||
# the following should be enabled for release
|
# the following should be enabled for release
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
extras_require=extras_require,
|
extras_require=extras_require,
|
||||||
packages=['_pytest', '_pytest.assertion', '_pytest.vendored_packages'],
|
packages=['_pytest', '_pytest.assertion', '_pytest._code', '_pytest.vendored_packages'],
|
||||||
py_modules=['pytest'],
|
py_modules=['pytest'],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import sys
|
import sys
|
||||||
import py, pytest
|
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
|
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
|
||||||
|
|
||||||
|
|
||||||
|
@ -197,7 +200,7 @@ class TestGeneralUsage:
|
||||||
def test_chdir(self, testdir):
|
def test_chdir(self, testdir):
|
||||||
testdir.tmpdir.join("py").mksymlinkto(py._pydir)
|
testdir.tmpdir.join("py").mksymlinkto(py._pydir)
|
||||||
p = testdir.tmpdir.join("main.py")
|
p = testdir.tmpdir.join("main.py")
|
||||||
p.write(py.code.Source("""
|
p.write(_pytest._code.Source("""
|
||||||
import sys, os
|
import sys, os
|
||||||
sys.path.insert(0, '')
|
sys.path.insert(0, '')
|
||||||
import py
|
import py
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def test_ne():
|
||||||
|
code1 = _pytest._code.Code(compile('foo = "bar"', '', 'exec'))
|
||||||
|
assert code1 == code1
|
||||||
|
code2 = _pytest._code.Code(compile('foo = "baz"', '', 'exec'))
|
||||||
|
assert code2 != code1
|
||||||
|
|
||||||
|
def test_code_gives_back_name_for_not_existing_file():
|
||||||
|
name = 'abc-123'
|
||||||
|
co_code = compile("pass\n", name, 'exec')
|
||||||
|
assert co_code.co_filename == name
|
||||||
|
code = _pytest._code.Code(co_code)
|
||||||
|
assert str(code.path) == name
|
||||||
|
assert code.fullsource is None
|
||||||
|
|
||||||
|
def test_code_with_class():
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
pytest.raises(TypeError, "_pytest._code.Code(A)")
|
||||||
|
|
||||||
|
if True:
|
||||||
|
def x():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_code_fullsource():
|
||||||
|
code = _pytest._code.Code(x)
|
||||||
|
full = code.fullsource
|
||||||
|
assert 'test_code_fullsource()' in str(full)
|
||||||
|
|
||||||
|
def test_code_source():
|
||||||
|
code = _pytest._code.Code(x)
|
||||||
|
src = code.source()
|
||||||
|
expected = """def x():
|
||||||
|
pass"""
|
||||||
|
assert str(src) == expected
|
||||||
|
|
||||||
|
def test_frame_getsourcelineno_myself():
|
||||||
|
def func():
|
||||||
|
return sys._getframe(0)
|
||||||
|
f = func()
|
||||||
|
f = _pytest._code.Frame(f)
|
||||||
|
source, lineno = f.code.fullsource, f.lineno
|
||||||
|
assert source[lineno].startswith(" return sys._getframe(0)")
|
||||||
|
|
||||||
|
def test_getstatement_empty_fullsource():
|
||||||
|
def func():
|
||||||
|
return sys._getframe(0)
|
||||||
|
f = func()
|
||||||
|
f = _pytest._code.Frame(f)
|
||||||
|
prop = f.code.__class__.fullsource
|
||||||
|
try:
|
||||||
|
f.code.__class__.fullsource = None
|
||||||
|
assert f.statement == _pytest._code.Source("")
|
||||||
|
finally:
|
||||||
|
f.code.__class__.fullsource = prop
|
||||||
|
|
||||||
|
def test_code_from_func():
|
||||||
|
co = _pytest._code.Code(test_frame_getsourcelineno_myself)
|
||||||
|
assert co.firstlineno
|
||||||
|
assert co.path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_builtin_patch_unpatch(monkeypatch):
|
||||||
|
cpy_builtin = py.builtin.builtins
|
||||||
|
comp = cpy_builtin.compile
|
||||||
|
def mycompile(*args, **kwargs):
|
||||||
|
return comp(*args, **kwargs)
|
||||||
|
class Sub(AssertionError):
|
||||||
|
pass
|
||||||
|
monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub)
|
||||||
|
monkeypatch.setattr(cpy_builtin, 'compile', mycompile)
|
||||||
|
_pytest._code.patch_builtins()
|
||||||
|
assert cpy_builtin.AssertionError != Sub
|
||||||
|
assert cpy_builtin.compile != mycompile
|
||||||
|
_pytest._code.unpatch_builtins()
|
||||||
|
assert cpy_builtin.AssertionError is Sub
|
||||||
|
assert cpy_builtin.compile == mycompile
|
||||||
|
|
||||||
|
|
||||||
|
def test_unicode_handling():
|
||||||
|
value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
|
||||||
|
def f():
|
||||||
|
raise Exception(value)
|
||||||
|
excinfo = pytest.raises(Exception, f)
|
||||||
|
str(excinfo)
|
||||||
|
if sys.version_info[0] < 3:
|
||||||
|
unicode(excinfo)
|
||||||
|
|
||||||
|
def test_code_getargs():
|
||||||
|
def f1(x):
|
||||||
|
pass
|
||||||
|
c1 = _pytest._code.Code(f1)
|
||||||
|
assert c1.getargs(var=True) == ('x',)
|
||||||
|
|
||||||
|
def f2(x, *y):
|
||||||
|
pass
|
||||||
|
c2 = _pytest._code.Code(f2)
|
||||||
|
assert c2.getargs(var=True) == ('x', 'y')
|
||||||
|
|
||||||
|
def f3(x, **z):
|
||||||
|
pass
|
||||||
|
c3 = _pytest._code.Code(f3)
|
||||||
|
assert c3.getargs(var=True) == ('x', 'z')
|
||||||
|
|
||||||
|
def f4(x, *y, **z):
|
||||||
|
pass
|
||||||
|
c4 = _pytest._code.Code(f4)
|
||||||
|
assert c4.getargs(var=True) == ('x', 'y', 'z')
|
||||||
|
|
||||||
|
|
||||||
|
def test_frame_getargs():
|
||||||
|
def f1(x):
|
||||||
|
return sys._getframe(0)
|
||||||
|
fr1 = _pytest._code.Frame(f1('a'))
|
||||||
|
assert fr1.getargs(var=True) == [('x', 'a')]
|
||||||
|
|
||||||
|
def f2(x, *y):
|
||||||
|
return sys._getframe(0)
|
||||||
|
fr2 = _pytest._code.Frame(f2('a', 'b', 'c'))
|
||||||
|
assert fr2.getargs(var=True) == [('x', 'a'), ('y', ('b', 'c'))]
|
||||||
|
|
||||||
|
def f3(x, **z):
|
||||||
|
return sys._getframe(0)
|
||||||
|
fr3 = _pytest._code.Frame(f3('a', b='c'))
|
||||||
|
assert fr3.getargs(var=True) == [('x', 'a'), ('z', {'b': 'c'})]
|
||||||
|
|
||||||
|
def f4(x, *y, **z):
|
||||||
|
return sys._getframe(0)
|
||||||
|
fr4 = _pytest._code.Frame(f4('a', 'b', c='d'))
|
||||||
|
assert fr4.getargs(var=True) == [('x', 'a'), ('y', ('b',)),
|
||||||
|
('z', {'c': 'd'})]
|
||||||
|
|
||||||
|
|
||||||
|
class TestExceptionInfo:
|
||||||
|
|
||||||
|
def test_bad_getsource(self):
|
||||||
|
try:
|
||||||
|
if False: pass
|
||||||
|
else: assert False
|
||||||
|
except AssertionError:
|
||||||
|
exci = _pytest._code.ExceptionInfo()
|
||||||
|
assert exci.getrepr()
|
||||||
|
|
||||||
|
|
||||||
|
class TestTracebackEntry:
|
||||||
|
|
||||||
|
def test_getsource(self):
|
||||||
|
try:
|
||||||
|
if False: pass
|
||||||
|
else: assert False
|
||||||
|
except AssertionError:
|
||||||
|
exci = _pytest._code.ExceptionInfo()
|
||||||
|
entry = exci.traceback[0]
|
||||||
|
source = entry.getsource()
|
||||||
|
assert len(source) == 4
|
||||||
|
assert 'else: assert False' in source[3]
|
|
@ -0,0 +1,911 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import _pytest
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
|
from _pytest._code.code import FormattedExcinfo, ReprExceptionInfo
|
||||||
|
|
||||||
|
queue = py.builtin._tryimport('queue', 'Queue')
|
||||||
|
|
||||||
|
failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
|
||||||
|
from test_source import astonly
|
||||||
|
|
||||||
|
try:
|
||||||
|
import importlib
|
||||||
|
except ImportError:
|
||||||
|
invalidate_import_caches = None
|
||||||
|
else:
|
||||||
|
invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
|
||||||
|
|
||||||
|
class TWMock:
|
||||||
|
def __init__(self):
|
||||||
|
self.lines = []
|
||||||
|
def sep(self, sep, line=None):
|
||||||
|
self.lines.append((sep, line))
|
||||||
|
def line(self, line, **kw):
|
||||||
|
self.lines.append(line)
|
||||||
|
def markup(self, text, **kw):
|
||||||
|
return text
|
||||||
|
|
||||||
|
fullwidth = 80
|
||||||
|
|
||||||
|
def test_excinfo_simple():
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
info = _pytest._code.ExceptionInfo()
|
||||||
|
assert info.type == ValueError
|
||||||
|
|
||||||
|
def test_excinfo_getstatement():
|
||||||
|
def g():
|
||||||
|
raise ValueError
|
||||||
|
def f():
|
||||||
|
g()
|
||||||
|
try:
|
||||||
|
f()
|
||||||
|
except ValueError:
|
||||||
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 3,
|
||||||
|
_pytest._code.getrawcode(f).co_firstlineno - 1 + 1,
|
||||||
|
_pytest._code.getrawcode(g).co_firstlineno - 1 + 1, ]
|
||||||
|
l = list(excinfo.traceback)
|
||||||
|
foundlinenumbers = [x.lineno for x in l]
|
||||||
|
assert foundlinenumbers == linenumbers
|
||||||
|
#for x in info:
|
||||||
|
# print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement)
|
||||||
|
#xxx
|
||||||
|
|
||||||
|
# testchain for getentries test below
|
||||||
|
def f():
|
||||||
|
#
|
||||||
|
raise ValueError
|
||||||
|
#
|
||||||
|
def g():
|
||||||
|
#
|
||||||
|
__tracebackhide__ = True
|
||||||
|
f()
|
||||||
|
#
|
||||||
|
def h():
|
||||||
|
#
|
||||||
|
g()
|
||||||
|
#
|
||||||
|
|
||||||
|
class TestTraceback_f_g_h:
|
||||||
|
def setup_method(self, method):
|
||||||
|
try:
|
||||||
|
h()
|
||||||
|
except ValueError:
|
||||||
|
self.excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
|
||||||
|
def test_traceback_entries(self):
|
||||||
|
tb = self.excinfo.traceback
|
||||||
|
entries = list(tb)
|
||||||
|
assert len(tb) == 4 # maybe fragile test
|
||||||
|
assert len(entries) == 4 # maybe fragile test
|
||||||
|
names = ['f', 'g', 'h']
|
||||||
|
for entry in entries:
|
||||||
|
try:
|
||||||
|
names.remove(entry.frame.code.name)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
assert not names
|
||||||
|
|
||||||
|
def test_traceback_entry_getsource(self):
|
||||||
|
tb = self.excinfo.traceback
|
||||||
|
s = str(tb[-1].getsource() )
|
||||||
|
assert s.startswith("def f():")
|
||||||
|
assert s.endswith("raise ValueError")
|
||||||
|
|
||||||
|
@astonly
|
||||||
|
@failsonjython
|
||||||
|
def test_traceback_entry_getsource_in_construct(self):
|
||||||
|
source = _pytest._code.Source("""\
|
||||||
|
def xyz():
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except somenoname:
|
||||||
|
pass
|
||||||
|
xyz()
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
exec (source.compile())
|
||||||
|
except NameError:
|
||||||
|
tb = _pytest._code.ExceptionInfo().traceback
|
||||||
|
print (tb[-1].getsource())
|
||||||
|
s = str(tb[-1].getsource())
|
||||||
|
assert s.startswith("def xyz():\n try:")
|
||||||
|
assert s.strip().endswith("except somenoname:")
|
||||||
|
|
||||||
|
def test_traceback_cut(self):
|
||||||
|
co = _pytest._code.Code(f)
|
||||||
|
path, firstlineno = co.path, co.firstlineno
|
||||||
|
traceback = self.excinfo.traceback
|
||||||
|
newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
||||||
|
assert len(newtraceback) == 1
|
||||||
|
newtraceback = traceback.cut(path=path, lineno=firstlineno+2)
|
||||||
|
assert len(newtraceback) == 1
|
||||||
|
|
||||||
|
def test_traceback_cut_excludepath(self, testdir):
|
||||||
|
p = testdir.makepyfile("def f(): raise ValueError")
|
||||||
|
excinfo = pytest.raises(ValueError, "p.pyimport().f()")
|
||||||
|
basedir = py.path.local(pytest.__file__).dirpath()
|
||||||
|
newtraceback = excinfo.traceback.cut(excludepath=basedir)
|
||||||
|
for x in newtraceback:
|
||||||
|
if hasattr(x, 'path'):
|
||||||
|
assert not py.path.local(x.path).relto(basedir)
|
||||||
|
assert newtraceback[-1].frame.code.path == p
|
||||||
|
|
||||||
|
def test_traceback_filter(self):
|
||||||
|
traceback = self.excinfo.traceback
|
||||||
|
ntraceback = traceback.filter()
|
||||||
|
assert len(ntraceback) == len(traceback) - 1
|
||||||
|
|
||||||
|
def test_traceback_recursion_index(self):
|
||||||
|
def f(n):
|
||||||
|
if n < 10:
|
||||||
|
n += 1
|
||||||
|
f(n)
|
||||||
|
excinfo = pytest.raises(RuntimeError, f, 8)
|
||||||
|
traceback = excinfo.traceback
|
||||||
|
recindex = traceback.recursionindex()
|
||||||
|
assert recindex == 3
|
||||||
|
|
||||||
|
def test_traceback_only_specific_recursion_errors(self, monkeypatch):
|
||||||
|
def f(n):
|
||||||
|
if n == 0:
|
||||||
|
raise RuntimeError("hello")
|
||||||
|
f(n-1)
|
||||||
|
|
||||||
|
excinfo = pytest.raises(RuntimeError, f, 100)
|
||||||
|
monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
|
||||||
|
repr = excinfo.getrepr()
|
||||||
|
assert "RuntimeError: hello" in str(repr.reprcrash)
|
||||||
|
|
||||||
|
def test_traceback_no_recursion_index(self):
|
||||||
|
def do_stuff():
|
||||||
|
raise RuntimeError
|
||||||
|
def reraise_me():
|
||||||
|
import sys
|
||||||
|
exc, val, tb = sys.exc_info()
|
||||||
|
py.builtin._reraise(exc, val, tb)
|
||||||
|
def f(n):
|
||||||
|
try:
|
||||||
|
do_stuff()
|
||||||
|
except:
|
||||||
|
reraise_me()
|
||||||
|
excinfo = pytest.raises(RuntimeError, f, 8)
|
||||||
|
traceback = excinfo.traceback
|
||||||
|
recindex = traceback.recursionindex()
|
||||||
|
assert recindex is None
|
||||||
|
|
||||||
|
def test_traceback_messy_recursion(self):
|
||||||
|
#XXX: simplified locally testable version
|
||||||
|
decorator = pytest.importorskip('decorator').decorator
|
||||||
|
|
||||||
|
def log(f, *k, **kw):
|
||||||
|
print('%s %s' % (k, kw))
|
||||||
|
f(*k, **kw)
|
||||||
|
log = decorator(log)
|
||||||
|
|
||||||
|
def fail():
|
||||||
|
raise ValueError('')
|
||||||
|
|
||||||
|
fail = log(log(fail))
|
||||||
|
|
||||||
|
excinfo = pytest.raises(ValueError, fail)
|
||||||
|
assert excinfo.traceback.recursionindex() is None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_traceback_getcrashentry(self):
|
||||||
|
def i():
|
||||||
|
__tracebackhide__ = True
|
||||||
|
raise ValueError
|
||||||
|
def h():
|
||||||
|
i()
|
||||||
|
def g():
|
||||||
|
__tracebackhide__ = True
|
||||||
|
h()
|
||||||
|
def f():
|
||||||
|
g()
|
||||||
|
|
||||||
|
excinfo = pytest.raises(ValueError, f)
|
||||||
|
tb = excinfo.traceback
|
||||||
|
entry = tb.getcrashentry()
|
||||||
|
co = _pytest._code.Code(h)
|
||||||
|
assert entry.frame.code.path == co.path
|
||||||
|
assert entry.lineno == co.firstlineno + 1
|
||||||
|
assert entry.frame.code.name == 'h'
|
||||||
|
|
||||||
|
def test_traceback_getcrashentry_empty(self):
|
||||||
|
def g():
|
||||||
|
__tracebackhide__ = True
|
||||||
|
raise ValueError
|
||||||
|
def f():
|
||||||
|
__tracebackhide__ = True
|
||||||
|
g()
|
||||||
|
|
||||||
|
excinfo = pytest.raises(ValueError, f)
|
||||||
|
tb = excinfo.traceback
|
||||||
|
entry = tb.getcrashentry()
|
||||||
|
co = _pytest._code.Code(g)
|
||||||
|
assert entry.frame.code.path == co.path
|
||||||
|
assert entry.lineno == co.firstlineno + 2
|
||||||
|
assert entry.frame.code.name == 'g'
|
||||||
|
|
||||||
|
def hello(x):
|
||||||
|
x + 5
|
||||||
|
|
||||||
|
def test_tbentry_reinterpret():
|
||||||
|
try:
|
||||||
|
hello("hello")
|
||||||
|
except TypeError:
|
||||||
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
tbentry = excinfo.traceback[-1]
|
||||||
|
msg = tbentry.reinterpret()
|
||||||
|
assert msg.startswith("TypeError: ('hello' + 5)")
|
||||||
|
|
||||||
|
def test_excinfo_exconly():
|
||||||
|
excinfo = pytest.raises(ValueError, h)
|
||||||
|
assert excinfo.exconly().startswith('ValueError')
|
||||||
|
excinfo = pytest.raises(ValueError,
|
||||||
|
"raise ValueError('hello\\nworld')")
|
||||||
|
msg = excinfo.exconly(tryshort=True)
|
||||||
|
assert msg.startswith('ValueError')
|
||||||
|
assert msg.endswith("world")
|
||||||
|
|
||||||
|
def test_excinfo_repr():
|
||||||
|
excinfo = pytest.raises(ValueError, h)
|
||||||
|
s = repr(excinfo)
|
||||||
|
assert s == "<ExceptionInfo ValueError tblen=4>"
|
||||||
|
|
||||||
|
def test_excinfo_str():
|
||||||
|
excinfo = pytest.raises(ValueError, h)
|
||||||
|
s = str(excinfo)
|
||||||
|
assert s.startswith(__file__[:-9]) # pyc file and $py.class
|
||||||
|
assert s.endswith("ValueError")
|
||||||
|
assert len(s.split(":")) >= 3 # on windows it's 4
|
||||||
|
|
||||||
|
def test_excinfo_errisinstance():
|
||||||
|
excinfo = pytest.raises(ValueError, h)
|
||||||
|
assert excinfo.errisinstance(ValueError)
|
||||||
|
|
||||||
|
def test_excinfo_no_sourcecode():
|
||||||
|
try:
|
||||||
|
exec ("raise ValueError()")
|
||||||
|
except ValueError:
|
||||||
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
s = str(excinfo.traceback[-1])
|
||||||
|
if py.std.sys.version_info < (2,5):
|
||||||
|
assert s == " File '<string>':1 in ?\n ???\n"
|
||||||
|
else:
|
||||||
|
assert s == " File '<string>':1 in <module>\n ???\n"
|
||||||
|
|
||||||
|
def test_excinfo_no_python_sourcecode(tmpdir):
|
||||||
|
#XXX: simplified locally testable version
|
||||||
|
tmpdir.join('test.txt').write("{{ h()}}:")
|
||||||
|
|
||||||
|
jinja2 = pytest.importorskip('jinja2')
|
||||||
|
loader = jinja2.FileSystemLoader(str(tmpdir))
|
||||||
|
env = jinja2.Environment(loader=loader)
|
||||||
|
template = env.get_template('test.txt')
|
||||||
|
excinfo = pytest.raises(ValueError,
|
||||||
|
template.render, h=h)
|
||||||
|
for item in excinfo.traceback:
|
||||||
|
print(item) #XXX: for some reason jinja.Template.render is printed in full
|
||||||
|
item.source # shouldnt fail
|
||||||
|
if item.path.basename == 'test.txt':
|
||||||
|
assert str(item.source) == '{{ h()}}:'
|
||||||
|
|
||||||
|
|
||||||
|
def test_entrysource_Queue_example():
|
||||||
|
try:
|
||||||
|
queue.Queue().get(timeout=0.001)
|
||||||
|
except queue.Empty:
|
||||||
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
entry = excinfo.traceback[-1]
|
||||||
|
source = entry.getsource()
|
||||||
|
assert source is not None
|
||||||
|
s = str(source).strip()
|
||||||
|
assert s.startswith("def get")
|
||||||
|
|
||||||
|
def test_codepath_Queue_example():
|
||||||
|
try:
|
||||||
|
queue.Queue().get(timeout=0.001)
|
||||||
|
except queue.Empty:
|
||||||
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
entry = excinfo.traceback[-1]
|
||||||
|
path = entry.path
|
||||||
|
assert isinstance(path, py.path.local)
|
||||||
|
assert path.basename.lower() == "queue.py"
|
||||||
|
assert path.check()
|
||||||
|
|
||||||
|
class TestFormattedExcinfo:
|
||||||
|
def pytest_funcarg__importasmod(self, request):
|
||||||
|
def importasmod(source):
|
||||||
|
source = _pytest._code.Source(source)
|
||||||
|
tmpdir = request.getfuncargvalue("tmpdir")
|
||||||
|
modpath = tmpdir.join("mod.py")
|
||||||
|
tmpdir.ensure("__init__.py")
|
||||||
|
modpath.write(source)
|
||||||
|
if invalidate_import_caches is not None:
|
||||||
|
invalidate_import_caches()
|
||||||
|
return modpath.pyimport()
|
||||||
|
return importasmod
|
||||||
|
|
||||||
|
def excinfo_from_exec(self, source):
|
||||||
|
source = _pytest._code.Source(source).strip()
|
||||||
|
try:
|
||||||
|
exec (source.compile())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
return _pytest._code.ExceptionInfo()
|
||||||
|
assert 0, "did not raise"
|
||||||
|
|
||||||
|
def test_repr_source(self):
|
||||||
|
pr = FormattedExcinfo()
|
||||||
|
source = _pytest._code.Source("""
|
||||||
|
def f(x):
|
||||||
|
pass
|
||||||
|
""").strip()
|
||||||
|
pr.flow_marker = "|"
|
||||||
|
lines = pr.get_source(source, 0)
|
||||||
|
assert len(lines) == 2
|
||||||
|
assert lines[0] == "| def f(x):"
|
||||||
|
assert lines[1] == " pass"
|
||||||
|
|
||||||
|
def test_repr_source_excinfo(self):
|
||||||
|
""" check if indentation is right """
|
||||||
|
pr = FormattedExcinfo()
|
||||||
|
excinfo = self.excinfo_from_exec("""
|
||||||
|
def f():
|
||||||
|
assert 0
|
||||||
|
f()
|
||||||
|
""")
|
||||||
|
pr = FormattedExcinfo()
|
||||||
|
source = pr._getentrysource(excinfo.traceback[-1])
|
||||||
|
lines = pr.get_source(source, 1, excinfo)
|
||||||
|
assert lines == [
|
||||||
|
' def f():',
|
||||||
|
'> assert 0',
|
||||||
|
'E assert 0'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_repr_source_not_existing(self):
|
||||||
|
pr = FormattedExcinfo()
|
||||||
|
co = compile("raise ValueError()", "", "exec")
|
||||||
|
try:
|
||||||
|
exec (co)
|
||||||
|
except ValueError:
|
||||||
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
repr = pr.repr_excinfo(excinfo)
|
||||||
|
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
|
||||||
|
|
||||||
|
def test_repr_many_line_source_not_existing(self):
|
||||||
|
pr = FormattedExcinfo()
|
||||||
|
co = compile("""
|
||||||
|
a = 1
|
||||||
|
raise ValueError()
|
||||||
|
""", "", "exec")
|
||||||
|
try:
|
||||||
|
exec (co)
|
||||||
|
except ValueError:
|
||||||
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
|
repr = pr.repr_excinfo(excinfo)
|
||||||
|
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
|
||||||
|
|
||||||
|
def test_repr_source_failing_fullsource(self):
|
||||||
|
pr = FormattedExcinfo()
|
||||||
|
|
||||||
|
class FakeCode(object):
|
||||||
|
class raw:
|
||||||
|
co_filename = '?'
|
||||||
|
path = '?'
|
||||||
|
firstlineno = 5
|
||||||
|
|
||||||
|
def fullsource(self):
|
||||||
|
return None
|
||||||
|
fullsource = property(fullsource)
|
||||||
|
|
||||||
|
class FakeFrame(object):
|
||||||
|
code = FakeCode()
|
||||||
|
f_locals = {}
|
||||||
|
f_globals = {}
|
||||||
|
|
||||||
|
class FakeTracebackEntry(_pytest._code.Traceback.Entry):
|
||||||
|
def __init__(self, tb):
|
||||||
|
self.lineno = 5+3
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frame(self):
|
||||||
|
return FakeFrame()
|
||||||
|
|
||||||
|
class Traceback(_pytest._code.Traceback):
|
||||||
|
Entry = FakeTracebackEntry
|
||||||
|
|
||||||
|
class FakeExcinfo(_pytest._code.ExceptionInfo):
|
||||||
|
typename = "Foo"
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exconly(self, tryshort):
|
||||||
|
return "EXC"
|
||||||
|
def errisinstance(self, cls):
|
||||||
|
return False
|
||||||
|
|
||||||
|
excinfo = FakeExcinfo()
|
||||||
|
class FakeRawTB(object):
|
||||||
|
tb_next = None
|
||||||
|
tb = FakeRawTB()
|
||||||
|
excinfo.traceback = Traceback(tb)
|
||||||
|
|
||||||
|
fail = IOError() # noqa
|
||||||
|
repr = pr.repr_excinfo(excinfo)
|
||||||
|
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
|
||||||
|
|
||||||
|
fail = py.error.ENOENT # noqa
|
||||||
|
repr = pr.repr_excinfo(excinfo)
|
||||||
|
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
|
||||||
|
|
||||||
|
|
||||||
|
def test_repr_local(self):
|
||||||
|
p = FormattedExcinfo(showlocals=True)
|
||||||
|
loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}}
|
||||||
|
reprlocals = p.repr_locals(loc)
|
||||||
|
assert reprlocals.lines
|
||||||
|
assert reprlocals.lines[0] == '__builtins__ = <builtins>'
|
||||||
|
assert reprlocals.lines[1] == 'x = 3'
|
||||||
|
assert reprlocals.lines[2] == 'y = 5'
|
||||||
|
assert reprlocals.lines[3] == 'z = 7'
|
||||||
|
|
||||||
|
def test_repr_tracebackentry_lines(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def func1():
|
||||||
|
raise ValueError("hello\\nworld")
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.func1)
|
||||||
|
excinfo.traceback = excinfo.traceback.filter()
|
||||||
|
p = FormattedExcinfo()
|
||||||
|
reprtb = p.repr_traceback_entry(excinfo.traceback[-1])
|
||||||
|
|
||||||
|
# test as intermittent entry
|
||||||
|
lines = reprtb.lines
|
||||||
|
assert lines[0] == ' def func1():'
|
||||||
|
assert lines[1] == '> raise ValueError("hello\\nworld")'
|
||||||
|
|
||||||
|
# test as last entry
|
||||||
|
p = FormattedExcinfo(showlocals=True)
|
||||||
|
repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
||||||
|
lines = repr_entry.lines
|
||||||
|
assert lines[0] == ' def func1():'
|
||||||
|
assert lines[1] == '> raise ValueError("hello\\nworld")'
|
||||||
|
assert lines[2] == 'E ValueError: hello'
|
||||||
|
assert lines[3] == 'E world'
|
||||||
|
assert not lines[4:]
|
||||||
|
|
||||||
|
loc = repr_entry.reprlocals is not None
|
||||||
|
loc = repr_entry.reprfileloc
|
||||||
|
assert loc.path == mod.__file__
|
||||||
|
assert loc.lineno == 3
|
||||||
|
#assert loc.message == "ValueError: hello"
|
||||||
|
|
||||||
|
def test_repr_tracebackentry_lines2(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def func1(m, x, y, z):
|
||||||
|
raise ValueError("hello\\nworld")
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.func1, "m"*90, 5, 13, "z"*120)
|
||||||
|
excinfo.traceback = excinfo.traceback.filter()
|
||||||
|
entry = excinfo.traceback[-1]
|
||||||
|
p = FormattedExcinfo(funcargs=True)
|
||||||
|
reprfuncargs = p.repr_args(entry)
|
||||||
|
assert reprfuncargs.args[0] == ('m', repr("m"*90))
|
||||||
|
assert reprfuncargs.args[1] == ('x', '5')
|
||||||
|
assert reprfuncargs.args[2] == ('y', '13')
|
||||||
|
assert reprfuncargs.args[3] == ('z', repr("z" * 120))
|
||||||
|
|
||||||
|
p = FormattedExcinfo(funcargs=True)
|
||||||
|
repr_entry = p.repr_traceback_entry(entry)
|
||||||
|
assert repr_entry.reprfuncargs.args == reprfuncargs.args
|
||||||
|
tw = TWMock()
|
||||||
|
repr_entry.toterminal(tw)
|
||||||
|
assert tw.lines[0] == "m = " + repr('m' * 90)
|
||||||
|
assert tw.lines[1] == "x = 5, y = 13"
|
||||||
|
assert tw.lines[2] == "z = " + repr('z' * 120)
|
||||||
|
|
||||||
|
def test_repr_tracebackentry_lines_var_kw_args(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def func1(x, *y, **z):
|
||||||
|
raise ValueError("hello\\nworld")
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.func1, 'a', 'b', c='d')
|
||||||
|
excinfo.traceback = excinfo.traceback.filter()
|
||||||
|
entry = excinfo.traceback[-1]
|
||||||
|
p = FormattedExcinfo(funcargs=True)
|
||||||
|
reprfuncargs = p.repr_args(entry)
|
||||||
|
assert reprfuncargs.args[0] == ('x', repr('a'))
|
||||||
|
assert reprfuncargs.args[1] == ('y', repr(('b',)))
|
||||||
|
assert reprfuncargs.args[2] == ('z', repr({'c': 'd'}))
|
||||||
|
|
||||||
|
p = FormattedExcinfo(funcargs=True)
|
||||||
|
repr_entry = p.repr_traceback_entry(entry)
|
||||||
|
assert repr_entry.reprfuncargs.args == reprfuncargs.args
|
||||||
|
tw = TWMock()
|
||||||
|
repr_entry.toterminal(tw)
|
||||||
|
assert tw.lines[0] == "x = 'a', y = ('b',), z = {'c': 'd'}"
|
||||||
|
|
||||||
|
def test_repr_tracebackentry_short(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def func1():
|
||||||
|
raise ValueError("hello")
|
||||||
|
def entry():
|
||||||
|
func1()
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
p = FormattedExcinfo(style="short")
|
||||||
|
reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
|
||||||
|
lines = reprtb.lines
|
||||||
|
basename = py.path.local(mod.__file__).basename
|
||||||
|
assert lines[0] == ' func1()'
|
||||||
|
assert basename in str(reprtb.reprfileloc.path)
|
||||||
|
assert reprtb.reprfileloc.lineno == 5
|
||||||
|
|
||||||
|
# test last entry
|
||||||
|
p = FormattedExcinfo(style="short")
|
||||||
|
reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
||||||
|
lines = reprtb.lines
|
||||||
|
assert lines[0] == ' raise ValueError("hello")'
|
||||||
|
assert lines[1] == 'E ValueError: hello'
|
||||||
|
assert basename in str(reprtb.reprfileloc.path)
|
||||||
|
assert reprtb.reprfileloc.lineno == 3
|
||||||
|
|
||||||
|
def test_repr_tracebackentry_no(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def func1():
|
||||||
|
raise ValueError("hello")
|
||||||
|
def entry():
|
||||||
|
func1()
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
p = FormattedExcinfo(style="no")
|
||||||
|
p.repr_traceback_entry(excinfo.traceback[-2])
|
||||||
|
|
||||||
|
p = FormattedExcinfo(style="no")
|
||||||
|
reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
||||||
|
lines = reprentry.lines
|
||||||
|
assert lines[0] == 'E ValueError: hello'
|
||||||
|
assert not lines[1:]
|
||||||
|
|
||||||
|
def test_repr_traceback_tbfilter(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def f(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def entry():
|
||||||
|
f(0)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
p = FormattedExcinfo(tbfilter=True)
|
||||||
|
reprtb = p.repr_traceback(excinfo)
|
||||||
|
assert len(reprtb.reprentries) == 2
|
||||||
|
p = FormattedExcinfo(tbfilter=False)
|
||||||
|
reprtb = p.repr_traceback(excinfo)
|
||||||
|
assert len(reprtb.reprentries) == 3
|
||||||
|
|
||||||
|
def test_traceback_short_no_source(self, importasmod, monkeypatch):
|
||||||
|
mod = importasmod("""
|
||||||
|
def func1():
|
||||||
|
raise ValueError("hello")
|
||||||
|
def entry():
|
||||||
|
func1()
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
from _pytest._code.code import Code
|
||||||
|
monkeypatch.setattr(Code, 'path', 'bogus')
|
||||||
|
excinfo.traceback[0].frame.code.path = "bogus"
|
||||||
|
p = FormattedExcinfo(style="short")
|
||||||
|
reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
|
||||||
|
lines = reprtb.lines
|
||||||
|
last_p = FormattedExcinfo(style="short")
|
||||||
|
last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
||||||
|
last_lines = last_reprtb.lines
|
||||||
|
monkeypatch.undo()
|
||||||
|
assert lines[0] == ' func1()'
|
||||||
|
|
||||||
|
assert last_lines[0] == ' raise ValueError("hello")'
|
||||||
|
assert last_lines[1] == 'E ValueError: hello'
|
||||||
|
|
||||||
|
def test_repr_traceback_and_excinfo(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def f(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def entry():
|
||||||
|
f(0)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
|
||||||
|
for style in ("long", "short"):
|
||||||
|
p = FormattedExcinfo(style=style)
|
||||||
|
reprtb = p.repr_traceback(excinfo)
|
||||||
|
assert len(reprtb.reprentries) == 2
|
||||||
|
assert reprtb.style == style
|
||||||
|
assert not reprtb.extraline
|
||||||
|
repr = p.repr_excinfo(excinfo)
|
||||||
|
assert repr.reprtraceback
|
||||||
|
assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
|
||||||
|
assert repr.reprcrash.path.endswith("mod.py")
|
||||||
|
assert repr.reprcrash.message == "ValueError: 0"
|
||||||
|
|
||||||
|
def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch):
|
||||||
|
mod = importasmod("""
|
||||||
|
def f(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def entry():
|
||||||
|
f(0)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
|
||||||
|
p = FormattedExcinfo()
|
||||||
|
def raiseos():
|
||||||
|
raise OSError(2)
|
||||||
|
monkeypatch.setattr(py.std.os, 'getcwd', raiseos)
|
||||||
|
assert p._makepath(__file__) == __file__
|
||||||
|
p.repr_traceback(excinfo)
|
||||||
|
|
||||||
|
def test_repr_excinfo_addouterr(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def entry():
|
||||||
|
raise ValueError()
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
repr = excinfo.getrepr()
|
||||||
|
repr.addsection("title", "content")
|
||||||
|
twmock = TWMock()
|
||||||
|
repr.toterminal(twmock)
|
||||||
|
assert twmock.lines[-1] == "content"
|
||||||
|
assert twmock.lines[-2] == ("-", "title")
|
||||||
|
|
||||||
|
def test_repr_excinfo_reprcrash(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def entry():
|
||||||
|
raise ValueError()
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
repr = excinfo.getrepr()
|
||||||
|
assert repr.reprcrash.path.endswith("mod.py")
|
||||||
|
assert repr.reprcrash.lineno == 3
|
||||||
|
assert repr.reprcrash.message == "ValueError"
|
||||||
|
assert str(repr.reprcrash).endswith("mod.py:3: ValueError")
|
||||||
|
|
||||||
|
def test_repr_traceback_recursion(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def rec2(x):
|
||||||
|
return rec1(x+1)
|
||||||
|
def rec1(x):
|
||||||
|
return rec2(x-1)
|
||||||
|
def entry():
|
||||||
|
rec1(42)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(RuntimeError, mod.entry)
|
||||||
|
|
||||||
|
for style in ("short", "long", "no"):
|
||||||
|
p = FormattedExcinfo(style="short")
|
||||||
|
reprtb = p.repr_traceback(excinfo)
|
||||||
|
assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
|
||||||
|
assert str(reprtb)
|
||||||
|
|
||||||
|
def test_tb_entry_AssertionError(self, importasmod):
|
||||||
|
# probably this test is a bit redundant
|
||||||
|
# as py/magic/testing/test_assertion.py
|
||||||
|
# already tests correctness of
|
||||||
|
# assertion-reinterpretation logic
|
||||||
|
mod = importasmod("""
|
||||||
|
def somefunc():
|
||||||
|
x = 1
|
||||||
|
assert x == 2
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(AssertionError, mod.somefunc)
|
||||||
|
|
||||||
|
p = FormattedExcinfo()
|
||||||
|
reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
||||||
|
lines = reprentry.lines
|
||||||
|
assert lines[-1] == "E assert 1 == 2"
|
||||||
|
|
||||||
|
def test_reprexcinfo_getrepr(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def f(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def entry():
|
||||||
|
f(0)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.entry)
|
||||||
|
|
||||||
|
for style in ("short", "long", "no"):
|
||||||
|
for showlocals in (True, False):
|
||||||
|
repr = excinfo.getrepr(style=style, showlocals=showlocals)
|
||||||
|
assert isinstance(repr, ReprExceptionInfo)
|
||||||
|
assert repr.reprtraceback.style == style
|
||||||
|
|
||||||
|
def test_reprexcinfo_unicode(self):
|
||||||
|
from _pytest._code.code import TerminalRepr
|
||||||
|
class MyRepr(TerminalRepr):
|
||||||
|
def toterminal(self, tw):
|
||||||
|
tw.line(py.builtin._totext("я", "utf-8"))
|
||||||
|
x = py.builtin._totext(MyRepr())
|
||||||
|
assert x == py.builtin._totext("я", "utf-8")
|
||||||
|
|
||||||
|
def test_toterminal_long(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def g(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def f():
|
||||||
|
g(3)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
|
excinfo.traceback = excinfo.traceback.filter()
|
||||||
|
repr = excinfo.getrepr()
|
||||||
|
tw = TWMock()
|
||||||
|
repr.toterminal(tw)
|
||||||
|
assert tw.lines[0] == ""
|
||||||
|
tw.lines.pop(0)
|
||||||
|
assert tw.lines[0] == " def f():"
|
||||||
|
assert tw.lines[1] == "> g(3)"
|
||||||
|
assert tw.lines[2] == ""
|
||||||
|
assert tw.lines[3].endswith("mod.py:5: ")
|
||||||
|
assert tw.lines[4] == ("_ ", None)
|
||||||
|
assert tw.lines[5] == ""
|
||||||
|
assert tw.lines[6] == " def g(x):"
|
||||||
|
assert tw.lines[7] == "> raise ValueError(x)"
|
||||||
|
assert tw.lines[8] == "E ValueError: 3"
|
||||||
|
assert tw.lines[9] == ""
|
||||||
|
assert tw.lines[10].endswith("mod.py:3: ValueError")
|
||||||
|
|
||||||
|
def test_toterminal_long_missing_source(self, importasmod, tmpdir):
|
||||||
|
mod = importasmod("""
|
||||||
|
def g(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def f():
|
||||||
|
g(3)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
|
tmpdir.join('mod.py').remove()
|
||||||
|
excinfo.traceback = excinfo.traceback.filter()
|
||||||
|
repr = excinfo.getrepr()
|
||||||
|
tw = TWMock()
|
||||||
|
repr.toterminal(tw)
|
||||||
|
assert tw.lines[0] == ""
|
||||||
|
tw.lines.pop(0)
|
||||||
|
assert tw.lines[0] == "> ???"
|
||||||
|
assert tw.lines[1] == ""
|
||||||
|
assert tw.lines[2].endswith("mod.py:5: ")
|
||||||
|
assert tw.lines[3] == ("_ ", None)
|
||||||
|
assert tw.lines[4] == ""
|
||||||
|
assert tw.lines[5] == "> ???"
|
||||||
|
assert tw.lines[6] == "E ValueError: 3"
|
||||||
|
assert tw.lines[7] == ""
|
||||||
|
assert tw.lines[8].endswith("mod.py:3: ValueError")
|
||||||
|
|
||||||
|
def test_toterminal_long_incomplete_source(self, importasmod, tmpdir):
|
||||||
|
mod = importasmod("""
|
||||||
|
def g(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def f():
|
||||||
|
g(3)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
|
tmpdir.join('mod.py').write('asdf')
|
||||||
|
excinfo.traceback = excinfo.traceback.filter()
|
||||||
|
repr = excinfo.getrepr()
|
||||||
|
tw = TWMock()
|
||||||
|
repr.toterminal(tw)
|
||||||
|
assert tw.lines[0] == ""
|
||||||
|
tw.lines.pop(0)
|
||||||
|
assert tw.lines[0] == "> ???"
|
||||||
|
assert tw.lines[1] == ""
|
||||||
|
assert tw.lines[2].endswith("mod.py:5: ")
|
||||||
|
assert tw.lines[3] == ("_ ", None)
|
||||||
|
assert tw.lines[4] == ""
|
||||||
|
assert tw.lines[5] == "> ???"
|
||||||
|
assert tw.lines[6] == "E ValueError: 3"
|
||||||
|
assert tw.lines[7] == ""
|
||||||
|
assert tw.lines[8].endswith("mod.py:3: ValueError")
|
||||||
|
|
||||||
|
def test_toterminal_long_filenames(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def f():
|
||||||
|
raise ValueError()
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
|
tw = TWMock()
|
||||||
|
path = py.path.local(mod.__file__)
|
||||||
|
old = path.dirpath().chdir()
|
||||||
|
try:
|
||||||
|
repr = excinfo.getrepr(abspath=False)
|
||||||
|
repr.toterminal(tw)
|
||||||
|
line = tw.lines[-1]
|
||||||
|
x = py.path.local().bestrelpath(path)
|
||||||
|
if len(x) < len(str(path)):
|
||||||
|
assert line == "mod.py:3: ValueError"
|
||||||
|
|
||||||
|
repr = excinfo.getrepr(abspath=True)
|
||||||
|
repr.toterminal(tw)
|
||||||
|
line = tw.lines[-1]
|
||||||
|
assert line == "%s:3: ValueError" %(path,)
|
||||||
|
finally:
|
||||||
|
old.chdir()
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('reproptions', [
|
||||||
|
{'style': style, 'showlocals': showlocals,
|
||||||
|
'funcargs': funcargs, 'tbfilter': tbfilter
|
||||||
|
} for style in ("long", "short", "no")
|
||||||
|
for showlocals in (True, False)
|
||||||
|
for tbfilter in (True, False)
|
||||||
|
for funcargs in (True, False)])
|
||||||
|
def test_format_excinfo(self, importasmod, reproptions):
|
||||||
|
mod = importasmod("""
|
||||||
|
def g(x):
|
||||||
|
raise ValueError(x)
|
||||||
|
def f():
|
||||||
|
g(3)
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
|
tw = py.io.TerminalWriter(stringio=True)
|
||||||
|
repr = excinfo.getrepr(**reproptions)
|
||||||
|
repr.toterminal(tw)
|
||||||
|
assert tw.stringio.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def test_native_style(self):
|
||||||
|
excinfo = self.excinfo_from_exec("""
|
||||||
|
assert 0
|
||||||
|
""")
|
||||||
|
repr = excinfo.getrepr(style='native')
|
||||||
|
assert "assert 0" in str(repr.reprcrash)
|
||||||
|
s = str(repr)
|
||||||
|
assert s.startswith('Traceback (most recent call last):\n File')
|
||||||
|
assert s.endswith('\nAssertionError: assert 0')
|
||||||
|
assert 'exec (source.compile())' in s
|
||||||
|
# python 2.4 fails to get the source line for the assert
|
||||||
|
if py.std.sys.version_info >= (2, 5):
|
||||||
|
assert s.count('assert 0') == 2
|
||||||
|
|
||||||
|
def test_traceback_repr_style(self, importasmod):
|
||||||
|
mod = importasmod("""
|
||||||
|
def f():
|
||||||
|
g()
|
||||||
|
def g():
|
||||||
|
h()
|
||||||
|
def h():
|
||||||
|
i()
|
||||||
|
def i():
|
||||||
|
raise ValueError()
|
||||||
|
""")
|
||||||
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
|
excinfo.traceback = excinfo.traceback.filter()
|
||||||
|
excinfo.traceback[1].set_repr_style("short")
|
||||||
|
excinfo.traceback[2].set_repr_style("short")
|
||||||
|
r = excinfo.getrepr(style="long")
|
||||||
|
tw = TWMock()
|
||||||
|
r.toterminal(tw)
|
||||||
|
for line in tw.lines: print (line)
|
||||||
|
assert tw.lines[0] == ""
|
||||||
|
assert tw.lines[1] == " def f():"
|
||||||
|
assert tw.lines[2] == "> g()"
|
||||||
|
assert tw.lines[3] == ""
|
||||||
|
assert tw.lines[4].endswith("mod.py:3: ")
|
||||||
|
assert tw.lines[5] == ("_ ", None)
|
||||||
|
assert tw.lines[6].endswith("in g")
|
||||||
|
assert tw.lines[7] == " h()"
|
||||||
|
assert tw.lines[8].endswith("in h")
|
||||||
|
assert tw.lines[9] == " i()"
|
||||||
|
assert tw.lines[10] == ("_ ", None)
|
||||||
|
assert tw.lines[11] == ""
|
||||||
|
assert tw.lines[12] == " def i():"
|
||||||
|
assert tw.lines[13] == "> raise ValueError()"
|
||||||
|
assert tw.lines[14] == "E ValueError"
|
||||||
|
assert tw.lines[15] == ""
|
||||||
|
assert tw.lines[16].endswith("mod.py:9: ValueError")
|
|
@ -0,0 +1,659 @@
|
||||||
|
# flake8: noqa
|
||||||
|
# disable flake check on this file because some constructs are strange
|
||||||
|
# or redundant on purpose and can't be disable on a line-by-line basis
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
|
from _pytest._code import Source
|
||||||
|
from _pytest._code.source import _ast
|
||||||
|
|
||||||
|
if _ast is not None:
|
||||||
|
astonly = pytest.mark.nothing
|
||||||
|
else:
|
||||||
|
astonly = pytest.mark.xfail("True", reason="only works with AST-compile")
|
||||||
|
|
||||||
|
failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
|
||||||
|
|
||||||
|
def test_source_str_function():
|
||||||
|
x = Source("3")
|
||||||
|
assert str(x) == "3"
|
||||||
|
|
||||||
|
x = Source(" 3")
|
||||||
|
assert str(x) == "3"
|
||||||
|
|
||||||
|
x = Source("""
|
||||||
|
3
|
||||||
|
""", rstrip=False)
|
||||||
|
assert str(x) == "\n3\n "
|
||||||
|
|
||||||
|
x = Source("""
|
||||||
|
3
|
||||||
|
""", rstrip=True)
|
||||||
|
assert str(x) == "\n3"
|
||||||
|
|
||||||
|
def test_unicode():
|
||||||
|
try:
|
||||||
|
unicode
|
||||||
|
except NameError:
|
||||||
|
return
|
||||||
|
x = Source(unicode("4"))
|
||||||
|
assert str(x) == "4"
|
||||||
|
co = _pytest._code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval')
|
||||||
|
val = eval(co)
|
||||||
|
assert isinstance(val, unicode)
|
||||||
|
|
||||||
|
def test_source_from_function():
|
||||||
|
source = _pytest._code.Source(test_source_str_function)
|
||||||
|
assert str(source).startswith('def test_source_str_function():')
|
||||||
|
|
||||||
|
def test_source_from_method():
|
||||||
|
class TestClass:
|
||||||
|
def test_method(self):
|
||||||
|
pass
|
||||||
|
source = _pytest._code.Source(TestClass().test_method)
|
||||||
|
assert source.lines == ["def test_method(self):",
|
||||||
|
" pass"]
|
||||||
|
|
||||||
|
def test_source_from_lines():
|
||||||
|
lines = ["a \n", "b\n", "c"]
|
||||||
|
source = _pytest._code.Source(lines)
|
||||||
|
assert source.lines == ['a ', 'b', 'c']
|
||||||
|
|
||||||
|
def test_source_from_inner_function():
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
source = _pytest._code.Source(f, deindent=False)
|
||||||
|
assert str(source).startswith(' def f():')
|
||||||
|
source = _pytest._code.Source(f)
|
||||||
|
assert str(source).startswith('def f():')
|
||||||
|
|
||||||
|
def test_source_putaround_simple():
|
||||||
|
source = Source("raise ValueError")
|
||||||
|
source = source.putaround(
|
||||||
|
"try:", """\
|
||||||
|
except ValueError:
|
||||||
|
x = 42
|
||||||
|
else:
|
||||||
|
x = 23""")
|
||||||
|
assert str(source)=="""\
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
x = 42
|
||||||
|
else:
|
||||||
|
x = 23"""
|
||||||
|
|
||||||
|
def test_source_putaround():
|
||||||
|
source = Source()
|
||||||
|
source = source.putaround("""
|
||||||
|
if 1:
|
||||||
|
x=1
|
||||||
|
""")
|
||||||
|
assert str(source).strip() == "if 1:\n x=1"
|
||||||
|
|
||||||
|
def test_source_strips():
|
||||||
|
source = Source("")
|
||||||
|
assert source == Source()
|
||||||
|
assert str(source) == ''
|
||||||
|
assert source.strip() == source
|
||||||
|
|
||||||
|
def test_source_strip_multiline():
|
||||||
|
source = Source()
|
||||||
|
source.lines = ["", " hello", " "]
|
||||||
|
source2 = source.strip()
|
||||||
|
assert source2.lines == [" hello"]
|
||||||
|
|
||||||
|
def test_syntaxerror_rerepresentation():
|
||||||
|
ex = pytest.raises(SyntaxError, _pytest._code.compile, 'xyz xyz')
|
||||||
|
assert ex.value.lineno == 1
|
||||||
|
assert ex.value.offset in (4,7) # XXX pypy/jython versus cpython?
|
||||||
|
assert ex.value.text.strip(), 'x x'
|
||||||
|
|
||||||
|
def test_isparseable():
|
||||||
|
assert Source("hello").isparseable()
|
||||||
|
assert Source("if 1:\n pass").isparseable()
|
||||||
|
assert Source(" \nif 1:\n pass").isparseable()
|
||||||
|
assert not Source("if 1:\n").isparseable()
|
||||||
|
assert not Source(" \nif 1:\npass").isparseable()
|
||||||
|
assert not Source(chr(0)).isparseable()
|
||||||
|
|
||||||
|
class TestAccesses:
|
||||||
|
source = Source("""\
|
||||||
|
def f(x):
|
||||||
|
pass
|
||||||
|
def g(x):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
def test_getrange(self):
|
||||||
|
x = self.source[0:2]
|
||||||
|
assert x.isparseable()
|
||||||
|
assert len(x.lines) == 2
|
||||||
|
assert str(x) == "def f(x):\n pass"
|
||||||
|
|
||||||
|
def test_getline(self):
|
||||||
|
x = self.source[0]
|
||||||
|
assert x == "def f(x):"
|
||||||
|
|
||||||
|
def test_len(self):
|
||||||
|
assert len(self.source) == 4
|
||||||
|
|
||||||
|
def test_iter(self):
|
||||||
|
l = [x for x in self.source]
|
||||||
|
assert len(l) == 4
|
||||||
|
|
||||||
|
class TestSourceParsingAndCompiling:
|
||||||
|
source = Source("""\
|
||||||
|
def f(x):
|
||||||
|
assert (x ==
|
||||||
|
3 +
|
||||||
|
4)
|
||||||
|
""").strip()
|
||||||
|
|
||||||
|
def test_compile(self):
|
||||||
|
co = _pytest._code.compile("x=3")
|
||||||
|
d = {}
|
||||||
|
exec (co, d)
|
||||||
|
assert d['x'] == 3
|
||||||
|
|
||||||
|
def test_compile_and_getsource_simple(self):
|
||||||
|
co = _pytest._code.compile("x=3")
|
||||||
|
exec (co)
|
||||||
|
source = _pytest._code.Source(co)
|
||||||
|
assert str(source) == "x=3"
|
||||||
|
|
||||||
|
def test_compile_and_getsource_through_same_function(self):
|
||||||
|
def gensource(source):
|
||||||
|
return _pytest._code.compile(source)
|
||||||
|
co1 = gensource("""
|
||||||
|
def f():
|
||||||
|
raise KeyError()
|
||||||
|
""")
|
||||||
|
co2 = gensource("""
|
||||||
|
def f():
|
||||||
|
raise ValueError()
|
||||||
|
""")
|
||||||
|
source1 = py.std.inspect.getsource(co1)
|
||||||
|
assert 'KeyError' in source1
|
||||||
|
source2 = py.std.inspect.getsource(co2)
|
||||||
|
assert 'ValueError' in source2
|
||||||
|
|
||||||
|
def test_getstatement(self):
|
||||||
|
#print str(self.source)
|
||||||
|
ass = str(self.source[1:])
|
||||||
|
for i in range(1, 4):
|
||||||
|
#print "trying start in line %r" % self.source[i]
|
||||||
|
s = self.source.getstatement(i)
|
||||||
|
#x = s.deindent()
|
||||||
|
assert str(s) == ass
|
||||||
|
|
||||||
|
def test_getstatementrange_triple_quoted(self):
|
||||||
|
#print str(self.source)
|
||||||
|
source = Source("""hello('''
|
||||||
|
''')""")
|
||||||
|
s = source.getstatement(0)
|
||||||
|
assert s == str(source)
|
||||||
|
s = source.getstatement(1)
|
||||||
|
assert s == str(source)
|
||||||
|
|
||||||
|
@astonly
|
||||||
|
def test_getstatementrange_within_constructs(self):
|
||||||
|
source = Source("""\
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except SomeThing:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
42
|
||||||
|
""")
|
||||||
|
assert len(source) == 7
|
||||||
|
# check all lineno's that could occur in a traceback
|
||||||
|
#assert source.getstatementrange(0) == (0, 7)
|
||||||
|
#assert source.getstatementrange(1) == (1, 5)
|
||||||
|
assert source.getstatementrange(2) == (2, 3)
|
||||||
|
assert source.getstatementrange(3) == (3, 4)
|
||||||
|
assert source.getstatementrange(4) == (4, 5)
|
||||||
|
#assert source.getstatementrange(5) == (0, 7)
|
||||||
|
assert source.getstatementrange(6) == (6, 7)
|
||||||
|
|
||||||
|
def test_getstatementrange_bug(self):
|
||||||
|
source = Source("""\
|
||||||
|
try:
|
||||||
|
x = (
|
||||||
|
y +
|
||||||
|
z)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
assert len(source) == 6
|
||||||
|
assert source.getstatementrange(2) == (1, 4)
|
||||||
|
|
||||||
|
def test_getstatementrange_bug2(self):
|
||||||
|
source = Source("""\
|
||||||
|
assert (
|
||||||
|
33
|
||||||
|
==
|
||||||
|
[
|
||||||
|
X(3,
|
||||||
|
b=1, c=2
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
assert len(source) == 9
|
||||||
|
assert source.getstatementrange(5) == (0, 9)
|
||||||
|
|
||||||
|
def test_getstatementrange_ast_issue58(self):
|
||||||
|
source = Source("""\
|
||||||
|
|
||||||
|
def test_some():
|
||||||
|
for a in [a for a in
|
||||||
|
CAUSE_ERROR]: pass
|
||||||
|
|
||||||
|
x = 3
|
||||||
|
""")
|
||||||
|
assert getstatement(2, source).lines == source.lines[2:3]
|
||||||
|
assert getstatement(3, source).lines == source.lines[3:4]
|
||||||
|
|
||||||
|
@pytest.mark.skipif("sys.version_info < (2,6)")
|
||||||
|
def test_getstatementrange_out_of_bounds_py3(self):
|
||||||
|
source = Source("if xxx:\n from .collections import something")
|
||||||
|
r = source.getstatementrange(1)
|
||||||
|
assert r == (1,2)
|
||||||
|
|
||||||
|
def test_getstatementrange_with_syntaxerror_issue7(self):
|
||||||
|
source = Source(":")
|
||||||
|
pytest.raises(SyntaxError, lambda: source.getstatementrange(0))
|
||||||
|
|
||||||
|
@pytest.mark.skipif("sys.version_info < (2,6)")
|
||||||
|
def test_compile_to_ast(self):
|
||||||
|
import ast
|
||||||
|
source = Source("x = 4")
|
||||||
|
mod = source.compile(flag=ast.PyCF_ONLY_AST)
|
||||||
|
assert isinstance(mod, ast.Module)
|
||||||
|
compile(mod, "<filename>", "exec")
|
||||||
|
|
||||||
|
def test_compile_and_getsource(self):
|
||||||
|
co = self.source.compile()
|
||||||
|
py.builtin.exec_(co, globals())
|
||||||
|
f(7)
|
||||||
|
excinfo = pytest.raises(AssertionError, "f(6)")
|
||||||
|
frame = excinfo.traceback[-1].frame
|
||||||
|
stmt = frame.code.fullsource.getstatement(frame.lineno)
|
||||||
|
#print "block", str(block)
|
||||||
|
assert str(stmt).strip().startswith('assert')
|
||||||
|
|
||||||
|
def test_compilefuncs_and_path_sanity(self):
|
||||||
|
def check(comp, name):
|
||||||
|
co = comp(self.source, name)
|
||||||
|
if not name:
|
||||||
|
expected = "codegen %s:%d>" %(mypath, mylineno+2+1)
|
||||||
|
else:
|
||||||
|
expected = "codegen %r %s:%d>" % (name, mypath, mylineno+2+1)
|
||||||
|
fn = co.co_filename
|
||||||
|
assert fn.endswith(expected)
|
||||||
|
|
||||||
|
mycode = _pytest._code.Code(self.test_compilefuncs_and_path_sanity)
|
||||||
|
mylineno = mycode.firstlineno
|
||||||
|
mypath = mycode.path
|
||||||
|
|
||||||
|
for comp in _pytest._code.compile, _pytest._code.Source.compile:
|
||||||
|
for name in '', None, 'my':
|
||||||
|
yield check, comp, name
|
||||||
|
|
||||||
|
def test_offsetless_synerr(self):
|
||||||
|
pytest.raises(SyntaxError, _pytest._code.compile, "lambda a,a: 0", mode='eval')
|
||||||
|
|
||||||
|
def test_getstartingblock_singleline():
|
||||||
|
class A:
|
||||||
|
def __init__(self, *args):
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
self.source = _pytest._code.Frame(frame).statement
|
||||||
|
|
||||||
|
x = A('x', 'y')
|
||||||
|
|
||||||
|
l = [i for i in x.source.lines if i.strip()]
|
||||||
|
assert len(l) == 1
|
||||||
|
|
||||||
|
def test_getstartingblock_multiline():
|
||||||
|
class A:
|
||||||
|
def __init__(self, *args):
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
self.source = _pytest._code.Frame(frame).statement
|
||||||
|
|
||||||
|
x = A('x',
|
||||||
|
'y' \
|
||||||
|
,
|
||||||
|
'z')
|
||||||
|
|
||||||
|
l = [i for i in x.source.lines if i.strip()]
|
||||||
|
assert len(l) == 4
|
||||||
|
|
||||||
|
def test_getline_finally():
|
||||||
|
def c(): pass
|
||||||
|
excinfo = pytest.raises(TypeError, """
|
||||||
|
teardown = None
|
||||||
|
try:
|
||||||
|
c(1)
|
||||||
|
finally:
|
||||||
|
if teardown:
|
||||||
|
teardown()
|
||||||
|
""")
|
||||||
|
source = excinfo.traceback[-1].statement
|
||||||
|
assert str(source).strip() == 'c(1)'
|
||||||
|
|
||||||
|
def test_getfuncsource_dynamic():
|
||||||
|
source = """
|
||||||
|
def f():
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
def g(): pass
|
||||||
|
"""
|
||||||
|
co = _pytest._code.compile(source)
|
||||||
|
py.builtin.exec_(co, globals())
|
||||||
|
assert str(_pytest._code.Source(f)).strip() == 'def f():\n raise ValueError'
|
||||||
|
assert str(_pytest._code.Source(g)).strip() == 'def g(): pass'
|
||||||
|
|
||||||
|
|
||||||
|
def test_getfuncsource_with_multine_string():
|
||||||
|
def f():
|
||||||
|
c = '''while True:
|
||||||
|
pass
|
||||||
|
'''
|
||||||
|
assert str(_pytest._code.Source(f)).strip() == "def f():\n c = '''while True:\n pass\n'''"
|
||||||
|
|
||||||
|
|
||||||
|
def test_deindent():
|
||||||
|
from _pytest._code.source import deindent as deindent
|
||||||
|
assert deindent(['\tfoo', '\tbar', ]) == ['foo', 'bar']
|
||||||
|
|
||||||
|
def f():
|
||||||
|
c = '''while True:
|
||||||
|
pass
|
||||||
|
'''
|
||||||
|
import inspect
|
||||||
|
lines = deindent(inspect.getsource(f).splitlines())
|
||||||
|
assert lines == ["def f():", " c = '''while True:", " pass", "'''"]
|
||||||
|
|
||||||
|
source = """
|
||||||
|
def f():
|
||||||
|
def g():
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
lines = deindent(source.splitlines())
|
||||||
|
assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
|
||||||
|
|
||||||
|
@pytest.mark.xfail("sys.version_info[:3] < (2,7,0) or "
|
||||||
|
"((3,0) <= sys.version_info[:2] < (3,2))")
|
||||||
|
def test_source_of_class_at_eof_without_newline(tmpdir):
|
||||||
|
# this test fails because the implicit inspect.getsource(A) below
|
||||||
|
# does not return the "x = 1" last line.
|
||||||
|
source = _pytest._code.Source('''
|
||||||
|
class A(object):
|
||||||
|
def method(self):
|
||||||
|
x = 1
|
||||||
|
''')
|
||||||
|
path = tmpdir.join("a.py")
|
||||||
|
path.write(source)
|
||||||
|
s2 = _pytest._code.Source(tmpdir.join("a.py").pyimport().A)
|
||||||
|
assert str(source).strip() == str(s2).strip()
|
||||||
|
|
||||||
|
if True:
|
||||||
|
def x():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_getsource_fallback():
|
||||||
|
from _pytest._code.source import getsource
|
||||||
|
expected = """def x():
|
||||||
|
pass"""
|
||||||
|
src = getsource(x)
|
||||||
|
assert src == expected
|
||||||
|
|
||||||
|
def test_idem_compile_and_getsource():
|
||||||
|
from _pytest._code.source import getsource
|
||||||
|
expected = "def x(): pass"
|
||||||
|
co = _pytest._code.compile(expected)
|
||||||
|
src = getsource(co)
|
||||||
|
assert src == expected
|
||||||
|
|
||||||
|
def test_findsource_fallback():
|
||||||
|
from _pytest._code.source import findsource
|
||||||
|
src, lineno = findsource(x)
|
||||||
|
assert 'test_findsource_simple' in str(src)
|
||||||
|
assert src[lineno] == ' def x():'
|
||||||
|
|
||||||
|
def test_findsource():
|
||||||
|
from _pytest._code.source import findsource
|
||||||
|
co = _pytest._code.compile("""if 1:
|
||||||
|
def x():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
|
||||||
|
src, lineno = findsource(co)
|
||||||
|
assert 'if 1:' in str(src)
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
eval(co, d)
|
||||||
|
src, lineno = findsource(d['x'])
|
||||||
|
assert 'if 1:' in str(src)
|
||||||
|
assert src[lineno] == " def x():"
|
||||||
|
|
||||||
|
|
||||||
|
def test_getfslineno():
|
||||||
|
from _pytest._code import getfslineno
|
||||||
|
|
||||||
|
def f(x):
|
||||||
|
pass
|
||||||
|
|
||||||
|
fspath, lineno = getfslineno(f)
|
||||||
|
|
||||||
|
assert fspath.basename == "test_source.py"
|
||||||
|
assert lineno == _pytest._code.getrawcode(f).co_firstlineno - 1 # see findsource
|
||||||
|
|
||||||
|
class A(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
fspath, lineno = getfslineno(A)
|
||||||
|
|
||||||
|
_, A_lineno = py.std.inspect.findsource(A)
|
||||||
|
assert fspath.basename == "test_source.py"
|
||||||
|
assert lineno == A_lineno
|
||||||
|
|
||||||
|
assert getfslineno(3) == ("", -1)
|
||||||
|
class B:
|
||||||
|
pass
|
||||||
|
B.__name__ = "B2"
|
||||||
|
assert getfslineno(B)[1] == -1
|
||||||
|
|
||||||
|
def test_code_of_object_instance_with_call():
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
pytest.raises(TypeError, lambda: _pytest._code.Source(A()))
|
||||||
|
class WithCall:
|
||||||
|
def __call__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
code = _pytest._code.Code(WithCall())
|
||||||
|
assert 'pass' in str(code.source())
|
||||||
|
|
||||||
|
class Hello(object):
|
||||||
|
def __call__(self):
|
||||||
|
pass
|
||||||
|
pytest.raises(TypeError, lambda: _pytest._code.Code(Hello))
|
||||||
|
|
||||||
|
|
||||||
|
def getstatement(lineno, source):
|
||||||
|
from _pytest._code.source import getstatementrange_ast
|
||||||
|
source = _pytest._code.Source(source, deindent=False)
|
||||||
|
ast, start, end = getstatementrange_ast(lineno, source)
|
||||||
|
return source[start:end]
|
||||||
|
|
||||||
|
def test_oneline():
|
||||||
|
source = getstatement(0, "raise ValueError")
|
||||||
|
assert str(source) == "raise ValueError"
|
||||||
|
|
||||||
|
def test_comment_and_no_newline_at_end():
|
||||||
|
from _pytest._code.source import getstatementrange_ast
|
||||||
|
source = Source(['def test_basic_complex():',
|
||||||
|
' assert 1 == 2',
|
||||||
|
'# vim: filetype=pyopencl:fdm=marker'])
|
||||||
|
ast, start, end = getstatementrange_ast(1, source)
|
||||||
|
assert end == 2
|
||||||
|
|
||||||
|
def test_oneline_and_comment():
|
||||||
|
source = getstatement(0, "raise ValueError\n#hello")
|
||||||
|
assert str(source) == "raise ValueError"
|
||||||
|
|
||||||
|
@pytest.mark.xfail(hasattr(sys, "pypy_version_info"),
|
||||||
|
reason='does not work on pypy')
|
||||||
|
def test_comments():
|
||||||
|
source = '''def test():
|
||||||
|
"comment 1"
|
||||||
|
x = 1
|
||||||
|
# comment 2
|
||||||
|
# comment 3
|
||||||
|
|
||||||
|
assert False
|
||||||
|
|
||||||
|
"""
|
||||||
|
comment 4
|
||||||
|
"""
|
||||||
|
'''
|
||||||
|
for line in range(2,6):
|
||||||
|
assert str(getstatement(line, source)) == ' x = 1'
|
||||||
|
for line in range(6,10):
|
||||||
|
assert str(getstatement(line, source)) == ' assert False'
|
||||||
|
assert str(getstatement(10, source)) == '"""'
|
||||||
|
|
||||||
|
def test_comment_in_statement():
|
||||||
|
source = '''test(foo=1,
|
||||||
|
# comment 1
|
||||||
|
bar=2)
|
||||||
|
'''
|
||||||
|
for line in range(1,3):
|
||||||
|
assert str(getstatement(line, source)) == \
|
||||||
|
'test(foo=1,\n # comment 1\n bar=2)'
|
||||||
|
|
||||||
|
def test_single_line_else():
|
||||||
|
source = getstatement(1, "if False: 2\nelse: 3")
|
||||||
|
assert str(source) == "else: 3"
|
||||||
|
|
||||||
|
def test_single_line_finally():
|
||||||
|
source = getstatement(1, "try: 1\nfinally: 3")
|
||||||
|
assert str(source) == "finally: 3"
|
||||||
|
|
||||||
|
def test_issue55():
|
||||||
|
source = ('def round_trip(dinp):\n assert 1 == dinp\n'
|
||||||
|
'def test_rt():\n round_trip("""\n""")\n')
|
||||||
|
s = getstatement(3, source)
|
||||||
|
assert str(s) == ' round_trip("""\n""")'
|
||||||
|
|
||||||
|
|
||||||
|
def XXXtest_multiline():
|
||||||
|
source = getstatement(0, """\
|
||||||
|
raise ValueError(
|
||||||
|
23
|
||||||
|
)
|
||||||
|
x = 3
|
||||||
|
""")
|
||||||
|
assert str(source) == "raise ValueError(\n 23\n)"
|
||||||
|
|
||||||
|
class TestTry:
|
||||||
|
pytestmark = astonly
|
||||||
|
source = """\
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except Something:
|
||||||
|
raise IndexError(1)
|
||||||
|
else:
|
||||||
|
raise KeyError()
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_body(self):
|
||||||
|
source = getstatement(1, self.source)
|
||||||
|
assert str(source) == " raise ValueError"
|
||||||
|
|
||||||
|
def test_except_line(self):
|
||||||
|
source = getstatement(2, self.source)
|
||||||
|
assert str(source) == "except Something:"
|
||||||
|
|
||||||
|
def test_except_body(self):
|
||||||
|
source = getstatement(3, self.source)
|
||||||
|
assert str(source) == " raise IndexError(1)"
|
||||||
|
|
||||||
|
def test_else(self):
|
||||||
|
source = getstatement(5, self.source)
|
||||||
|
assert str(source) == " raise KeyError()"
|
||||||
|
|
||||||
|
class TestTryFinally:
|
||||||
|
source = """\
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
finally:
|
||||||
|
raise IndexError(1)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_body(self):
|
||||||
|
source = getstatement(1, self.source)
|
||||||
|
assert str(source) == " raise ValueError"
|
||||||
|
|
||||||
|
def test_finally(self):
|
||||||
|
source = getstatement(3, self.source)
|
||||||
|
assert str(source) == " raise IndexError(1)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestIf:
|
||||||
|
pytestmark = astonly
|
||||||
|
source = """\
|
||||||
|
if 1:
|
||||||
|
y = 3
|
||||||
|
elif False:
|
||||||
|
y = 5
|
||||||
|
else:
|
||||||
|
y = 7
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_body(self):
|
||||||
|
source = getstatement(1, self.source)
|
||||||
|
assert str(source) == " y = 3"
|
||||||
|
|
||||||
|
def test_elif_clause(self):
|
||||||
|
source = getstatement(2, self.source)
|
||||||
|
assert str(source) == "elif False:"
|
||||||
|
|
||||||
|
def test_elif(self):
|
||||||
|
source = getstatement(3, self.source)
|
||||||
|
assert str(source) == " y = 5"
|
||||||
|
|
||||||
|
def test_else(self):
|
||||||
|
source = getstatement(5, self.source)
|
||||||
|
assert str(source) == " y = 7"
|
||||||
|
|
||||||
|
def test_semicolon():
|
||||||
|
s = """\
|
||||||
|
hello ; pytest.skip()
|
||||||
|
"""
|
||||||
|
source = getstatement(0, s)
|
||||||
|
assert str(source) == s.strip()
|
||||||
|
|
||||||
|
def test_def_online():
|
||||||
|
s = """\
|
||||||
|
def func(): raise ValueError(42)
|
||||||
|
|
||||||
|
def something():
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
source = getstatement(0, s)
|
||||||
|
assert str(source) == "def func(): raise ValueError(42)"
|
||||||
|
|
||||||
|
def XXX_test_expression_multiline():
|
||||||
|
source = """\
|
||||||
|
something
|
||||||
|
'''
|
||||||
|
'''"""
|
||||||
|
result = getstatement(1, source)
|
||||||
|
assert str(result) == "'''\n'''"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
import pytest, py
|
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||||
|
|
||||||
|
|
||||||
|
@ -598,13 +601,13 @@ class TestConftestCustomization:
|
||||||
|
|
||||||
def test_customized_pymakemodule_issue205_subdir(self, testdir):
|
def test_customized_pymakemodule_issue205_subdir(self, testdir):
|
||||||
b = testdir.mkdir("a").mkdir("b")
|
b = testdir.mkdir("a").mkdir("b")
|
||||||
b.join("conftest.py").write(py.code.Source("""
|
b.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_pycollect_makemodule(__multicall__):
|
def pytest_pycollect_makemodule(__multicall__):
|
||||||
mod = __multicall__.execute()
|
mod = __multicall__.execute()
|
||||||
mod.obj.hello = "world"
|
mod.obj.hello = "world"
|
||||||
return mod
|
return mod
|
||||||
"""))
|
"""))
|
||||||
b.join("test_module.py").write(py.code.Source("""
|
b.join("test_module.py").write(_pytest._code.Source("""
|
||||||
def test_hello():
|
def test_hello():
|
||||||
assert hello == "world"
|
assert hello == "world"
|
||||||
"""))
|
"""))
|
||||||
|
@ -613,7 +616,7 @@ class TestConftestCustomization:
|
||||||
|
|
||||||
def test_customized_pymakeitem(self, testdir):
|
def test_customized_pymakeitem(self, testdir):
|
||||||
b = testdir.mkdir("a").mkdir("b")
|
b = testdir.mkdir("a").mkdir("b")
|
||||||
b.join("conftest.py").write(py.code.Source("""
|
b.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_pycollect_makeitem():
|
def pytest_pycollect_makeitem():
|
||||||
|
@ -624,7 +627,7 @@ class TestConftestCustomization:
|
||||||
for func in result:
|
for func in result:
|
||||||
func._some123 = "world"
|
func._some123 = "world"
|
||||||
"""))
|
"""))
|
||||||
b.join("test_module.py").write(py.code.Source("""
|
b.join("test_module.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
@ -662,7 +665,7 @@ class TestConftestCustomization:
|
||||||
def test_setup_only_available_in_subdir(testdir):
|
def test_setup_only_available_in_subdir(testdir):
|
||||||
sub1 = testdir.mkpydir("sub1")
|
sub1 = testdir.mkpydir("sub1")
|
||||||
sub2 = testdir.mkpydir("sub2")
|
sub2 = testdir.mkpydir("sub2")
|
||||||
sub1.join("conftest.py").write(py.code.Source("""
|
sub1.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
assert item.fspath.purebasename == "test_in_sub1"
|
assert item.fspath.purebasename == "test_in_sub1"
|
||||||
|
@ -671,7 +674,7 @@ def test_setup_only_available_in_subdir(testdir):
|
||||||
def pytest_runtest_teardown(item):
|
def pytest_runtest_teardown(item):
|
||||||
assert item.fspath.purebasename == "test_in_sub1"
|
assert item.fspath.purebasename == "test_in_sub1"
|
||||||
"""))
|
"""))
|
||||||
sub2.join("conftest.py").write(py.code.Source("""
|
sub2.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
assert item.fspath.purebasename == "test_in_sub2"
|
assert item.fspath.purebasename == "test_in_sub2"
|
||||||
|
@ -787,7 +790,7 @@ class TestTracebackCutting:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_, _, tb = sys.exc_info()
|
_, _, tb = sys.exc_info()
|
||||||
|
|
||||||
tb = py.code.Traceback(tb)
|
tb = _pytest._code.Traceback(tb)
|
||||||
assert isinstance(tb[-1].path, str)
|
assert isinstance(tb[-1].path, str)
|
||||||
assert not filter_traceback(tb[-1])
|
assert not filter_traceback(tb[-1])
|
||||||
|
|
||||||
|
@ -810,7 +813,7 @@ class TestTracebackCutting:
|
||||||
_, _, tb = sys.exc_info()
|
_, _, tb = sys.exc_info()
|
||||||
|
|
||||||
testdir.tmpdir.join('filter_traceback_entry_as_str.py').remove()
|
testdir.tmpdir.join('filter_traceback_entry_as_str.py').remove()
|
||||||
tb = py.code.Traceback(tb)
|
tb = _pytest._code.Traceback(tb)
|
||||||
assert isinstance(tb[-1].path, str)
|
assert isinstance(tb[-1].path, str)
|
||||||
assert filter_traceback(tb[-1])
|
assert filter_traceback(tb[-1])
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import pytest, py, sys
|
|
||||||
from _pytest import python as funcargs
|
|
||||||
from _pytest.python import FixtureLookupError
|
|
||||||
from _pytest.pytester import get_public_names
|
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
from _pytest import python as funcargs
|
||||||
|
from _pytest.pytester import get_public_names
|
||||||
|
from _pytest.python import FixtureLookupError
|
||||||
|
|
||||||
|
|
||||||
def test_getfuncargnames():
|
def test_getfuncargnames():
|
||||||
def f(): pass
|
def f(): pass
|
||||||
assert not funcargs.getfuncargnames(f)
|
assert not funcargs.getfuncargnames(f)
|
||||||
|
@ -86,12 +90,12 @@ class TestFillFixtures:
|
||||||
def test_conftest_funcargs_only_available_in_subdir(self, testdir):
|
def test_conftest_funcargs_only_available_in_subdir(self, testdir):
|
||||||
sub1 = testdir.mkpydir("sub1")
|
sub1 = testdir.mkpydir("sub1")
|
||||||
sub2 = testdir.mkpydir("sub2")
|
sub2 = testdir.mkpydir("sub2")
|
||||||
sub1.join("conftest.py").write(py.code.Source("""
|
sub1.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
def pytest_funcarg__arg1(request):
|
def pytest_funcarg__arg1(request):
|
||||||
pytest.raises(Exception, "request.getfuncargvalue('arg2')")
|
pytest.raises(Exception, "request.getfuncargvalue('arg2')")
|
||||||
"""))
|
"""))
|
||||||
sub2.join("conftest.py").write(py.code.Source("""
|
sub2.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
def pytest_funcarg__arg2(request):
|
def pytest_funcarg__arg2(request):
|
||||||
pytest.raises(Exception, "request.getfuncargvalue('arg1')")
|
pytest.raises(Exception, "request.getfuncargvalue('arg1')")
|
||||||
|
@ -156,7 +160,7 @@ class TestFillFixtures:
|
||||||
return 'spam'
|
return 'spam'
|
||||||
""")
|
""")
|
||||||
pkg = testdir.mkpydir("pkg")
|
pkg = testdir.mkpydir("pkg")
|
||||||
pkg.join("conftest.py").write(py.code.Source("""
|
pkg.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -164,7 +168,7 @@ class TestFillFixtures:
|
||||||
return spam * 2
|
return spam * 2
|
||||||
"""))
|
"""))
|
||||||
testfile = pkg.join("test_spam.py")
|
testfile = pkg.join("test_spam.py")
|
||||||
testfile.write(py.code.Source("""
|
testfile.write(_pytest._code.Source("""
|
||||||
def test_spam(spam):
|
def test_spam(spam):
|
||||||
assert spam == "spamspam"
|
assert spam == "spamspam"
|
||||||
"""))
|
"""))
|
||||||
|
@ -258,7 +262,7 @@ class TestFillFixtures:
|
||||||
return request.param
|
return request.param
|
||||||
""")
|
""")
|
||||||
subdir = testdir.mkpydir('subdir')
|
subdir = testdir.mkpydir('subdir')
|
||||||
subdir.join("conftest.py").write(py.code.Source("""
|
subdir.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -266,7 +270,7 @@ class TestFillFixtures:
|
||||||
return 'spam'
|
return 'spam'
|
||||||
"""))
|
"""))
|
||||||
testfile = subdir.join("test_spam.py")
|
testfile = subdir.join("test_spam.py")
|
||||||
testfile.write(py.code.Source("""
|
testfile.write(_pytest._code.Source("""
|
||||||
def test_spam(spam):
|
def test_spam(spam):
|
||||||
assert spam == "spam"
|
assert spam == "spam"
|
||||||
"""))
|
"""))
|
||||||
|
@ -312,7 +316,7 @@ class TestFillFixtures:
|
||||||
return 'spam'
|
return 'spam'
|
||||||
""")
|
""")
|
||||||
subdir = testdir.mkpydir('subdir')
|
subdir = testdir.mkpydir('subdir')
|
||||||
subdir.join("conftest.py").write(py.code.Source("""
|
subdir.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture(params=[1, 2, 3])
|
@pytest.fixture(params=[1, 2, 3])
|
||||||
|
@ -320,7 +324,7 @@ class TestFillFixtures:
|
||||||
return request.param
|
return request.param
|
||||||
"""))
|
"""))
|
||||||
testfile = subdir.join("test_spam.py")
|
testfile = subdir.join("test_spam.py")
|
||||||
testfile.write(py.code.Source("""
|
testfile.write(_pytest._code.Source("""
|
||||||
params = {'spam': 1}
|
params = {'spam': 1}
|
||||||
|
|
||||||
def test_spam(spam):
|
def test_spam(spam):
|
||||||
|
@ -609,7 +613,7 @@ class TestRequestBasic:
|
||||||
def test_fixtures_sub_subdir_normalize_sep(self, testdir):
|
def test_fixtures_sub_subdir_normalize_sep(self, testdir):
|
||||||
# this tests that normalization of nodeids takes place
|
# this tests that normalization of nodeids takes place
|
||||||
b = testdir.mkdir("tests").mkdir("unit")
|
b = testdir.mkdir("tests").mkdir("unit")
|
||||||
b.join("conftest.py").write(py.code.Source("""
|
b.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_funcarg__arg1():
|
def pytest_funcarg__arg1():
|
||||||
pass
|
pass
|
||||||
"""))
|
"""))
|
||||||
|
@ -1349,7 +1353,7 @@ class TestAutouseDiscovery:
|
||||||
class TestAutouseManagement:
|
class TestAutouseManagement:
|
||||||
def test_autouse_conftest_mid_directory(self, testdir):
|
def test_autouse_conftest_mid_directory(self, testdir):
|
||||||
pkgdir = testdir.mkpydir("xyz123")
|
pkgdir = testdir.mkpydir("xyz123")
|
||||||
pkgdir.join("conftest.py").write(py.code.Source("""
|
pkgdir.join("conftest.py").write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def app():
|
def app():
|
||||||
|
@ -1357,7 +1361,7 @@ class TestAutouseManagement:
|
||||||
sys._myapp = "hello"
|
sys._myapp = "hello"
|
||||||
"""))
|
"""))
|
||||||
t = pkgdir.ensure("tests", "test_app.py")
|
t = pkgdir.ensure("tests", "test_app.py")
|
||||||
t.write(py.code.Source("""
|
t.write(_pytest._code.Source("""
|
||||||
import sys
|
import sys
|
||||||
def test_app():
|
def test_app():
|
||||||
assert sys._myapp == "hello"
|
assert sys._myapp == "hello"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import runner
|
|
||||||
from _pytest import python
|
from _pytest import python
|
||||||
|
from _pytest import runner
|
||||||
|
|
||||||
|
|
||||||
class TestOEJSKITSpecials:
|
class TestOEJSKITSpecials:
|
||||||
def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
|
def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest, py
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
from _pytest import python as funcargs
|
from _pytest import python as funcargs
|
||||||
|
|
||||||
class TestMetafunc:
|
class TestMetafunc:
|
||||||
|
@ -838,11 +840,11 @@ class TestMetafuncFunctional:
|
||||||
def test_generate_tests_only_done_in_subdir(self, testdir):
|
def test_generate_tests_only_done_in_subdir(self, testdir):
|
||||||
sub1 = testdir.mkpydir("sub1")
|
sub1 = testdir.mkpydir("sub1")
|
||||||
sub2 = testdir.mkpydir("sub2")
|
sub2 = testdir.mkpydir("sub2")
|
||||||
sub1.join("conftest.py").write(py.code.Source("""
|
sub1.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
assert metafunc.function.__name__ == "test_1"
|
assert metafunc.function.__name__ == "test_1"
|
||||||
"""))
|
"""))
|
||||||
sub2.join("conftest.py").write(py.code.Source("""
|
sub2.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
assert metafunc.function.__name__ == "test_2"
|
assert metafunc.function.__name__ == "test_2"
|
||||||
"""))
|
"""))
|
||||||
|
|
|
@ -38,10 +38,11 @@ class TestRaises:
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
import py, pytest
|
import py, pytest
|
||||||
|
import _pytest._code
|
||||||
|
|
||||||
def test_simple():
|
def test_simple():
|
||||||
with pytest.raises(ZeroDivisionError) as excinfo:
|
with pytest.raises(ZeroDivisionError) as excinfo:
|
||||||
assert isinstance(excinfo, py.code.ExceptionInfo)
|
assert isinstance(excinfo, _pytest._code.ExceptionInfo)
|
||||||
1/0
|
1/0
|
||||||
print (excinfo)
|
print (excinfo)
|
||||||
assert excinfo.type == ZeroDivisionError
|
assert excinfo.type == ZeroDivisionError
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
"PYTEST_DONT_REWRITE"
|
"PYTEST_DONT_REWRITE"
|
||||||
import pytest, py
|
import py
|
||||||
|
import pytest
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
|
|
||||||
|
|
||||||
def exvalue():
|
def exvalue():
|
||||||
return py.std.sys.exc_info()[1]
|
return py.std.sys.exc_info()[1]
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
import py, pytest
|
|
||||||
import _pytest.assertion as plugin
|
import _pytest.assertion as plugin
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
from _pytest.assertion import reinterpret
|
from _pytest.assertion import reinterpret
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ def mock_config():
|
||||||
|
|
||||||
|
|
||||||
def interpret(expr):
|
def interpret(expr):
|
||||||
return reinterpret.reinterpret(expr, py.code.Frame(sys._getframe(1)))
|
return reinterpret.reinterpret(expr, _pytest._code.Frame(sys._getframe(1)))
|
||||||
|
|
||||||
class TestBinReprIntegration:
|
class TestBinReprIntegration:
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ if sys.platform.startswith("java"):
|
||||||
# XXX should be xfail
|
# XXX should be xfail
|
||||||
pytest.skip("assert rewrite does currently not work on jython")
|
pytest.skip("assert rewrite does currently not work on jython")
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
from _pytest.assertion.rewrite import rewrite_asserts, PYTEST_TAG
|
from _pytest.assertion.rewrite import rewrite_asserts, PYTEST_TAG
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||||
|
@ -17,7 +18,7 @@ from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||||
|
|
||||||
def setup_module(mod):
|
def setup_module(mod):
|
||||||
mod._old_reprcompare = util._reprcompare
|
mod._old_reprcompare = util._reprcompare
|
||||||
py.code._reprcompare = None
|
_pytest._code._reprcompare = None
|
||||||
|
|
||||||
def teardown_module(mod):
|
def teardown_module(mod):
|
||||||
util._reprcompare = mod._old_reprcompare
|
util._reprcompare = mod._old_reprcompare
|
||||||
|
@ -31,7 +32,7 @@ def rewrite(src):
|
||||||
|
|
||||||
def getmsg(f, extra_ns=None, must_pass=False):
|
def getmsg(f, extra_ns=None, must_pass=False):
|
||||||
"""Rewrite the assertions in f, run it, and get the failure message."""
|
"""Rewrite the assertions in f, run it, and get the failure message."""
|
||||||
src = '\n'.join(py.code.Code(f).source().lines)
|
src = '\n'.join(_pytest._code.Code(f).source().lines)
|
||||||
mod = rewrite(src)
|
mod = rewrite(src)
|
||||||
code = compile(mod, "<test>", "exec")
|
code = compile(mod, "<test>", "exec")
|
||||||
ns = {}
|
ns = {}
|
||||||
|
@ -669,7 +670,7 @@ class TestAssertionRewriteHookDetails(object):
|
||||||
"""Implement optional PEP302 api (#808).
|
"""Implement optional PEP302 api (#808).
|
||||||
"""
|
"""
|
||||||
path = testdir.mkpydir("foo")
|
path = testdir.mkpydir("foo")
|
||||||
path.join("test_foo.py").write(py.code.Source("""
|
path.join("test_foo.py").write(_pytest._code.Source("""
|
||||||
class Test:
|
class Test:
|
||||||
def test_foo(self):
|
def test_foo(self):
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import _pytest
|
||||||
import pytest
|
import pytest
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import py
|
|
||||||
|
|
||||||
pytest_plugins = "pytester",
|
pytest_plugins = "pytester",
|
||||||
|
|
||||||
|
@ -129,6 +130,7 @@ def test_cache_show(testdir):
|
||||||
|
|
||||||
|
|
||||||
class TestLastFailed:
|
class TestLastFailed:
|
||||||
|
|
||||||
def test_lastfailed_usecase(self, testdir, monkeypatch):
|
def test_lastfailed_usecase(self, testdir, monkeypatch):
|
||||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
|
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
|
@ -143,7 +145,7 @@ class TestLastFailed:
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*2 failed*",
|
"*2 failed*",
|
||||||
])
|
])
|
||||||
p.write(py.code.Source("""
|
p.write(_pytest._code.Source("""
|
||||||
def test_1():
|
def test_1():
|
||||||
assert 1
|
assert 1
|
||||||
|
|
||||||
|
@ -175,11 +177,11 @@ class TestLastFailed:
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_failedfirst_order(self, testdir):
|
def test_failedfirst_order(self, testdir):
|
||||||
testdir.tmpdir.join('test_a.py').write(py.code.Source("""
|
testdir.tmpdir.join('test_a.py').write(_pytest._code.Source("""
|
||||||
def test_always_passes():
|
def test_always_passes():
|
||||||
assert 1
|
assert 1
|
||||||
"""))
|
"""))
|
||||||
testdir.tmpdir.join('test_b.py').write(py.code.Source("""
|
testdir.tmpdir.join('test_b.py').write(_pytest._code.Source("""
|
||||||
def test_always_fails():
|
def test_always_fails():
|
||||||
assert 0
|
assert 0
|
||||||
"""))
|
"""))
|
||||||
|
@ -218,7 +220,7 @@ class TestLastFailed:
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*1 failed*",
|
"*1 failed*",
|
||||||
])
|
])
|
||||||
p2.write(py.code.Source("""
|
p2.write(_pytest._code.Source("""
|
||||||
def test_b1():
|
def test_b1():
|
||||||
assert 1
|
assert 1
|
||||||
"""))
|
"""))
|
||||||
|
@ -238,7 +240,7 @@ class TestLastFailed:
|
||||||
assert 0
|
assert 0
|
||||||
""")
|
""")
|
||||||
p2 = testdir.tmpdir.join("test_something.py")
|
p2 = testdir.tmpdir.join("test_something.py")
|
||||||
p2.write(py.code.Source("""
|
p2.write(_pytest._code.Source("""
|
||||||
def test_2():
|
def test_2():
|
||||||
assert 0
|
assert 0
|
||||||
"""))
|
"""))
|
||||||
|
|
|
@ -4,6 +4,8 @@ from __future__ import with_statement
|
||||||
import pickle
|
import pickle
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
import contextlib
|
import contextlib
|
||||||
|
@ -481,7 +483,7 @@ class TestCaptureFixture:
|
||||||
|
|
||||||
def test_setup_failure_does_not_kill_capturing(testdir):
|
def test_setup_failure_does_not_kill_capturing(testdir):
|
||||||
sub1 = testdir.mkpydir("sub1")
|
sub1 = testdir.mkpydir("sub1")
|
||||||
sub1.join("conftest.py").write(py.code.Source("""
|
sub1.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
raise ValueError(42)
|
raise ValueError(42)
|
||||||
"""))
|
"""))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import py, pytest
|
import py, pytest
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
from _pytest.config import getcfg, get_common_ancestor, determine_setup
|
from _pytest.config import getcfg, get_common_ancestor, determine_setup
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ class TestParseIni:
|
||||||
def test_getcfg_and_config(self, testdir, tmpdir):
|
def test_getcfg_and_config(self, testdir, tmpdir):
|
||||||
sub = tmpdir.mkdir("sub")
|
sub = tmpdir.mkdir("sub")
|
||||||
sub.chdir()
|
sub.chdir()
|
||||||
tmpdir.join("setup.cfg").write(py.code.Source("""
|
tmpdir.join("setup.cfg").write(_pytest._code.Source("""
|
||||||
[pytest]
|
[pytest]
|
||||||
name = value
|
name = value
|
||||||
"""))
|
"""))
|
||||||
|
@ -21,7 +22,7 @@ class TestParseIni:
|
||||||
|
|
||||||
def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
|
def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
|
||||||
monkeypatch.setenv('PYTEST_ADDOPTS', '--color no -rs --tb="short"')
|
monkeypatch.setenv('PYTEST_ADDOPTS', '--color no -rs --tb="short"')
|
||||||
tmpdir.join("setup.cfg").write(py.code.Source("""
|
tmpdir.join("setup.cfg").write(_pytest._code.Source("""
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = --verbose
|
addopts = --verbose
|
||||||
"""))
|
"""))
|
||||||
|
@ -296,7 +297,7 @@ class TestConfigFromdictargs:
|
||||||
assert config.option.capture == 'no'
|
assert config.option.capture == 'no'
|
||||||
|
|
||||||
def test_inifilename(self, tmpdir):
|
def test_inifilename(self, tmpdir):
|
||||||
tmpdir.join("foo/bar.ini").ensure().write(py.code.Source("""
|
tmpdir.join("foo/bar.ini").ensure().write(_pytest._code.Source("""
|
||||||
[pytest]
|
[pytest]
|
||||||
name = value
|
name = value
|
||||||
"""))
|
"""))
|
||||||
|
@ -309,7 +310,7 @@ class TestConfigFromdictargs:
|
||||||
}
|
}
|
||||||
|
|
||||||
cwd = tmpdir.join('a/b')
|
cwd = tmpdir.join('a/b')
|
||||||
cwd.join('pytest.ini').ensure().write(py.code.Source("""
|
cwd.join('pytest.ini').ensure().write(_pytest._code.Source("""
|
||||||
[pytest]
|
[pytest]
|
||||||
name = wrong-value
|
name = wrong-value
|
||||||
should_not_be_set = true
|
should_not_be_set = true
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
import py, pytest
|
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
from _pytest.config import PytestPluginManager
|
from _pytest.config import PytestPluginManager
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
|
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
|
||||||
|
|
||||||
|
@ -156,7 +159,7 @@ def test_setinitial_conftest_subdirs(testdir, name):
|
||||||
def test_conftest_confcutdir(testdir):
|
def test_conftest_confcutdir(testdir):
|
||||||
testdir.makeconftest("assert 0")
|
testdir.makeconftest("assert 0")
|
||||||
x = testdir.mkdir("x")
|
x = testdir.mkdir("x")
|
||||||
x.join("conftest.py").write(py.code.Source("""
|
x.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--xyz", action="store_true")
|
parser.addoption("--xyz", action="store_true")
|
||||||
"""))
|
"""))
|
||||||
|
@ -174,7 +177,7 @@ def test_no_conftest(testdir):
|
||||||
|
|
||||||
def test_conftest_existing_resultlog(testdir):
|
def test_conftest_existing_resultlog(testdir):
|
||||||
x = testdir.mkdir("tests")
|
x = testdir.mkdir("tests")
|
||||||
x.join("conftest.py").write(py.code.Source("""
|
x.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--xyz", action="store_true")
|
parser.addoption("--xyz", action="store_true")
|
||||||
"""))
|
"""))
|
||||||
|
@ -184,7 +187,7 @@ def test_conftest_existing_resultlog(testdir):
|
||||||
|
|
||||||
def test_conftest_existing_junitxml(testdir):
|
def test_conftest_existing_junitxml(testdir):
|
||||||
x = testdir.mkdir("tests")
|
x = testdir.mkdir("tests")
|
||||||
x.join("conftest.py").write(py.code.Source("""
|
x.join("conftest.py").write(_pytest._code.Source("""
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--xyz", action="store_true")
|
parser.addoption("--xyz", action="store_true")
|
||||||
"""))
|
"""))
|
||||||
|
@ -361,18 +364,18 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error):
|
||||||
root = testdir.tmpdir
|
root = testdir.tmpdir
|
||||||
src = root.join('src').ensure(dir=1)
|
src = root.join('src').ensure(dir=1)
|
||||||
src.join('pytest.ini').write('[pytest]')
|
src.join('pytest.ini').write('[pytest]')
|
||||||
src.join('conftest.py').write(py.code.Source("""
|
src.join('conftest.py').write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def fix1(): pass
|
def fix1(): pass
|
||||||
"""))
|
"""))
|
||||||
src.join('test_foo.py').write(py.code.Source("""
|
src.join('test_foo.py').write(_pytest._code.Source("""
|
||||||
def test_1(fix1):
|
def test_1(fix1):
|
||||||
pass
|
pass
|
||||||
def test_2(out_of_reach):
|
def test_2(out_of_reach):
|
||||||
pass
|
pass
|
||||||
"""))
|
"""))
|
||||||
root.join('conftest.py').write(py.code.Source("""
|
root.join('conftest.py').write(_pytest._code.Source("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def out_of_reach(): pass
|
def out_of_reach(): pass
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
import sys
|
import sys
|
||||||
|
import _pytest._code
|
||||||
from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
|
from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
|
||||||
import py
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
class TestDoctests:
|
class TestDoctests:
|
||||||
|
@ -181,7 +181,7 @@ class TestDoctests:
|
||||||
assert 'text-line-after' not in result.stdout.str()
|
assert 'text-line-after' not in result.stdout.str()
|
||||||
|
|
||||||
def test_doctest_linedata_missing(self, testdir):
|
def test_doctest_linedata_missing(self, testdir):
|
||||||
testdir.tmpdir.join('hello.py').write(py.code.Source("""
|
testdir.tmpdir.join('hello.py').write(_pytest._code.Source("""
|
||||||
class Fun(object):
|
class Fun(object):
|
||||||
@property
|
@property
|
||||||
def test(self):
|
def test(self):
|
||||||
|
@ -201,7 +201,7 @@ class TestDoctests:
|
||||||
|
|
||||||
|
|
||||||
def test_doctest_unex_importerror(self, testdir):
|
def test_doctest_unex_importerror(self, testdir):
|
||||||
testdir.tmpdir.join("hello.py").write(py.code.Source("""
|
testdir.tmpdir.join("hello.py").write(_pytest._code.Source("""
|
||||||
import asdalsdkjaslkdjasd
|
import asdalsdkjaslkdjasd
|
||||||
"""))
|
"""))
|
||||||
testdir.maketxtfile("""
|
testdir.maketxtfile("""
|
||||||
|
@ -229,7 +229,7 @@ class TestDoctests:
|
||||||
|
|
||||||
def test_doctestmodule_external_and_issue116(self, testdir):
|
def test_doctestmodule_external_and_issue116(self, testdir):
|
||||||
p = testdir.mkpydir("hello")
|
p = testdir.mkpydir("hello")
|
||||||
p.join("__init__.py").write(py.code.Source("""
|
p.join("__init__.py").write(_pytest._code.Source("""
|
||||||
def somefunc():
|
def somefunc():
|
||||||
'''
|
'''
|
||||||
>>> i = 0
|
>>> i = 0
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
import py
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
|
|
||||||
|
|
||||||
def runpdb_and_get_report(testdir, source):
|
def runpdb_and_get_report(testdir, source):
|
||||||
p = testdir.makepyfile(source)
|
p = testdir.makepyfile(source)
|
||||||
result = testdir.runpytest_inprocess("--pdb", p)
|
result = testdir.runpytest_inprocess("--pdb", p)
|
||||||
|
@ -27,7 +28,7 @@ class TestPDB:
|
||||||
""")
|
""")
|
||||||
assert rep.failed
|
assert rep.failed
|
||||||
assert len(pdblist) == 1
|
assert len(pdblist) == 1
|
||||||
tb = py.code.Traceback(pdblist[0][0])
|
tb = _pytest._code.Traceback(pdblist[0][0])
|
||||||
assert tb[-1].name == "test_func"
|
assert tb[-1].name == "test_func"
|
||||||
|
|
||||||
def test_pdb_on_xfail(self, testdir, pdblist):
|
def test_pdb_on_xfail(self, testdir, pdblist):
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import py, pytest
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
|
from _pytest.main import Node, Item, FSCollector
|
||||||
from _pytest.resultlog import generic_path, ResultLog, \
|
from _pytest.resultlog import generic_path, ResultLog, \
|
||||||
pytest_configure, pytest_unconfigure
|
pytest_configure, pytest_unconfigure
|
||||||
from _pytest.main import Node, Item, FSCollector
|
|
||||||
|
|
||||||
def test_generic_path(testdir):
|
def test_generic_path(testdir):
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
|
@ -140,7 +144,7 @@ class TestWithFunctionIntegration:
|
||||||
try:
|
try:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
reslog = ResultLog(None, py.io.TextIO())
|
reslog = ResultLog(None, py.io.TextIO())
|
||||||
reslog.pytest_internalerror(excinfo.getrepr(style=style))
|
reslog.pytest_internalerror(excinfo.getrepr(style=style))
|
||||||
entry = reslog.logfile.getvalue()
|
entry = reslog.logfile.getvalue()
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import pytest, py, sys, os
|
import _pytest._code
|
||||||
|
import os
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
from _pytest import runner, main
|
from _pytest import runner, main
|
||||||
|
|
||||||
class TestSetupState:
|
class TestSetupState:
|
||||||
|
@ -408,14 +412,14 @@ def test_pytest_exit():
|
||||||
try:
|
try:
|
||||||
pytest.exit("hello")
|
pytest.exit("hello")
|
||||||
except pytest.exit.Exception:
|
except pytest.exit.Exception:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
assert excinfo.errisinstance(KeyboardInterrupt)
|
assert excinfo.errisinstance(KeyboardInterrupt)
|
||||||
|
|
||||||
def test_pytest_fail():
|
def test_pytest_fail():
|
||||||
try:
|
try:
|
||||||
pytest.fail("hello")
|
pytest.fail("hello")
|
||||||
except pytest.fail.Exception:
|
except pytest.fail.Exception:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
s = excinfo.exconly(tryshort=True)
|
s = excinfo.exconly(tryshort=True)
|
||||||
assert s.startswith("Failed")
|
assert s.startswith("Failed")
|
||||||
|
|
||||||
|
@ -459,7 +463,7 @@ def test_exception_printing_skip():
|
||||||
try:
|
try:
|
||||||
pytest.skip("hello")
|
pytest.skip("hello")
|
||||||
except pytest.skip.Exception:
|
except pytest.skip.Exception:
|
||||||
excinfo = py.code.ExceptionInfo()
|
excinfo = _pytest._code.ExceptionInfo()
|
||||||
s = excinfo.exconly(tryshort=True)
|
s = excinfo.exconly(tryshort=True)
|
||||||
assert s.startswith("Skipped")
|
assert s.startswith("Skipped")
|
||||||
|
|
||||||
|
@ -488,7 +492,7 @@ def test_importorskip(monkeypatch):
|
||||||
mod2 = pytest.importorskip("hello123", minversion="1.3")
|
mod2 = pytest.importorskip("hello123", minversion="1.3")
|
||||||
assert mod2 == mod
|
assert mod2 == mod
|
||||||
except pytest.skip.Exception:
|
except pytest.skip.Exception:
|
||||||
print(py.code.ExceptionInfo())
|
print(_pytest._code.ExceptionInfo())
|
||||||
pytest.fail("spurious skip")
|
pytest.fail("spurious skip")
|
||||||
|
|
||||||
def test_importorskip_imports_last_module_part():
|
def test_importorskip_imports_last_module_part():
|
||||||
|
@ -505,7 +509,7 @@ def test_importorskip_dev_module(monkeypatch):
|
||||||
pytest.raises(pytest.skip.Exception, """
|
pytest.raises(pytest.skip.Exception, """
|
||||||
pytest.importorskip('mockmodule1', minversion='0.14.0')""")
|
pytest.importorskip('mockmodule1', minversion='0.14.0')""")
|
||||||
except pytest.skip.Exception:
|
except pytest.skip.Exception:
|
||||||
print(py.code.ExceptionInfo())
|
print(_pytest._code.ExceptionInfo())
|
||||||
pytest.fail("spurious skip")
|
pytest.fail("spurious skip")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,17 @@
|
||||||
terminal reporting of the full testing process.
|
terminal reporting of the full testing process.
|
||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
import pytest
|
|
||||||
import py
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import _pytest._pluggy as pluggy
|
||||||
|
import _pytest._code
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
|
from _pytest import runner
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||||
from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
|
from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
|
||||||
from _pytest.terminal import build_summary_stats_line, _plugin_nameversions
|
from _pytest.terminal import build_summary_stats_line, _plugin_nameversions
|
||||||
from _pytest import runner
|
|
||||||
import _pytest._pluggy as pluggy
|
|
||||||
|
|
||||||
def basic_run_report(item):
|
def basic_run_report(item):
|
||||||
runner.call_and_report(item, "setup", log=False)
|
runner.call_and_report(item, "setup", log=False)
|
||||||
|
@ -153,7 +155,7 @@ class TestTerminal:
|
||||||
|
|
||||||
def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir):
|
def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir):
|
||||||
a = testdir.mkpydir("a123")
|
a = testdir.mkpydir("a123")
|
||||||
a.join("test_hello123.py").write(py.code.Source("""
|
a.join("test_hello123.py").write(_pytest._code.Source("""
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_method(self):
|
def test_method(self):
|
||||||
pass
|
pass
|
||||||
|
@ -268,7 +270,7 @@ class TestCollectonly:
|
||||||
p = testdir.makepyfile("import Errlkjqweqwe")
|
p = testdir.makepyfile("import Errlkjqweqwe")
|
||||||
result = testdir.runpytest("--collect-only", p)
|
result = testdir.runpytest("--collect-only", p)
|
||||||
assert result.ret == 1
|
assert result.ret == 1
|
||||||
result.stdout.fnmatch_lines(py.code.Source("""
|
result.stdout.fnmatch_lines(_pytest._code.Source("""
|
||||||
*ERROR*
|
*ERROR*
|
||||||
*import Errlk*
|
*import Errlk*
|
||||||
*ImportError*
|
*ImportError*
|
||||||
|
|
|
@ -260,6 +260,7 @@ def test_testcase_custom_exception_info(testdir, type):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
import py, pytest
|
import py, pytest
|
||||||
|
import _pytest._code
|
||||||
class MyTestCase(TestCase):
|
class MyTestCase(TestCase):
|
||||||
def run(self, result):
|
def run(self, result):
|
||||||
excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
|
excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
|
||||||
|
@ -269,7 +270,7 @@ def test_testcase_custom_exception_info(testdir, type):
|
||||||
def t(*args):
|
def t(*args):
|
||||||
mp.undo()
|
mp.undo()
|
||||||
raise TypeError()
|
raise TypeError()
|
||||||
mp.setattr(py.code, 'ExceptionInfo', t)
|
mp.setattr(_pytest._code, 'ExceptionInfo', t)
|
||||||
try:
|
try:
|
||||||
excinfo = excinfo._excinfo
|
excinfo = excinfo._excinfo
|
||||||
result.add%(type)s(self, excinfo)
|
result.add%(type)s(self, excinfo)
|
||||||
|
|
Loading…
Reference in New Issue