0
# File Filtering
1
2
Flexible filtering system to control which file changes are monitored and reported. Provides built-in filters for common use cases and extensible base classes for custom filtering logic.
3
4
## Capabilities
5
6
### Base Filter Class
7
8
Foundation class for creating custom file filters with support for directory ignoring, pattern matching, and path exclusion.
9
10
```python { .api }
11
class BaseFilter:
12
"""
13
Base class for creating file filters.
14
15
Supports ignoring files through:
16
- Full directory names (ignore_dirs)
17
- Regex patterns for file/directory names (ignore_entity_patterns)
18
- Full path exclusions (ignore_paths)
19
"""
20
21
ignore_dirs: Sequence[str] = ()
22
ignore_entity_patterns: Sequence[str] = ()
23
ignore_paths: Sequence[Union[str, Path]] = ()
24
25
def __init__(self) -> None:
26
"""
27
Initialize filter by compiling regex patterns and processing paths.
28
"""
29
30
def __call__(self, change: Change, path: str) -> bool:
31
"""
32
Filter callable that determines if a file change should be included.
33
34
Parameters:
35
- change: Type of change (Change.added, Change.modified, Change.deleted)
36
- path: Raw path of the file or directory that changed
37
38
Returns:
39
bool: True if file should be included, False if ignored
40
"""
41
```
42
43
**Custom Filter Example:**
44
45
```python
46
from watchfiles import BaseFilter, Change
47
import re
48
49
class LogFilter(BaseFilter):
50
# Ignore common log directories
51
ignore_dirs = ('logs', 'tmp', '__pycache__')
52
53
# Ignore log files and temporary files
54
ignore_entity_patterns = (
55
r'\.log$', # Log files
56
r'\.tmp$', # Temporary files
57
r'^\.#', # Emacs temp files
58
r'~$', # Backup files
59
)
60
61
# Ignore specific paths
62
ignore_paths = ('/var/log', '/tmp')
63
64
# Use custom filter
65
from watchfiles import watch
66
67
for changes in watch('./src', watch_filter=LogFilter()):
68
print(f'Non-log changes: {changes}')
69
```
70
71
### Default Filter
72
73
Standard filter that ignores common development files and directories that typically shouldn't trigger reloads.
74
75
```python { .api }
76
class DefaultFilter(BaseFilter):
77
"""
78
Default filter ignoring common development artifacts.
79
"""
80
81
ignore_dirs: Sequence[str] = (
82
'__pycache__', # Python bytecode cache
83
'.git', # Git repository
84
'.hg', # Mercurial repository
85
'.svn', # Subversion repository
86
'.tox', # Tox testing environments
87
'.venv', # Python virtual environment
88
'.idea', # IntelliJ/PyCharm
89
'node_modules', # Node.js dependencies
90
'.mypy_cache', # MyPy type checker cache
91
'.pytest_cache', # Pytest cache
92
'.hypothesis', # Hypothesis testing cache
93
)
94
95
ignore_entity_patterns: Sequence[str] = (
96
r'\.py[cod]$', # Python bytecode files (.pyc, .pyo, .pyd)
97
r'\.___jb_...___$', # JetBrains temp files
98
r'\.sw.$', # Vim swap files
99
'~$', # Backup files
100
r'^\.\#', # Emacs temp files
101
r'^\.DS_Store$', # macOS metadata
102
r'^flycheck_', # Emacs flycheck temp files
103
)
104
105
def __init__(
106
self,
107
*,
108
ignore_dirs: Optional[Sequence[str]] = None,
109
ignore_entity_patterns: Optional[Sequence[str]] = None,
110
ignore_paths: Optional[Sequence[Union[str, Path]]] = None,
111
) -> None:
112
"""
113
Initialize DefaultFilter with optional overrides.
114
115
Parameters:
116
- ignore_dirs: Override default ignore_dirs if provided
117
- ignore_entity_patterns: Override default patterns if provided
118
- ignore_paths: Override default ignore_paths if provided
119
"""
120
```
121
122
**Usage Examples:**
123
124
```python
125
from watchfiles import watch, DefaultFilter
126
127
# Use default filter (this is the default behavior)
128
for changes in watch('./src'):
129
print(changes)
130
131
# Customize default filter
132
custom_filter = DefaultFilter(
133
ignore_dirs=('__pycache__', '.git', 'custom_ignore'),
134
ignore_entity_patterns=(r'\.pyc$', r'\.log$'),
135
ignore_paths=['/tmp/ignore_this']
136
)
137
138
for changes in watch('./src', watch_filter=custom_filter):
139
print(changes)
140
141
# Disable all filtering
142
for changes in watch('./src', watch_filter=None):
143
print(changes) # Will include all changes
144
```
145
146
### Python Filter
147
148
Specialized filter that only watches Python files while also applying the standard default ignores.
149
150
```python { .api }
151
class PythonFilter(DefaultFilter):
152
"""
153
Filter for Python files only.
154
155
Inherits all default ignores and additionally filters to only
156
include files with Python extensions: .py, .pyx, .pyd
157
"""
158
159
extensions: tuple # Set in __init__ to ('.py', '.pyx', '.pyd') + extra_extensions
160
161
def __init__(
162
self,
163
*,
164
ignore_paths: Optional[Sequence[Union[str, Path]]] = None,
165
extra_extensions: Sequence[str] = (),
166
) -> None:
167
"""
168
Initialize PythonFilter.
169
170
Parameters:
171
- ignore_paths: Additional paths to ignore
172
- extra_extensions: Additional file extensions to include beyond Python defaults
173
174
Note:
175
Only ignore_paths can be customized. Directory and pattern ignores
176
use the DefaultFilter defaults.
177
"""
178
179
def __call__(self, change: Change, path: str) -> bool:
180
"""
181
Filter that only includes Python files and applies default ignores.
182
183
Returns:
184
bool: True only if file has Python extension AND passes default filter
185
"""
186
```
187
188
**Usage Examples:**
189
190
```python
191
from watchfiles import watch, PythonFilter
192
193
# Watch only Python files
194
for changes in watch('./src', watch_filter=PythonFilter()):
195
print(f'Python file changes: {changes}')
196
197
# Include additional extensions
198
py_filter = PythonFilter(extra_extensions=('.pyi', '.pyx'))
199
200
for changes in watch('./src', watch_filter=py_filter):
201
print(f'Extended Python changes: {changes}')
202
203
# Ignore specific paths
204
py_filter = PythonFilter(ignore_paths=['./src/generated'])
205
206
for changes in watch('./src', watch_filter=py_filter):
207
print(f'Filtered Python changes: {changes}')
208
```
209
210
### Custom Callable Filters
211
212
Any callable that accepts `(Change, str)` and returns `bool` can be used as a filter:
213
214
```python
215
from watchfiles import watch, Change
216
217
def only_added_files(change: Change, path: str) -> bool:
218
"""Only watch for newly added files."""
219
return change == Change.added
220
221
def only_python_or_js(change: Change, path: str) -> bool:
222
"""Only watch Python and JavaScript files."""
223
return path.endswith(('.py', '.js', '.ts'))
224
225
def size_filter(change: Change, path: str) -> bool:
226
"""Only watch files under 1MB."""
227
try:
228
import os
229
return os.path.getsize(path) < 1024 * 1024
230
except (OSError, FileNotFoundError):
231
return True # Include if we can't check size
232
233
# Use custom filters
234
for changes in watch('./src', watch_filter=only_added_files):
235
print(f'New files: {changes}')
236
237
for changes in watch('./src', watch_filter=only_python_or_js):
238
print(f'Python/JS changes: {changes}')
239
```
240
241
### CLI Filter Integration
242
243
The CLI supports filter specification via command line arguments:
244
245
```bash
246
# Use default filter
247
watchfiles my_module.main ./src
248
249
# Use Python filter
250
watchfiles --filter python my_module.main ./src
251
252
# Use no filter (watch all files)
253
watchfiles --filter all my_module.main ./src
254
255
# Use custom filter class
256
watchfiles --filter mypackage.filters.CustomFilter my_module.main ./src
257
258
# Ignore specific paths (works with default and python filters)
259
watchfiles --filter python --ignore-paths "logs,tmp" my_module.main ./src
260
```
261
262
## Filter Implementation Details
263
264
### Pattern Matching
265
- `ignore_entity_patterns` are compiled as regular expressions
266
- Patterns match against the file/directory name only (not full path)
267
- Use raw strings (`r''`) for regex patterns to avoid escaping issues
268
269
### Path Matching
270
- `ignore_paths` uses `str.startswith()` for prefix matching
271
- Paths are converted to strings for comparison
272
- Both absolute and relative paths are supported
273
274
### Directory Matching
275
- `ignore_dirs` matches exact directory names in the path hierarchy
276
- Checks each component of the path, so subdirectories are also ignored
277
- Case-sensitive matching
278
279
### Performance Considerations
280
- Filters are called for every file change event
281
- Regex compilation happens once during filter initialization
282
- Directory and path checks are optimized for common cases
283
- More complex filters may impact performance with high-frequency changes
284
285
## Types
286
287
```python { .api }
288
# Type annotations for filter functions
289
FilterCallable = Callable[[Change, str], bool]
290
```