reddit-image-wall-getter/reddit_imgs/system/cmdline_parser.py

113 lines
3.5 KiB
Python

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import re
from inspect import _empty, getfullargspec, signature
from typing import Callable, Dict, List, Optional, Set, Type, TypeVar
import colored as clrlib
from .table_fmt import table_fmt
SPLITTER_RGX = re.compile(r'(?<!\~):')
def unescape_after_splitter(txt: str) -> str:
return txt.replace('~:', ':')
T = TypeVar('T')
def parse_cmdline(func: Callable[..., T], encoded_args: str) -> Optional[T]:
'''
Transforms a colon-separated key-pairs into callable arguments
Type-annotated fields will be converted.
'''
split_args = SPLITTER_RGX.split(encoded_args)
split_args = list(map(unescape_after_splitter, split_args))
brute_dict = dict(zip(split_args[0::2], split_args[1::2]))
full_arg_spec = getfullargspec(func)
sig = signature(func)
func_args = full_arg_spec.args
func_annotations = full_arg_spec.annotations
str_args = {k: v
for k, v in
brute_dict.items()
if k in func_args}
unknown_args = {k: v
for k, v in
brute_dict.items()
if k not in func_args}
if encoded_args == 'help' or len(unknown_args) > 0:
if len(unknown_args) > 0:
print(clrlib.stylize('Unknown arguments found:', [
clrlib.fg('light_red'),
clrlib.attr('bold'),
]))
for k, v in unknown_args.items():
print(clrlib.stylize(f' {k}: {repr(v)}', [
clrlib.fg('light_red'),
]))
print()
print(clrlib.stylize(f'Usage help for: {func.__module__}.{func.__name__}', [
clrlib.fg('light_cyan'),
clrlib.attr('bold'),
]))
tbl = list()
if len(sig.parameters) <= 0:
print(clrlib.stylize(' ' * 4 + 'No arguments accepted', [
clrlib.fg('light_cyan'),
]))
else:
for name, parameter in sig.parameters.items():
annotation = parameter.annotation if parameter.annotation != _empty else str
tbl.append((
str(name),
repr(annotation),
repr(parameter.default) if parameter.default != _empty else '-unset-',
))
print(clrlib.stylize(space_left_pad_text(4, table_fmt(
'name,type,default'.split(','),
tbl,
alignment='^'*3,
)), [
clrlib.fg('light_cyan'),
]))
return None
kwargs = dict()
for key in str_args:
kwargs[key] = convert_type(
func_annotations.get(key, str),
str_args[key]
)
print(clrlib.stylize(f'Calling {func.__module__}.{func.__name__} with arguments:', [
clrlib.fg('light_gray'),
clrlib.attr('dim'),
]))
if len(kwargs) <= 0:
print(clrlib.stylize(' --- no arguments given ---', [
clrlib.fg('light_gray'),
clrlib.attr('dim'),
]))
else:
for k, v in kwargs.items():
print(clrlib.stylize(f' {k}: {repr(v)}', [
clrlib.fg('light_gray'),
clrlib.attr('dim'),
]))
return func(**kwargs)
K = TypeVar('K')
def convert_type(cls: Type[K], data: str) -> K:
if cls not in (str, int, float):
cls = eval
return cls(data)
def space_left_pad_text(qty: int, multiline_text: str) -> str:
return '\n'.join([' ' * qty + line for line in multiline_text.splitlines()])