From 4a3506218b5a5d08b2f2d5fe340dd2d960e1dc2f Mon Sep 17 00:00:00 2001 From: Alan Du Date: Thu, 17 Jan 2019 10:40:51 -0500 Subject: [PATCH 1/4] Allow star args in ctypes.Array constructor --- mypy/plugins/ctypes.py | 25 +++++++++++++++++-------- test-data/unit/check-ctypes.test | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/mypy/plugins/ctypes.py b/mypy/plugins/ctypes.py index 6520cda6f0a0..b6adaca28d4b 100644 --- a/mypy/plugins/ctypes.py +++ b/mypy/plugins/ctypes.py @@ -4,6 +4,7 @@ # Fully qualified instead of "from mypy.plugin import ..." to avoid circular import problems. import mypy.plugin +from mypy import nodes from mypy.maptype import map_instance_to_supertype from mypy.subtypes import is_subtype from mypy.types import ( @@ -116,19 +117,27 @@ def array_constructor_callback(ctx: 'mypy.plugin.FunctionContext') -> Type: allowed = _autoconvertible_to_cdata(et, ctx.api) assert len(ctx.arg_types) == 1, \ "The stub of the ctypes.Array constructor should have a single vararg parameter" - for arg_num, arg_type in enumerate(ctx.arg_types[0], 1): - # TODO This causes false errors if the argument list contains *args. - # In a function hook, the type of an *args parameter is the type of the iterable being - # unpacked. However, FunctionContext currently doesn't provide a way to differentiate - # between normal arguments and *args, so the iterable type is considered invalid. - # Once FunctionContext has an API for this, *args should be allowed here if the - # iterable's element type is compatible with the array element type. - if not is_subtype(arg_type, allowed): + for arg_num, (arg_kind, arg_type) in enumerate(zip(ctx.arg_kinds[0], ctx.arg_types[0]), 1): + if arg_kind not in {nodes.ARG_POS, nodes.ARG_STAR}: + ctx.api.msg.fail( + "Array constructor does not allow keyword arguments", + ctx.context + ) + elif arg_kind == nodes.ARG_POS and not is_subtype(arg_type, allowed): ctx.api.msg.fail( 'Array constructor argument {} of type "{}"' ' is not convertible to the array element type "{}"' .format(arg_num, arg_type, et), ctx.context) + elif arg_kind == nodes.ARG_STAR: + ty = ctx.api.named_generic_type("typing.Iterable", [allowed]) + if not is_subtype(arg_type, ty): + ctx.api.msg.fail( + 'Array constructor argument {} of type "{}"' + ' is not convertible to the array element type "Iterable[{}]"' + .format(arg_num, arg_type, et), + ctx.context) + return ctx.default_return_type diff --git a/test-data/unit/check-ctypes.test b/test-data/unit/check-ctypes.test index 212c61acc86e..ed6e55c9ed5b 100644 --- a/test-data/unit/check-ctypes.test +++ b/test-data/unit/check-ctypes.test @@ -156,7 +156,7 @@ oa.value # E: ctypes.Array attribute "value" is only available with element typ oa.raw # E: ctypes.Array attribute "raw" is only available with element type c_char, not "ctypes.c_int" [builtins fixtures/floatdict.pyi] -[case testCtypesArrayConstructorStarargs-skip] +[case testCtypesArrayConstructorStarargs] import ctypes intarr4 = ctypes.c_int * 4 From 54504cc092fc536510b3da5d0d3fc9c7c267590d Mon Sep 17 00:00:00 2001 From: Alan Du Date: Fri, 18 Jan 2019 12:25:57 -0500 Subject: [PATCH 2/4] Test error for bad star-arg type for ctypes.Array --- test-data/unit/check-ctypes.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-data/unit/check-ctypes.test b/test-data/unit/check-ctypes.test index ed6e55c9ed5b..ac1ab00556cb 100644 --- a/test-data/unit/check-ctypes.test +++ b/test-data/unit/check-ctypes.test @@ -167,4 +167,7 @@ reveal_type(intarr4(*int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_ reveal_type(intarr4(*c_int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_int]' reveal_type(intarr6(1, ctypes.c_int(2), *int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_int]' reveal_type(intarr6(1, ctypes.c_int(2), *c_int_values)) # E: Revealed type is 'ctypes.Array[ctypes.c_int]' + +float_values = [1.0, 2.0, 3.0, 4.0] +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]" [builtins fixtures/floatdict.pyi] From 3f5d04532ba933d42dbc76c75c6a949b39ea9fe9 Mon Sep 17 00:00:00 2001 From: Alan Du Date: Fri, 18 Jan 2019 15:03:45 -0500 Subject: [PATCH 3/4] Add **kwargs test for ctypes.Array constructor --- test-data/unit/check-ctypes.test | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test-data/unit/check-ctypes.test b/test-data/unit/check-ctypes.test index ac1ab00556cb..508b3bfd49c9 100644 --- a/test-data/unit/check-ctypes.test +++ b/test-data/unit/check-ctypes.test @@ -171,3 +171,12 @@ reveal_type(intarr6(1, ctypes.c_int(2), *c_int_values)) # E: Revealed type is ' float_values = [1.0, 2.0, 3.0, 4.0] 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]" [builtins fixtures/floatdict.pyi] + +[case testCtypesArrayConstructorKwargs] +import ctypes +intarr4 = ctypes.c_int * 4 + +x = {"a": 1, "b": 2} +intarr4(**x) # E: Too many arguments for "Array" + +[builtins fixtures/floatdict.pyi] From 331865916d71b6aff9354863c56318430cd3eaaa Mon Sep 17 00:00:00 2001 From: Alan Du Date: Mon, 21 Jan 2019 19:28:54 -0500 Subject: [PATCH 4/4] Remove unused branch --- mypy/plugins/ctypes.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mypy/plugins/ctypes.py b/mypy/plugins/ctypes.py index b6adaca28d4b..00ad26087af2 100644 --- a/mypy/plugins/ctypes.py +++ b/mypy/plugins/ctypes.py @@ -118,12 +118,7 @@ def array_constructor_callback(ctx: 'mypy.plugin.FunctionContext') -> Type: assert len(ctx.arg_types) == 1, \ "The stub of the ctypes.Array constructor should have a single vararg parameter" for arg_num, (arg_kind, arg_type) in enumerate(zip(ctx.arg_kinds[0], ctx.arg_types[0]), 1): - if arg_kind not in {nodes.ARG_POS, nodes.ARG_STAR}: - ctx.api.msg.fail( - "Array constructor does not allow keyword arguments", - ctx.context - ) - elif arg_kind == nodes.ARG_POS and not is_subtype(arg_type, allowed): + if arg_kind == nodes.ARG_POS and not is_subtype(arg_type, allowed): ctx.api.msg.fail( 'Array constructor argument {} of type "{}"' ' is not convertible to the array element type "{}"'