0
# Exception Handling
1
2
Complete Python exception system integration allowing Java applications to handle Python errors, set Python exceptions from Java code, and work with Python's warning system. This provides seamless error handling between Java and Python runtimes.
3
4
## Capabilities
5
6
### Exception State Management
7
8
Core functions for checking and managing Python exception state.
9
10
```java { .api }
11
/**
12
* Check if an exception is currently set
13
* @return Exception object if set, NULL otherwise
14
*/
15
public static native PyObject PyErr_Occurred();
16
17
/**
18
* Clear the current exception state
19
*/
20
public static native void PyErr_Clear();
21
22
/**
23
* Check if exception matches specific type
24
* @param exc - Exception to check
25
* @param type - Exception type to match against
26
* @return Non-zero if exception matches type
27
*/
28
public static native int PyErr_ExceptionMatches(PyObject exc);
29
30
/**
31
* Check if current exception matches specific type
32
* @param type - Exception type to check against
33
* @return Non-zero if current exception matches type
34
*/
35
public static native int PyErr_GivenExceptionMatches(PyObject type);
36
```
37
38
**Usage Example:**
39
40
```java
41
import static org.bytedeco.cpython.global.python.*;
42
43
// Call a Python function that might raise an exception
44
PyObject result = PyRun_SimpleString("1 / 0"); // Division by zero
45
46
// Check for exceptions
47
if (PyErr_Occurred() != null) {
48
System.out.println("Python exception occurred!");
49
50
// Check specific exception type
51
if (PyErr_ExceptionMatches(PyExc_ZeroDivisionError())) {
52
System.out.println("Division by zero error");
53
}
54
55
// Clear the exception
56
PyErr_Clear();
57
}
58
```
59
60
### Setting Exceptions
61
62
Functions for setting Python exceptions from Java code.
63
64
```java { .api }
65
/**
66
* Set exception with no value (just the type)
67
* @param exception - Exception type to set
68
*/
69
public static native void PyErr_SetNone(PyObject exception);
70
71
/**
72
* Set exception with specific value object
73
* @param exception - Exception type
74
* @param value - Exception value object
75
*/
76
public static native void PyErr_SetObject(PyObject exception, PyObject value);
77
78
/**
79
* Set exception with string message
80
* @param exception - Exception type
81
* @param message - Error message string
82
*/
83
public static native void PyErr_SetString(PyObject exception, String message);
84
85
/**
86
* Set exception using formatted message
87
* @param exception - Exception type
88
* @param format - Printf-style format string
89
* @param args - Format arguments
90
*/
91
public static native PyObject PyErr_Format(PyObject exception, String format, Object... args);
92
93
/**
94
* Set MemoryError exception
95
*/
96
public static native PyObject PyErr_NoMemory();
97
98
/**
99
* Set exception for bad argument to function
100
*/
101
public static native PyObject PyErr_BadArgument();
102
103
/**
104
* Set exception for bad internal call
105
*/
106
public static native PyObject PyErr_BadInternalCall();
107
```
108
109
**Usage Example:**
110
111
```java
112
// Set a custom exception
113
PyErr_SetString(PyExc_ValueError(), "Invalid input parameter");
114
115
// Set formatted exception
116
PyErr_Format(PyExc_TypeError(), "Expected %s, got %s", "int", "str");
117
118
// Set memory error
119
PyErr_NoMemory();
120
121
// Set exception with object value
122
PyObject errorDetails = PyDict_New();
123
PyDict_SetItemString(errorDetails, "code", PyLong_FromLong(404));
124
PyErr_SetObject(PyExc_RuntimeError(), errorDetails);
125
```
126
127
### Exception Fetch and Restore
128
129
Advanced exception handling for preserving and restoring exception state.
130
131
```java { .api }
132
/**
133
* Fetch current exception info (clears current exception)
134
* @param type - Pointer to receive exception type
135
* @param value - Pointer to receive exception value
136
* @param traceback - Pointer to receive traceback object
137
*/
138
public static native void PyErr_Fetch(PointerPointer type, PointerPointer value, PointerPointer traceback);
139
140
/**
141
* Restore exception info (sets current exception)
142
* @param type - Exception type (can be NULL)
143
* @param value - Exception value (can be NULL)
144
* @param traceback - Traceback object (can be NULL)
145
*/
146
public static native void PyErr_Restore(PyObject type, PyObject value, PyObject traceback);
147
148
/**
149
* Normalize exception info (converts strings to proper exception objects)
150
* @param type - Pointer to exception type (modified)
151
* @param value - Pointer to exception value (modified)
152
* @param traceback - Pointer to traceback (modified)
153
*/
154
public static native void PyErr_NormalizeException(PointerPointer type, PointerPointer value, PointerPointer traceback);
155
```
156
157
### New Exception Handling (Python 3.12+)
158
159
Modern exception handling API introduced in Python 3.12.
160
161
```java { .api }
162
/**
163
* Get currently raised exception (new API)
164
* @return Current exception object, or NULL if none
165
*/
166
public static native PyObject PyErr_GetRaisedException();
167
168
/**
169
* Set currently raised exception (new API)
170
* @param exc - Exception object to set (can be NULL to clear)
171
*/
172
public static native void PyErr_SetRaisedException(PyObject exc);
173
174
/**
175
* Get currently handled exception
176
* @return Currently handled exception, or NULL if none
177
*/
178
public static native PyObject PyErr_GetHandledException();
179
180
/**
181
* Set currently handled exception
182
* @param exc - Exception object to set as handled
183
*/
184
public static native void PyErr_SetHandledException(PyObject exc);
185
```
186
187
**Usage Example:**
188
189
```java
190
// Modern exception handling pattern
191
PyObject exception = PyErr_GetRaisedException();
192
if (exception != null) {
193
// Process the exception
194
System.out.println("Handling exception: " + exception);
195
196
// Optionally re-raise or clear
197
PyErr_SetRaisedException(null); // Clear
198
// or PyErr_SetRaisedException(exception); // Re-raise
199
}
200
```
201
202
### Warning System
203
204
Python's warning system integration for handling warnings from Java.
205
206
```java { .api }
207
/**
208
* Issue a warning
209
* @param category - Warning category (or NULL for default)
210
* @param message - Warning message
211
* @param stack_level - Stack level for warning location
212
* @return 0 on success, -1 on error
213
*/
214
public static native int PyErr_WarnEx(PyObject category, String message, long stack_level);
215
216
/**
217
* Issue a formatted warning
218
* @param category - Warning category
219
* @param stack_level - Stack level for warning location
220
* @param format - Printf-style format string
221
* @param args - Format arguments
222
* @return 0 on success, -1 on error
223
*/
224
public static native int PyErr_WarnFormat(PyObject category, long stack_level, String format, Object... args);
225
226
/**
227
* Issue a resource warning
228
* @param source - Source object causing the warning
229
* @param stack_level - Stack level for warning location
230
* @param format - Printf-style format string
231
* @param args - Format arguments
232
* @return 0 on success, -1 on error
233
*/
234
public static native int PyErr_ResourceWarning(PyObject source, long stack_level, String format, Object... args);
235
236
/**
237
* Issue an explicit warning with full control
238
* @param category - Warning category
239
* @param message - Warning message
240
* @param filename - Source filename
241
* @param lineno - Line number
242
* @param module - Module name
243
* @param registry - Warning registry
244
* @return 0 on success, -1 on error
245
*/
246
public static native int PyErr_WarnExplicit(PyObject category, String message, String filename,
247
int lineno, String module, PyObject registry);
248
```
249
250
**Usage Example:**
251
252
```java
253
// Issue a deprecation warning
254
int result = PyErr_WarnEx(PyExc_DeprecationWarning(),
255
"This function is deprecated", 1);
256
257
if (result < 0) {
258
// Warning caused an exception (warnings treated as errors)
259
PyErr_Clear();
260
}
261
262
// Issue formatted warning
263
PyErr_WarnFormat(PyExc_UserWarning(), 2,
264
"Value %d is outside expected range", 150);
265
266
// Resource warning for unclosed file
267
PyErr_ResourceWarning(fileObject, 1,
268
"unclosed file %s", filename);
269
```
270
271
### Built-in Exception Types
272
273
Access to all standard Python exception types.
274
275
```java { .api }
276
// Base exceptions
277
public static native PyObject PyExc_BaseException();
278
public static native PyObject PyExc_Exception();
279
public static native PyObject PyExc_ArithmeticError();
280
public static native PyObject PyExc_LookupError();
281
282
// Common exceptions
283
public static native PyObject PyExc_AttributeError();
284
public static native PyObject PyExc_EOFError();
285
public static native PyObject PyExc_ImportError();
286
public static native PyObject PyExc_IndexError();
287
public static native PyObject PyExc_KeyError();
288
public static native PyObject PyExc_KeyboardInterrupt();
289
public static native PyObject PyExc_MemoryError();
290
public static native PyObject PyExc_NameError();
291
public static native PyObject PyExc_OSError();
292
public static native PyObject PyExc_RuntimeError();
293
public static native PyObject PyExc_StopIteration();
294
public static native PyObject PyExc_SyntaxError();
295
public static native PyObject PyExc_SystemError();
296
public static native PyObject PyExc_SystemExit();
297
public static native PyObject PyExc_TypeError();
298
public static native PyObject PyExc_ValueError();
299
public static native PyObject PyExc_ZeroDivisionError();
300
301
// Warning categories
302
public static native PyObject PyExc_Warning();
303
public static native PyObject PyExc_UserWarning();
304
public static native PyObject PyExc_DeprecationWarning();
305
public static native PyObject PyExc_PendingDeprecationWarning();
306
public static native PyObject PyExc_SyntaxWarning();
307
public static native PyObject PyExc_RuntimeWarning();
308
public static native PyObject PyExc_FutureWarning();
309
public static native PyObject PyExc_ImportWarning();
310
public static native PyObject PyExc_UnicodeWarning();
311
public static native PyObject PyExc_BytesWarning();
312
public static native PyObject PyExc_ResourceWarning();
313
```
314
315
## Exception Object Types
316
317
```java { .api }
318
/**
319
* Base exception object structure
320
*/
321
class PyBaseExceptionObject extends PyObject { }
322
323
/**
324
* Attribute error exception
325
*/
326
class PyAttributeErrorObject extends PyBaseExceptionObject { }
327
328
/**
329
* Import error exception
330
*/
331
class PyImportErrorObject extends PyBaseExceptionObject { }
332
333
/**
334
* Name error exception
335
*/
336
class PyNameErrorObject extends PyBaseExceptionObject { }
337
338
/**
339
* OS error exception
340
*/
341
class PyOSErrorObject extends PyBaseExceptionObject { }
342
343
/**
344
* Syntax error exception
345
*/
346
class PySyntaxErrorObject extends PyBaseExceptionObject { }
347
348
/**
349
* System exit exception
350
*/
351
class PySystemExitObject extends PyBaseExceptionObject { }
352
353
/**
354
* Unicode error exception
355
*/
356
class PyUnicodeErrorObject extends PyBaseExceptionObject { }
357
```
358
359
## Advanced Exception Handling Patterns
360
361
### Exception Context Preservation
362
363
```java
364
// Save current exception state before calling risky operation
365
PointerPointer savedType = new PointerPointer(1);
366
PointerPointer savedValue = new PointerPointer(1);
367
PointerPointer savedTraceback = new PointerPointer(1);
368
369
PyErr_Fetch(savedType, savedValue, savedTraceback);
370
371
try {
372
// Risky operation that might set different exception
373
PyObject result = riskyPythonOperation();
374
375
// If operation succeeded, restore original exception
376
if (PyErr_Occurred() == null) {
377
PyErr_Restore(new PyObject(savedType.get()),
378
new PyObject(savedValue.get()),
379
new PyObject(savedTraceback.get()));
380
}
381
382
} catch (Exception e) {
383
// Handle Java exception, but preserve Python exception
384
PyErr_Restore(new PyObject(savedType.get()),
385
new PyObject(savedValue.get()),
386
new PyObject(savedTraceback.get()));
387
throw e;
388
}
389
```
390
391
### Custom Exception Classes
392
393
```java
394
// Create custom exception type (typically done during module initialization)
395
public PyObject createCustomException(String name, String docstring) {
396
// Create exception class
397
PyObject exceptionClass = PyErr_NewException(name, PyExc_Exception(), null);
398
399
if (exceptionClass != null && docstring != null) {
400
// Set docstring
401
PyObject doc = PyUnicode_FromString(docstring);
402
PyObject_SetAttrString(exceptionClass, "__doc__", doc);
403
}
404
405
return exceptionClass;
406
}
407
```
408
409
### Exception Chaining
410
411
```java
412
// Demonstrate exception chaining (raise ... from ...)
413
try {
414
// Original operation that fails
415
PyObject result = PyLong_FromString("not_a_number", null, 10);
416
417
} catch (Exception e) {
418
// Chain with new exception
419
PyErr_SetString(PyExc_ValueError(), "Failed to process input");
420
421
// The original exception becomes the __cause__
422
// This is handled automatically by Python's exception system
423
}
424
```
425
426
### Comprehensive Error Handling
427
428
```java
429
public class PythonExceptionHandler {
430
431
public static boolean handlePythonError() {
432
if (PyErr_Occurred() == null) {
433
return false; // No error
434
}
435
436
// Get exception information
437
PointerPointer type = new PointerPointer(1);
438
PointerPointer value = new PointerPointer(1);
439
PointerPointer traceback = new PointerPointer(1);
440
441
PyErr_Fetch(type, value, traceback);
442
PyErr_NormalizeException(type, value, traceback);
443
444
PyObject exceptionType = new PyObject(type.get());
445
PyObject exceptionValue = new PyObject(value.get());
446
PyObject exceptionTraceback = new PyObject(traceback.get());
447
448
try {
449
// Log exception details
450
if (PyErr_GivenExceptionMatches(exceptionType, PyExc_ValueError())) {
451
System.err.println("ValueError occurred");
452
} else if (PyErr_GivenExceptionMatches(exceptionType, PyExc_TypeError())) {
453
System.err.println("TypeError occurred");
454
} else {
455
System.err.println("Other exception occurred");
456
}
457
458
// Get exception message
459
if (exceptionValue != null) {
460
PyObject strRepr = PyObject_Str(exceptionValue);
461
if (strRepr != null) {
462
// Convert to Java string and log
463
System.err.println("Exception message: " + strRepr);
464
}
465
}
466
467
return true; // Exception handled
468
469
} finally {
470
// Clean up or re-raise if needed
471
// PyErr_Restore(exceptionType, exceptionValue, exceptionTraceback);
472
}
473
}
474
}
475
```
476
477
### Warning Configuration
478
479
```java
480
// Configure warning behavior
481
public void configureWarnings() {
482
// Import warnings module
483
PyObject warningsModule = PyImport_ImportModule("warnings");
484
485
if (warningsModule != null) {
486
// Set warnings to be treated as errors
487
PyObject filterwarnings = PyObject_GetAttrString(warningsModule, "filterwarnings");
488
489
if (filterwarnings != null) {
490
PyObject args = PyTuple_New(1);
491
PyTuple_SetItem(args, 0, PyUnicode_FromString("error"));
492
493
PyObject result = PyObject_CallObject(filterwarnings, args);
494
495
if (result == null) {
496
// Handle error in warning configuration
497
PyErr_Clear();
498
}
499
}
500
}
501
}
502
```
503
504
## Integration with Java Exception Handling
505
506
### Translating Python Exceptions to Java
507
508
```java
509
public class PythonException extends RuntimeException {
510
private final String pythonExceptionType;
511
private final PyObject pythonException;
512
513
public PythonException(String type, String message, PyObject exception) {
514
super(message);
515
this.pythonExceptionType = type;
516
this.pythonException = exception;
517
}
518
519
public static PythonException fromPythonError() {
520
if (PyErr_Occurred() == null) {
521
return null;
522
}
523
524
// Extract exception details
525
PyObject exception = PyErr_GetRaisedException();
526
PyObject typeName = PyObject_GetAttrString(Py_TYPE(exception), "__name__");
527
PyObject message = PyObject_Str(exception);
528
529
String typeStr = (typeName != null) ? PyUnicode_AsUTF8(typeName) : "Unknown";
530
String msgStr = (message != null) ? PyUnicode_AsUTF8(message) : "No message";
531
532
return new PythonException(typeStr, msgStr, exception);
533
}
534
}
535
536
// Usage
537
public void callPython() throws PythonException {
538
PyRun_SimpleString("raise ValueError('Something went wrong')");
539
540
PythonException pe = PythonException.fromPythonError();
541
if (pe != null) {
542
throw pe;
543
}
544
}
545
```
546
547
## Important Notes
548
549
### Exception Ownership
550
551
When fetching exceptions with `PyErr_Fetch()`, the caller receives ownership of the exception objects and must manage their references appropriately.
552
553
### Thread Safety
554
555
Exception state is thread-specific. Each thread has its own exception state that doesn't interfere with other threads.
556
557
### Performance Considerations
558
559
- Exception checking is fast when no exception is set
560
- Avoid unnecessary exception fetching/restoration in performance-critical code
561
- Clear exceptions promptly to avoid confusion
562
563
### Error Propagation
564
565
Always check for Python exceptions after calling Python C API functions, especially when calling from Java code that doesn't expect Python exceptions.