Skip to content

Instantly share code, notes, and snippets.

@boxed
Last active September 14, 2018 00:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boxed/e60e3e19967385dc2c7f0de483723502 to your computer and use it in GitHub Desktop.
Save boxed/e60e3e19967385dc2c7f0de483723502 to your computer and use it in GitHub Desktop.
import os
from parso import parse
from collections import defaultdict
def get_arguments(i):
# parso represents functions with one argument and multiple arguments very differently, need to handle this here
if i.type == 'arglist':
return i.children
else:
return [i]
def param_name(i):
if i.children[0].type == 'tfpdef':
return i.children[0].children[0].value
else:
return i.children[0].value
def handle_list(result, filename):
for i in result.children:
handle_node(i, filename=filename)
def handle_node(i, filename):
t = i.type
if t == 'atom_expr':
arguments = None
if len(i.children) == 2:
# normal function call
if i.children[0].type == 'name':
function_name = i.children[0].value
arguments = [node for node in get_arguments(i.children[1].children[1]) if node.type != 'operator'] # filter out ,
else:
if (i.children[-2].children[0].type, i.children[-2].children[0].value) == ('operator', '.'):
# member function call
function_name = i.children[-2].children[1].value
arguments = [node for node in get_arguments(i.children[-1].children[1]) if node.type != 'operator'] # filter out ,
else:
# list comprehensions and stuff
pass
if arguments and arguments[0].type != 'subscript' and len(i.children) > 2:
argument_values = [handle_argument(argument) for argument in arguments]
if all([x != None for x in argument_values]) and len(argument_values) > 3: # all positional
print(filename, i.start_pos[0])
print(' ', i.get_code())
if hasattr(i, 'children'):
handle_list(i, filename=filename)
def handle_argument(argument):
global args_kwargs, args, kwargs, kwargs_foo_equal_foo
if argument.type == 'name':
# positional argument
return argument.value
def analyse_directory(directory):
for root, dirs, files in os.walk(directory):
dirs[:] = [d for d in dirs if not d.startswith('.') and not d.startswith('env') and not d.startswith('venv') and not d.endswith('_env') and d != 'node_modules']
for filename in files:
if filename.endswith('.py'):
with open(os.path.join(root, filename)) as file:
try:
contents = file.read()
except:
continue
handle_list(parse(contents, error_recovery=True), filename=filename)
import sys
if len(sys.argv) != 2:
print('This tool analyses your code base for cases where a short for for keyword arguments would be nice to have.')
print('Usage: supply one directory path to the code you wish to analyse.')
exit(1)
analyse_directory(sys.argv[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment