Skip to content

Commit bbf0fd3

Browse files
Alan Duilevkivskyi
authored andcommitted
Allow star args in ctypes.Array constructor (#6213)
1 parent f03ad70 commit bbf0fd3

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

mypy/plugins/ctypes.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
# Fully qualified instead of "from mypy.plugin import ..." to avoid circular import problems.
66
import mypy.plugin
7+
from mypy import nodes
78
from mypy.maptype import map_instance_to_supertype
89
from mypy.subtypes import is_subtype
910
from mypy.types import (
@@ -116,19 +117,22 @@ def array_constructor_callback(ctx: 'mypy.plugin.FunctionContext') -> Type:
116117
allowed = _autoconvertible_to_cdata(et, ctx.api)
117118
assert len(ctx.arg_types) == 1, \
118119
"The stub of the ctypes.Array constructor should have a single vararg parameter"
119-
for arg_num, arg_type in enumerate(ctx.arg_types[0], 1):
120-
# TODO This causes false errors if the argument list contains *args.
121-
# In a function hook, the type of an *args parameter is the type of the iterable being
122-
# unpacked. However, FunctionContext currently doesn't provide a way to differentiate
123-
# between normal arguments and *args, so the iterable type is considered invalid.
124-
# Once FunctionContext has an API for this, *args should be allowed here if the
125-
# iterable's element type is compatible with the array element type.
126-
if not is_subtype(arg_type, allowed):
120+
for arg_num, (arg_kind, arg_type) in enumerate(zip(ctx.arg_kinds[0], ctx.arg_types[0]), 1):
121+
if arg_kind == nodes.ARG_POS and not is_subtype(arg_type, allowed):
127122
ctx.api.msg.fail(
128123
'Array constructor argument {} of type "{}"'
129124
' is not convertible to the array element type "{}"'
130125
.format(arg_num, arg_type, et),
131126
ctx.context)
127+
elif arg_kind == nodes.ARG_STAR:
128+
ty = ctx.api.named_generic_type("typing.Iterable", [allowed])
129+
if not is_subtype(arg_type, ty):
130+
ctx.api.msg.fail(
131+
'Array constructor argument {} of type "{}"'
132+
' is not convertible to the array element type "Iterable[{}]"'
133+
.format(arg_num, arg_type, et),
134+
ctx.context)
135+
132136
return ctx.default_return_type
133137

134138

test-data/unit/check-ctypes.test

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ oa.value # E: ctypes.Array attribute "value" is only available with element typ
156156
oa.raw # E: ctypes.Array attribute "raw" is only available with element type c_char, not "ctypes.c_int"
157157
[builtins fixtures/floatdict.pyi]
158158

159-
[case testCtypesArrayConstructorStarargs-skip]
159+
[case testCtypesArrayConstructorStarargs]
160160
import ctypes
161161

162162
intarr4 = ctypes.c_int * 4
@@ -167,4 +167,16 @@ reveal_type(intarr4(*int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_
167167
reveal_type(intarr4(*c_int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_int]'
168168
reveal_type(intarr6(1, ctypes.c_int(2), *int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_int]'
169169
reveal_type(intarr6(1, ctypes.c_int(2), *c_int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_int]'
170+
171+
float_values = [1.0, 2.0, 3.0, 4.0]
172+
intarr4(*float_values) # E: Array constructor argument 1 of type "builtins.list[builtins.float*]" is not convertible to the array element type "Iterable[ctypes.c_int]"
173+
[builtins fixtures/floatdict.pyi]
174+
175+
[case testCtypesArrayConstructorKwargs]
176+
import ctypes
177+
intarr4 = ctypes.c_int * 4
178+
179+
x = {"a": 1, "b": 2}
180+
intarr4(**x) # E: Too many arguments for "Array"
181+
170182
[builtins fixtures/floatdict.pyi]

0 commit comments

Comments
 (0)