tools: Make python_gut more correct.
- Using the ast module to correctly determine the line numbers of the function bodies. - Should have done it this way in the beginning, but didn't know about ast module tracking line numbers. - No more heuristics. - Copes with different size indentations.
This commit is contained in:
parent
c8638c1bbe
commit
d471934946
2 changed files with 25 additions and 60 deletions
66
eris/gut.py
66
eris/gut.py
|
|
@ -6,66 +6,44 @@
|
||||||
This can be useful when initially reading a codebase.
|
This can be useful when initially reading a codebase.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import ast
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
USAGE = """Usage: gut.py <python file>
|
USAGE = """Usage: gut.py <python-file>
|
||||||
|
|
||||||
# gut.py test.py"""
|
# gut.py test.py"""
|
||||||
|
|
||||||
|
|
||||||
INDENT_SIZE = 4
|
def _function_body_lines(module_contents):
|
||||||
TAB_SIZE = 4
|
ranges = []
|
||||||
|
class FuncNodeVisitor(ast.NodeVisitor):
|
||||||
|
|
||||||
|
def _line_range(self, body):
|
||||||
|
return body[0].lineno - 1, body[-1].end_lineno
|
||||||
|
|
||||||
def _indentation_of_line(line):
|
def visit_FunctionDef(self, node):
|
||||||
indentation = 0
|
ranges.append(self._line_range(node.body))
|
||||||
for character in line:
|
|
||||||
if character == " ":
|
|
||||||
indentation += 1
|
|
||||||
elif character == "\t":
|
|
||||||
indentation += TAB_SIZE
|
|
||||||
elif character == "\n":
|
|
||||||
return None
|
|
||||||
else: # Is a non-whitespace character.
|
|
||||||
return indentation
|
|
||||||
|
|
||||||
|
def visit_AsyncFunctionDef(self, node):
|
||||||
def _is_start_line_of_signature(line):
|
ranges.append(self._line_range(node.body))
|
||||||
return re.match(r"^\s*(async)?\s*def\s", line) is not None
|
visitor = FuncNodeVisitor()
|
||||||
|
tree = ast.parse(module_contents)
|
||||||
|
visitor.visit(tree)
|
||||||
def _is_end_line_of_signature(line):
|
return ranges
|
||||||
return (re.match(r".*\):\s*\n$", line) is not None or
|
|
||||||
re.match(r".*\):\s*#.*\n$", line) is not None)
|
|
||||||
|
|
||||||
|
|
||||||
def gut_module(module_contents):
|
def gut_module(module_contents):
|
||||||
"""Gut a string of a module's contents."""
|
ranges = _function_body_lines(module_contents)
|
||||||
SIGNATURE, BODY, TOP_LEVEL = 1, 2, 3
|
lines = module_contents.splitlines(keepends=True)
|
||||||
state = TOP_LEVEL
|
deleted = 0
|
||||||
body_depth = 0
|
for start_line, end_line in ranges:
|
||||||
result = []
|
del lines[start_line-deleted:end_line-deleted]
|
||||||
for line in module_contents.splitlines(keepends=True):
|
deleted += (end_line - start_line)
|
||||||
indent = _indentation_of_line(line)
|
return "".join(lines)
|
||||||
if state == BODY and indent is not None and \
|
|
||||||
indent < body_depth:
|
|
||||||
state = TOP_LEVEL
|
|
||||||
result.append("\n")
|
|
||||||
if state == TOP_LEVEL and _is_start_line_of_signature(line):
|
|
||||||
state = SIGNATURE
|
|
||||||
body_depth = indent + INDENT_SIZE
|
|
||||||
if state == SIGNATURE and _is_end_line_of_signature(line):
|
|
||||||
result.append(line)
|
|
||||||
state = BODY
|
|
||||||
elif state != BODY:
|
|
||||||
result.append(line)
|
|
||||||
return "".join(result)
|
|
||||||
|
|
||||||
|
|
||||||
def main(module_path):
|
def main(module_path):
|
||||||
"""Gut the module at module_path."""
|
|
||||||
with open(module_path) as module_file:
|
with open(module_path) as module_file:
|
||||||
print(gut_module(module_file.read()))
|
print(gut_module(module_file.read()))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,27 +90,16 @@ class GutTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def test_multiline_signature(self):
|
def test_multiline_signature(self):
|
||||||
program = textwrap.dedent("""
|
program = textwrap.dedent("""
|
||||||
def bar(a, b
|
def bar(a, b,
|
||||||
c, d):
|
c, d):
|
||||||
a = 1
|
a = 1
|
||||||
""")
|
""")
|
||||||
expected = textwrap.dedent("""
|
expected = textwrap.dedent("""
|
||||||
def bar(a, b
|
def bar(a, b,
|
||||||
c, d):
|
c, d):
|
||||||
""")
|
""")
|
||||||
self.assertEqual(gut.gut_module(program), expected)
|
self.assertEqual(gut.gut_module(program), expected)
|
||||||
|
|
||||||
def test_tab_in_indentation(self):
|
|
||||||
program = textwrap.dedent("""
|
|
||||||
def bar():
|
|
||||||
a = 1
|
|
||||||
\tb=2
|
|
||||||
""")
|
|
||||||
expected = textwrap.dedent("""
|
|
||||||
def bar():
|
|
||||||
""")
|
|
||||||
self.assertEqual(gut.gut_module(program), expected)
|
|
||||||
|
|
||||||
def test_comment_in_signature_line(self):
|
def test_comment_in_signature_line(self):
|
||||||
program = textwrap.dedent("""
|
program = textwrap.dedent("""
|
||||||
def bar(): # comment
|
def bar(): # comment
|
||||||
|
|
@ -142,9 +131,6 @@ class GutTestCase(unittest.TestCase):
|
||||||
""")
|
""")
|
||||||
expected = textwrap.dedent("""
|
expected = textwrap.dedent("""
|
||||||
def bar():
|
def bar():
|
||||||
|
|
||||||
# comment
|
|
||||||
pass
|
|
||||||
""")
|
""")
|
||||||
self.assertEqual(gut.gut_module(program), expected)
|
self.assertEqual(gut.gut_module(program), expected)
|
||||||
|
|
||||||
|
|
@ -153,6 +139,7 @@ class GutTestCase(unittest.TestCase):
|
||||||
def bar():
|
def bar():
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# comment
|
# comment
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue