Skip to content

Commit f8791c9

Browse files
authored
Merge pull request #3292 from RonnyPfannschmidt/exception-attrs
internal refactor port exc FOrmattedExcinfo to attrs, remove old code
2 parents 0557ab4 + d2dbbd4 commit f8791c9

File tree

3 files changed

+23
-66
lines changed

3 files changed

+23
-66
lines changed

_pytest/_code/code.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import sys
44
import traceback
55
from inspect import CO_VARARGS, CO_VARKEYWORDS
6+
7+
import attr
68
import re
79
from weakref import ref
810
from _pytest.compat import _PY2, _PY3, PY35, safe_str
@@ -458,19 +460,19 @@ def match(self, regexp):
458460
return True
459461

460462

463+
@attr.s
461464
class FormattedExcinfo(object):
462465
""" presenting information about failing Functions and Generators. """
463466
# for traceback entries
464467
flow_marker = ">"
465468
fail_marker = "E"
466469

467-
def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
468-
self.showlocals = showlocals
469-
self.style = style
470-
self.tbfilter = tbfilter
471-
self.funcargs = funcargs
472-
self.abspath = abspath
473-
self.astcache = {}
470+
showlocals = attr.ib(default=False)
471+
style = attr.ib(default="long")
472+
abspath = attr.ib(default=True)
473+
tbfilter = attr.ib(default=True)
474+
funcargs = attr.ib(default=False)
475+
astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False)
474476

475477
def _getindent(self, source):
476478
# figure out indent for given source

_pytest/_code/source.py

Lines changed: 13 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, *parts, **kwargs):
2626
for part in parts:
2727
if not part:
2828
partlines = []
29-
if isinstance(part, Source):
29+
elif isinstance(part, Source):
3030
partlines = part.lines
3131
elif isinstance(part, (tuple, list)):
3232
partlines = [x.rstrip("\n") for x in part]
@@ -98,14 +98,14 @@ def indent(self, indent=' ' * 4):
9898
newsource.lines = [(indent + line) for line in self.lines]
9999
return newsource
100100

101-
def getstatement(self, lineno, assertion=False):
101+
def getstatement(self, lineno):
102102
""" return Source statement which contains the
103103
given linenumber (counted from 0).
104104
"""
105-
start, end = self.getstatementrange(lineno, assertion)
105+
start, end = self.getstatementrange(lineno)
106106
return self[start:end]
107107

108-
def getstatementrange(self, lineno, assertion=False):
108+
def getstatementrange(self, lineno):
109109
""" return (start, end) tuple which spans the minimal
110110
statement region which containing the given lineno.
111111
"""
@@ -131,13 +131,7 @@ def isparseable(self, deindent=True):
131131
""" return True if source is parseable, heuristically
132132
deindenting it by default.
133133
"""
134-
try:
135-
import parser
136-
except ImportError:
137-
def syntax_checker(x):
138-
return compile(x, 'asd', 'exec')
139-
else:
140-
syntax_checker = parser.suite
134+
from parser import suite as syntax_checker
141135

142136
if deindent:
143137
source = str(self.deindent())
@@ -219,9 +213,9 @@ def getfslineno(obj):
219213
""" Return source location (path, lineno) for the given object.
220214
If the source cannot be determined return ("", -1)
221215
"""
222-
import _pytest._code
216+
from .code import Code
223217
try:
224-
code = _pytest._code.Code(obj)
218+
code = Code(obj)
225219
except TypeError:
226220
try:
227221
fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
@@ -259,8 +253,8 @@ def findsource(obj):
259253

260254

261255
def getsource(obj, **kwargs):
262-
import _pytest._code
263-
obj = _pytest._code.getrawcode(obj)
256+
from .code import getrawcode
257+
obj = getrawcode(obj)
264258
try:
265259
strsrc = inspect.getsource(obj)
266260
except IndentationError:
@@ -286,8 +280,6 @@ def deindent(lines, offset=None):
286280
def readline_generator(lines):
287281
for line in lines:
288282
yield line + '\n'
289-
while True:
290-
yield ''
291283

292284
it = readline_generator(lines)
293285

@@ -318,9 +310,9 @@ def get_statement_startend2(lineno, node):
318310
# AST's line numbers start indexing at 1
319311
values = []
320312
for x in ast.walk(node):
321-
if isinstance(x, ast.stmt) or isinstance(x, ast.ExceptHandler):
313+
if isinstance(x, (ast.stmt, ast.ExceptHandler)):
322314
values.append(x.lineno - 1)
323-
for name in "finalbody", "orelse":
315+
for name in ("finalbody", "orelse"):
324316
val = getattr(x, name, None)
325317
if val:
326318
# treat the finally/orelse part as its own statement
@@ -338,11 +330,8 @@ def get_statement_startend2(lineno, node):
338330
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
339331
if astnode is None:
340332
content = str(source)
341-
try:
342-
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
343-
except ValueError:
344-
start, end = getstatementrange_old(lineno, source, assertion)
345-
return None, start, end
333+
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
334+
346335
start, end = get_statement_startend2(lineno, astnode)
347336
# we need to correct the end:
348337
# - ast-parsing strips comments
@@ -374,38 +363,3 @@ def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
374363
else:
375364
break
376365
return astnode, start, end
377-
378-
379-
def getstatementrange_old(lineno, source, assertion=False):
380-
""" return (start, end) tuple which spans the minimal
381-
statement region which containing the given lineno.
382-
raise an IndexError if no such statementrange can be found.
383-
"""
384-
# XXX this logic is only used on python2.4 and below
385-
# 1. find the start of the statement
386-
from codeop import compile_command
387-
for start in range(lineno, -1, -1):
388-
if assertion:
389-
line = source.lines[start]
390-
# the following lines are not fully tested, change with care
391-
if 'super' in line and 'self' in line and '__init__' in line:
392-
raise IndexError("likely a subclass")
393-
if "assert" not in line and "raise" not in line:
394-
continue
395-
trylines = source.lines[start:lineno + 1]
396-
# quick hack to prepare parsing an indented line with
397-
# compile_command() (which errors on "return" outside defs)
398-
trylines.insert(0, 'def xxx():')
399-
trysource = '\n '.join(trylines)
400-
# ^ space here
401-
try:
402-
compile_command(trysource)
403-
except (SyntaxError, OverflowError, ValueError):
404-
continue
405-
406-
# 2. find the end of the statement
407-
for end in range(lineno + 1, len(source) + 1):
408-
trysource = source[start:end]
409-
if trysource.isparseable():
410-
return start, end
411-
raise SyntaxError("no valid source range around line %d " % (lineno,))

changelog/3292.trivial.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Internal refactoring of ``FormattedExcinfo`` to use ``attrs`` facilities and remove old support code for legacy Python versions.

0 commit comments

Comments
 (0)