Source code for plan_view.decorators

"""Decorators for command functions."""

import argparse
import sys
from collections.abc import Callable
from functools import wraps
from typing import Concatenate

from plan_view.io import load_plan


[docs] def require_plan[**P, R]( func: Callable[Concatenate[dict, argparse.Namespace, P], R], ) -> Callable[Concatenate[argparse.Namespace, P], R]: """Decorator to load plan from args.file and inject it as first argument. This eliminates the boilerplate of loading plan, checking for None, and asserting for the type checker. The wrapped function receives the loaded plan dict as its first positional argument. Args: func: Command function that takes (plan: dict, args: Namespace, ...) -> R Returns: Wrapped function that takes (args: Namespace, ...) -> R Example:: @require_plan def cmd_add_task(plan: dict, args: argparse.Namespace) -> None: phase = find_phase(plan, args.phase) ... """ @wraps(func) def wrapper(args: argparse.Namespace, /, *inner_args: P.args, **inner_kwargs: P.kwargs) -> R: # Get the file path from args file_path = args.file # Load the plan plan = load_plan(file_path) # Exit with error if plan not found if plan is None: sys.exit(1) # Call the original function with plan as first arg return func(plan, args, *inner_args, **inner_kwargs) return wrapper