or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

constants.mdexceptions.mdindex.mdprocess.mdsensors.mdsystem-info.md

exceptions.mddocs/

0

# Exception Hierarchy

1

2

psutil defines a comprehensive exception hierarchy for handling various error conditions that can occur during system monitoring and process management operations. Proper exception handling is essential for robust applications using psutil.

3

4

## Exception Hierarchy

5

6

```python

7

# psutil exception hierarchy:

8

# Exception (built-in)

9

# └── Error (psutil base exception)

10

# ├── NoSuchProcess

11

# ├── ZombieProcess

12

# ├── AccessDenied

13

# └── TimeoutExpired

14

```

15

16

## Base Exception Class

17

18

### Error

19

20

```python

21

import psutil

22

23

# Base exception class for all psutil exceptions

24

try:

25

p = psutil.Process(99999) # Non-existent PID

26

print(p.name())

27

except psutil.Error as e:

28

print(f"psutil error occurred: {e}")

29

print(f"Exception type: {type(e).__name__}")

30

```

31

{ .api }

32

33

## Core Exception Types

34

35

### NoSuchProcess

36

37

Raised when a process no longer exists or was never found.

38

39

```python

40

# NoSuchProcess - Process not found or terminated

41

try:

42

p = psutil.Process(99999) # Non-existent PID

43

print(p.name())

44

except psutil.NoSuchProcess as e:

45

print(f"Process not found: {e}")

46

print(f"PID: {e.pid}")

47

print(f"Process name: {e.name}")

48

print(f"Message: {e.msg}")

49

50

# Common scenario - process terminates between operations

51

def monitor_process_safely(pid):

52

"""Safely monitor a process that might terminate."""

53

try:

54

p = psutil.Process(pid)

55

56

while True:

57

try:

58

print(f"CPU: {p.cpu_percent()}")

59

print(f"Memory: {p.memory_info().rss / 1024**2:.1f} MB")

60

time.sleep(1)

61

except psutil.NoSuchProcess:

62

print(f"Process {pid} terminated")

63

break

64

65

except psutil.NoSuchProcess:

66

print(f"Process {pid} not found")

67

```

68

{ .api }

69

70

### AccessDenied

71

72

Raised when insufficient permissions prevent accessing process information.

73

74

```python

75

# AccessDenied - Permission denied

76

import os

77

78

try:

79

# Try to access system process (may require admin/root)

80

if psutil.WINDOWS:

81

system_pid = 4 # System process on Windows

82

else:

83

system_pid = 1 # Init process on Unix

84

85

p = psutil.Process(system_pid)

86

print(p.cmdline()) # May require elevated privileges

87

88

except psutil.AccessDenied as e:

89

print(f"Access denied: {e}")

90

print(f"PID: {e.pid}")

91

print(f"Process name: {e.name}")

92

print(f"Current user: {os.getenv('USER') or os.getenv('USERNAME')}")

93

print("Try running with elevated privileges")

94

95

# Graceful handling of access denied

96

def get_accessible_process_info(pid):

97

"""Get process info with graceful handling of access denied."""

98

try:

99

p = psutil.Process(pid)

100

info = {'pid': pid, 'accessible': True}

101

102

# Try to get basic info

103

try:

104

info['name'] = p.name()

105

except psutil.AccessDenied:

106

info['name'] = 'Access Denied'

107

108

try:

109

info['status'] = p.status()

110

except psutil.AccessDenied:

111

info['status'] = 'Unknown'

112

113

try:

114

info['cpu_percent'] = p.cpu_percent()

115

except psutil.AccessDenied:

116

info['cpu_percent'] = None

117

118

return info

119

120

except psutil.NoSuchProcess:

121

return {'pid': pid, 'accessible': False, 'error': 'Process not found'}

122

123

# Get info for current process (should work)

124

info = get_accessible_process_info(os.getpid())

125

print(f"Current process info: {info}")

126

```

127

{ .api }

128

129

### ZombieProcess

130

131

Raised when trying to access a zombie process (Unix systems).

132

133

```python

134

# ZombieProcess - Process is in zombie state

135

try:

136

# Zombie processes are common on Unix systems

137

for proc in psutil.process_iter():

138

try:

139

if proc.status() == psutil.STATUS_ZOMBIE:

140

print(f"Found zombie: PID {proc.pid}")

141

# Trying to access zombie process info may raise ZombieProcess

142

print(proc.name()) # This might raise ZombieProcess

143

144

except psutil.ZombieProcess as e:

145

print(f"Zombie process detected: {e}")

146

print(f"PID: {e.pid}")

147

print(f"Process name: {e.name}")

148

# Zombie processes can't provide most information

149

150

except (psutil.NoSuchProcess, psutil.AccessDenied):

151

pass # Skip inaccessible processes

152

153

# Handling zombie processes in monitoring

154

def safe_process_iterator():

155

"""Iterate processes safely handling zombies."""

156

accessible_procs = []

157

zombie_count = 0

158

159

for proc in psutil.process_iter(['pid', 'name', 'status']):

160

try:

161

# Check if process is zombie

162

if proc.info['status'] == psutil.STATUS_ZOMBIE:

163

zombie_count += 1

164

continue

165

166

# Try to get additional info

167

proc.cpu_percent() # This might raise ZombieProcess

168

accessible_procs.append(proc)

169

170

except psutil.ZombieProcess:

171

zombie_count += 1

172

except (psutil.NoSuchProcess, psutil.AccessDenied):

173

pass

174

175

print(f"Found {len(accessible_procs)} accessible processes")

176

print(f"Found {zombie_count} zombie processes")

177

return accessible_procs

178

179

# accessible_procs = safe_process_iterator()

180

```

181

{ .api }

182

183

### TimeoutExpired

184

185

Raised when an operation exceeds the specified timeout.

186

187

```python

188

# TimeoutExpired - Operation timed out

189

import time

190

191

try:

192

# Create a process to demonstrate timeout

193

import subprocess

194

proc = subprocess.Popen(['sleep', '10']) # Unix command

195

196

p = psutil.Process(proc.pid)

197

198

# Wait with timeout

199

p.wait(timeout=2) # Will timeout after 2 seconds

200

201

except psutil.TimeoutExpired as e:

202

print(f"Operation timed out: {e}")

203

print(f"PID: {e.pid}")

204

print(f"Process name: {e.name}")

205

print(f"Timeout seconds: {e.seconds}")

206

207

# Clean up the process

208

try:

209

p.terminate()

210

p.wait(timeout=5) # Give it time to terminate gracefully

211

except psutil.TimeoutExpired:

212

p.kill() # Force kill if it won't terminate

213

214

finally:

215

# Ensure cleanup

216

try:

217

proc.terminate()

218

except:

219

pass

220

221

# Timeout handling in monitoring

222

def wait_for_process_with_retry(pid, timeout=30, retries=3):

223

"""Wait for process termination with retry logic."""

224

for attempt in range(retries):

225

try:

226

p = psutil.Process(pid)

227

p.wait(timeout=timeout)

228

print(f"Process {pid} terminated normally")

229

return True

230

231

except psutil.TimeoutExpired:

232

print(f"Attempt {attempt + 1}: Process {pid} did not terminate within {timeout}s")

233

if attempt < retries - 1:

234

print("Retrying...")

235

else:

236

print("Giving up - process still running")

237

return False

238

239

except psutil.NoSuchProcess:

240

print(f"Process {pid} already terminated")

241

return True

242

243

return False

244

```

245

{ .api }

246

247

## Exception Handling Patterns

248

249

### Comprehensive Exception Handling

250

251

```python

252

def robust_process_operation(pid, operation_name="operation"):

253

"""Perform process operations with comprehensive exception handling."""

254

try:

255

p = psutil.Process(pid)

256

257

# Perform the actual operation

258

if operation_name == "get_info":

259

return {

260

'pid': p.pid,

261

'name': p.name(),

262

'status': p.status(),

263

'cpu_percent': p.cpu_percent(),

264

'memory_info': p.memory_info()

265

}

266

elif operation_name == "terminate":

267

p.terminate()

268

p.wait(timeout=10)

269

return {"result": "terminated"}

270

271

except psutil.NoSuchProcess as e:

272

return {

273

"error": "NoSuchProcess",

274

"message": f"Process {e.pid} ({e.name}) not found or terminated",

275

"details": str(e)

276

}

277

278

except psutil.AccessDenied as e:

279

return {

280

"error": "AccessDenied",

281

"message": f"Permission denied for process {e.pid} ({e.name})",

282

"details": str(e),

283

"suggestion": "Try running with elevated privileges"

284

}

285

286

except psutil.ZombieProcess as e:

287

return {

288

"error": "ZombieProcess",

289

"message": f"Process {e.pid} ({e.name}) is in zombie state",

290

"details": str(e)

291

}

292

293

except psutil.TimeoutExpired as e:

294

return {

295

"error": "TimeoutExpired",

296

"message": f"Operation on process {e.pid} ({e.name}) timed out after {e.seconds}s",

297

"details": str(e)

298

}

299

300

except psutil.Error as e:

301

return {

302

"error": "GeneralPsutilError",

303

"message": f"Unexpected psutil error: {str(e)}",

304

"details": str(e)

305

}

306

307

except Exception as e:

308

return {

309

"error": "UnexpectedError",

310

"message": f"Unexpected error: {str(e)}",

311

"details": str(e)

312

}

313

314

# Example usage

315

result = robust_process_operation(os.getpid(), "get_info")

316

print(f"Operation result: {result}")

317

```

318

{ .api }

319

320

### Batch Operation Exception Handling

321

322

```python

323

def process_batch_operation(pids, operation_func):

324

"""Perform operations on multiple processes with proper exception handling."""

325

results = {

326

'successful': [],

327

'failed': [],

328

'summary': {

329

'total': len(pids),

330

'successful': 0,

331

'no_such_process': 0,

332

'access_denied': 0,

333

'zombie_process': 0,

334

'timeout_expired': 0,

335

'other_errors': 0

336

}

337

}

338

339

for pid in pids:

340

try:

341

result = operation_func(pid)

342

results['successful'].append({

343

'pid': pid,

344

'result': result

345

})

346

results['summary']['successful'] += 1

347

348

except psutil.NoSuchProcess as e:

349

results['failed'].append({

350

'pid': pid,

351

'error': 'NoSuchProcess',

352

'details': str(e)

353

})

354

results['summary']['no_such_process'] += 1

355

356

except psutil.AccessDenied as e:

357

results['failed'].append({

358

'pid': pid,

359

'error': 'AccessDenied',

360

'details': str(e)

361

})

362

results['summary']['access_denied'] += 1

363

364

except psutil.ZombieProcess as e:

365

results['failed'].append({

366

'pid': pid,

367

'error': 'ZombieProcess',

368

'details': str(e)

369

})

370

results['summary']['zombie_process'] += 1

371

372

except psutil.TimeoutExpired as e:

373

results['failed'].append({

374

'pid': pid,

375

'error': 'TimeoutExpired',

376

'details': str(e)

377

})

378

results['summary']['timeout_expired'] += 1

379

380

except Exception as e:

381

results['failed'].append({

382

'pid': pid,

383

'error': 'OtherError',

384

'details': str(e)

385

})

386

results['summary']['other_errors'] += 1

387

388

return results

389

390

# Example: Get CPU usage for multiple processes

391

def get_cpu_percent(pid):

392

p = psutil.Process(pid)

393

return p.cpu_percent()

394

395

# Test with some PIDs

396

test_pids = [os.getpid(), 1, 99999] # Current, init, non-existent

397

batch_results = process_batch_operation(test_pids, get_cpu_percent)

398

399

print("Batch operation results:")

400

print(f"Summary: {batch_results['summary']}")

401

```

402

{ .api }

403

404

### Context Manager for Process Operations

405

406

```python

407

class SafeProcess:

408

"""Context manager for safe process operations."""

409

410

def __init__(self, pid):

411

self.pid = pid

412

self.process = None

413

self.error = None

414

415

def __enter__(self):

416

try:

417

self.process = psutil.Process(self.pid)

418

return self.process

419

except psutil.Error as e:

420

self.error = e

421

return None

422

423

def __exit__(self, exc_type, exc_val, exc_tb):

424

# Handle any cleanup if needed

425

if exc_type and issubclass(exc_type, psutil.Error):

426

# Log the psutil exception

427

print(f"psutil exception in context: {exc_val}")

428

return True # Suppress the exception

429

return False

430

431

# Usage example

432

def monitor_with_context_manager(pid):

433

"""Monitor process using context manager."""

434

with SafeProcess(pid) as p:

435

if p is None:

436

print(f"Could not access process {pid}")

437

return None

438

439

try:

440

return {

441

'name': p.name(),

442

'cpu_percent': p.cpu_percent(),

443

'memory_info': p.memory_info()

444

}

445

except psutil.Error as e:

446

print(f"Error during monitoring: {e}")

447

return None

448

449

# Test the context manager

450

info = monitor_with_context_manager(os.getpid())

451

print(f"Process info: {info}")

452

```

453

{ .api }

454

455

## Exception Analysis and Debugging

456

457

### Exception Information Extraction

458

459

```python

460

def analyze_psutil_exception(exception):

461

"""Analyze a psutil exception and extract useful information."""

462

analysis = {

463

'type': type(exception).__name__,

464

'message': str(exception),

465

'is_psutil_exception': isinstance(exception, psutil.Error)

466

}

467

468

# Extract exception-specific attributes

469

if hasattr(exception, 'pid'):

470

analysis['pid'] = exception.pid

471

if hasattr(exception, 'name'):

472

analysis['name'] = exception.name

473

if hasattr(exception, 'msg'):

474

analysis['msg'] = exception.msg

475

if hasattr(exception, 'seconds'):

476

analysis['seconds'] = exception.seconds

477

478

# Provide suggestions based on exception type

479

if isinstance(exception, psutil.NoSuchProcess):

480

analysis['suggestion'] = "Process may have terminated - check if PID is still valid"

481

elif isinstance(exception, psutil.AccessDenied):

482

analysis['suggestion'] = "Run with elevated privileges or check process ownership"

483

elif isinstance(exception, psutil.ZombieProcess):

484

analysis['suggestion'] = "Process is zombie - limited information available"

485

elif isinstance(exception, psutil.TimeoutExpired):

486

analysis['suggestion'] = "Increase timeout value or check if process is responsive"

487

488

return analysis

489

490

# Example exception analysis

491

try:

492

p = psutil.Process(99999)

493

p.name()

494

except psutil.Error as e:

495

analysis = analyze_psutil_exception(e)

496

print("Exception analysis:", analysis)

497

```

498

{ .api }

499

500

### Logging Integration

501

502

```python

503

import logging

504

505

# Set up logging for psutil exceptions

506

logging.basicConfig(level=logging.INFO)

507

logger = logging.getLogger(__name__)

508

509

def logged_process_operation(pid, operation_name):

510

"""Process operation with comprehensive logging."""

511

logger.info(f"Starting {operation_name} for PID {pid}")

512

513

try:

514

p = psutil.Process(pid)

515

516

if operation_name == "info":

517

result = p.as_dict(['pid', 'name', 'status', 'cpu_percent'])

518

logger.info(f"Successfully retrieved info for PID {pid}: {result}")

519

return result

520

521

except psutil.NoSuchProcess as e:

522

logger.warning(f"Process not found - PID: {e.pid}, Name: {e.name}")

523

raise

524

525

except psutil.AccessDenied as e:

526

logger.error(f"Access denied - PID: {e.pid}, Name: {e.name}")

527

raise

528

529

except psutil.ZombieProcess as e:

530

logger.warning(f"Zombie process - PID: {e.pid}, Name: {e.name}")

531

raise

532

533

except psutil.TimeoutExpired as e:

534

logger.error(f"Timeout expired - PID: {e.pid}, Name: {e.name}, Timeout: {e.seconds}s")

535

raise

536

537

except psutil.Error as e:

538

logger.error(f"General psutil error for PID {pid}: {e}")

539

raise

540

541

# Example with logging

542

try:

543

info = logged_process_operation(os.getpid(), "info")

544

except psutil.Error:

545

pass # Exception already logged

546

```

547

{ .api }

548

549

## Best Practices for Exception Handling

550

551

### Defensive Programming

552

553

```python

554

def defensive_process_monitor():

555

"""Example of defensive programming with psutil."""

556

557

# Always expect processes to disappear

558

active_processes = set()

559

560

while True:

561

try:

562

# Get current processes

563

current_pids = set(p.pid for p in psutil.process_iter())

564

565

# Find new processes

566

new_pids = current_pids - active_processes

567

for pid in new_pids:

568

try:

569

p = psutil.Process(pid)

570

print(f"New process: {pid} ({p.name()})")

571

except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):

572

pass # Process already gone or inaccessible

573

574

# Update active set

575

active_processes = current_pids

576

577

time.sleep(5)

578

579

except KeyboardInterrupt:

580

print("Monitoring stopped")

581

break

582

except Exception as e:

583

print(f"Unexpected error: {e}")

584

time.sleep(1) # Brief pause before retry

585

586

# Run defensive monitor

587

# defensive_process_monitor()

588

```

589

{ .api }

590

591

## Related Documentation

592

593

- [Process Management](process.md) - Process operations that may raise exceptions

594

- [System Information](system-info.md) - System operations and their exceptions

595

- [Constants](constants.md) - Constants used in exception handling

596

- [Sensors](sensors.md) - Sensor operations that may raise AttributeError