Skip to content

Fix ctypes plugin trying to look up bytes in Python 2 #5924

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 2 commits into from
Nov 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 21 additions & 7 deletions mypy/plugins/ctypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@
from mypy.types import AnyType, CallableType, Instance, Type, TypeOfAny, UnionType, union_items


def _get_bytes_type(api: 'mypy.plugin.CheckerPluginInterface') -> Instance:
"""Return the type corresponding to bytes on the current Python version.

This is bytes in Python 3, and str in Python 2.
"""
return api.named_generic_type(
'builtins.bytes' if api.options.python_version >= (3,) else 'builtins.str', [])


def _get_text_type(api: 'mypy.plugin.CheckerPluginInterface') -> Instance:
"""Return the type corresponding to Text on the current Python version.

This is str in Python 3, and unicode in Python 2.
"""
return api.named_generic_type(
'builtins.str' if api.options.python_version >= (3,) else 'builtins.unicode', [])


def _find_simplecdata_base_arg(tp: Instance, api: 'mypy.plugin.CheckerPluginInterface'
) -> Optional[Type]:
"""Try to find a parametrized _SimpleCData in tp's bases and return its single type argument.
Expand Down Expand Up @@ -170,13 +188,9 @@ def array_value_callback(ctx: 'mypy.plugin.AttributeContext') -> Type:
if isinstance(tp, AnyType):
types.append(AnyType(TypeOfAny.from_another_any, source_any=tp))
elif isinstance(tp, Instance) and tp.type.fullname() == 'ctypes.c_char':
types.append(ctx.api.named_generic_type('builtins.bytes', []))
types.append(_get_bytes_type(ctx.api))
elif isinstance(tp, Instance) and tp.type.fullname() == 'ctypes.c_wchar':
types.append(ctx.api.named_generic_type(
'builtins.str'
if ctx.api.options.python_version >= (3,)
else 'builtins.unicode',
[]))
types.append(_get_text_type(ctx.api))
else:
ctx.api.msg.fail(
'ctypes.Array attribute "value" is only available'
Expand All @@ -195,7 +209,7 @@ def array_raw_callback(ctx: 'mypy.plugin.AttributeContext') -> Type:
for tp in union_items(et):
if (isinstance(tp, AnyType)
or isinstance(tp, Instance) and tp.type.fullname() == 'ctypes.c_char'):
types.append(ctx.api.named_generic_type('builtins.bytes', []))
types.append(_get_bytes_type(ctx.api))
else:
ctx.api.msg.fail(
'ctypes.Array attribute "raw" is only available'
Expand Down
11 changes: 10 additions & 1 deletion test-data/unit/check-ctypes.test
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ reveal_type(ca.value) # E: Revealed type is 'builtins.bytes'
reveal_type(ca.raw) # E: Revealed type is 'builtins.bytes'
[builtins fixtures/floatdict.pyi]

[case testCtypesCharArrayAttrsPy2]
# flags: --py2
import ctypes

ca = (ctypes.c_char * 4)('a', 'b', 'c', '\x00')
reveal_type(ca.value) # E: Revealed type is 'builtins.str'
reveal_type(ca.raw) # E: Revealed type is 'builtins.str'
[builtins_py2 fixtures/floatdict_python2.pyi]

[case testCtypesWcharArrayAttrs]
import ctypes

Expand All @@ -95,7 +104,7 @@ import ctypes
wca = (ctypes.c_wchar * 4)(u'a', u'b', u'c', u'\x00')
reveal_type(wca.value) # E: Revealed type is 'builtins.unicode'
wca.raw # E: ctypes.Array attribute "raw" is only available with element type c_char, not "ctypes.c_wchar"
[builtins_py2 fixtures/floatdict.pyi]
[builtins_py2 fixtures/floatdict_python2.pyi]

[case testCtypesCharUnionArrayAttrs]
import ctypes
Expand Down
1 change: 0 additions & 1 deletion test-data/unit/fixtures/floatdict.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class str:
def __rmul__(self, n: int) -> str: ...

class bytes: pass
class unicode: pass

class tuple(Generic[T]): pass
class slice: pass
Expand Down
68 changes: 68 additions & 0 deletions test-data/unit/fixtures/floatdict_python2.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from typing import TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union

T = TypeVar('T')
KT = TypeVar('KT')
VT = TypeVar('VT')

Any = 0

class object:
def __init__(self) -> None: pass

class type:
def __init__(self, x: Any) -> None: pass

class str:
def __add__(self, other: 'str') -> 'str': pass
def __rmul__(self, n: int) -> str: ...

class unicode: pass

class tuple(Generic[T]): pass
class slice: pass
class function: pass

class ellipsis: pass

class list(Iterable[T], Generic[T]):
@overload
def __init__(self) -> None: pass
@overload
def __init__(self, x: Iterable[T]) -> None: pass
def __iter__(self) -> Iterator[T]: pass
def __add__(self, x: list[T]) -> list[T]: pass
def __mul__(self, x: int) -> list[T]: pass
def __getitem__(self, x: int) -> T: pass
def append(self, x: T) -> None: pass
def extend(self, x: Iterable[T]) -> None: pass

class dict(Iterable[KT], Mapping[KT, VT], Generic[KT, VT]):
@overload
def __init__(self, **kwargs: VT) -> None: pass
@overload
def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass
def __setitem__(self, k: KT, v: VT) -> None: pass
def __getitem__(self, k: KT) -> VT: pass
def __iter__(self) -> Iterator[KT]: pass
def update(self, a: Mapping[KT, VT]) -> None: pass
@overload
def get(self, k: KT) -> Optional[VT]: pass
@overload
def get(self, k: KT, default: Union[KT, T]) -> Union[VT, T]: pass


class int:
def __float__(self) -> float: ...
def __int__(self) -> int: ...
def __mul__(self, x: int) -> int: ...
def __rmul__(self, x: int) -> int: ...
def __truediv__(self, x: int) -> int: ...
def __rtruediv__(self, x: int) -> int: ...

class float:
def __float__(self) -> float: ...
def __int__(self) -> int: ...
def __mul__(self, x: float) -> float: ...
def __rmul__(self, x: float) -> float: ...
def __truediv__(self, x: float) -> float: ...
def __rtruediv__(self, x: float) -> float: ...