Merge pull request #4069 from asottile/deindent_4066
Fix source reindenting by using `textwrap.dedent` directly.
This commit is contained in:
commit
b098292352
|
@ -0,0 +1 @@
|
||||||
|
Fix source reindenting by using ``textwrap.dedent`` directly.
|
|
@ -7,6 +7,7 @@ import linecache
|
||||||
import sys
|
import sys
|
||||||
import six
|
import six
|
||||||
import inspect
|
import inspect
|
||||||
|
import textwrap
|
||||||
import tokenize
|
import tokenize
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -23,7 +24,6 @@ class Source(object):
|
||||||
def __init__(self, *parts, **kwargs):
|
def __init__(self, *parts, **kwargs):
|
||||||
self.lines = lines = []
|
self.lines = lines = []
|
||||||
de = kwargs.get("deindent", True)
|
de = kwargs.get("deindent", True)
|
||||||
rstrip = kwargs.get("rstrip", True)
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if not part:
|
if not part:
|
||||||
partlines = []
|
partlines = []
|
||||||
|
@ -33,11 +33,6 @@ class Source(object):
|
||||||
partlines = [x.rstrip("\n") for x in part]
|
partlines = [x.rstrip("\n") for x in part]
|
||||||
elif isinstance(part, six.string_types):
|
elif isinstance(part, six.string_types):
|
||||||
partlines = part.split("\n")
|
partlines = part.split("\n")
|
||||||
if rstrip:
|
|
||||||
while partlines:
|
|
||||||
if partlines[-1].strip():
|
|
||||||
break
|
|
||||||
partlines.pop()
|
|
||||||
else:
|
else:
|
||||||
partlines = getsource(part, deindent=de).lines
|
partlines = getsource(part, deindent=de).lines
|
||||||
if de:
|
if de:
|
||||||
|
@ -115,17 +110,10 @@ class Source(object):
|
||||||
ast, start, end = getstatementrange_ast(lineno, self)
|
ast, start, end = getstatementrange_ast(lineno, self)
|
||||||
return start, end
|
return start, end
|
||||||
|
|
||||||
def deindent(self, offset=None):
|
def deindent(self):
|
||||||
""" return a new source object deindented by offset.
|
"""return a new source object deindented."""
|
||||||
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 = Source()
|
||||||
newsource.lines[:] = deindent(self.lines, offset)
|
newsource.lines[:] = deindent(self.lines)
|
||||||
return newsource
|
return newsource
|
||||||
|
|
||||||
def isparseable(self, deindent=True):
|
def isparseable(self, deindent=True):
|
||||||
|
@ -268,47 +256,8 @@ def getsource(obj, **kwargs):
|
||||||
return Source(strsrc, **kwargs)
|
return Source(strsrc, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def deindent(lines, offset=None):
|
def deindent(lines):
|
||||||
if offset is None:
|
return textwrap.dedent("\n".join(lines)).splitlines()
|
||||||
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"
|
|
||||||
|
|
||||||
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):
|
def get_statement_startend2(lineno, node):
|
||||||
|
|
|
@ -27,16 +27,7 @@ def test_source_str_function():
|
||||||
x = Source(
|
x = Source(
|
||||||
"""
|
"""
|
||||||
3
|
3
|
||||||
""",
|
|
||||||
rstrip=False,
|
|
||||||
)
|
|
||||||
assert str(x) == "\n3\n "
|
|
||||||
|
|
||||||
x = Source(
|
|
||||||
"""
|
"""
|
||||||
3
|
|
||||||
""",
|
|
||||||
rstrip=True,
|
|
||||||
)
|
)
|
||||||
assert str(x) == "\n3"
|
assert str(x) == "\n3"
|
||||||
|
|
||||||
|
@ -400,10 +391,13 @@ def test_getfuncsource_with_multine_string():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert (
|
expected = '''\
|
||||||
str(_pytest._code.Source(f)).strip()
|
def f():
|
||||||
== 'def f():\n c = """while True:\n pass\n"""'
|
c = """while True:
|
||||||
)
|
pass
|
||||||
|
"""
|
||||||
|
'''
|
||||||
|
assert str(_pytest._code.Source(f)) == expected.rstrip()
|
||||||
|
|
||||||
|
|
||||||
def test_deindent():
|
def test_deindent():
|
||||||
|
@ -411,21 +405,13 @@ def test_deindent():
|
||||||
|
|
||||||
assert deindent(["\tfoo", "\tbar"]) == ["foo", "bar"]
|
assert deindent(["\tfoo", "\tbar"]) == ["foo", "bar"]
|
||||||
|
|
||||||
def f():
|
source = """\
|
||||||
c = """while True:
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
|
|
||||||
lines = deindent(inspect.getsource(f).splitlines())
|
|
||||||
assert lines == ["def f():", ' c = """while True:', " pass", '"""']
|
|
||||||
|
|
||||||
source = """
|
|
||||||
def f():
|
def f():
|
||||||
def g():
|
def g():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
lines = deindent(source.splitlines())
|
lines = deindent(source.splitlines())
|
||||||
assert lines == ["", "def f():", " def g():", " pass", " "]
|
assert lines == ["def f():", " def g():", " pass"]
|
||||||
|
|
||||||
|
|
||||||
def test_source_of_class_at_eof_without_newline(tmpdir):
|
def test_source_of_class_at_eof_without_newline(tmpdir):
|
||||||
|
|
Loading…
Reference in New Issue