|
9 | 9 | from typing import ( |
10 | 10 | Any, |
11 | 11 | Callable, |
| 12 | + Iterable, |
12 | 13 | List, |
13 | 14 | Union, |
14 | 15 | Optional, |
@@ -201,20 +202,60 @@ class CtypesRef(Generic[CtypesCData]): |
201 | 202 |
|
202 | 203 |
|
203 | 204 | def ctypes_function_for_shared_library(lib: ctypes.CDLL): |
204 | | - """Decorator for defining ctypes functions with type hints""" |
| 205 | + """Create a decorator used to bind typed Python declarations to C symbols. |
| 206 | +
|
| 207 | + The returned decorator accepts either a single exported symbol name or an |
| 208 | + iterable of ABI-compatible aliases. When aliases are provided, they are |
| 209 | + checked in order and the first available symbol is selected. |
| 210 | + """ |
205 | 211 |
|
206 | 212 | def ctypes_function( |
207 | | - name: str, argtypes: List[Any], restype: Any, enabled: bool = True |
| 213 | + name: Union[str, Iterable[str]], |
| 214 | + argtypes: List[Any], |
| 215 | + restype: Any, |
| 216 | + enabled: bool = True, |
208 | 217 | ): |
| 218 | + """Bind a Python declaration to one of the requested C symbols. |
| 219 | +
|
| 220 | + Args: |
| 221 | + name: A symbol name or an ordered iterable of compatible aliases. |
| 222 | + argtypes: The ctypes argument types assigned to the C function. |
| 223 | + restype: The ctypes return type assigned to the C function. |
| 224 | + enabled: Return the original Python declaration when disabled. |
| 225 | +
|
| 226 | + Raises: |
| 227 | + ValueError: If no symbol names are provided. |
| 228 | + AttributeError: If none of the requested symbols exist in the |
| 229 | + shared library. |
| 230 | + """ |
| 231 | + symbol_names = (name,) if isinstance(name, str) else tuple(name) |
| 232 | + |
| 233 | + if not symbol_names: |
| 234 | + raise ValueError("At least one shared library symbol name is required") |
| 235 | + |
209 | 236 | def decorator(f: F) -> F: |
210 | | - if enabled: |
211 | | - func = getattr(lib, name) |
| 237 | + if not enabled: |
| 238 | + return f |
| 239 | + |
| 240 | + for symbol_name in symbol_names: |
| 241 | + try: |
| 242 | + func = getattr(lib, symbol_name) |
| 243 | + except AttributeError: |
| 244 | + continue |
| 245 | + |
212 | 246 | func.argtypes = argtypes |
213 | 247 | func.restype = restype |
214 | | - functools.wraps(f)(func) |
| 248 | + functools.update_wrapper(func, f) |
| 249 | + |
| 250 | + # Preserve the actual exported symbol selected at runtime for |
| 251 | + # diagnostics, especially when ABI aliases are being used. |
| 252 | + func.__ctypes_symbol_name__ = symbol_name |
215 | 253 | return func |
216 | | - else: |
217 | | - return f |
| 254 | + |
| 255 | + raise AttributeError( |
| 256 | + "None of the shared library symbols were found: " |
| 257 | + + ", ".join(symbol_names) |
| 258 | + ) |
218 | 259 |
|
219 | 260 | return decorator |
220 | 261 |
|
|
0 commit comments