0
# Loop-on-Fail (Deprecated)
1
2
Legacy functionality for automatically re-running failed tests when files change. This feature is deprecated and will be removed in pytest-xdist 4.0.
3
4
**⚠️ DEPRECATION WARNING: The --looponfail command line argument and looponfailroots config variable are deprecated. The loop-on-fail feature will be removed in pytest-xdist 4.0.**
5
6
## Capabilities
7
8
### Loop-on-Fail Main Function
9
10
Entry point for loop-on-fail functionality that continuously monitors for file changes and re-runs failed tests.
11
12
```python { .api }
13
def pytest_cmdline_main(config: pytest.Config) -> int | None:
14
"""
15
Handle looponfail command line option.
16
17
Args:
18
config: pytest configuration object
19
20
Returns:
21
int: Exit code 2 for looponfail mode, None for normal processing
22
23
Raises:
24
pytest.UsageError: If --pdb is used with --looponfail (incompatible)
25
"""
26
27
def looponfail_main(config: pytest.Config) -> None:
28
"""
29
Main loop-on-fail functionality.
30
31
Continuously monitors files for changes and re-runs failed tests
32
until all tests pass or interrupted with Ctrl-C.
33
34
Args:
35
config: pytest configuration object
36
"""
37
```
38
39
### Remote Control
40
41
Controls remote test execution in a subprocess for loop-on-fail mode.
42
43
```python { .api }
44
class RemoteControl:
45
"""Controls remote test execution for loop-on-fail."""
46
47
def __init__(self, config: pytest.Config) -> None:
48
"""
49
Initialize remote control.
50
51
Args:
52
config: pytest configuration object
53
"""
54
55
def setup(self) -> None:
56
"""Set up remote worker session for test execution."""
57
58
def teardown(self) -> None:
59
"""Tear down remote worker session."""
60
61
def loop_once(self) -> None:
62
"""Execute one iteration of the test loop."""
63
```
64
65
### File System Monitoring
66
67
Monitors file system changes to trigger test re-runs.
68
69
```python { .api }
70
class StatRecorder:
71
"""Records and monitors file system changes."""
72
73
def __init__(self, rootdirs: Sequence[Path]) -> None:
74
"""
75
Initialize file system monitor.
76
77
Args:
78
rootdirs: directories to monitor for changes
79
"""
80
81
def waitonchange(self, checkinterval: float = 1.0) -> None:
82
"""
83
Wait for file system changes.
84
85
Args:
86
checkinterval: interval in seconds between checks
87
"""
88
```
89
90
### Configuration Options
91
92
Command line options for loop-on-fail functionality.
93
94
```python { .api }
95
def pytest_addoption(parser: pytest.Parser) -> None:
96
"""
97
Add looponfail command line option.
98
99
Adds the -f/--looponfail option to pytest's argument parser.
100
101
Args:
102
parser: pytest argument parser
103
"""
104
```
105
106
**Options:**
107
- `-f/--looponfail`: Run tests in subprocess, wait for file modifications, then re-run failing test set until all pass
108
109
**Configuration:**
110
- `looponfailroots`: List of directories to monitor for changes (ini file setting)
111
112
## Usage Examples
113
114
### Basic Loop-on-Fail Usage
115
116
```bash
117
# DEPRECATED - will be removed in pytest-xdist 4.0
118
pytest -f
119
120
# With specific directories to monitor
121
pytest -f --looponfailroots=src,tests
122
```
123
124
### Configuration File Setup
125
126
```ini
127
# In pytest.ini or tox.ini - DEPRECATED
128
[tool:pytest]
129
looponfailroots = src tests lib
130
```
131
132
### Programmatic Usage (Not Recommended)
133
134
```python
135
# DEPRECATED - for reference only
136
import pytest
137
from xdist.looponfail import looponfail_main
138
139
def run_loop_on_fail():
140
"""Example of programmatic loop-on-fail usage."""
141
# Create pytest config
142
config = pytest.Config()
143
config.option.looponfail = True
144
145
try:
146
looponfail_main(config)
147
except KeyboardInterrupt:
148
print("Loop-on-fail interrupted by user")
149
```
150
151
### Migration Path
152
153
Since loop-on-fail is deprecated, consider these alternatives:
154
155
#### Alternative 1: pytest-watch
156
157
```bash
158
# Install pytest-watch as replacement
159
pip install pytest-watch
160
161
# Use ptw instead of pytest -f
162
ptw --runner "pytest -x"
163
```
164
165
#### Alternative 2: File Watching Scripts
166
167
```python
168
# custom_watch.py - Simple replacement
169
import time
170
import subprocess
171
from watchdog.observers import Observer
172
from watchdog.events import FileSystemEventHandler
173
174
class TestRunner(FileSystemEventHandler):
175
def __init__(self):
176
self.last_run = 0
177
178
def on_modified(self, event):
179
if event.is_directory:
180
return
181
182
# Debounce rapid file changes
183
now = time.time()
184
if now - self.last_run < 2:
185
return
186
187
self.last_run = now
188
189
# Run tests
190
print(f"File {event.src_path} changed, running tests...")
191
result = subprocess.run(['pytest', '--tb=short'],
192
capture_output=True, text=True)
193
194
if result.returncode == 0:
195
print("All tests passed!")
196
else:
197
print("Tests failed, watching for changes...")
198
199
if __name__ == "__main__":
200
event_handler = TestRunner()
201
observer = Observer()
202
observer.schedule(event_handler, path='.', recursive=True)
203
observer.start()
204
205
try:
206
while True:
207
time.sleep(1)
208
except KeyboardInterrupt:
209
observer.stop()
210
observer.join()
211
```
212
213
#### Alternative 3: IDE Integration
214
215
Modern IDEs provide built-in test watching functionality:
216
217
```python
218
# VS Code with Python extension
219
# - Enable "python.testing.autoTestDiscoverOnSaveEnabled"
220
# - Use Test Explorer for continuous testing
221
222
# PyCharm Professional
223
# - Use "Run with Coverage" in continuous mode
224
# - Enable "Rerun tests automatically"
225
```
226
227
## Migration Guide
228
229
1. **Replace `-f/--looponfail`**:
230
```bash
231
# Old (deprecated)
232
pytest -f
233
234
# New alternatives
235
pip install pytest-watch
236
ptw --runner "pytest -x"
237
238
# Or use IDE integration
239
```
240
241
2. **Replace `looponfailroots` configuration**:
242
```bash
243
# Old pytest.ini
244
[tool:pytest]
245
looponfailroots = src tests
246
247
# New with pytest-watch
248
ptw --runner "pytest -x" src tests
249
```
250
251
3. **Update CI/CD pipelines**:
252
```yaml
253
# Remove looponfail from CI scripts
254
# It was never intended for CI use anyway
255
256
# Old
257
- run: pytest -f # Don't do this in CI
258
259
# Correct
260
- run: pytest # Standard test execution
261
```
262
263
4. **Development workflow adjustment**:
264
```bash
265
# Instead of pytest -f, use:
266
# Option 1: pytest-watch
267
ptw
268
269
# Option 2: IDE auto-test features
270
# Configure your IDE for automatic test execution
271
272
# Option 3: Custom file watching
273
# Implement custom solution using watchdog or similar
274
```