Skip to content

Try to re-add stubs for ctypes #1906

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Mar 17, 2018
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
acb456f
Revert "Delete ctypes. It is not yet ready for prime time, alas."
dgelessus Feb 20, 2018
acb3758
Convert type comments to variable annotations in ctypes.__init__
dgelessus Feb 20, 2018
45ac1a7
Add missing List import in ctypes.__init__
dgelessus Feb 20, 2018
38fabd0
Add missing bound kwarg to _CT TypeVar in ctypes.__init__
dgelessus Feb 20, 2018
265a831
Fix ctypes._CData classmethod definitions
dgelessus Feb 20, 2018
2bb0fe9
Fix ctypes Structure/Union __getattr__ definitions
dgelessus Feb 20, 2018
a73cd5e
Fix ctypes._CData classmethod definitions properly this time
dgelessus Feb 20, 2018
8919823
Fix swapped parameter types in ctypes._CData.in_dll
dgelessus Feb 20, 2018
0dc881a
Add limited support for ctypes._CData.__class__.__mul__
dgelessus Feb 20, 2018
ba2a9e5
Make ctypes._FuncPtr extend ctypes._CData
dgelessus Feb 20, 2018
aabc546
Improve typing of ctypes.cast
dgelessus Feb 20, 2018
8841185
Mark class attributes with ClassVar in ctypes.__init__
dgelessus Feb 20, 2018
0413982
Mark ctypes._CData.from_buffer[_copy] offset arg as optional
dgelessus Feb 20, 2018
50e44c9
Remove trailing whitespace in ctypes.__init__
dgelessus Feb 20, 2018
535a008
Don't export ctypes.UnionT
dgelessus Mar 6, 2018
aedd0d9
Add ctypes._DLL.__getitem__
dgelessus Mar 6, 2018
2311ced
Make ctypes._DLL.__get(attr|item)__ return _FuncPtr instead of Any
dgelessus Mar 6, 2018
854e22e
Change ctypes DLL inheritance hierarchy to match the real one better
dgelessus Mar 6, 2018
e765dec
Add some missing attributes to ctypes.CDLL
dgelessus Mar 6, 2018
7a34b64
Rename ctypes._FuncPtr so it doesn't conflict with CDLL._FuncPtr
dgelessus Mar 6, 2018
0619771
Fix type of ctypes.CDLL._FuncPtr
dgelessus Mar 6, 2018
94a9853
Merge _FuncProto into _FuncPointer
dgelessus Mar 6, 2018
1091074
Fix some leftover references to ctypes._DLL
dgelessus Mar 6, 2018
253fc4a
Simplify definition of ctypes._DLLT
dgelessus Mar 6, 2018
cc3f49a
Add ctypes.LibraryLoader.__get(attr|item)__
dgelessus Mar 6, 2018
7ff6017
Use Text instead of str where appropriate in ctypes.__init__
dgelessus Mar 6, 2018
66a74a8
Make ctypes.c_char accept ints
dgelessus Mar 6, 2018
e5d1642
Make ctypes.c_[w]char_p accept None
dgelessus Mar 6, 2018
b81de47
Remove unneeded Generic base from ctypes.py_object
dgelessus Mar 6, 2018
a7507ea
Make ctypes.cast accept _cparam
dgelessus Mar 6, 2018
d8d431e
Fix ctypes._PF being declared too late
dgelessus Mar 6, 2018
a38f579
Remove incorrect ctypes.PyDLL.__init__ override
dgelessus Mar 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 255 additions & 0 deletions stdlib/2and3/ctypes/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
# Stubs for ctypes

from typing import (
Any, Callable, ClassVar, Iterable, List, Mapping, Optional, Sequence, Sized, Text, Tuple, Type,
Generic, TypeVar, overload,
)
from typing import Union as _UnionT
import sys

_T = TypeVar('_T')
_DLLT = TypeVar('_DLLT', bound=CDLL)
_CT = TypeVar('_CT', bound=_CData)


RTLD_GLOBAL: int = ...
RTLD_LOCAL: int = ...
DEFAULT_MODE: int = ...


class CDLL(object):
_func_flags_: ClassVar[int] = ...
_func_restype_: ClassVar[_CData] = ...
_name: str = ...
_handle: int = ...
_FuncPtr: Type[_FuncPointer] = ...
def __init__(self, name: str, mode: int = ..., handle: Optional[int] = ...,
use_errno: bool = ..., use_last_error: bool = ...) -> None: ...
def __getattr__(self, name: str) -> _FuncPointer: ...
def __getitem__(self, name: str) -> _FuncPointer: ...
if sys.platform == 'win32':
class OleDLL(CDLL): ...
class WinDLL(CDLL): ...
class PyDLL(CDLL): ...

class LibraryLoader(Generic[_DLLT]):
def __init__(self, dlltype: Type[_DLLT]) -> None: ...
def __getattr__(self, name: str) -> _DLLT: ...
def __getitem__(self, name: str) -> _DLLT: ...
def LoadLibrary(self, name: str) -> _DLLT: ...

cdll: LibraryLoader[CDLL] = ...
if sys.platform == 'win32':
windll: LibraryLoader[WinDLL] = ...
oledll: LibraryLoader[OleDLL] = ...
pydll: LibraryLoader[PyDLL] = ...
pythonapi: PyDLL = ...

class _CDataMeta(type):
# TODO The return type is not accurate. The method definition *should* look like this:
# def __mul__(cls: Type[_CT], other: int) -> Type[Array[_CT]]: ...
# but that is not valid, because technically a _CDataMeta might not be a Type[_CT].
# This can never actually happen, because all _CDataMeta instances are _CData subclasses, but a typechecker doesn't know that.
def __mul__(cls: _CDataMeta, other: int) -> Type[Array[_CT]]: ...
def __rmul__(cls: _CDataMeta, other: int) -> Type[Array[_CT]]: ...
class _CData(metaclass=_CDataMeta):
_b_base: int = ...
_b_needsfree_: bool = ...
_objects: Optional[Mapping[Any, int]] = ...
@classmethod
def from_buffer(cls: Type[_CT], source: bytearray, offset: int = ...) -> _CT: ...
@classmethod
def from_buffer_copy(cls: Type[_CT], source: bytearray, offset: int = ...) -> _CT: ...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be bytes not bytearray?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of the bytearray types are inadequate actually - from_buffer_copy accepts any object that implements the buffer protocol, and similarly from_buffer accepts any writable buffer. bytearray and bytes are common examples of read-write and read-only buffer objects, but there are others like memoryview and array.array. All ctypes data objects (_CData subclasses) are writable buffers as well.

Does typeshed/typing have a type for "anything implementing the buffer protocol"? If not, we probably need to use Unions and list the most common stdlib buffer types.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can define a new protocol. I think there are already many examples in typeshed (grep for Protocol).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, it is C-level, in this case looks like a big union is the only option.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't bytes already treated by mypy as anything implementing the buffer protocol?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what the typing documentation says:

This type [ByteString] represents the types bytes, bytearray, and memoryview.

As a shorthand for this type, bytes can be used to annotate arguments of any of the types mentioned above.

This should probably be added to PEP 484.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix here: #2610

@classmethod
def from_address(cls: Type[_CT], address: int) -> _CT: ...
@classmethod
def from_param(cls: Type[_CT], obj: Any) -> _UnionT[_CT, _cparam]: ...
@classmethod
def in_dll(cls: Type[_CT], library: CDLL, name: str) -> _CT: ...

class _PointerLike(_CData): pass

_ECT = Callable[[Optional[Type[_CData]],
_FuncPointer,
Tuple[_CData, ...]],
_CData]
_PF = _UnionT[
Tuple[int],
Tuple[int, str],
Tuple[int, str, Any]
]
class _FuncPointer(_PointerLike, _CData):
restype: _UnionT[Type[_CData], Callable[[int], None], None] = ...
argtypes: Tuple[Type[_CData], ...] = ...
errcheck: _ECT = ...
@overload
def __init__(self, address: int) -> None: ...
@overload
def __init__(self, callable: Callable[..., Any]) -> None: ...
@overload
def __init__(self, func_spec: Tuple[_UnionT[str, int], CDLL],
paramflags: Tuple[_PF, ...] = ...) -> None: ...
@overload
def __init__(self, vtlb_index: int, name: str,
paramflags: Tuple[_PF, ...] = ...,
iid: _Pointer[c_int] = ...) -> None: ...
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...

class ArgumentError(Exception): ...


def CFUNCTYPE(restype: Type[_CData],
*argtypes: Type[_CData],
use_errno: bool = ...,
use_last_error: bool = ...) -> Type[_FuncPointer]: ...
if sys.platform == 'win32':
def WINFUNCTYPE(restype: Type[_CData],
*argtypes: Type[_CData],
use_errno: bool = ...,
use_last_error: bool = ...) -> Type[_FuncPointer]: ...
def PYFUNCTYPE(restype: Type[_CData],
*argtypes: Type[_CData]) -> Type[_FuncPointer]: ...

class _cparam: ...

def addressof(obj: _CData) -> int: ...
def alignment(obj_or_type: _UnionT[_CData, Type[_CData]]) -> int: ...
def byref(obj: _CData, offset: int = ...) -> _cparam: ...
_PT = TypeVar('_PT', bound=_PointerLike)
def cast(obj: _UnionT[_CData, _cparam], type: Type[_PT]) -> _PT: ...
def create_string_buffer(init_or_size: _UnionT[int, bytes],
size: Optional[int] = ...) -> Array[c_char]: ...
c_buffer = create_string_buffer
def create_unicode_buffer(init_or_size: _UnionT[int, Text],
size: Optional[int] = ...) -> Array[c_wchar]: ...
if sys.platform == 'win32':
def DllCanUnloadNow() -> int: ...
def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ... # TODO not documented
def FormatError(code: int) -> str: ...
def GetLastError() -> int: ...
def get_errno() -> int: ...
if sys.platform == 'win32':
def get_last_error() -> int: ...
def memmove(dst: _UnionT[int, _CData],
src: _UnionT[int, _CData],
count: int) -> None: ...
def memset(dst: _UnionT[int, _CData],
c: int, count: int) -> None: ...
def POINTER(type: Type[_CT]) -> Type[_Pointer[_CT]]: ...
def pointer(obj: _CT) -> _Pointer[_CT]: ...
def resize(obj: _CData, size: int) -> None: ...
if sys.version_info < (3,):
def set_conversion_mode(encoding: str, errors: str) -> Tuple[str, str]: ...
def set_errno(value: int) -> int: ...
if sys.platform == 'win32':
def set_last_error(value: int) -> int: ...
def sizeof(obj_or_type: _UnionT[_CData, Type[_CData]]) -> int: ...
def string_at(address: int, size: int = ...) -> bytes: ...
if sys.platform == 'win32':
def WinError(code: Optional[int] = ...,
desc: Optional[str] = ...) -> OSError: ...
def wstring_at(address: int, size: int = ...) -> str: ...

class _SimpleCData(Generic[_T], _CData):
value: _T = ...
def __init__(self, value: _T = ...) -> None: ...

class c_byte(_SimpleCData[int]): ...

class c_char(_SimpleCData[bytes]):
def __init__(self, value: _UnionT[int, bytes] = ...) -> None: ...
class c_char_p(_PointerLike, _SimpleCData[Optional[bytes]]):
def __init__(self, value: Optional[_UnionT[int, bytes]] = ...) -> None: ...

class c_double(_SimpleCData[float]): ...
class c_longdouble(_SimpleCData[float]): ...
class c_float(_SimpleCData[float]): ...

class c_int(_SimpleCData[int]): ...
class c_int8(_SimpleCData[int]): ...
class c_int16(_SimpleCData[int]): ...
class c_int32(_SimpleCData[int]): ...
class c_int64(_SimpleCData[int]): ...

class c_long(_SimpleCData[int]): ...
class c_longlong(_SimpleCData[int]): ...

class c_short(_SimpleCData[int]): ...

class c_size_t(_SimpleCData[int]): ...
class c_ssize_t(_SimpleCData[int]): ...

class c_ubyte(_SimpleCData[int]): ...

class c_uint(_SimpleCData[int]): ...
class c_uint8(_SimpleCData[int]): ...
class c_uint16(_SimpleCData[int]): ...
class c_uint32(_SimpleCData[int]): ...
class c_uint64(_SimpleCData[int]): ...

class c_ulong(_SimpleCData[int]): ...
class c_ulonglong(_SimpleCData[int]): ...

class c_ushort(_SimpleCData[int]): ...

class c_void_p(_PointerLike, _SimpleCData[Optional[int]]): ...

class c_wchar(_SimpleCData[Text]): ...
class c_wchar_p(_PointerLike, _SimpleCData[Optional[Text]]):
def __init__(self, value: Optional[_UnionT[int, Text]] = ...) -> None: ...

class c_bool(_SimpleCData[bool]):
def __init__(self, value: bool) -> None: ...

if sys.platform == 'win32':
class HRESULT(_SimpleCData[Any]): ... # TODO undocumented

class py_object(_SimpleCData[_T]): ...

class _CField:
offset: int = ...
size: int = ...
class _StructUnionMeta(_CDataMeta):
_fields_: Sequence[_UnionT[Tuple[str, Type[_CData]], Tuple[str, Type[_CData], int]]] = ...
_pack_: int = ...
_anonymous_: Sequence[str] = ...
def __getattr__(self, name: str) -> _CField: ...
class _StructUnionBase(_CData, metaclass=_StructUnionMeta):
def __init__(self, *args: Any, **kw: Any) -> None: ...
def __getattr__(self, name: str) -> Any: ...
def __setattr__(self, name: str, value: Any) -> None: ...

class Union(_StructUnionBase): pass
class Structure(_StructUnionBase): pass
class BigEndianStructure(Structure): pass
class LittleEndianStructure(Structure): pass

class Array(Generic[_T], Sized, _CData):
_length_: ClassVar[int] = ...
_type_: ClassVar[Type[_T]] = ...
raw: bytes = ... # TODO only available with _T == c_char
value: bytes = ... # TODO only available with _T == c_char
def __init__(self, *args: _T) -> None: ...
@overload
def __getitem__(self, i: int) -> _T: ...
@overload
def __getitem__(self, s: slice) -> List[_T]: ...
@overload
def __setitem__(self, i: int, o: _T) -> None: ...
@overload
def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ...
def __iter__(self) -> Iterable[_T]: ...


class _Pointer(Generic[_T], _PointerLike, _CData):
_type_: ClassVar[Type[_T]] = ...
contents: _T = ...
def __init__(self, arg: _T = ...) -> None: ...
@overload
def __getitem__(self, i: int) -> _T: ...
@overload
def __getitem__(self, s: slice) -> List[_T]: ...
@overload
def __setitem__(self, i: int, o: _T) -> None: ...
@overload
def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ...
8 changes: 8 additions & 0 deletions stdlib/2and3/ctypes/util.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Stubs for ctypes.util

from typing import Optional
import sys

def find_library(name: str) -> Optional[str]: ...
if sys.platform == 'win32':
def find_msvcrt() -> Optional[str]: ...