Run process
run_process ¶
run_process(*paths: Union[Path, str], target: Union[str, Callable[..., Any]], args: Tuple[Any, ...] = (), kwargs: Optional[Dict[str, Any]] = None, target_type: Literal['function', 'command', 'auto'] = 'auto', callback: Optional[Callable[[Set[FileChange]], None]] = None, watch_filter: Optional[Callable[[Change, str], bool]] = DefaultFilter(), grace_period: float = 0, debounce: int = 1600, step: int = 50, debug: Optional[bool] = None, sigint_timeout: int = 5, sigkill_timeout: int = 1, recursive: bool = True, ignore_permission_denied: bool = False) -> int
Run a process and restart it upon file changes.
run_process can work in two ways:
- Using
multiprocessing.Process† to run a python function - Or, using
subprocess.Popento run a command
Note
† technically multiprocessing.get_context('spawn').Process to avoid forking and improve
code reload/import.
Internally, run_process uses watch with raise_interrupt=False so the function
exits cleanly upon Ctrl+C.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*paths |
Union[Path, str]
|
matches the same argument of |
()
|
target |
Union[str, Callable[..., Any]]
|
function or command to run |
required |
args |
Tuple[Any, ...]
|
arguments to pass to |
()
|
kwargs |
Optional[Dict[str, Any]]
|
keyword arguments to pass to |
None
|
target_type |
Literal['function', 'command', 'auto']
|
type of target. Can be |
'auto'
|
callback |
Optional[Callable[[Set[FileChange]], None]]
|
function to call on each reload, the function should accept a set of changes as the sole argument |
None
|
watch_filter |
Optional[Callable[[Change, str], bool]]
|
matches the same argument of |
DefaultFilter()
|
grace_period |
float
|
number of seconds after the process is started before watching for changes |
0
|
debounce |
int
|
matches the same argument of |
1600
|
step |
int
|
matches the same argument of |
50
|
debug |
Optional[bool]
|
matches the same argument of |
None
|
sigint_timeout |
int
|
the number of seconds to wait after sending sigint before sending sigkill |
5
|
sigkill_timeout |
int
|
the number of seconds to wait after sending sigkill before raising an exception |
1
|
recursive |
bool
|
matches the same argument of |
True
|
Returns:
| Type | Description |
|---|---|
int
|
number of times the function was reloaded. |
from watchfiles import run_process
def callback(changes):
print('changes detected:', changes)
def foobar(a, b):
print('foobar called with:', a, b)
if __name__ == '__main__':
run_process('./path/to/dir', target=foobar, args=(1, 2), callback=callback)
As well as using a callback function, changes can be accessed from within the target function,
using the WATCHFILES_CHANGES environment variable.
from watchfiles import run_process
def foobar(a, b, c):
# changes will be an empty list "[]" the first time the function is called
changes = os.getenv('WATCHFILES_CHANGES')
changes = json.loads(changes)
print('foobar called due to changes:', changes)
if __name__ == '__main__':
run_process('./path/to/dir', target=foobar, args=(1, 2, 3))
Again with the target as command, WATCHFILES_CHANGES can be used
to access changes.
arun_process
async
¶
arun_process(*paths: Union[Path, str], target: Union[str, Callable[..., Any]], args: Tuple[Any, ...] = (), kwargs: Optional[Dict[str, Any]] = None, target_type: Literal['function', 'command', 'auto'] = 'auto', callback: Optional[Callable[[Set[FileChange]], Any]] = None, watch_filter: Optional[Callable[[Change, str], bool]] = DefaultFilter(), grace_period: float = 0, debounce: int = 1600, step: int = 50, debug: Optional[bool] = None, recursive: bool = True, ignore_permission_denied: bool = False) -> int
Async equivalent of run_process, all arguments match those of run_process except
callback which can be a coroutine.
Starting and stopping the process and watching for changes is done in a separate thread.
As with run_process, internally arun_process uses awatch, however KeyboardInterrupt
cannot be caught and suppressed in awatch so these errors need to be caught separately, see below.
import asyncio
from watchfiles import arun_process
async def callback(changes):
await asyncio.sleep(0.1)
print('changes detected:', changes)
def foobar(a, b):
print('foobar called with:', a, b)
async def main():
await arun_process('.', target=foobar, args=(1, 2), callback=callback)
if __name__ == '__main__':
try:
asyncio.run(main())
except KeyboardInterrupt:
print('stopped via KeyboardInterrupt')
detect_target_type ¶
Used by run_process, arun_process
and indirectly the CLI to determine the target type with target_type is auto.
Detects the target type - either function or command. This method is only called with target_type='auto'.
The following logic is employed:
- If
targetis not a string, it is assumed to be a function - If
targetends with.pyor.sh, it is assumed to be a command - Otherwise, the target is assumed to be a function if it matches the regex
[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+
If this logic does not work for you, specify the target type explicitly using the target_type function argument
or --target-type command line argument.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target |
Union[str, Callable[..., Any]]
|
The target value |
required |
Returns:
| Type | Description |
|---|---|
Literal['function', 'command']
|
either |