Skip to content

Commit eb25cb8

Browse files
authored
Merge pull request #82 from jorenham/improve/LowLevelCallable
improved `scipy.LowLevelCallable`
2 parents 3026bf6 + 702b0e9 commit eb25cb8

File tree

3 files changed

+113
-42
lines changed

3 files changed

+113
-42
lines changed

poetry.lock

Lines changed: 1 addition & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ python = "^3.10.1"
3333
numpy = ">=1.25"
3434
scipy = ">=1.14.1"
3535
optype = "^0.6.1"
36-
types-cffi = ">=1.16.0"
3736

3837
[tool.poetry.group.lint.dependencies]
3938
basedmypy = "^2.6.0"

scipy-stubs/_lib/_ccallback.pyi

Lines changed: 112 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,141 @@
11
import ctypes as ct
22
from _ctypes import CFuncPtr as PyCFuncPtr
33
from types import ModuleType
4-
from typing import Generic, Literal, NoReturn, TypeAlias
5-
from typing_extensions import CapsuleType, Never, Self, TypeVar, override
4+
from typing import ClassVar, Generic, Literal, NoReturn, Protocol, TypeAlias, final, overload, type_check_only
5+
from typing_extensions import CapsuleType as PyCapsule, Self, TypeVar, TypeVarTuple, Unpack, override
66

7-
from cffi.model import FunctionPtrType as _CFFIFuncP, PointerType as _CFFIVoidP
8-
from scipy._typing import Untyped
7+
# some quick interfaces for the relevant `cffi` types
98

10-
_Function: TypeAlias = CapsuleType | PyCFuncPtr | _CFFIFuncP | CData
11-
_UserData: TypeAlias = CapsuleType | ct.c_void_p | _CFFIVoidP
9+
@type_check_only
10+
@final
11+
class _CFFIBackendType(Protocol):
12+
cname: str
13+
kind: Literal[
14+
"primitive",
15+
"bool",
16+
"int",
17+
"float",
18+
"char",
19+
"byte",
20+
"pointer",
21+
"charp",
22+
"bytep",
23+
"voidp",
24+
"generic",
25+
"struct",
26+
"union",
27+
"enum",
28+
"anonymous",
29+
"typedef",
30+
"function",
31+
]
32+
33+
_CTs = TypeVarTuple("_CTs", default=Unpack[tuple[_CFFIType, ...]])
34+
_CT_co = TypeVar("_CT_co", covariant=True, bound=_CFFIType, default=_CFFIType)
35+
36+
@type_check_only
37+
class _CFFIType(Protocol):
38+
is_array_type: ClassVar[bool]
39+
is_raw_function: ClassVar[bool]
40+
41+
def is_integer_type(self, /) -> bool: ...
42+
def has_c_name(self, /) -> bool: ...
43+
def get_c_name(self, /, replace_with: str = "", context: str = "a C file", quals: int = 0) -> str: ...
44+
def get_cached_btype(self, ffi: object, finishlist: list[object], can_delay: bool = False) -> _CFFIBackendType: ...
45+
46+
# virtual
47+
def build_backend_type(self, /, ffi: object, finishlist: list[object]) -> _CFFIBackendType: ...
48+
@property
49+
def c_name_with_marker(self, /) -> str: ...
50+
51+
@type_check_only
52+
@final
53+
class _CFFIVoid(_CFFIType, Protocol):
54+
is_array_type: ClassVar = False
55+
is_raw_function: ClassVar = False
56+
57+
def __init__(self, /) -> None: ...
58+
59+
@type_check_only
60+
class _CFFIFunc(_CFFIType, Protocol[_CT_co, Unpack[_CTs]]):
61+
is_array_type: ClassVar = False
62+
63+
@property
64+
def args(self, /) -> tuple[Unpack[_CTs]]: ...
65+
@property
66+
def result(self, /) -> _CT_co: ...
67+
@property
68+
def ellipsis(self, /) -> bool: ...
69+
@property
70+
def abi(self, /) -> int | str | None: ...
71+
def __init__(self, /, args: tuple[Unpack[_CTs]], result: _CT_co, ellipsis: bool, abi: int | None = None) -> None: ...
72+
73+
@type_check_only
74+
@final
75+
class _CFFIFuncPtr(_CFFIFunc[_CT_co, Unpack[_CTs]], Protocol[_CT_co, Unpack[_CTs]]):
76+
is_raw_function: ClassVar = False
77+
78+
def as_raw_function(self, /) -> _CFFIFunc[_CT_co, Unpack[_CTs]]: ...
79+
80+
@type_check_only
81+
class _CFFIPointerType(_CFFIType, Protocol[_CT_co]):
82+
is_array_type: ClassVar = False
83+
is_raw_function: ClassVar = False
84+
85+
@property
86+
def totype(self, /) -> _CT_co: ...
87+
@property
88+
def quals(self, /) -> int: ...
89+
def __init__(self, /, totype: _CT_co, quals: int = 0) -> None: ...
90+
91+
_CFFIVoidP: TypeAlias = _CFFIPointerType[_CFFIVoid]
92+
93+
# helper aliases
94+
95+
_Function: TypeAlias = PyCapsule | PyCFuncPtr | _CFFIFuncPtr | CData
96+
_UserData: TypeAlias = PyCapsule | ct.c_void_p | _CFFIVoidP
1297

1398
_FuncT_co = TypeVar("_FuncT_co", bound=_Function, covariant=True, default=_Function)
14-
_DataT_co = TypeVar("_DataT_co", bound=_UserData | None, covariant=True, default=_UserData)
99+
_DataT = TypeVar("_DataT", bound=_UserData | None)
100+
_DataT_co = TypeVar("_DataT_co", bound=_UserData | None, covariant=True, default=None)
15101

16102
ffi: Literal[False] | None
17103

104+
# public api
105+
106+
@final
18107
class CData: ...
19108

20-
class LowLevelCallable(tuple[CapsuleType, _FuncT_co, _DataT_co], Generic[_FuncT_co, _DataT_co]):
109+
class LowLevelCallable(tuple[PyCapsule, _FuncT_co, _DataT_co], Generic[_FuncT_co, _DataT_co]):
21110
@property
22111
def function(self, /) -> _FuncT_co: ...
23112
@property
24113
def user_data(self, /) -> _DataT_co: ...
25114
@property
26115
def signature(self, /) -> str: ...
27-
def __new__(
116+
@overload
117+
def __new__(cls, function: Self, user_data: _DataT_co | None = None, signature: str | None = None) -> Self: ...
118+
@overload
119+
def __new__(cls, function: _FuncT_co, user_data: _DataT_co = ..., signature: str | None = None) -> Self: ...
120+
@classmethod
121+
@overload
122+
def from_cython(
28123
cls,
29-
function: _FuncT_co | LowLevelCallable[_FuncT_co, _DataT_co],
30-
user_data: Untyped | None = None,
124+
module: ModuleType,
125+
name: str,
126+
user_data: None = None,
31127
signature: str | None = None,
32-
) -> Self: ...
128+
) -> LowLevelCallable[PyCapsule, None]: ...
33129
@classmethod
130+
@overload
34131
def from_cython(
35132
cls,
36133
module: ModuleType,
37134
name: str,
38-
user_data: _UserData | None = None,
135+
user_data: _DataT,
39136
signature: str | None = None,
40-
) -> Self: ...
137+
) -> LowLevelCallable[PyCapsule, _DataT]: ...
41138

42139
# NOTE: `__getitem__` will always raise a `ValueError`
43140
@override
44-
def __getitem__(self, idx: Never, /) -> NoReturn: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
141+
def __getitem__(self, idx: object, /) -> NoReturn: ...

0 commit comments

Comments
 (0)