0
# Actions and Execution
1
2
Classes and utilities for defining task actions, including shell commands, Python functions, and specialized interactive actions for different execution scenarios.
3
4
## Capabilities
5
6
### Action Base Classes
7
8
Foundation classes for all task actions providing common functionality and interfaces.
9
10
```python { .api }
11
class BaseAction:
12
"""
13
Base class for all actions.
14
15
All action classes inherit from this base and must implement the execute method.
16
Provides common functionality for preparing keyword arguments from task metadata.
17
"""
18
19
def execute(self, out=None, err=None):
20
"""Execute the action (must be implemented by subclasses)"""
21
22
@staticmethod
23
def _prepare_kwargs(task, func, args, kwargs):
24
"""Prepare keyword arguments with task metadata and command line options"""
25
```
26
27
### Shell Command Actions
28
29
Actions for executing shell commands with different interaction patterns and output handling.
30
31
```python { .api }
32
class LongRunning(CmdAction):
33
"""
34
Action to handle long running shell processes (servers, services).
35
36
Properties:
37
- Output is never captured
38
- Always considered successful (return code ignored)
39
- Swallows KeyboardInterrupt for graceful shutdown
40
41
Use for processes that run indefinitely like web servers, background services,
42
or any process where you want to ignore the exit code.
43
"""
44
45
def execute(self, out=None, err=None):
46
"""Execute long-running command, handling KeyboardInterrupt gracefully"""
47
```
48
49
```python { .api }
50
class Interactive(CmdAction):
51
"""
52
Action to handle interactive shell processes.
53
54
Properties:
55
- Output is never captured (allows user interaction)
56
- Respects return code for success/failure
57
- Suitable for processes requiring user input
58
59
Use for commands that need user interaction like editors, prompts,
60
or any process where you need to preserve stdin/stdout/stderr.
61
"""
62
63
def execute(self, out=None, err=None):
64
"""Execute interactive command, preserving user interaction"""
65
```
66
67
### Python Function Actions
68
69
Actions for executing Python functions with different interaction and output patterns.
70
71
```python { .api }
72
class PythonInteractiveAction(PythonAction):
73
"""
74
Action to handle interactive Python function execution.
75
76
Properties:
77
- Output is never captured
78
- Successful unless exception is raised
79
- Allows function to interact directly with user
80
- Can return string results or dictionaries
81
82
Use for Python functions that need direct user interaction or
83
when you want to avoid output capture.
84
"""
85
86
def execute(self, out=None, err=None):
87
"""Execute Python function interactively, handling return values and exceptions"""
88
```
89
90
### Action Factory Functions
91
92
Utility functions for creating and normalizing action objects from various input formats.
93
94
```python { .api }
95
def normalize_callable(ref):
96
"""
97
Return a list with (callable, *args, **kwargs) from various input formats.
98
99
Handles both simple callables and tuples containing callable with arguments.
100
101
Args:
102
ref: A callable or tuple (callable, args, kwargs)
103
104
Returns:
105
list: [callable, args_tuple, kwargs_dict]
106
"""
107
```
108
109
### Legacy Support
110
111
Deprecated aliases maintained for backward compatibility.
112
113
```python { .api }
114
InteractiveAction = LongRunning # Deprecated alias for LongRunning (removed in 0.25)
115
```
116
117
### Usage Examples
118
119
#### Long Running Service
120
121
```python
122
from doit.tools import LongRunning
123
124
def task_start_server():
125
"""Start development server"""
126
return {
127
'actions': [LongRunning('python manage.py runserver 8000')],
128
'verbosity': 2
129
}
130
131
# Server will run until Ctrl+C, then gracefully shut down
132
```
133
134
#### Interactive Command
135
136
```python
137
from doit.tools import Interactive
138
139
def task_edit_config():
140
"""Edit configuration file interactively"""
141
return {
142
'actions': [Interactive('nano config.yaml')],
143
'file_dep': ['config.yaml']
144
}
145
146
# Opens editor for user interaction, respects exit code
147
```
148
149
#### Interactive Python Function
150
151
```python
152
from doit.tools import PythonInteractiveAction
153
154
def interactive_setup():
155
"""Interactive setup with user prompts"""
156
name = input("Enter project name: ")
157
version = input("Enter version [1.0.0]: ") or "1.0.0"
158
159
config = {'name': name, 'version': version}
160
print(f"Creating project {name} v{version}")
161
162
# Return dict to save as task values
163
return config
164
165
def task_setup():
166
"""Interactive project setup"""
167
return {
168
'actions': [PythonInteractiveAction(interactive_setup)],
169
'verbosity': 2
170
}
171
```
172
173
#### Mixed Action Types
174
175
```python
176
from doit.tools import Interactive, LongRunning
177
178
def task_development():
179
"""Complete development workflow"""
180
return {
181
'actions': [
182
'python setup.py develop', # Regular command
183
Interactive('git add .'), # Interactive for commit message
184
Interactive('git commit'), # Interactive for commit message
185
LongRunning('python -m pytest --watch') # Long-running test watcher
186
],
187
'verbosity': 2
188
}
189
```
190
191
#### Normalized Callable Usage
192
193
```python
194
from doit.action import normalize_callable
195
196
def my_function(arg1, arg2, kwarg1=None):
197
return f"Called with {arg1}, {arg2}, {kwarg1}"
198
199
# Various ways to specify callables
200
callable_formats = [
201
my_function, # Simple function
202
(my_function, ('hello', 'world'), {'kwarg1': 'test'}), # Full specification
203
(my_function, ('hello',), {}), # Partial specification
204
]
205
206
for fmt in callable_formats:
207
func, args, kwargs = normalize_callable(fmt)
208
print(func(*args, **kwargs))
209
```