Code Monkey home page Code Monkey logo

Comments (1)

PythonFZ avatar PythonFZ commented on August 30, 2024

Implementation by https://www.reddit.com/user/Dasher38/

import argparse

def _resolve_arg(name, annotation, x):
    if isinstance(x, PositionalArgument):
        pass
    elif len(name) == 1:
        name = f'-{name}'
    else:
        name = f'--{name.replace("_", "-")}'

    if isinstance(x, str):
        spec = dict(help=x)
    elif isinstance(x, Argument):
        spec = x.spec
    else:
        raise TypeError(f'Unsupported argument specification: {x}')

    if 'type' not in spec and annotation is not None:
        spec['type'] = annotation

    if spec.get('type') == bool and 'action' not in spec:
        spec['action'] = 'store_true'
        del spec['type']

    return (name, spec)

def _resolve_subparser(name, cls):
    subparsers, pos_arguments, arguments = _get_arguments(cls.__dict__)
    if subparsers:
        raise ValueError('Nested subparsers are not allowed')

    spec = dict(
        description=cls.__doc__
    )
    return name, spec, pos_arguments, arguments


def _get_arguments(dct):
    annotations = dct.get('__annotations__', {})

    arguments = {}
    pos_arguments = {}
    subparsers = {}
    for attr, spec in dct.items():
        if attr.startswith('__'):
            continue

        if isinstance(spec, type):
            _name, _spec, _pos_arguments, _arguments = _resolve_subparser(attr, spec)
            subparsers[_name] = (_spec, _pos_arguments, _arguments)
        else:
            _name, _spec = _resolve_arg(attr, annotations.get(attr), spec)
            if _name.startswith('-'):
                arguments[_name] = _spec
            else:
                pos_arguments[_name] = _spec

    return subparsers, pos_arguments, arguments

def _make_parser(dct, subparser_dst='subcommand', parser_spec=None):
    parser_spec = parser_spec or {}
    parser_spec.setdefault('description', dct.get('__doc__'))

    parser = argparse.ArgumentParser(**parser_spec)
    subparsers, pos_arguments, arguments = _get_arguments(dct)

    for name, spec in pos_arguments.items():
        parser.add_argument(name, **spec)

    for name, spec in arguments.items():
        parser.add_argument(name, **spec)

    if subparsers:
        _subparsers = parser.add_subparsers(title='subcommands', dest=subparser_dst)

        for name, (spec, pos_arguments, arguments) in subparsers.items():
            subparser = _subparsers.add_parser(name, **spec)

            for name, spec in pos_arguments.items():
                subparser.add_argument(name, **spec)
            for name, spec in arguments.items():
                subparser.add_argument(name, **spec)
    return parser


class Argument:
    def __init__(self, **kwargs):
        self.spec = kwargs

class PositionalArgument(Argument):
    pass

class ArgParserMeta(type):
    def __new__(cls, name, bases, dct, _is_base=False, subparser_dst='subcommand', **kwargs):
        if _is_base:
            return super().__new__(cls, name, bases, dct)
        else:
            return _make_parser(dct, subparser_dst=subparser_dst, parser_spec=kwargs)


class ArgParser(metaclass=ArgParserMeta, _is_base=True):
    pass



# class MyParser(ArgParser, subparser_dst='XXX'):

# Any keyword argument other than subparser_dst is forwarded to
# argparse.ArgumentParser
class MyParser(ArgParser, epilog='this is the epilog'):
    """
    This is my nice application
    """
    a: int = Argument(help='hello')

    x: int = PositionalArgument(help='pos1')
    y: int = PositionalArgument(help='pos2')

    class subcmd:
        "my subcommand"
        b: float = 'world'
        c: bool = 'mybool'
        a_long_opt: bool = 'bar'

args = MyParser.parse_args()
print(args)

from aaargs.

Related Issues (1)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.