Simple, modern and high performance file watching and code reload in python.
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.
Foundation class for creating custom file filters with support for directory ignoring, pattern matching, and path exclusion.
class BaseFilter:
"""
Base class for creating file filters.
Supports ignoring files through:
- Full directory names (ignore_dirs)
- Regex patterns for file/directory names (ignore_entity_patterns)
- Full path exclusions (ignore_paths)
"""
ignore_dirs: Sequence[str] = ()
ignore_entity_patterns: Sequence[str] = ()
ignore_paths: Sequence[Union[str, Path]] = ()
def __init__(self) -> None:
"""
Initialize filter by compiling regex patterns and processing paths.
"""
def __call__(self, change: Change, path: str) -> bool:
"""
Filter callable that determines if a file change should be included.
Parameters:
- change: Type of change (Change.added, Change.modified, Change.deleted)
- path: Raw path of the file or directory that changed
Returns:
bool: True if file should be included, False if ignored
"""Custom Filter Example:
from watchfiles import BaseFilter, Change
import re
class LogFilter(BaseFilter):
# Ignore common log directories
ignore_dirs = ('logs', 'tmp', '__pycache__')
# Ignore log files and temporary files
ignore_entity_patterns = (
r'\.log$', # Log files
r'\.tmp$', # Temporary files
r'^\.#', # Emacs temp files
r'~$', # Backup files
)
# Ignore specific paths
ignore_paths = ('/var/log', '/tmp')
# Use custom filter
from watchfiles import watch
for changes in watch('./src', watch_filter=LogFilter()):
print(f'Non-log changes: {changes}')Standard filter that ignores common development files and directories that typically shouldn't trigger reloads.
class DefaultFilter(BaseFilter):
"""
Default filter ignoring common development artifacts.
"""
ignore_dirs: Sequence[str] = (
'__pycache__', # Python bytecode cache
'.git', # Git repository
'.hg', # Mercurial repository
'.svn', # Subversion repository
'.tox', # Tox testing environments
'.venv', # Python virtual environment
'.idea', # IntelliJ/PyCharm
'node_modules', # Node.js dependencies
'.mypy_cache', # MyPy type checker cache
'.pytest_cache', # Pytest cache
'.hypothesis', # Hypothesis testing cache
)
ignore_entity_patterns: Sequence[str] = (
r'\.py[cod]$', # Python bytecode files (.pyc, .pyo, .pyd)
r'\.___jb_...___$', # JetBrains temp files
r'\.sw.$', # Vim swap files
'~$', # Backup files
r'^\.\#', # Emacs temp files
r'^\.DS_Store$', # macOS metadata
r'^flycheck_', # Emacs flycheck temp files
)
def __init__(
self,
*,
ignore_dirs: Optional[Sequence[str]] = None,
ignore_entity_patterns: Optional[Sequence[str]] = None,
ignore_paths: Optional[Sequence[Union[str, Path]]] = None,
) -> None:
"""
Initialize DefaultFilter with optional overrides.
Parameters:
- ignore_dirs: Override default ignore_dirs if provided
- ignore_entity_patterns: Override default patterns if provided
- ignore_paths: Override default ignore_paths if provided
"""Usage Examples:
from watchfiles import watch, DefaultFilter
# Use default filter (this is the default behavior)
for changes in watch('./src'):
print(changes)
# Customize default filter
custom_filter = DefaultFilter(
ignore_dirs=('__pycache__', '.git', 'custom_ignore'),
ignore_entity_patterns=(r'\.pyc$', r'\.log$'),
ignore_paths=['/tmp/ignore_this']
)
for changes in watch('./src', watch_filter=custom_filter):
print(changes)
# Disable all filtering
for changes in watch('./src', watch_filter=None):
print(changes) # Will include all changesSpecialized filter that only watches Python files while also applying the standard default ignores.
class PythonFilter(DefaultFilter):
"""
Filter for Python files only.
Inherits all default ignores and additionally filters to only
include files with Python extensions: .py, .pyx, .pyd
"""
extensions: tuple # Set in __init__ to ('.py', '.pyx', '.pyd') + extra_extensions
def __init__(
self,
*,
ignore_paths: Optional[Sequence[Union[str, Path]]] = None,
extra_extensions: Sequence[str] = (),
) -> None:
"""
Initialize PythonFilter.
Parameters:
- ignore_paths: Additional paths to ignore
- extra_extensions: Additional file extensions to include beyond Python defaults
Note:
Only ignore_paths can be customized. Directory and pattern ignores
use the DefaultFilter defaults.
"""
def __call__(self, change: Change, path: str) -> bool:
"""
Filter that only includes Python files and applies default ignores.
Returns:
bool: True only if file has Python extension AND passes default filter
"""Usage Examples:
from watchfiles import watch, PythonFilter
# Watch only Python files
for changes in watch('./src', watch_filter=PythonFilter()):
print(f'Python file changes: {changes}')
# Include additional extensions
py_filter = PythonFilter(extra_extensions=('.pyi', '.pyx'))
for changes in watch('./src', watch_filter=py_filter):
print(f'Extended Python changes: {changes}')
# Ignore specific paths
py_filter = PythonFilter(ignore_paths=['./src/generated'])
for changes in watch('./src', watch_filter=py_filter):
print(f'Filtered Python changes: {changes}')Any callable that accepts (Change, str) and returns bool can be used as a filter:
from watchfiles import watch, Change
def only_added_files(change: Change, path: str) -> bool:
"""Only watch for newly added files."""
return change == Change.added
def only_python_or_js(change: Change, path: str) -> bool:
"""Only watch Python and JavaScript files."""
return path.endswith(('.py', '.js', '.ts'))
def size_filter(change: Change, path: str) -> bool:
"""Only watch files under 1MB."""
try:
import os
return os.path.getsize(path) < 1024 * 1024
except (OSError, FileNotFoundError):
return True # Include if we can't check size
# Use custom filters
for changes in watch('./src', watch_filter=only_added_files):
print(f'New files: {changes}')
for changes in watch('./src', watch_filter=only_python_or_js):
print(f'Python/JS changes: {changes}')The CLI supports filter specification via command line arguments:
# Use default filter
watchfiles my_module.main ./src
# Use Python filter
watchfiles --filter python my_module.main ./src
# Use no filter (watch all files)
watchfiles --filter all my_module.main ./src
# Use custom filter class
watchfiles --filter mypackage.filters.CustomFilter my_module.main ./src
# Ignore specific paths (works with default and python filters)
watchfiles --filter python --ignore-paths "logs,tmp" my_module.main ./srcignore_entity_patterns are compiled as regular expressionsr'') for regex patterns to avoid escaping issuesignore_paths uses str.startswith() for prefix matchingignore_dirs matches exact directory names in the path hierarchy# Type annotations for filter functions
FilterCallable = Callable[[Change, str], bool]Install with Tessl CLI
npx tessl i tessl/pypi-watchfiles