Build a Python wrapper around an `@[export]`'d Lean function. Conventions for the C ABI of Lean-exported functions: * Most parameter types are `lean_object*`. Exceptions: scalar value types (Bool, UInt8/16/32/64, USize, Float) are passed as their natural C types, but induc
(lib: ctypes.CDLL, finfo: FuncInfo, marshaller: Marshaller)
| 187 | return LeanInductiveValue(self._ti.name, self._ctor.name, self._ctor.tag, tuple(args)) |
| 188 | |
| 189 | |
| 190 | # Optional runtime validation of arguments against their Lean `TypeRepr`. |
| 191 | # Off by default (the marshaller is intentionally lenient); enable it for |
| 192 | # clearer errors during development. See `set_argument_typechecking`. |
| 193 | _argument_typechecking = False |
| 194 | |
| 195 | |
| 196 | def set_argument_typechecking(enabled: bool) -> None: |
| 197 | """Enable/disable runtime validation of arguments to `@[python]` functions. |
| 198 | |
| 199 | When enabled, each argument is checked against the same `TypeRepr` that |
| 200 | drives marshalling (:meth:`lean_py.registry.TypeRepr.check`), raising a |
| 201 | clear ``TypeError`` before the value crosses the FFI boundary. Applies to |
| 202 | functions bound after — and calls made while — it is enabled. |
| 203 | """ |
| 204 | global _argument_typechecking |
| 205 | _argument_typechecking = bool(enabled) |
| 206 | |
| 207 | |
| 208 | # ============================================================================ |
| 209 | # Generated function wrappers |
| 210 | # ============================================================================ |
| 211 | |
| 212 | |
| 213 | def _build_callable(lib: ctypes.CDLL, finfo: FuncInfo, marshaller: Marshaller) -> Callable: |
| 214 | """Build a Python wrapper around an `@[export]`'d Lean function. |
| 215 | |
| 216 | Conventions for the C ABI of Lean-exported functions: |
| 217 | * Most parameter types are `lean_object*`. Exceptions: scalar value |
| 218 | types (Bool, UInt8/16/32/64, USize, Float) are passed as their |
| 219 | natural C types, but inductive parameters are pointer-shaped. |
| 220 | * IO-returning functions take a trailing `lean_io.RealWorld` arg |
| 221 | (just `lean_box(0)`); the result is a tagged Result object. |
| 222 | """ |
| 223 | try: |
| 224 | cfn = getattr(lib, finfo.exportName) |
| 225 | except AttributeError as e: |
| 226 | raise RuntimeError( |
| 227 | f"Symbol {finfo.exportName} not found in library " |
| 228 | f"(declared in registry but missing from dylib)" |
| 229 | ) from e |
| 230 | |
| 231 | ret = finfo.returnType |
| 232 | is_io = ret.kind == "io" |
| 233 | |
| 234 | pwraps: list[TypeWrapper] = [marshaller.wrapper_for(p) for p in finfo.params] |
| 235 | rwrap: TypeWrapper = marshaller.wrapper_for(ret) |
| 236 | |
| 237 | # We use `c_void_p` for any pointer-shaped argument or return value |
| 238 | # to sidestep a ctypes oddity on macOS where storing a returned |
| 239 | # `POINTER(struct)` and then passing it to another C function |
| 240 | # corrupts the pointer in some configurations. |
| 241 | def _ctype_for_call(ct): |
| 242 | # Pointer-shaped: collapse to c_void_p. |
| 243 | if isinstance(ct, type) and issubclass(ct, ctypes._Pointer): |
| 244 | return c_void_p |
| 245 | return ct |
| 246 |
no test coverage detected