or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdfile-filtering.mdfile-watching.mdindex.mdprocess-management.md

process-management.mddocs/

0

# Process Management

1

2

Automatic process restarting and management functionality that watches for file changes and restarts processes accordingly. Supports both Python functions and shell commands with configurable signal handling, graceful shutdown, and callback mechanisms.

3

4

## Capabilities

5

6

### Synchronous Process Management

7

8

Run a process and automatically restart it when file changes are detected. Supports Python functions and shell commands with extensive configuration options.

9

10

```python { .api }

11

def run_process(

12

*paths: Union[Path, str],

13

target: Union[str, Callable[..., Any]],

14

args: Tuple[Any, ...] = (),

15

kwargs: Optional[Dict[str, Any]] = None,

16

target_type: Literal['function', 'command', 'auto'] = 'auto',

17

callback: Optional[Callable[[Set[FileChange]], None]] = None,

18

watch_filter: Optional[Callable[[Change, str], bool]] = DefaultFilter(),

19

grace_period: float = 0,

20

debounce: int = 1_600,

21

step: int = 50,

22

debug: Optional[bool] = None,

23

sigint_timeout: int = 5,

24

sigkill_timeout: int = 1,

25

recursive: bool = True,

26

ignore_permission_denied: bool = False,

27

) -> int:

28

"""

29

Run a process and restart it upon file changes.

30

31

Parameters:

32

- *paths: Filesystem paths to watch (same as watch())

33

- target: Function or command to run

34

- args: Arguments to pass to target (function only)

35

- kwargs: Keyword arguments to pass to target (function only)

36

- target_type: 'function', 'command', or 'auto' (uses detect_target_type)

37

- callback: Function called on each reload with changes as argument

38

- watch_filter: File filter (same as watch())

39

- grace_period: Seconds after process start before watching changes

40

- debounce: Debounce time in ms (same as watch())

41

- step: Step time in ms (same as watch())

42

- debug: Enable debug output

43

- sigint_timeout: Seconds to wait after SIGINT before SIGKILL

44

- sigkill_timeout: Seconds to wait after SIGKILL before exception

45

- recursive: Watch recursively (same as watch())

46

- ignore_permission_denied: Ignore permission errors

47

48

Returns:

49

int: Number of times the process was reloaded

50

51

Note:

52

Uses multiprocessing.get_context('spawn').Process for Python functions

53

to avoid forking and improve code reload/import behavior.

54

"""

55

```

56

57

**Usage Examples:**

58

59

```python

60

from watchfiles import run_process

61

62

# Run a Python function

63

def my_app(name, port=8000):

64

print(f"Starting {name} on port {port}...")

65

# App logic here

66

67

# Watch current directory and restart function on changes

68

reloads = run_process('.', target=my_app, args=('MyApp',), kwargs={'port': 3000})

69

print(f"Process reloaded {reloads} times")

70

71

# Run a shell command

72

run_process('./src', target='python main.py')

73

74

# With callback function

75

def on_reload(changes):

76

print(f"Reloading due to: {changes}")

77

78

run_process('./src', target='python server.py', callback=on_reload)

79

80

# With grace period (wait before watching)

81

run_process('./src', target=my_app, grace_period=2.0)

82

```

83

84

### Asynchronous Process Management

85

86

Async equivalent of run_process with support for async callbacks and proper integration with async event loops.

87

88

```python { .api }

89

async def arun_process(

90

*paths: Union[Path, str],

91

target: Union[str, Callable[..., Any]],

92

args: Tuple[Any, ...] = (),

93

kwargs: Optional[Dict[str, Any]] = None,

94

target_type: Literal['function', 'command', 'auto'] = 'auto',

95

callback: Optional[Callable[[Set[FileChange]], Any]] = None,

96

watch_filter: Optional[Callable[[Change, str], bool]] = DefaultFilter(),

97

grace_period: float = 0,

98

debounce: int = 1_600,

99

step: int = 50,

100

debug: Optional[bool] = None,

101

recursive: bool = True,

102

ignore_permission_denied: bool = False,

103

) -> int:

104

"""

105

Async version of run_process.

106

107

Parameters:

108

Same as run_process except:

109

- callback: Can be a coroutine function

110

- No sigint_timeout/sigkill_timeout (handled by async framework)

111

112

Returns:

113

int: Number of times the process was reloaded

114

115

Note:

116

Starting/stopping processes and watching done in separate threads.

117

KeyboardInterrupt must be caught at asyncio.run() level.

118

"""

119

```

120

121

**Usage Examples:**

122

123

```python

124

import asyncio

125

from watchfiles import arun_process

126

127

async def async_callback(changes):

128

await asyncio.sleep(0.1) # Async operation

129

print(f"Async reload callback: {changes}")

130

131

def my_app():

132

print("App running...")

133

134

async def main():

135

reloads = await arun_process('./src', target=my_app, callback=async_callback)

136

print(f"Reloaded {reloads} times")

137

138

try:

139

asyncio.run(main())

140

except KeyboardInterrupt:

141

print("Stopped via KeyboardInterrupt")

142

```

143

144

### Target Type Detection

145

146

Automatically detect whether a target should be run as a function or command when `target_type='auto'`.

147

148

```python { .api }

149

def detect_target_type(target: Union[str, Callable[..., Any]]) -> Literal['function', 'command']:

150

"""

151

Used by run_process and arun_process to determine the target type when target_type is 'auto'.

152

153

Detects the target type - either 'function' or 'command'. This method is only called with target_type='auto'.

154

155

The following logic is employed:

156

- If target is not a string, it is assumed to be a function

157

- If target ends with '.py' or '.sh', it is assumed to be a command

158

- Otherwise, the target is assumed to be a function if it matches the regex [a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)+

159

160

If this logic does not work for you, specify the target type explicitly using the target_type function argument

161

or --target-type command line argument.

162

163

Parameters:

164

- target: The target value to analyze

165

166

Returns:

167

Either 'function' or 'command'

168

"""

169

```

170

171

**Usage Examples:**

172

173

```python

174

from watchfiles import detect_target_type

175

176

# These return 'function'

177

print(detect_target_type(my_function)) # 'function'

178

print(detect_target_type('mymodule.main')) # 'function'

179

180

# These return 'command'

181

print(detect_target_type('python main.py')) # 'command'

182

print(detect_target_type('main.py')) # 'command'

183

print(detect_target_type('./script.sh')) # 'command'

184

```

185

186

### Import String Utility

187

188

Import a callable from a dotted module path string, used internally for function targets.

189

190

```python { .api }

191

def import_string(dotted_path: str) -> Any:

192

"""

193

Import a dotted module path and return the attribute/class designated by the

194

last name in the path. Raise ImportError if the import fails.

195

196

Stolen approximately from django. This is used to import function targets

197

when they are specified as dotted strings.

198

199

Parameters:

200

- dotted_path: Dotted module path like 'mypackage.module.function'

201

202

Returns:

203

The imported attribute/function/class

204

205

Raises:

206

ImportError: If dotted_path doesn't look like a module path or if the

207

module doesn't define the specified attribute

208

"""

209

```

210

211

**Usage Examples:**

212

213

```python

214

from watchfiles import import_string

215

216

# Import a function

217

func = import_string('mypackage.utils.helper_function')

218

result = func(arg1, arg2)

219

220

# Import a class

221

MyClass = import_string('mypackage.models.MyModel')

222

instance = MyClass()

223

```

224

225

### Process Environment Variables

226

227

When processes are started, the `WATCHFILES_CHANGES` environment variable is set to a JSON string containing the file changes that triggered the restart:

228

229

```python

230

import os

231

import json

232

233

def my_target_function():

234

# Access changes that triggered this restart

235

changes_json = os.getenv('WATCHFILES_CHANGES', '[]')

236

changes = json.loads(changes_json)

237

print(f"Restarted due to: {changes}")

238

# changes is a list of [change_type_string, path] pairs

239

```

240

241

**Shell Command Example:**

242

243

```bash

244

#!/bin/bash

245

echo "Changes that triggered restart: $WATCHFILES_CHANGES"

246

```

247

248

### Signal Handling

249

250

Process management includes comprehensive signal handling for graceful shutdown:

251

252

1. **SIGINT** (Ctrl+C) sent to process first

253

2. Wait `sigint_timeout` seconds for graceful shutdown

254

3. If process still running, send **SIGKILL**

255

4. Wait `sigkill_timeout` seconds before raising exception

256

257

**SIGTERM Handling:**

258

The watchfiles process automatically registers a SIGTERM handler that raises KeyboardInterrupt, ensuring clean shutdown in containerized environments.

259

260

## Types

261

262

```python { .api }

263

# Internal types used by process management

264

class CombinedProcess:

265

"""Wrapper for both subprocess.Popen and multiprocessing.Process"""

266

267

def __init__(self, p: Union[SpawnProcess, subprocess.Popen[bytes]]) -> None: ...

268

def stop(self, sigint_timeout: int = 5, sigkill_timeout: int = 1) -> None: ...

269

def is_alive(self) -> bool: ...

270

@property

271

def pid(self) -> int: ...

272

def join(self, timeout: int) -> None: ...

273

@property

274

def exitcode(self) -> Optional[int]: ...

275

```