Skip to content

Instantly share code, notes, and snippets.

@xni
Last active June 16, 2019 22:20
Show Gist options
  • Save xni/f6ce6d5a832b8c5543d46150c83b1c92 to your computer and use it in GitHub Desktop.
Save xni/f6ce6d5a832b8c5543d46150c83b1c92 to your computer and use it in GitHub Desktop.
Try counter
"""
This module counts amount of try and try/else statements
in the files.
Usage:
find ../cpython/Lib/ -type f -name '*.py' | xargs python3.7 main.py output.md FILE1 FILE2 ...
"""
import ast
import logging
import multiprocessing
import pathlib
import os
import sys
class TryVisitor(ast.NodeVisitor):
"""
TryVisitor collects all of the finally statements from the code,
caprturing the enclosing function definitions.
"""
def __init__(self):
self.total_trys = 0
self.trys_elses = 0
super(TryVisitor, self).__init__()
def visit_Try(self, node):
self.total_trys += 1
if node.orelse:
self.trys_elses += 1
def get_try_cnts(path):
logger = logging.getLogger(__name__)
try:
tree = ast.parse(path.read_text('utf-8'))
except:
logger.info("Unable to parse the file %s", path)
return 0, 0
v = TryVisitor()
v.visit(tree)
logger.debug("%s Try's with else: %d, total try's: %d", path, v.trys_elses, v.total_trys)
return v.trys_elses, v.total_trys
def main(paths):
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
logger = logging.getLogger(__name__)
pool = multiprocessing.Pool(processes=4)
else_trys = 0
total_trys = 0
paths = filter(lambda p: not p.name.startswith('test_'), paths)
analyzed_files = 0
for else_trys_in_file, total_trys_in_file in pool.imap_unordered(get_try_cnts, paths):
analyzed_files += 1
else_trys += else_trys_in_file
total_trys += total_trys_in_file
logger.info("Try's with else: %d, total try's: %d in %d files", else_trys, total_trys, analyzed_files)
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument('paths', nargs='+')
ns = parser.parse_args()
sys.exit(
main([pathlib.Path(p) for p in ns.paths])
)
"""
This module provides a tool to search for
try:
STMT
else:
STMT
finally:
STMT
patterns in the files.
Usage:
find ../cpython/Lib/ -type f -name '*.py' | xargs -n 1 -I{} python3.7 main.py {} output.md
"""
import ast
import logging
import pathlib
import os
import sys
class TryVisitor(ast.NodeVisitor):
"""
TryVisitor collects all of the finally statements from the code,
caprturing the enclosing function definitions.
"""
def __init__(self):
self.enclosing_fn = None
self._found_fns = {}
super(TryVisitor, self).__init__()
def visit_Try(self, node):
if node.finalbody and node.orelse:
self.on_found()
def visit_FunctionDef(self, node):
self.enclosing_fn = node
self.generic_visit(node)
def on_found(self):
fn_hash = self.enclosing_fn.lineno
self._found_fns[fn_hash] = self.enclosing_fn
@property
def found_fns(self):
return self._found_fns.values()
def get_nodes_line_range(arg_node):
"""
get_nodes_line_range finds the range of lines where the function's
source code is located.
"""
max_child_line = arg_node.lineno
for subnode in ast.walk(arg_node):
try:
max_child_line = max(max_child_line, subnode.lineno)
except:
pass
return arg_node.lineno, max_child_line
def get_next_line(tree, lineno):
result = None
for node in ast.walk(tree):
try:
if node.lineno > lineno and (not result or result > node.lineno):
result = node.lineno
except:
pass
return result
def main(path, output):
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
logger.info('Analyzing %s', path)
if (path.name.startswith('test_')):
logger.info('Skipping because it is a test')
return
filecontents = path.read_text('utf-8')
try:
tree = ast.parse(filecontents)
except:
logger.info("Unable to parse the file")
return
visitor = TryVisitor()
visitor.visit(tree)
lines = filecontents.splitlines()
if not visitor.found_fns:
logger.info("No patterns found")
return
data = [f"== {path}"]
for fn_node in visitor.found_fns:
rg = get_nodes_line_range(fn_node)
line_after_rg = get_next_line(tree, rg[1])
if line_after_rg:
line_after_rg -= 1
else:
line_after_rg = len(lines)
data.append(f"=== {fn_node.name} ({rg[0]} - {line_after_rg})")
offset = fn_node.col_offset
data.append('```')
data.extend(
lines[line][offset:] for line in range(rg[0]-1, line_after_rg)
)
data.append('```')
with open(output, 'a', encoding='utf-8') as output_file:
for line in data:
output_file.write(line)
output_file.write('\n')
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument('path')
parser.add_argument('output')
ns = parser.parse_args()
sys.exit(main(pathlib.Path(ns.path), pathlib.Path(ns.output)))
try:
st = _stat(fn)
except OSError:
# File most likely does not exist
pass
else:
# XXX What about other special files? (sockets, devices...)
if stat.S_ISFIFO(st.st_mode):
fn = fn.path if isinstance(fn, os.DirEntry) else fn
raise SpecialFileError("`%s` is a named pipe" % fn)
if _WINDOWS and i == 0:
file_size = st.st_size
try:
method = getattr(router, action)
except AttributeError:
# If the router doesn't have a method, skip to the next one.
pass
else:
chosen_db = method(model, **hints)
if chosen_db:
return chosen_db
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment