Source code for clik.app

# -*- coding: utf-8 -*-
"""
Top-level :class:`App` class and helpers.

:author: Joe Joyce <joe@decafjoe.com>
:copyright: Copyright (c) Joe Joyce and contributors, 2009-2019.
:license: BSD
"""
import sys

from clik.argparse import ALLOW_UNKNOWN, ArgumentParser, ArgumentParserExit
from clik.command import Command
from clik.context import Context
from clik.magic import Magic
from clik.util import AttributeDict


#: Magic variable containing parsed arguments.
#:
#: :type: :class:`clik.magic.Magic` -- :class:`argparse.Namespace`
args = Magic('args')

#: Magic variable containing the active application instance.
#:
#: :type: :class:`clik.magic.Magic` -- :class:`clik.app.App`
current_app = Magic('current_app')

#: Magic variable containing globals.
#:
#: :type: :class:`clik.magic.Magic` -- :class:`clik.app.AttributeDict`
g = Magic('g')

#: Magic variable containing the current parser.
#:
#: :type: :class:`clik.magic.Magic` -- :class:`clik.argparse.ArgumentParser`
parser = Magic('parser')

#: Magic variable containing the function to run child commands.
#:
#: :type: :class:`clik.magic.Magic`
run_children = Magic('run_children')

#: Magic variable containing unknown arguments.
#:
#: :type: :class:`clik.magic.Magic` -- :class:`list`
unknown_args = Magic('unknown_args')


[docs]def app(fn=None, name=None): """ Decorate the main application generator function. If the decorator is given no arguments, the name of the application is the name of the decorated generator function:: # Application will be named 'myapp' in this case @app def myapp(): yield The application name can be set by passing a string to ``name``:: # Application will be named 'theapp' in this case @app(name='theapp') def myapp(): yield :param fn: Main application generator function. Name of the application will be set to ``fn.__name__``. :param name: Overrides name of application. Must not be used with the ``fn`` argument (if used with ``fn``, ``name`` is ignored). :return: :class:`App` if ``fn`` is ``None``, otherwise a decorator returning :class:`App`. """ def decorate(fn): return App(fn, name) if fn is None: return decorate return decorate(fn)
[docs]class App(Command): """ :class:`clik.Command` subclass that implements the :meth:`main` method. :meth:`main` is the user-level API for starting the application. """ def __init__(self, fn, name=None): """ Initialize the application object. :param fn: Main application generator function. :param name: Optional :class:`str` specifying the application name. If not supplied the application name is set to ``fn.__name__``. """ super(App, self).__init__(Context(), fn, name=name)
[docs] def main(self, argv=None, exit=sys.exit): """ Start the application. :param argv: Optional list of command-line arguments. If not supplied, this defaults to ``sys.argv``. :param exit: Optional function to call on exit. If not supplied, this defaults to :func:`sys.exit`. :type exit: ``fn(integer_exit_code)`` :return: Return value of ``exit`` """ if argv is None: # pragma: no cover (hard to test, obviously correct) argv = sys.argv description, epilog = self._split_docstring(self._fn) root_parser = ArgumentParser( prog=self._name, description=description, epilog=epilog, ) root_parser.set_defaults(**{ALLOW_UNKNOWN: False}) magics = (args, current_app, g, parser, run_children, unknown_args) with self._ctx.acquire(*magics): with self._ctx(current_app=self, g=AttributeDict()): with self._ctx(args=None): ec = self._configure_parser(root_parser) if ec: return exit(ec) try: known, unknown = root_parser.parse_known_args(argv[1:]) except ArgumentParserExit: return exit(1) if unknown and not getattr(known, ALLOW_UNKNOWN): try: root_parser.parse_args(argv[1:]) except ArgumentParserExit: return exit(1) with self._ctx(args=known, unknown_args=unknown): return exit(self._run())