From 7cb4202a64e8a97c62ca1ed19ee85d13d1ee87ed Mon Sep 17 00:00:00 2001 From: Patrick Elmer Date: Wed, 16 Aug 2023 13:17:41 +0900 Subject: [PATCH] Remove src layout --- src/magicli.py | 143 ------------------------------------------------- 1 file changed, 143 deletions(-) delete mode 100644 src/magicli.py diff --git a/src/magicli.py b/src/magicli.py deleted file mode 100644 index c6a336f..0000000 --- a/src/magicli.py +++ /dev/null @@ -1,143 +0,0 @@ -import inspect -import sys -import re -from pargv import parse_args - - -def magicli(exclude=['main'], 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) - - if 'version' in kwargs: - print(glbls['__version__']) if '__version__' in glbls else print('Unknown version.') - exit() - - if re.search('\.pyc?$', app_name): - app_name = sys.orig_argv[0] + ' ' + app_name - - function_to_call, *commands = [f for f in filter_functions(glbls) if f.__name__ not in exclude] - command_names = [f.__name__ for f in commands] - - # Call command if specified - if args and args[0] in command_names: - function_to_call = glbls.get(args[0]) - app_name += f" {args[0]}" - commands = [] - args = args[1:] - - if 'help' in kwargs: - print(help_message(app_name, function_to_call, commands)) - exit() - - try: - function_to_call(*args, **kwargs) - except TypeError: - print(help_message(app_name, function_to_call, commands)) - exit() - - -def format_args(argv): - args, kwargs = parse_args(argv) - app_name, *args = args - return app_name, args, kwargs - - -def filter_functions(glbls): - """ - Gets list of functions from the globals variable of a specific module. - """ - return [v for v in glbls.values() if inspect.isfunction(v) and v.__module__ == glbls['__name__']] - - -def help_message(app_name, function, commands=[]): - """ - Automatically create a help message. - """ - specs = inspect.getfullargspec(function) - - arguments = specs.args - defaults = list(specs.defaults) - options = list(reversed([arguments.pop() for _ in defaults])) if defaults else [] - - arguments_docs = [extract_annotation(specs.annotations[arg]) if arg in specs.annotations else '' for arg in arguments] - options_docs = [extract_annotation(specs.annotations[opt]) if opt in specs.annotations else '' for opt in options] - commands_docs = [c.__doc__ if c.__doc__ else '' for c in commands] - - options += ['help', 'version'] - options_docs += ['Show the help message.', 'Show version information.'] - defaults += ['', ''] - - help_text = [] - usage = [app_name] - if arguments: - usage.append(' '.join(arguments)) - help_text.append({'lines': [[argument, doc] for argument, doc in zip(arguments, arguments_docs)], 'heading': 'Arguments:'}) - if options: - usage.append('[options]') - help_text.append({'lines': [[f"--{option}", f"{doc} ".lstrip() + f"(default: {default})"*(default!='')] for option, doc, default in zip(options, options_docs, defaults)], 'heading': 'Options:'}) - - usage = [' '.join(usage)] - - if commands: - usage.append(f"{app_name} command [...]") - help_text.append({'lines': [[c.__name__, doc] for c, doc in zip(commands, commands_docs)], 'heading': 'Commands:'}) - - if function.__doc__: - help_text = [{'lines': [line.strip() for line in function.__doc__.strip().split('\n')]}] + help_text - - return format_help_message([{'lines': usage, 'heading': 'Usage:'}] + help_text) - - -def extract_annotation(spec): - return '' if isinstance(spec, type) else spec.__metadata__[0] - - -def type_to_str(text): - found = re.findall("'(.*)'", str(text)) - return found[0] if found else '' - - -def format_help_message(list_of_dicts): - """Usage example: - - format_help_message([ - {'lines': ['--all', 'All files.'], 'heading': 'Options:'}, - {'lines': ['run', 'Run script.'], 'heading': 'Commands:'}, - {'lines': 'Copyright 2023'}, - ]) - """ - return '\n\n'.join(format_block(**kwargs) for kwargs in list_of_dicts) - - -def format_block(lines, heading='', indent=2): - if isinstance(lines[0], list): - max_length = len(max([l[0] for l in lines], key=len)) - lines = [f"{line[0]:{max_length}} {line[1]}" for line in lines] - elif isinstance(lines, str): - lines = [lines] - if heading: - return '\n'.join([heading]+['\n'.join([indent*' '+l for l in lines])]) - return '\n'.join(['\n'.join([indent*' '+l for l in lines])]) - - -def first_calling_frame(): - for s in reversed(inspect.stack()): - if s.code_context == None: - continue - if s.code_context[0].lstrip().startswith('import magicli'): - return s.frame - return None - - -frame = first_calling_frame() -if frame != None: - sys.exit(magicli(glbls=frame.f_globals, exclude=[]))