-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Detect impossible unpacking? #18783
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
This is clearly a bug, unpacking should be checked consistently. It already happens if the callable is "good", but plain from typing import Any
def fn1(*args: Any, **kwargs: Any) -> None: ...
fn2: Any
fn3: int
def baz(x: int) -> None:
fn1(*x, **x) # E: Expected iterable as variadic argument [misc] \
# E: Argument after ** must be a mapping, not "int" [arg-type]
fn2(*x, **x)
# Note there's an error, but an unrelated one
fn3(*x, **x) # E: "int" not callable [operator] |
See related https://github.com/python/mypy/pull/18207/files and |
Hey, I'm really not fluent in Is it a good starting point to look around here? Lines 2484 to 2492 in e37d92d
I'm unsure because it seems that this would confront an actual call to a desired signature, whereas we only need to analyze the call here. Also I could not really understand quickly how |
@ego-thales hi! Thanks for your interest. Here's a little prototype to help you working on this feature: diff --git mypy/checkexpr.py mypy/checkexpr.py
index 1017009ce..eac3a759d 100644
--- mypy/checkexpr.py
+++ mypy/checkexpr.py
@@ -1583,7 +1583,7 @@ class ExpressionChecker(ExpressionVisitor[Type]):
callee, args, arg_kinds, arg_names, callable_name, object_type, context
)
elif isinstance(callee, AnyType) or not self.chk.in_checked_function():
- return self.check_any_type_call(args, callee)
+ return self.check_any_type_call(args, callee, arg_kinds, context)
elif isinstance(callee, UnionType):
return self.check_union_call(callee, args, arg_kinds, arg_names, context)
elif isinstance(callee, Instance):
@@ -2481,15 +2481,7 @@ class ExpressionChecker(ExpressionVisitor[Type]):
# Keep track of consumed tuple *arg items.
mapper = ArgTypeExpander(self.argument_infer_context())
- for arg_type, arg_kind in zip(arg_types, arg_kinds):
- arg_type = get_proper_type(arg_type)
- if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type):
- self.msg.invalid_var_arg(arg_type, context)
- if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(arg_type):
- is_mapping = is_subtype(
- arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem")
- )
- self.msg.invalid_keyword_var_arg(arg_type, is_mapping, context)
+ self.check_args_unpacking(arg_types, arg_kinds, context)
for i, actuals in enumerate(formal_to_actual):
orig_callee_arg_type = get_proper_type(callee.arg_types[i])
@@ -3292,8 +3284,17 @@ class ExpressionChecker(ExpressionVisitor[Type]):
skip_unsatisfied=skip_unsatisfied,
)
- def check_any_type_call(self, args: list[Expression], callee: Type) -> tuple[Type, Type]:
- self.infer_arg_types_in_empty_context(args)
+ def check_any_type_call(
+ self,
+ args: list[Expression],
+ callee: Type,
+ arg_kinds: list[ArgKind],
+ context: Context,
+ ) -> tuple[Type, Type]:
+ arg_types = self.infer_arg_types_in_empty_context(args)
+
+ self.check_args_unpacking(arg_types, arg_kinds, context)
+
callee = get_proper_type(callee)
if isinstance(callee, AnyType):
return (
@@ -3303,6 +3304,17 @@ class ExpressionChecker(ExpressionVisitor[Type]):
else:
return AnyType(TypeOfAny.special_form), AnyType(TypeOfAny.special_form)
+ def check_args_unpacking(self, arg_types: list[Type], arg_kinds: list[ArgKind], context: Context) -> None:
+ for arg_type, arg_kind in zip(arg_types, arg_kinds):
+ arg_type = get_proper_type(arg_type)
+ if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type):
+ self.msg.invalid_var_arg(arg_type, context)
+ if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(arg_type):
+ is_mapping = is_subtype(
+ arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem")
+ )
+ self.msg.invalid_keyword_var_arg(arg_type, is_mapping, context)
+
def check_union_call(
self,
callee: UnionType,
I didn't test this, but it should probably work :) |
I'd like to work on this, too. |
@Jdwashin9 go ahead! My diff above can be a nice starting point. |
Oh thank you so much, because despite the really helpful contribution from @sobolevn, I could not find enough time to go further with this, requiring time to understand the core machanisms of the project. I had not forgotten, it still sat in my todo list, but it was not realistically going to happen before summer from my side. Thanks in advance and good luck @Jdwashin9! |
Hi,
Consider the following code, which passes with no error.
I think it would be neat to have
mypy
raising error on this, sinceint
cannot be unpacked in any way.Is it something to consider or am I missing something?
Thanks in advance.
Élie
The text was updated successfully, but these errors were encountered: