magicli/src/magicli.py
2023-08-12 13:46:15 +09:00

87 lines
2.5 KiB
Python

import inspect
import sys
import os
from pargv import parse_args
def magicli(exclude=['main'], help_message=True, glbls=None, argv=None):
"""
Get all functions from calling file and interprets them as CLI commands.
Parses command line arguments for function to call
and calls it with all specified arguments.
Displays a help message and exits if the --help flag is set
or if no callable function is found.
Errors out with a TypeError if the specified arguments are invalid.
"""
glbls = glbls if glbls else inspect.currentframe().f_back.f_globals
argv = argv if argv else sys.argv
app_name, args, kwargs = format_args(argv)
functions = [f for f in filter_functions(glbls) if f.__name__ not in exclude]
if help_message and 'help' in kwargs:
print_help_and_exit(app_name, functions)
possible_commands = [f.__name__ for f in functions]
function_name = None
if len(args) and args[0] in possible_commands:
function_name = args[0]
args = args[1:]
elif app_name in possible_commands:
function_name = app_name
else:
print_help_and_exit(app_name, functions)
function_to_call = glbls.get(function_name)
try:
function_to_call(*args, **kwargs)
except TypeError:
pass
def format_args(argv):
args, kwargs = parse_args(argv)
app_name = os.path.basename(args[0])
args = args[1:]
return app_name, args, kwargs
def filter_functions(args):
"""
Gets list of functions from the globals variable of a specific module (default: __main__).
"""
return [v for v in args.values() if inspect.isfunction(v) and v.__module__ == args['__name__']]
def print_help_and_exit(app_name, functions):
"""
Print the help message based on functions contained in the calling file.
Exits after displaying the help message.
"""
print('Usage:')
for f in functions:
words = []
if app_name != f.__name__:
words.append(app_name)
words.append(f.__name__)
specs = inspect.getfullargspec(f)
words += ['--' + arg for arg in specs.args]
print(' '*4 + ' '.join(words))
exit()
def first_calling_frame():
for s in reversed(inspect.stack()):
if s.code_context == None:
continue
if s.code_context[0].startswith('import magicli'):
return s.frame
return None
frame = first_calling_frame()
if frame != None:
magicli(glbls=frame.f_globals)