0
# Error Handling and Utilities
1
2
Error management, system integration utilities, and platform-specific functionality including errno handling and Windows Unicode support.
3
4
## Capabilities
5
6
### System Error Handling
7
8
Access and manipulation of C library error codes through the errno mechanism.
9
10
```python { .api }
11
errno: property # Property for C errno access
12
13
def _get_errno(self):
14
"""Get current errno value"""
15
16
def _set_errno(self, errno):
17
"""Set errno value"""
18
```
19
20
**Usage Examples:**
21
22
```python
23
import os
24
25
# Access C errno
26
ffi = FFI()
27
ffi.cdef("FILE* fopen(const char* filename, const char* mode);")
28
libc = ffi.dlopen(None)
29
30
# Try to open non-existent file
31
result = libc.fopen(b"/nonexistent/file.txt", b"r")
32
if result == ffi.NULL:
33
error_code = ffi.errno
34
print(f"C errno: {error_code}")
35
print(f"Python equivalent: {os.strerror(error_code)}")
36
37
# Set errno manually
38
ffi.errno = 42
39
print(f"Set errno to: {ffi.errno}")
40
```
41
42
### Windows Error Handling
43
44
Windows-specific error code retrieval and formatting.
45
46
```python { .api }
47
def getwinerror(self, code=-1):
48
"""
49
Get Windows error information.
50
51
Parameters:
52
- code (int): Error code (-1 for GetLastError())
53
54
Returns:
55
tuple: (error_code, error_message) on Windows, OSError on other platforms
56
"""
57
```
58
59
**Usage Example:**
60
61
```python
62
# Windows-specific error handling
63
if sys.platform == "win32":
64
try:
65
# Some Windows API call that might fail
66
ffi.cdef("void* CreateFileA(const char* name, int access, int share, void* security, int creation, int flags, void* template);")
67
kernel32 = ffi.dlopen("kernel32.dll")
68
69
handle = kernel32.CreateFileA(b"invalid\\path\\file.txt", 0x80000000, 0, ffi.NULL, 3, 0, ffi.NULL)
70
if handle == ffi.cast("void*", -1): # INVALID_HANDLE_VALUE
71
error_code, error_msg = ffi.getwinerror()
72
print(f"Windows Error {error_code}: {error_msg}")
73
except OSError:
74
print("Not on Windows platform")
75
```
76
77
### FFI Instance Inclusion
78
79
Includes type definitions from another FFI instance for modular design.
80
81
```python { .api }
82
def include(self, ffi_to_include):
83
"""
84
Include types from another FFI instance.
85
86
Parameters:
87
- ffi_to_include: Another FFI instance to include types from
88
89
Returns:
90
None
91
92
Note: Only includes types, not functions or variables
93
"""
94
```
95
96
**Usage Example:**
97
98
```python
99
# Base types FFI
100
base_ffi = FFI()
101
base_ffi.cdef("""
102
typedef struct {
103
int x, y;
104
} point_t;
105
106
typedef struct {
107
point_t top_left;
108
point_t bottom_right;
109
} rect_t;
110
""")
111
112
# Graphics FFI that uses base types
113
graphics_ffi = FFI()
114
graphics_ffi.include(base_ffi) # Include point_t and rect_t
115
graphics_ffi.cdef("""
116
// Can now use point_t and rect_t
117
void draw_rect(rect_t rect);
118
point_t get_center(rect_t rect);
119
""")
120
```
121
122
### Windows Unicode Support
123
124
Configures Windows-specific Unicode type definitions and macros.
125
126
```python { .api }
127
def set_unicode(self, enabled_flag):
128
"""
129
Configure Windows Unicode support.
130
131
Parameters:
132
- enabled_flag (bool): Enable Unicode types and macros
133
134
Returns:
135
None
136
137
Note: Can only be called once per FFI instance
138
"""
139
```
140
141
**Usage Example:**
142
143
```python
144
# Configure for Unicode Windows API
145
ffi = FFI()
146
ffi.set_unicode(True) # Enables UNICODE and _UNICODE macros
147
148
# Now TCHAR maps to wchar_t, LPTSTR to wchar_t*, etc.
149
ffi.cdef("""
150
int MessageBoxW(void* hWnd, const TCHAR* text, const TCHAR* caption, unsigned int type);
151
""")
152
153
# For ANSI API
154
ansi_ffi = FFI()
155
ansi_ffi.set_unicode(False) # TCHAR maps to char
156
157
ansi_ffi.cdef("""
158
int MessageBoxA(void* hWnd, const TCHAR* text, const TCHAR* caption, unsigned int type);
159
""")
160
```
161
162
### One-Time Initialization
163
164
Executes functions exactly once per tag, useful for expensive initialization operations.
165
166
```python { .api }
167
def init_once(self, func, tag):
168
"""
169
Execute function once per tag.
170
171
Parameters:
172
- func: Function to execute
173
- tag: Unique identifier for this initialization
174
175
Returns:
176
Result of func() on first call, cached result on subsequent calls
177
"""
178
```
179
180
**Usage Example:**
181
182
```python
183
# Expensive initialization
184
def initialize_crypto():
185
print("Initializing cryptographic library...")
186
# Expensive setup code here
187
return {"initialized": True, "algorithms": ["AES", "RSA"]}
188
189
ffi = FFI()
190
191
# Called multiple times, but initialization happens only once
192
result1 = ffi.init_once(initialize_crypto, "crypto_init")
193
result2 = ffi.init_once(initialize_crypto, "crypto_init") # Uses cached result
194
195
print(result1 is result2) # True - same object returned
196
```
197
198
### Embedding Support
199
200
Advanced features for embedding Python in C applications.
201
202
```python { .api }
203
def embedding_api(self, csource, packed=False, pack=None):
204
"""
205
Define API for embedding Python in C.
206
207
Parameters:
208
- csource (str): C declarations for embedding API
209
- packed (bool): Pack structures
210
- pack (int): Packing alignment
211
212
Returns:
213
None
214
"""
215
216
def embedding_init_code(self, pysource):
217
"""
218
Set Python initialization code for embedding.
219
220
Parameters:
221
- pysource (str): Python code to execute on embedding initialization
222
223
Returns:
224
None
225
"""
226
227
def def_extern(self, *args, **kwds):
228
"""
229
Define external function for API mode (embedding).
230
231
Note: Only available on API-mode FFI objects
232
"""
233
```
234
235
**Usage Example:**
236
237
```python
238
# Embedding setup
239
embed_ffi = FFI()
240
241
# Define embedding API
242
embed_ffi.embedding_api("""
243
int process_data(int* input, int count);
244
char* get_status();
245
""")
246
247
# Set initialization code
248
embed_ffi.embedding_init_code("""
249
def process_data(input_ptr, count):
250
# Convert C array to Python list
251
data = ffi.unpack(ffi.cast("int*", input_ptr), count)
252
253
# Process in Python
254
result = sum(x * 2 for x in data)
255
return result
256
257
def get_status():
258
return ffi.new("char[]", b"Ready")
259
260
# Register functions
261
ffi.def_extern(process_data)
262
ffi.def_extern(get_status)
263
""")
264
```
265
266
## Exception Classes and Handling
267
268
### Exception Hierarchy
269
270
```python { .api }
271
class FFIError(Exception):
272
"""Base exception for CFFI errors"""
273
274
class CDefError(Exception):
275
"""C declaration parsing errors"""
276
277
class VerificationError(Exception):
278
"""Code verification and compilation errors"""
279
280
class VerificationMissing(Exception):
281
"""Incomplete structure definition errors"""
282
283
class PkgConfigError(Exception):
284
"""Package configuration errors"""
285
```
286
287
**Usage Examples:**
288
289
```python
290
try:
291
ffi = FFI()
292
ffi.cdef("invalid C syntax here")
293
except CDefError as e:
294
print(f"C definition error: {e}")
295
296
try:
297
ffi.cdef("struct incomplete;")
298
incomplete = ffi.new("struct incomplete *") # Error: incomplete type
299
except VerificationMissing as e:
300
print(f"Incomplete structure: {e}")
301
302
try:
303
ffi.verify("int func() { syntax error }")
304
except VerificationError as e:
305
print(f"Compilation failed: {e}")
306
```
307
308
## Advanced Error Handling Patterns
309
310
### Error Context Management
311
312
```python
313
class CFfiErrorContext:
314
def __init__(self, ffi, operation_name):
315
self.ffi = ffi
316
self.operation_name = operation_name
317
self.saved_errno = None
318
319
def __enter__(self):
320
# Save current errno
321
self.saved_errno = self.ffi.errno
322
return self
323
324
def __exit__(self, exc_type, exc_val, exc_tb):
325
if exc_type is None:
326
return
327
328
# Add context to exceptions
329
if isinstance(exc_val, (OSError, FFIError)):
330
current_errno = self.ffi.errno
331
if current_errno != self.saved_errno:
332
exc_val.args = exc_val.args + (f"errno changed to {current_errno} during {self.operation_name}",)
333
334
# Restore errno
335
self.ffi.errno = self.saved_errno
336
337
# Usage
338
with CFfiErrorContext(ffi, "file operations"):
339
# File operations that might change errno
340
pass
341
```
342
343
### Comprehensive Error Checking
344
345
```python
346
def safe_library_call(ffi, lib, func_name, *args, check_errno=True, check_return=None):
347
"""Safely call C library function with error checking"""
348
349
# Save errno
350
old_errno = ffi.errno if check_errno else None
351
if check_errno:
352
ffi.errno = 0
353
354
try:
355
# Get function
356
func = getattr(lib, func_name)
357
358
# Call function
359
result = func(*args)
360
361
# Check return value
362
if check_return and not check_return(result):
363
raise RuntimeError(f"{func_name} returned error value: {result}")
364
365
# Check errno
366
if check_errno and ffi.errno != 0:
367
error_msg = os.strerror(ffi.errno)
368
raise OSError(ffi.errno, f"{func_name} failed: {error_msg}")
369
370
return result
371
372
finally:
373
# Restore errno
374
if check_errno and old_errno is not None:
375
ffi.errno = old_errno
376
377
# Usage
378
ffi.cdef("FILE* fopen(const char* name, const char* mode);")
379
libc = ffi.dlopen(None)
380
381
try:
382
file_ptr = safe_library_call(
383
ffi, libc, "fopen",
384
b"test.txt", b"r",
385
check_return=lambda x: x != ffi.NULL
386
)
387
print("File opened successfully")
388
except (OSError, RuntimeError) as e:
389
print(f"Failed to open file: {e}")
390
```
391
392
### Cross-Platform Error Handling
393
394
```python
395
def get_system_error(ffi):
396
"""Get system error in a cross-platform way"""
397
if sys.platform == "win32":
398
try:
399
error_code, error_msg = ffi.getwinerror()
400
return f"Windows Error {error_code}: {error_msg}"
401
except OSError:
402
pass
403
404
# Fall back to errno
405
errno_val = ffi.errno
406
if errno_val != 0:
407
return f"System Error {errno_val}: {os.strerror(errno_val)}"
408
409
return "No system error"
410
411
# Usage in error handling
412
try:
413
# Some system operation
414
pass
415
except Exception as e:
416
system_error = get_system_error(ffi)
417
print(f"Operation failed: {e}")
418
print(f"System error: {system_error}")
419
```
420
421
### Logging Integration
422
423
```python
424
import logging
425
426
class CFfiLogger:
427
def __init__(self, ffi, logger_name="cffi"):
428
self.ffi = ffi
429
self.logger = logging.getLogger(logger_name)
430
431
def log_system_state(self, level=logging.DEBUG):
432
"""Log current system error state"""
433
errno_val = self.ffi.errno
434
self.logger.log(level, f"Current errno: {errno_val}")
435
436
if sys.platform == "win32":
437
try:
438
win_error = self.ffi.getwinerror()
439
self.logger.log(level, f"Windows error: {win_error}")
440
except OSError:
441
pass
442
443
def wrap_call(self, func, *args, **kwargs):
444
"""Wrap function call with logging"""
445
self.logger.debug(f"Calling {func.__name__} with args: {args}")
446
self.log_system_state()
447
448
try:
449
result = func(*args, **kwargs)
450
self.logger.debug(f"{func.__name__} returned: {result}")
451
return result
452
except Exception as e:
453
self.logger.error(f"{func.__name__} failed: {e}")
454
self.log_system_state(logging.ERROR)
455
raise
456
457
# Usage
458
logger = CFfiLogger(ffi)
459
logger.wrap_call(libc.fopen, b"test.txt", b"r")
460
```