Source code for cloup._commands

import abc
from typing import (
    Dict,
    Iterable,
    Optional,
    Type,
    cast,
)

import click

from ._option_groups import OptionGroupMixin
from ._sections import Section, SectionMixin
from .constraints import ConstraintMixin


[docs]class Command(ConstraintMixin, OptionGroupMixin, click.Command): """ A ``click.Command`` supporting option groups. This class is obtained by mixing :class:`click.Command` with :class:`cloup.OptionGroupMixin`. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)
[docs]class MultiCommand(SectionMixin, click.MultiCommand, metaclass=abc.ABCMeta): """ A ``click.MultiCommand`` that allows to organize its subcommands in multiple help sections and and whose subcommands are, by default, of type :class:`cloup.Command`. This class is just a :class:`click.MultiCommand` mixed with :class:`SectionMixin`. See the docstring of the two superclasses for more details. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)
[docs]class Group(SectionMixin, click.Group): """ A ``click.Group`` that allows to organize its subcommands in multiple help sections and and whose subcommands are, by default, of type :class:`cloup.Command`. This class is just a :class:`click.Group` mixed with :class:`SectionMixin` that overrides the decorators :meth:`command` and :meth:`group` so that a ``section`` for the created subcommand can be specified. See the docstring of the two superclasses for more details. """ def __init__(self, name: Optional[str] = None, commands: Optional[Dict[str, click.Command]] = None, sections: Iterable[Section] = (), align_sections: bool = True, **attrs): """ :param name: name of the command :param commands: dict {name: command}; this command will be added to the default section. :param sections: a list of Section objects :param align_sections: if True, the help column of all columns will be aligned; if False, each section will be formatted independently :param attrs: """ super().__init__( name=name, commands=commands, sections=sections, align_sections=align_sections, **attrs)
[docs] def command( # type: ignore self, name: Optional[str] = None, section: Optional[Section] = None, cls: Optional[Type[click.Command]] = None, **attrs, ): """Creates a new command and adds it to this group.""" if cls is None: cls = Command def decorator(f): cmd = command(name=name, cls=cls, **attrs)(f) self.add_command(cmd, section=section) return cmd return decorator
[docs] def group( # type: ignore self, name: Optional[str] = None, section: Optional[Section] = None, cls: Optional[Type[click.Group]] = None, **attrs, ): if cls is None: cls = Group return self.command(name=name, section=section, cls=cls, **attrs)
[docs]def group(name: Optional[str] = None, cls: Type[click.Group] = Group, **attrs): """ Creates a new :class:`Group` using the decorated function as callback. This is just a convenience function equivalent to:: click.group(name, cls=cloup.Group, **attrs) :param name: name of the command :param cls: type of Group :param attrs: any argument you can pass to :func:`click.group` """ return cast(Group, click.group(name=name, cls=cls, **attrs))
[docs]def command( name: Optional[str] = None, cls: Type[click.Command] = Command, **attrs ): """ Decorator that creates a new command using the wrapped function as callback. The only differences with respect to ``click.commands`` are: - this decorator creates a ``cloup.Command`` by default; - this decorator supports ``@constraint``. :param name: name of the command :param cls: type of click.Command :param attrs: any argument you can pass to :func:`click.command` """ def wrapper(f): if hasattr(f, '__constraints'): if not issubclass(cls, ConstraintMixin): raise TypeError( f"a Command must inherits from cloup.ConstraintMixin to support " f"constraints; {cls} doesn't") constraints = tuple(reversed(f.__constraints)) del f.__constraints attrs['constraints'] = constraints return click.command(name, cls=cls, **attrs)(f) return wrapper