taskipy

repository·master·Indexed 20 days ago

https://github.com/taskipy/taskipy

A complementary task runner for Python projects, inspired by npm's run scripts. It allows developers to define, group, and compose development tasks—such as testing, linting, or building—within a `pyproject.toml` file. It supports pre- and post-task hooks, variable injection, custom working directories, and a global runner prefix for all tasks.

Tokens
5.4K
Snippets
22
Records
27
Agent score
71%

What's inside taskipy

  1. Compose tasks using hooks and grouping

    master

    Taskipy allows you to create complex workflows by composing tasks.

    Grouping Subtasks

    You can create a composite task by stringing multiple tasks together using shell operators like &&.

    Pre-task Hooks

    To make a task depend on another, create a task named pre_<task_name>. Taskipy will run this hook before the main task. If the hook fails, the main task will not run.

    Post-task Hooks

    To run a task after a successful execution, create a task named post_<task_name>. Taskipy will run this hook only if the main task succeeds. If the main task fails, the hook will not run.

    [tool.taskipy.tasks]
    build = "make ."
    pre_test = "task build"
    test = "python -m unittest tests/test_*.py"
    
    lint = "pylint tests"
    post_test = "task lint"
  2. Define and add tasks in pyproject.toml

    master

    Tasks are defined in the [tool.taskipy.tasks] section of your pyproject.toml file. You can define them using a simple string or an explicit inline table for more context.

    Simple String Definition

    Use a key-value map where the key is the task name and the value is the shell command.

    Explicit Inline Table Definition

    Use an inline table to provide a cmd (the command) and an optional help (a description).

    Requirements:

    • Python 3.6 or newer.
    • A valid pyproject.toml file in the project directory.
    [tool.taskipy.tasks]
    test = "python -m unittest tests/test_*.py"
    lint = { cmd = "pylint tests taskipy", help = "confirms code style using pylint" }
  3. Install taskipy

    master

    You can install taskipy depending on your environment and package manager:

    Add it as a development dependency:

    poetry add --dev taskipy

    Using PIP (For non-Poetry projects)

    Install it in your machine or virtual environment:

    pip install taskipy

    Using Conda

    Available via conda-forge:

    conda install -c conda-forge taskipy
    poetry add --dev taskipy
  4. Use a global runner to prefix all tasks

    master

    If you need to run every task within a specific context—such as a specific shell, a remote ssh session, a custom virtualenv wrapper, or using dotenv to load environment variables—you can use the tool.taskipy.settings.runner configuration.

    When this option is set, taskipy automatically prepends the specified command to every task defined in your [tool.taskipy.tasks] section. This prevents you from having to manually repeat the prefix for every individual task.

    [tool.taskipy.settings]
    runner = "dotenv run"
    
    [tool.taskipy.tasks]
    test = "unittest ."
    lint = "pylint taskipy"
  5. Set the working directory for tasks

    master

    By default, tasks run from the directory where they are called. You can override this using the cwd setting.

    Per-task working directory

    Define cwd in the task's inline table. The path is relative to the project root (where pyproject.toml resides).

    Global working directory

    Set cwd in the [tool.taskipy.settings] table to force all tasks to run from the same directory.

    [tool.taskipy.tasks]
    echo = { cmd = "python -c 'import os; print(os.getcwd())'", cwd = "." }
    
    [tool.taskipy.settings]
    cwd = "."
  6. Use variables in tasks

    master

    To avoid repetition (DRY), you can define variables in [tool.taskipy.variables] and inject them into tasks using {var_name} syntax. This uses Python's string.format method.

    Enabling Variables

    Variables are opt-in by default. You can enable them in two ways:

    1. Per-task: Set use_vars = true in the task's inline table.
    2. Globally: Set use_vars = true in the [tool.taskipy.settings] table.

    Recursive Variables

    By default, variables are not recursive. To allow a variable to contain other variables, set recursive = true in its definition.

    [tool.taskipy.settings]
    use_vars = true
    
    [tool.taskipy.variables]
    src_dir = "src"
    package_dir = { var = "{src_dir}/package", recursive = true }
    
    [tool.taskipy.tasks]
    echo = "echo {package_dir}"
  7. Configure the `runner` setting in `taskipy`

    master

    The runner setting is a global configuration option located under [tool.taskipy.settings]. It accepts a string representing the command that should act as a prefix for all tasks.

    Common Use Cases:

    • Loading environment variables: Set runner = "dotenv run" to ensure dotenv initializes the environment before every task.
    • Virtual environments: Use a wrapper command to ensure tasks run within a specific virtualenv.
    • Remote execution: Use ssh commands to run tasks on a remote host.
    • Custom shells: Prefix tasks with a specific shell invocation.
  8. Run tasks with taskipy

    master

    To execute a task, use the task command.

    Running a specific task

    If using Poetry:

    poetry run task <task_name>

    If using PIP/Standard installation:

    task <task_name>

    Listing all tasks

    To see all available tasks and their commands (or descriptions if provided):

    poetry run task --list

    Passing arguments to tasks

    You can pass positional or named arguments to a task by appending them to the end of the command.

    Note: Arguments are passed to the task itself, but not to pre_ or post_ hooks.

    poetry run task test -h
  9. Use the TaskRunner class to execute tasks

    master

    The TaskRunner class is the primary interface for programmatically executing tasks defined in a pyproject.toml file. You initialize it with a working directory, and it handles task discovery, variable resolution, pre/post task execution, and command running.

    Key Methods

    • list(): Prints a list of all available tasks to stdout.
    • run(task_name: str, args: List[str]) -> int: Executes a specific task by name. It automatically handles pre_{task_name} and post_{task_name} hooks. It returns the exit code of the command (0 for success).

    Lifecycle of a Task Run

    When calling run(), the runner follows this sequence:

    1. Discovery: Finds the main task and any associated pre_ or post_ tasks.
    2. Variable Resolution: Resolves variables if the task uses them or if use_vars is enabled in settings.
    3. Pre-task: Executes the pre_ task. If it fails (non-zero exit code), the sequence stops.
    4. Main Task: Executes the primary command with provided args.
    5. Post-task: Executes the post_ task. If it fails, the runner returns that exit code.
    from pathlib import Path
    from taskipy.task_runner import TaskRunner
    
    # Initialize with the directory containing your pyproject.toml
    runner = TaskRunner(cwd=Path("./my-project"))
    
    # List available tasks
    runner.list()
    
    # Run a task named 'test' with arguments ['--verbose']
    exit_code = runner.run("test", args=["--verbose"])
    print(f"Task finished with exit code: {exit_code}")
  10. Format and print available tasks with TasksListFormatter

    master

    The TasksListFormatter class is used to display a list of available tasks in a formatted, human-readable way. It calculates column widths based on the longest task name and wraps descriptions to fit the terminal width. Task names are automatically highlighted in cyan.

    Usage

    Initialize the formatter with an iterable of Task objects. If the iterable is empty, it raises an EmptyTasksSectionError.

    Call .print() to output the list to the terminal. You can optionally provide a line_width to control the wrapping behavior; if omitted, it defaults to the current terminal width.

    from taskipy.list import TasksListFormatter
    
    # Assuming 'tasks' is an iterable of Task objects
    formatter = TasksListFormatter(tasks)
    formatter.print()
  11. Run taskipy programmatically with run()

    master

    You can execute taskipy logic within your own Python code using the run function. This is useful for integrating task execution into larger automation scripts or custom runners.

    Arguments:

    • args: A list of strings representing the command-line arguments (e.g., ['test', '--verbose']).
    • cwd: (Optional) The working directory where the task should be executed. If not provided, it defaults to the current working directory.

    Returns:

    • 0 on success.
    • > 0 if an error occurred (the specific exit code returned by the task or the error handler).
    from taskipy.cli import run
    
    # Run the 'test' task in a specific directory
    exit_code = run(['test'], cwd='/path/to/project')
    
    if exit_code == 0:
        print("Task succeeded!")
    else:
        print(f"Task failed with exit code {exit_code}")
  12. Use the PyProject class to load taskipy configuration

    master

    The PyProject class is used to locate and parse the pyproject.toml file to extract taskipy-specific configurations. When initialized with a base_dir (a pathlib.Path), it searches upwards from that directory to find a pyproject.toml file. If no file is found, it raises MissingPyProjectFileError. If the file is not valid TOML, it raises MalformedPyProjectError.

    from pathlib import Path
    from taskipy.pyproject import PyProject
    
    # Initialize with the current working directory or a specific project path
    pyproject = PyProject(Path.cwd())
    print(f"Found pyproject at: {pyproject.dirpath}")