eris/eris/gut.py

81 lines
2 KiB
Python
Executable file

#!/usr/bin/env python3.8
# Copyright (C) 2015-2019 Andrew Hamilton. All rights reserved.
# Licensed under the Artistic License 2.0.
"""Gut shows a python file with the bodies of functions and methods removed.
This can be useful when initially reading a codebase.
"""
import re
import sys
USAGE = """Usage: gut.py <python file>
# gut.py test.py"""
INDENT_SIZE = 4
TAB_SIZE = 4
def _indentation_of_line(line):
indentation = 0
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 _is_start_line_of_signature(line):
return re.match(r"^\s*(async)?\s*def\s", line) is not None
def _is_end_line_of_signature(line):
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):
"""Gut a string of a module's contents."""
SIGNATURE, BODY, TOP_LEVEL = 1, 2, 3
state = TOP_LEVEL
body_depth = 0
result = []
for line in module_contents.splitlines(keepends=True):
indent = _indentation_of_line(line)
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):
"""Gut the module at module_path."""
with open(module_path) as module_file:
print(gut_module(module_file.read()))
if __name__ == "__main__":
if len(sys.argv) != 2:
print(USAGE)
sys.exit(-1)
main(sys.argv[1])