Skip to content

cli

Extend Invoke for Calcipy.

Classes⚓︎

CalcipyConfig ⚓︎

Bases: Config

Opinionated Config with better defaults.

Source code in calcipy/cli.py
class CalcipyConfig(Config):
    """Opinionated Config with better defaults."""

    @staticmethod
    def global_defaults() -> Dict:  # type: ignore[type-arg]  # pragma: no cover
        """Override the global defaults."""
        invoke_defaults = Config.global_defaults()
        calcipy_defaults = {
            'run': {
                'echo': True,
                'echo_format': '\033[2;3;37mRunning: {command}\033[0m',
                'pty': use_pty(),
            },
        }
        return merge_dicts(invoke_defaults, calcipy_defaults)

Functions⚓︎

global_defaults staticmethod ⚓︎
global_defaults()

Override the global defaults.

Source code in calcipy/cli.py
@staticmethod
def global_defaults() -> Dict:  # type: ignore[type-arg]  # pragma: no cover
    """Override the global defaults."""
    invoke_defaults = Config.global_defaults()
    calcipy_defaults = {
        'run': {
            'echo': True,
            'echo_format': '\033[2;3;37mRunning: {command}\033[0m',
            'pty': use_pty(),
        },
    }
    return merge_dicts(invoke_defaults, calcipy_defaults)

Functions⚓︎

start_program ⚓︎

start_program(pkg_name, pkg_version, module=None, collection=None)

Run the customized Invoke Program.

FYI: recommendation is to extend the core_args method, but this won’t parse positional arguments: https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments

Source code in calcipy/cli.py
@beartype
def start_program(  # noqa: CAC001
    pkg_name: str,
    pkg_version: str,
    module: Optional[ModuleType] = None,
    collection: Optional[Union[Collection, InvokeCollection]] = None,
) -> None:  # pragma: no cover
    """Run the customized Invoke Program.

    FYI: recommendation is to extend the `core_args` method, but this won't parse positional arguments:
    https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments

    """
    # Manipulate 'sys.argv' to hide arguments that invoke can't parse
    _gto = GlobalTaskOptions()
    sys_argv: List[str] = sys.argv[:1]
    last_argv = ''
    for argv in sys.argv[1:]:
        if not last_argv.startswith('-') and Path(argv).is_file():
            _gto.file_args.append(Path(argv))
        # Check for CLI flags
        elif argv in {'-v', '-vv', '-vvv', '--verbose'}:
            _gto.verbose = argv.count('v')
        elif argv == '--keep-going':
            _gto.keep_going = True
        # Check for CLI arguments with values
        elif last_argv == '--working-dir':
            _gto.working_dir = Path(argv).resolve()
        elif argv != '--working-dir':
            sys_argv.append(argv)
        last_argv = argv
    _gto.file_args = [
        _f if _f.is_absolute() else Path.cwd() / _f
        for _f in _gto.file_args
    ]
    sys.argv = sys_argv

    class _CalcipyConfig(CalcipyConfig):

        gto: GlobalTaskOptions = _gto

    if module and collection:
        raise ValueError('Only one of collection or module can be specified')

    _CalcipyProgram(
        name=pkg_name,
        version=pkg_version,
        namespace=Collection.from_module(module) if module else collection,
        config_class=_CalcipyConfig,
    ).run()

task ⚓︎

task(*dec_args, **dec_kwargs)

Marks wrapped callable object as a valid Invoke task.

Source code in calcipy/cli.py
def task(*dec_args: Any, **dec_kwargs: Any) -> Callable:  # type: ignore[type-arg]
    """Marks wrapped callable object as a valid Invoke task."""
    def wrapper(func: Any) -> Callable:  # type: ignore[type-arg]
        # Attach arguments for Task
        setattr(func, TASK_ARGS_ATTR, dec_args)
        setattr(func, TASK_KWARGS_ATTR, dec_kwargs)
        # Attach public attributes from invoke that are expected
        func.help = dec_kwargs.pop('help', {})

        def _with_kwargs(**extra_kwargs: Any) -> Callable:  # type: ignore[type-arg] # nosem
            """Support building partial tasks."""
            if extra_kwargs:
                # Set a unique name when 'extra_kwargs' was provided
                # https://github.com/pyinvoke/invoke/blob/07b836f2663bb073a7bcef3d6c454e1dc6b867ae/invoke/tasks.py#L81-L104
                encoded = b64encode(str(extra_kwargs).encode())
                func.__name__ = f'{func.__name__}_{encoded.decode().rstrip("=")}'

            @wraps(func)  # nosem
            def _with_kwargs_inner(*args: Any, **kwargs: Any) -> Any:
                return func(*args, **kwargs, **extra_kwargs)
            return _with_kwargs_inner

        func.with_kwargs = _with_kwargs

        @wraps(func)  # nosem
        def _inner(*args: Any, **kwargs: Any) -> Any:
            return func(*args, **kwargs)

        return _inner

    # Handle the case when the decorator is called without arguments
    if (
        len(dec_args) == 1
        and callable(dec_args[0])
        and not hasattr(dec_args[0], TASK_ARGS_ATTR)
    ):
        return wrapper(dec_args[0])

    return wrapper