A task runner for Python projects that enables task definition and execution through pyproject.toml configuration.
Core task execution engine that handles task discovery, execution, pre/post hooks, and process management with comprehensive signal handling.
The central class for managing and executing tasks within a project directory.
class TaskRunner:
def __init__(self, cwd: Union[str, Path]):
"""
Initialize TaskRunner with working directory.
Args:
cwd: Working directory path (str or Path object)
"""Display available tasks with descriptions and commands in a formatted output.
def list(self):
"""
Lists tasks to stdout.
Displays all available tasks with their names and descriptions
(or commands if no description provided) in a formatted table.
"""Execute a named task with optional arguments, including pre/post hook processing.
def run(self, task_name: str, args: List[str]) -> int:
"""
Execute a task with optional arguments.
Args:
task_name: Name of the task to run
args: Arguments to pass to the task command
Returns:
Exit code (0 for success, >0 for error)
"""from taskipy.task_runner import TaskRunner
from pathlib import Path
# Initialize runner
runner = TaskRunner(Path('/path/to/project'))
# List available tasks
runner.list()
# Run a task
exit_code = runner.run('test', [])
# Run a task with arguments
exit_code = runner.run('test', ['--verbose', '--failfast'])from taskipy.task_runner import TaskRunner
from taskipy.exceptions import TaskNotFoundError, TaskipyError
runner = TaskRunner(Path('.'))
try:
exit_code = runner.run('nonexistent_task', [])
except TaskNotFoundError as e:
print(f"Task not found: {e}")
if e.suggestion:
print(f"Did you mean: {e.suggestion}")
except TaskipyError as e:
print(f"Taskipy error: {e}")
exit_code = e.exit_code# Multi-project repository
runner = TaskRunner(Path('/path/to/monorepo/subproject'))
# Project with custom pyproject.toml location
# TaskRunner automatically searches parent directories for pyproject.toml
runner = TaskRunner(Path('/path/to/nested/directory'))TaskRunner automatically executes pre and post hooks for tasks:
pre_{task_name} (if exists){task_name}post_{task_name} (if exists)[tool.taskipy.tasks]
pre_test = "echo 'Setting up test environment'"
test = "python -m pytest"
post_test = "echo 'Cleaning up test artifacts'"
pre_lint = "echo 'Preparing linting'"
lint = "pylint src tests"
post_lint = "echo 'Linting complete'"TaskRunner handles variable substitution in task commands when enabled:
# Variables are resolved before command execution
# Example with variables enabled:
# Task: "pylint {src_path}"
# Variables: {"src_path": "src/mypackage"}
# Executed: "pylint src/mypackage"TaskRunner provides intelligent process management with proper signal handling:
cwd parametercwd setting applies to all taskscwd overrides global setting# Commands are executed through shell with proper argument quoting
# Example: task test --verbose "with spaces"
# Becomes: python -m pytest --verbose "with spaces"TaskRunner supports custom command runners that prefix all task executions:
[tool.taskipy.settings]
runner = "poetry run"This prefixes all task commands with the runner, useful for:
dotenv run)ssh server)Arguments passed to tasks are properly quoted and appended to the task command:
# runner.run('test', ['--verbose', '--output-file', 'results.xml'])
# Executes: python -m pytest --verbose --output-file results.xmlNote: Arguments are only passed to the main task, not to pre/post hooks.
When a task is not found, TaskRunner provides intelligent suggestions using difflib:
# If user types 'tets' instead of 'test'
# TaskNotFoundError will include suggestion: "did you mean 'test'?"TaskRunner creates subprocess with:
Install with Tessl CLI
npx tessl i tessl/pypi-taskipy