Automation tool that brings the power of build-tools to execute any kind of task with efficient DAG-based execution and plugin architecture
—
Classes and utilities for defining task actions, including shell commands, Python functions, and specialized interactive actions for different execution scenarios.
Foundation classes for all task actions providing common functionality and interfaces.
class BaseAction:
"""
Base class for all actions.
All action classes inherit from this base and must implement the execute method.
Provides common functionality for preparing keyword arguments from task metadata.
"""
def execute(self, out=None, err=None):
"""Execute the action (must be implemented by subclasses)"""
@staticmethod
def _prepare_kwargs(task, func, args, kwargs):
"""Prepare keyword arguments with task metadata and command line options"""Actions for executing shell commands with different interaction patterns and output handling.
class LongRunning(CmdAction):
"""
Action to handle long running shell processes (servers, services).
Properties:
- Output is never captured
- Always considered successful (return code ignored)
- Swallows KeyboardInterrupt for graceful shutdown
Use for processes that run indefinitely like web servers, background services,
or any process where you want to ignore the exit code.
"""
def execute(self, out=None, err=None):
"""Execute long-running command, handling KeyboardInterrupt gracefully"""class Interactive(CmdAction):
"""
Action to handle interactive shell processes.
Properties:
- Output is never captured (allows user interaction)
- Respects return code for success/failure
- Suitable for processes requiring user input
Use for commands that need user interaction like editors, prompts,
or any process where you need to preserve stdin/stdout/stderr.
"""
def execute(self, out=None, err=None):
"""Execute interactive command, preserving user interaction"""Actions for executing Python functions with different interaction and output patterns.
class PythonInteractiveAction(PythonAction):
"""
Action to handle interactive Python function execution.
Properties:
- Output is never captured
- Successful unless exception is raised
- Allows function to interact directly with user
- Can return string results or dictionaries
Use for Python functions that need direct user interaction or
when you want to avoid output capture.
"""
def execute(self, out=None, err=None):
"""Execute Python function interactively, handling return values and exceptions"""Utility functions for creating and normalizing action objects from various input formats.
def normalize_callable(ref):
"""
Return a list with (callable, *args, **kwargs) from various input formats.
Handles both simple callables and tuples containing callable with arguments.
Args:
ref: A callable or tuple (callable, args, kwargs)
Returns:
list: [callable, args_tuple, kwargs_dict]
"""Deprecated aliases maintained for backward compatibility.
InteractiveAction = LongRunning # Deprecated alias for LongRunning (removed in 0.25)from doit.tools import LongRunning
def task_start_server():
"""Start development server"""
return {
'actions': [LongRunning('python manage.py runserver 8000')],
'verbosity': 2
}
# Server will run until Ctrl+C, then gracefully shut downfrom doit.tools import Interactive
def task_edit_config():
"""Edit configuration file interactively"""
return {
'actions': [Interactive('nano config.yaml')],
'file_dep': ['config.yaml']
}
# Opens editor for user interaction, respects exit codefrom doit.tools import PythonInteractiveAction
def interactive_setup():
"""Interactive setup with user prompts"""
name = input("Enter project name: ")
version = input("Enter version [1.0.0]: ") or "1.0.0"
config = {'name': name, 'version': version}
print(f"Creating project {name} v{version}")
# Return dict to save as task values
return config
def task_setup():
"""Interactive project setup"""
return {
'actions': [PythonInteractiveAction(interactive_setup)],
'verbosity': 2
}from doit.tools import Interactive, LongRunning
def task_development():
"""Complete development workflow"""
return {
'actions': [
'python setup.py develop', # Regular command
Interactive('git add .'), # Interactive for commit message
Interactive('git commit'), # Interactive for commit message
LongRunning('python -m pytest --watch') # Long-running test watcher
],
'verbosity': 2
}from doit.action import normalize_callable
def my_function(arg1, arg2, kwarg1=None):
return f"Called with {arg1}, {arg2}, {kwarg1}"
# Various ways to specify callables
callable_formats = [
my_function, # Simple function
(my_function, ('hello', 'world'), {'kwarg1': 'test'}), # Full specification
(my_function, ('hello',), {}), # Partial specification
]
for fmt in callable_formats:
func, args, kwargs = normalize_callable(fmt)
print(func(*args, **kwargs))Install with Tessl CLI
npx tessl i tessl/pypi-doit