-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Try to re-add stubs for ctypes #1906
Conversation
This reverts commit 73bdb70.
Both Structure's metaclass and Structure itself define a __getattr__. The former returns field objects (each representing a field definition in the Structure), while the latter can return anything (the value of the field in a specific Structure instance). Structure also defines a __setattr__, while Structure's metaclass does not. BigEndianStructure, LittleEndianStructure and Union support the same operations as Structure does, but the semantics obviously differ. Depending on the system endianness, exactly one of the EndianStructures is an alias for Structure, and the other one is a special Structure subclass. For simplicity, both EndianStructures are considered subclasses of Structure here, even though this is technically not always accurate.
The classmethods return *instances* of the class of course, not the class itself.
It's not possible to specify the correct return type for __mul__ and __rmul__ here - see comments for explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this, and sorry for the wait before the review!
I left a couple of comments.
stdlib/2and3/ctypes/__init__.pyi
Outdated
Any, Callable, ClassVar, Iterable, List, Mapping, Optional, Sequence, Sized, Tuple, Type, | ||
Generic, TypeVar, overload, | ||
) | ||
from typing import Union as UnionT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should name this _UnionT
so it doesn't get re-exported. (With the current definition, mypy would let users write ctypes.UnionT
.)
stdlib/2and3/ctypes/__init__.pyi
Outdated
class _DLL: | ||
def __init__(self, name: str, mode: int = ..., handle: Optional[int] = ..., | ||
use_errno: bool = ..., use_last_error: bool = ...) -> None: ... | ||
def __getattr__(self, name: str) -> Any: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there's also __getitem__
.
stdlib/2and3/ctypes/__init__.pyi
Outdated
class PyDLL(_DLL): | ||
_handle: int = ... | ||
_name: str = ... | ||
def __init__(self, name: str, mode: int = ..., |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the __init__
just inherited from the base class? https://github.com/python/cpython/blob/2.7/Lib/ctypes/__init__.py#L389
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically yes, but the documentation lists the constructor as not accepting the use_errno
and use_last_error
kwargs. I don't know what the correct procedure is in cases like this - is the documentation or the code right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd generally favor the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I've removed the __init__
from PyDLL
, it should now inherit the one from CDLL
again.
stdlib/2and3/ctypes/__init__.pyi
Outdated
|
||
class LibraryLoader(Generic[_DLLT]): | ||
def __init__(self, dlltype: Type[_DLLT]) -> None: ... | ||
def LoadLibrary(self, name: str) -> _DLLT: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing __getattr__
and __getitem__
.
stdlib/2and3/ctypes/__init__.pyi
Outdated
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, str], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be Text for Python 2. The actual code checks for (str, unicode).
stdlib/2and3/ctypes/__init__.pyi
Outdated
|
||
class c_byte(_SimpleCData[int]): ... | ||
|
||
class c_char(_SimpleCData[bytes]): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The __init__
argument here can also be an int.
stdlib/2and3/ctypes/__init__.pyi
Outdated
class c_byte(_SimpleCData[int]): ... | ||
|
||
class c_char(_SimpleCData[bytes]): ... | ||
class c_char_p(_PointerLike, _SimpleCData[Optional[bytes]]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it Optional? If the value is inaccessible, it just segfaults (as I just found out experimentally).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not here - the value
of a null pointer is None
.
Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
>>> import ctypes
>>> ccp = ctypes.c_char_p(None)
>>> ccp.value
>>> cwp = ctypes.c_wchar_p(None)
>>> cwp.value
stdlib/2and3/ctypes/__init__.pyi
Outdated
|
||
class c_void_p(_PointerLike, _SimpleCData[Optional[int]]): ... | ||
|
||
class c_wchar(_SimpleCData[str]): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be Text?
stdlib/2and3/ctypes/__init__.pyi
Outdated
if sys.platform == 'win32': | ||
class HRESULT(_SimpleCData[Any]): ... # TODO undocumented | ||
|
||
class py_object(Generic[_T], _SimpleCData[_T]): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need the explicit Generic base class.
The function pointer types returned by CFUNCTYPE and friends are the same as those encountered elsewhere, so there's no need to treat them differently.
OK, should all be fixed (along with a couple of other issues that I noticed/remembered). |
@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: ... |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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 Union
s and list the most common stdlib buffer types.
There was a problem hiding this comment.
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
).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 typesbytes
,bytearray
, andmemoryview
.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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix here: #2610
Fixes the issue discussed here: python#1906 (comment)
Fixes the issue discussed here: #1906 (comment)
* Convert type comments to variable annotations in ctypes.__init__ * Add missing List import in ctypes.__init__ * Add missing bound kwarg to _CT TypeVar in ctypes.__init__ * Fix ctypes._CData classmethod definitions * Fix ctypes Structure/Union __getattr__ definitions Both Structure's metaclass and Structure itself define a __getattr__. The former returns field objects (each representing a field definition in the Structure), while the latter can return anything (the value of the field in a specific Structure instance). Structure also defines a __setattr__, while Structure's metaclass does not. BigEndianStructure, LittleEndianStructure and Union support the same operations as Structure does, but the semantics obviously differ. Depending on the system endianness, exactly one of the EndianStructures is an alias for Structure, and the other one is a special Structure subclass. For simplicity, both EndianStructures are considered subclasses of Structure here, even though this is technically not always accurate. * Fix swapped parameter types in ctypes._CData.in_dll * Add limited support for ctypes._CData.__class__.__mul__ It's not possible to specify the correct return type for __mul__ and __rmul__ here - see comments for explanation. * Make ctypes._FuncPtr extend ctypes._CData * Improve typing of ctypes.cast * Mark class attributes with ClassVar in ctypes.__init__ * Mark ctypes._CData.from_buffer[_copy] offset arg as optional * Remove trailing whitespace in ctypes.__init__ * Don't export ctypes.UnionT * Add ctypes._DLL.__getitem__ * Make ctypes._DLL.__get(attr|item)__ return _FuncPtr instead of Any * Change ctypes DLL inheritance hierarchy to match the real one better * Add some missing attributes to ctypes.CDLL * Rename ctypes._FuncPtr so it doesn't conflict with CDLL._FuncPtr * Fix type of ctypes.CDLL._FuncPtr * Merge _FuncProto into _FuncPointer The function pointer types returned by CFUNCTYPE and friends are the same as those encountered elsewhere, so there's no need to treat them differently. * Fix some leftover references to ctypes._DLL * Simplify definition of ctypes._DLLT * Add ctypes.LibraryLoader.__get(attr|item)__ * Use Text instead of str where appropriate in ctypes.__init__ * Make ctypes.c_char accept ints * Make ctypes.c_[w]char_p accept None * Remove unneeded Generic base from ctypes.py_object * Make ctypes.cast accept _cparam * Fix ctypes._PF being declared too late * Remove incorrect ctypes.PyDLL.__init__ override
…#2610) Fixes the issue discussed here: python#1906 (comment)
Two years ago, some stubs for
ctypes
were added (#454) but removed soon afterwards, because they caused problems with correct code (#475). As I understand it, this was in part because of limitations in mypy at the time. Some of these limitations don't seem to exist anymore, so I tried to make thectypes
stubs work again.I took the code in its last state before it was removed and tried to fix the issues reported in #475, as well as a few issues that I came across myself when using the stubs with my own code. Details on what exactly I've changed can be found in the individual commit messages. I'm sure there are still issues that I haven't noticed, so if anyone has a
ctypes
-using codebase and could try out these stubs, I would be happy to get some feedback.Finally, I'd like to note that I don't have a lot of experience with mypy and writing
typing
stubs. Previously I've type-annotated some bits of my code and used typeshed through PyCharm's typechecker, but other than that this is my first time properly using mypy andtyping
. So if there's anything that I'm doing incorrectly or not according to convention, please let me know! 😃