Skip to content

Pass literals as kwargs #9

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

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open

Conversation

arvi18
Copy link

@arvi18 arvi18 commented Apr 25, 2025

Closes python#10023

Checks if kwarg is Mapping type AND check if list of strings extracted from mapping key type is not empty.

Summary by CodeRabbit

  • New Features

    • Improved handling of mappings with literal keys when used as keyword arguments, providing more precise error reporting for unexpected or missing keyword arguments.
    • Added new test coverage for scenarios involving mappings with literal keys passed as keyword arguments.
  • Style

    • Updated type annotations in tests from comment-based to inline variable annotations and signatures for improved readability and modern syntax.
  • Bug Fixes

    • Enhanced validation for types accepted as **kwargs, reducing false positives for empty or incorrectly-typed mappings.

@arvi18
Copy link
Author

arvi18 commented Apr 25, 2025

Could you please merge the conflicts?

btw, just one code style nit: how about expand the compound bool expressions into several if and else branches (with True or False returns)?

@arvi18
Copy link
Author

arvi18 commented Apr 25, 2025

Previous PR: python#9629

@arvi18
Copy link
Author

arvi18 commented Apr 25, 2025

Diff from mypy_primer, showing the effect of this PR on open source code:

boostedblob (https://github.com/hauntsaninja/boostedblob)
- Warning: unused section(s) in mypy.ini: [mypy-tests.*]
+ boostedblob/globals.py:144: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.970+dev.cb7c148c62d50edc1c70943b209b82d1e0940fd7
+ boostedblob/globals.py:144: : note: use --pdb to drop into pdb
+ Traceback (most recent call last):
+   File "", line 8, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 12, in console_entry
+     main(None, sys.stdout, sys.stderr)
+   File "/main.py", line 96, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 173, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 154, in build
+     result = _build(
+   File "/build.py", line 230, in _build
+     graph = dispatch(sources, manager, stdout)
+   File "/build.py", line 2729, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 3087, in process_graph
+     process_stale_scc(graph, scc, manager)
+   File "/build.py", line 3185, in process_stale_scc
+     graph[id].type_check_first_pass()
+   File "/build.py", line 2180, in type_check_first_pass
+     self.type_checker().check_first_pass()
+   File "/checker.py", line 325, in check_first_pass
+     self.accept(d)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 840, in accept
+     return visitor.visit_decorator(self)
+   File "/checker.py", line 3984, in visit_decorator
+     self.check_func_item(e.func, name=e.func.name)
+   File "/checker.py", line 848, in check_func_item
+     self.check_func_def(defn, typ, name)
+   File "/checker.py", line 1033, in check_func_def
+     self.accept(item.body)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1118, in accept
+     return visitor.visit_expression_stmt(self)
+   File "/checker.py", line 3487, in visit_expression_stmt
+     expr_type = self.expr_checker.accept(s.expr, allow_none_return=True, always_allow_any=True)
+   File "/checkexpr.py", line 4006, in accept
+     typ = self.visit_call_expr(node, allow_none_return=True)
+   File "/checkexpr.py", line 305, in visit_call_expr
+     return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
+   File "/checkexpr.py", line 383, in visit_call_expr_inner
+     ret_type = self.check_call_expr_with_callee_type(callee_type, e, fullname,
+   File "/checkexpr.py", line 892, in check_call_expr_with_callee_type
+     ret_type, callee_type = self.check_call(
+   File "/checkexpr.py", line 960, in check_call
+     return self.check_overload_call(callee, args, arg_kinds, arg_names, callable_name,
+   File "/checkexpr.py", line 1608, in check_overload_call
+     plausible_targets = self.plausible_overload_call_targets(arg_types, arg_kinds,
+   File "/checkexpr.py", line 1737, in plausible_overload_call_targets
+     if self.check_argument_count(typ, arg_types, arg_kinds, arg_names,
+   File "/checkexpr.py", line 1417, in check_argument_count
+     ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments(
+   File "/checkexpr.py", line 1499, in check_for_extra_actual_arguments
+     if messages and supertype.args:
+ NameError: name 'messages' is not defined

mypy_primer (https://github.com/hauntsaninja/mypy_primer)
+ /tmp/mypy_primer/projects/mypy_primer/mypy_primer.py:99: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.970+dev.cb7c148c62d50edc1c70943b209b82d1e0940fd7
+ Traceback (most recent call last):
+   File "", line 8, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 12, in console_entry
+     main(None, sys.stdout, sys.stderr)
+   File "/main.py", line 96, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 173, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 154, in build
+     result = _build(
+   File "/build.py", line 230, in _build
+     graph = dispatch(sources, manager, stdout)
+   File "/build.py", line 2729, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 3087, in process_graph
+     process_stale_scc(graph, scc, manager)
+   File "/build.py", line 3185, in process_stale_scc
+     graph[id].type_check_first_pass()
+   File "/build.py", line 2180, in type_check_first_pass
+     self.type_checker().check_first_pass()
+   File "/checker.py", line 325, in check_first_pass
+     self.accept(d)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 748, in accept
+     return visitor.visit_func_def(self)
+   File "/checker.py", line 782, in visit_func_def
+     self._visit_func_def(defn)
+   File "/checker.py", line 786, in _visit_func_def
+     self.check_func_item(defn, name=defn.name)
+   File "/checker.py", line 848, in check_func_item
+     self.check_func_def(defn, typ, name)
+   File "/checker.py", line 1033, in check_func_def
+     self.accept(item.body)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1397, in accept
+     return visitor.visit_with_stmt(self)
+   File "/checker.py", line 4078, in visit_with_stmt
+     self.accept(s.body)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1324, in accept
+     return visitor.visit_if_stmt(self)
+   File "/checker.py", line 3597, in visit_if_stmt
+     self.accept(b)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1166, in accept
+     return visitor.visit_assignment_stmt(self)
+   File "/checker.py", line 2220, in visit_assignment_stmt
+     self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax)
+   File "/checker.py", line 2414, in check_assignment
+     rvalue_type = self.expr_checker.accept(rvalue)
+   File "/checkexpr.py", line 4012, in accept
+     typ = node.accept(self)
+   File "/nodes.py", line 2508, in accept
+     return visitor.visit_await_expr(self)
+   File "/checkexpr.py", line 4137, in visit_await_expr
+     actual_type = get_proper_type(self.accept(e.expr, expected_type))
+   File "/checkexpr.py", line 4012, in accept
+     typ = node.accept(self)
+   File "/nodes.py", line 1761, in accept
+     return visitor.visit_call_expr(self)
+   File "/checkexpr.py", line 305, in visit_call_expr
+     return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
+   File "/checkexpr.py", line 383, in visit_call_expr_inner
+     ret_type = self.check_call_expr_with_callee_type(callee_type, e, fullname,
+   File "/checkexpr.py", line 892, in check_call_expr_with_callee_type
+     ret_type, callee_type = self.check_call(
+   File "/checkexpr.py", line 957, in check_call
+     return self.check_callable_call(callee, args, arg_kinds, context, arg_names,
+   File "/checkexpr.py", line 1067, in check_callable_call
+     self.check_argument_count(callee, arg_types, arg_kinds,
+   File "/checkexpr.py", line 1417, in check_argument_count
+     ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments(
+   File "/checkexpr.py", line 1499, in check_for_extra_actual_arguments
+     if messages and supertype.args:
+ NameError: name 'messages' is not defined

ignite (https://github.com/pytorch/ignite)
+ ignite/distributed/comp_models/base.py:175: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.970+dev.cb7c148c62d50edc1c70943b209b82d1e0940fd7
+ ignite/distributed/comp_models/base.py:175: : note: use --pdb to drop into pdb
- ignite/engine/deterministic.py:38: error: Class cannot subclass "BatchSampler" (has type "Any")  [misc]
- ignite/engine/deterministic.py:227: error: Unused "type: ignore" comment
- ignite/engine/deterministic.py:254: error: Unused "type: ignore" comment
- ignite/engine/deterministic.py:259: error: Unused "type: ignore" comment
- ignite/distributed/auto.py:282: error: Class cannot subclass "DistributedSampler" (has type "Any")  [misc]
- ignite/distributed/auto.py:308: error: Unused "type: ignore" comment
- ignite/distributed/auto.py:351: error: Class cannot subclass "Optimizer" (has type "Any")  [misc]
- ignite/distributed/auto.py:353: error: Unused "type: ignore" comment
- ignite/metrics/precision.py:50: error: Unused "type: ignore" comment
- ignite/metrics/precision.py:51: error: Unused "type: ignore" comment
- ignite/metrics/metric.py:267: error: Untyped decorator makes function "iteration_completed" untyped  [misc]
- ignite/metrics/gan/utils.py:8: error: Class cannot subclass "Module" (has type "Any")  [misc]
- ignite/metrics/gan/utils.py:33: error: Untyped decorator makes function "forward" untyped  [misc]
- ignite/metrics/gan/utils.py:64: error: Cannot determine type of "_feature_extractor"  [has-type]
- ignite/engine/__init__.py:375: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:854: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:864: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:870: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:875: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:877: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:911: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:924: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:1406: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:1411: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:1413: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:1420: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:1544: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:1564: error: Unused "type: ignore" comment
- ignite/handlers/param_scheduler.py:1565: error: Unused "type: ignore" comment
- ignite/handlers/lr_finder.py:476: error: Class cannot subclass "_LRScheduler" (has type "Any")  [misc]
- ignite/handlers/lr_finder.py:498: error: Unused "type: ignore" comment
- ignite/handlers/lr_finder.py:499: error: Unused "type: ignore" comment
- ignite/contrib/handlers/wandb_logger.py:138: error: Unused "type: ignore" comment
- ignite/contrib/handlers/visdom_logger.py:188: error: Unused "type: ignore" comment
- ignite/contrib/handlers/visdom_logger.py:196: error: Unused "type: ignore" comment
- ignite/contrib/handlers/visdom_logger.py:241: error: Unused "type: ignore" comment
- ignite/contrib/handlers/tensorboard_logger.py:153: error: Unused "type: ignore" comment
+ Traceback (most recent call last):
+   File "", line 8, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 12, in console_entry
+     main(None, sys.stdout, sys.stderr)
+   File "/main.py", line 96, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 173, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 154, in build
+     result = _build(
+   File "/build.py", line 230, in _build
+     graph = dispatch(sources, manager, stdout)
+   File "/build.py", line 2729, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 3087, in process_graph
+     process_stale_scc(graph, scc, manager)
+   File "/build.py", line 3185, in process_stale_scc
+     graph[id].type_check_first_pass()
+   File "/build.py", line 2180, in type_check_first_pass
+     self.type_checker().check_first_pass()
+   File "/checker.py", line 325, in check_first_pass
+     self.accept(d)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1029, in accept
+     return visitor.visit_class_def(self)
+   File "/checker.py", line 1816, in visit_class_def
+     self.accept(defn.defs)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 748, in accept
+     return visitor.visit_func_def(self)
+   File "/checker.py", line 782, in visit_func_def
+     self._visit_func_def(defn)
+   File "/checker.py", line 786, in _visit_func_def
+     self.check_func_item(defn, name=defn.name)
+   File "/checker.py", line 848, in check_func_item
+     self.check_func_def(defn, typ, name)
+   File "/checker.py", line 1033, in check_func_def
+     self.accept(item.body)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1166, in accept
+     return visitor.visit_assignment_stmt(self)
+   File "/checker.py", line 2220, in visit_assignment_stmt
+     self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax)
+   File "/checker.py", line 2389, in check_assignment
+     rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, context=rvalue,
+   File "/checker.py", line 3307, in check_simple_assignment
+     rvalue_type = self.expr_checker.accept(rvalue, lvalue_type,
+   File "/checkexpr.py", line 4012, in accept
+     typ = node.accept(self)
+   File "/nodes.py", line 1761, in accept
+     return visitor.visit_call_expr(self)
+   File "/checkexpr.py", line 305, in visit_call_expr
+     return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
+   File "/checkexpr.py", line 383, in visit_call_expr_inner
+     ret_type = self.check_call_expr_with_callee_type(callee_type, e, fullname,
+   File "/checkexpr.py", line 892, in check_call_expr_with_callee_type
+     ret_type, callee_type = self.check_call(
+   File "/checkexpr.py", line 957, in check_call
+     return self.check_callable_call(callee, args, arg_kinds, context, arg_names,
+   File "/checkexpr.py", line 1067, in check_callable_call
+     self.check_argument_count(callee, arg_types, arg_kinds,
+   File "/checkexpr.py", line 1417, in check_argument_count
+     ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments(
+   File "/checkexpr.py", line 1499, in check_for_extra_actual_arguments
+     if messages and supertype.args:
+ NameError: name 'messages' is not defined

bidict (https://github.com/jab/bidict)
+ bidict/_base.py:453: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.970+dev.cb7c148c62d50edc1c70943b209b82d1e0940fd7
+ bidict/_base.py:453: : note: use --pdb to drop into pdb
+ Traceback (most recent call last):
+   File "", line 8, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 12, in console_entry
+     main(None, sys.stdout, sys.stderr)
+   File "/main.py", line 96, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 173, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 154, in build
+     result = _build(
+   File "/build.py", line 230, in _build
+     graph = dispatch(sources, manager, stdout)
+   File "/build.py", line 2729, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 3087, in process_graph
+     process_stale_scc(graph, scc, manager)
+   File "/build.py", line 3185, in process_stale_scc
+     graph[id].type_check_first_pass()
+   File "/build.py", line 2180, in type_check_first_pass
+     self.type_checker().check_first_pass()
+   File "/checker.py", line 325, in check_first_pass
+     self.accept(d)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1029, in accept
+     return visitor.visit_class_def(self)
+   File "/checker.py", line 1816, in visit_class_def
+     self.accept(defn.defs)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 748, in accept
+     return visitor.visit_func_def(self)
+   File "/checker.py", line 782, in visit_func_def
+     self._visit_func_def(defn)
+   File "/checker.py", line 786, in _visit_func_def
+     self.check_func_item(defn, name=defn.name)
+   File "/checker.py", line 848, in check_func_item
+     self.check_func_def(defn, typ, name)
+   File "/checker.py", line 1033, in check_func_def
+     self.accept(item.body)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1244, in accept
+     return visitor.visit_for_stmt(self)
+   File "/checker.py", line 3895, in visit_for_stmt
+     iterator_type, item_type = self.analyze_iterable_item_type(s.expr)
+   File "/checker.py", line 3914, in analyze_iterable_item_type
+     iterable = get_proper_type(echk.accept(expr))
+   File "/checkexpr.py", line 4012, in accept
+     typ = node.accept(self)
+   File "/nodes.py", line 1761, in accept
+     return visitor.visit_call_expr(self)
+   File "/checkexpr.py", line 305, in visit_call_expr
+     return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
+   File "/checkexpr.py", line 383, in visit_call_expr_inner
+     ret_type = self.check_call_expr_with_callee_type(callee_type, e, fullname,
+   File "/checkexpr.py", line 892, in check_call_expr_with_callee_type
+     ret_type, callee_type = self.check_call(
+   File "/checkexpr.py", line 957, in check_call
+     return self.check_callable_call(callee, args, arg_kinds, context, arg_names,
+   File "/checkexpr.py", line 1067, in check_callable_call
+     self.check_argument_count(callee, arg_types, arg_kinds,
+   File "/checkexpr.py", line 1417, in check_argument_count
+     ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments(
+   File "/checkexpr.py", line 1499, in check_for_extra_actual_arguments
+     if messages and supertype.args:
+ NameError: name 'messages' is not defined

python-sop (https://gitlab.com/dkg/python-sop)
+ sop/__init__.py:456: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.970+dev.cb7c148c62d50edc1c70943b209b82d1e0940fd7
+ sop/__init__.py:456: : note: use --pdb to drop into pdb
+ Traceback (most recent call last):
+   File "", line 8, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 12, in console_entry
+     main(None, sys.stdout, sys.stderr)
+   File "/main.py", line 96, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 173, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 154, in build
+     result = _build(
+   File "/build.py", line 230, in _build
+     graph = dispatch(sources, manager, stdout)
+   File "/build.py", line 2729, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 3087, in process_graph
+     process_stale_scc(graph, scc, manager)
+   File "/build.py", line 3185, in process_stale_scc
+     graph[id].type_check_first_pass()
+   File "/build.py", line 2180, in type_check_first_pass
+     self.type_checker().check_first_pass()
+   File "/checker.py", line 325, in check_first_pass
+     self.accept(d)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1029, in accept
+     return visitor.visit_class_def(self)
+   File "/checker.py", line 1816, in visit_class_def
+     self.accept(defn.defs)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 748, in accept
+     return visitor.visit_func_def(self)
+   File "/checker.py", line 782, in visit_func_def
+     self._visit_func_def(defn)
+   File "/checker.py", line 786, in _visit_func_def
+     self.check_func_item(defn, name=defn.name)
+   File "/checker.py", line 848, in check_func_item
+     self.check_func_def(defn, typ, name)
+   File "/checker.py", line 1033, in check_func_def
+     self.accept(item.body)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1257, in accept
+     return visitor.visit_return_stmt(self)
+   File "/checker.py", line 3498, in visit_return_stmt
+     self.check_return_stmt(s)
+   File "/checker.py", line 3530, in check_return_stmt
+     typ = get_proper_type(self.expr_checker.accept(
+   File "/checkexpr.py", line 4012, in accept
+     typ = node.accept(self)
+   File "/nodes.py", line 1761, in accept
+     return visitor.visit_call_expr(self)
+   File "/checkexpr.py", line 305, in visit_call_expr
+     return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
+   File "/checkexpr.py", line 383, in visit_call_expr_inner
+     ret_type = self.check_call_expr_with_callee_type(callee_type, e, fullname,
+   File "/checkexpr.py", line 892, in check_call_expr_with_callee_type
+     ret_type, callee_type = self.check_call(
+   File "/checkexpr.py", line 957, in check_call
+     return self.check_callable_call(callee, args, arg_kinds, context, arg_names,
+   File "/checkexpr.py", line 1067, in check_callable_call
+     self.check_argument_count(callee, arg_types, arg_kinds,
+   File "/checkexpr.py", line 1417, in check_argument_count
+     ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments(
+   File "/checkexpr.py", line 1499, in check_for_extra_actual_arguments
+     if messages and supertype.args:
+ NameError: name 'messages' is not defined

pegen (https://github.com/we-like-parsers/pegen)
- src/pegen/parser.py:297: error: "Parser" has no attribute "start"  [attr-defined]
- src/pegen/python_generator.py:220: error: Need type annotation for "cleanup_statements" (hint: "cleanup_statements: List[<type>] = ...")  [var-annotated]
- src/pegen/utils.py:19: error: Argument 1 to "module_from_spec" has incompatible type "Optional[ModuleSpec]"; expected "ModuleSpec"  [arg-type]
- src/pegen/utils.py:23: error: Item "None" of "Optional[ModuleSpec]" has no attribute "loader"  [union-attr]
- src/pegen/utils.py:52: error: "Parser" has no attribute "start"  [attr-defined]
+ src/pegen/grammar.py:31: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.970+dev.cb7c148c62d50edc1c70943b209b82d1e0940fd7
+ src/pegen/grammar.py:31: : note: use --pdb to drop into pdb
+ Traceback (most recent call last):
+   File "", line 8, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 12, in console_entry
+     main(None, sys.stdout, sys.stderr)
+   File "/main.py", line 96, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 173, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 154, in build
+     result = _build(
+   File "/build.py", line 230, in _build
+     graph = dispatch(sources, manager, stdout)
+   File "/build.py", line 2729, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 3087, in process_graph
+     process_stale_scc(graph, scc, manager)
+   File "/build.py", line 3185, in process_stale_scc
+     graph[id].type_check_first_pass()
+   File "/build.py", line 2180, in type_check_first_pass
+     self.type_checker().check_first_pass()
+   File "/checker.py", line 325, in check_first_pass
+     self.accept(d)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1029, in accept
+     return visitor.visit_class_def(self)
+   File "/checker.py", line 1816, in visit_class_def
+     self.accept(defn.defs)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 748, in accept
+     return visitor.visit_func_def(self)
+   File "/checker.py", line 782, in visit_func_def
+     self._visit_func_def(defn)
+   File "/checker.py", line 786, in _visit_func_def
+     self.check_func_item(defn, name=defn.name)
+   File "/checker.py", line 848, in check_func_item
+     self.check_func_def(defn, typ, name)
+   File "/checker.py", line 1033, in check_func_def
+     self.accept(item.body)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1100, in accept
+     return visitor.visit_block(self)
+   File "/checker.py", line 2178, in visit_block
+     self.accept(s)
+   File "/checker.py", line 431, in accept
+     stmt.accept(self)
+   File "/nodes.py", line 1257, in accept
+     return visitor.visit_return_stmt(self)
+   File "/checker.py", line 3498, in visit_return_stmt
+     self.check_return_stmt(s)
+   File "/checker.py", line 3530, in check_return_stmt
+     typ = get_proper_type(self.expr_checker.accept(
+   File "/checkexpr.py", line 4006, in accept
+     typ = self.visit_call_expr(node, allow_none_return=True)
+   File "/checkexpr.py", line 305, in visit_call_expr
+     return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
+   File "/checkexpr.py", line 383, in visit_call_expr_inner
+     ret_type = self.check_call_expr_with_callee_type(callee_type, e, fullname,
+   File "/checkexpr.py", line 892, in check_call_expr_with_callee_type
+     ret_type, callee_type = self.check_call(
+   File "/checkexpr.py", line 965, in check_call
+     return self.check_union_call(callee, args, arg_kinds, arg_names, context)
+   File "/checkexpr.py", line 2080, in check_union_call
+     results = [
+   File "/checkexpr.py", line 2081, in <listcomp>
+     self.check_call(
+   File "/checkexpr.py", line 957, in check_call
+     return self.check_callable_call(callee, args, arg_kinds, context, arg_names,
+   File "/checkexpr.py", line 1067, in check_callable_call
+     self.check_argument_count(callee, arg_types, arg_kinds,
+   File "/checkexpr.py", line 1417, in check_argument_count
+     ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments(
+   File "/checkexpr.py", line 1499, in check_for_extra_actual_arguments
+     if messages and supertype.args:
+ NameError: name 'messages' is not defined

SinbadCogs (https://github.com/mikeshardmind/SinbadCogs)
- Warning: unused section(s) in mypy.ini: [mypy-websockets.*], [mypy-numba.*], [mypy-numpy.*]
+ ./rolemanagement/converters.py:46: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 0.970+dev.cb7c148c62d50edc1c70943b209b82d1e0940fd7
+ ./rolemanagement/converters.py:46: : note: use --pdb to drop into pdb
- devtools/runner.py:26: error: Unused "type: ignore" comment
- scheduler/message.py:87: error: Unused "type: ignore" comment
- schedul

... (truncated 533294 chars) ...

Copy link

coderabbitai bot commented Apr 25, 2025

Walkthrough

The changes enhance the handling of extra actual arguments in function calls within the type checker, specifically improving the analysis of mapping types used as **kwargs. The core logic in mypy/checkexpr.py is updated to better recognize when mappings with literal string keys are passed as keyword arguments, providing more precise error reporting. The method for validating types used as **kwargs is also refined. In the test suite, all type comments are converted to inline annotations, and a new test case is added to verify the improved handling of mappings with literal keys as keyword arguments.

Changes

File(s) Change Summary
mypy/checkexpr.py Enhanced logic for detecting extra actual arguments when mapping types are passed as keyword arguments. Improved validation in is_valid_keyword_var_arg to better handle Mapping[str, Any] and mappings with literal string keys. Added imports for Iterable and try_getting_str_literals_from_type.
test-data/unit/check-kwargs.test Converted all type comments to inline variable and function annotations. Updated function and method signatures to use inline type annotations. Added a new test case, testPassingMappingLiteralsForKeywordVarArg, to test the handling of mappings with literal keys as keyword arguments. No changes to test logic or error expectations.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant TypeChecker
    participant Callee

    Caller->>TypeChecker: Call function with mapping as **kwargs
    TypeChecker->>TypeChecker: Check if mapping is subtype of Mapping[str, Any]
    alt Mapping has literal string keys
        TypeChecker->>TypeChecker: Extract literal keys
        alt Callee does not accept **kwargs
            TypeChecker->>Caller: Report unexpected keyword argument error
        else Callee accepts positional args but not all keys present
            TypeChecker->>Caller: Report too few arguments error
        end
    else Not a valid mapping for **kwargs
        TypeChecker->>Caller: Report invalid **kwargs type error
    end
    TypeChecker->>Callee: Pass validated arguments
Loading

Poem

In the warren of types, a mapping hops by,
With stringy little keys, it catches the checker’s eye.
No more will stray arguments slip through the gate,
For now, literal keys seal their fate.
Tests are adorned with annotations so neat—
A bunny’s delight, code modern and sweet!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @arvi18, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

Summary of Changes

This pull request, authored by arvi18, addresses issue python#10023 by enhancing mypy's type checking for keyword arguments, particularly when dealing with typing.Mapping types and literal types as keys. The changes modify mypy/checkexpr.py to improve the validation of **kwargs and add new test cases to test-data/unit/check-kwargs.test to cover these scenarios. The goal is to provide more accurate error messages when keyword arguments don't match the expected types, especially when literal types are involved.

Highlights

  • Keyword Argument Validation: The pull request improves the validation of keyword arguments, especially when typing.Mapping types are used with literal types as keys. This ensures that incorrect keyword arguments are flagged with appropriate error messages.
  • Typing Mapping: The code now checks if a kwarg is a Mapping type and if the list of strings extracted from the mapping key type is not empty.
  • New Test Cases: Several new test cases have been added to test-data/unit/check-kwargs.test to cover various scenarios involving keyword arguments, including those with dynamically typed callables, function objects, and literal types.

Changelog

Click here to see the changelog
  • mypy/checkexpr.py
    • Added Iterable to the imports from the typing module (line 7).
    • Imported try_getting_str_literals_from_type (line 72).
    • Added a check within check_for_extra_actual_arguments to handle cases where the actual type is an instance of typing.Mapping (lines 1493-1512). This check extracts string literals from the mapping key type and generates appropriate error messages if the arguments don't match.
    • Modified is_valid_keyword_var_arg to improve the validation of types used as **kwargs (lines 4047-4068). This includes checking for subtypes of typing.Mapping and handling empty dictionaries passed as **kwargs.
  • test-data/unit/check-kwargs.test
    • Modified existing test cases to use explicit type annotations instead of inline comments (e.g., f: Any = None instead of f = None # type: Any).
    • Added new test cases for passing mapping literals as keyword arguments, including scenarios with Literal types and incorrect key types (lines 350-379).
    • Added test cases to cover scenarios with varargs and kwargs, as well as comment signatures (lines 428-441).
    • Added test cases for passing keyword varargs to varargs only functions (lines 428-434).
    • Added test cases for passing keyword varargs to non-varargs functions (lines 393-401).
    • Added test cases for both kinds of varargs (lines 404-411).
    • Added test cases for keyword arguments and comment signatures (lines 436-458).
    • Added test cases for passing mapping literals for keyword vararg (lines 350-379).
    • Added test cases for passing keyword varargs to varargs only function (lines 428-434).
    • Added test cases for passing keyword varargs to non-varargs function (lines 393-401).
    • Added test cases for both kinds of varargs (lines 404-411).
    • Added test cases for keyword arguments and comment signatures (lines 436-458).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.


A map of strings,
Passed as keyword arguments,
Mypy checks with care.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces checks to ensure that keyword arguments passed to functions are valid, especially when dealing with typing.Mapping types and literal types. The changes look good overall, but there are a few areas where improvements can be made to enhance clarity and correctness.

Summary of Findings

  • Missing review comments for medium severity issues: There were several medium severity issues identified, but no review comments were provided for them as instructed. Please ensure that review comments are added for all medium severity issues.
  • Potential infinite recursion: The code introduces a new function try_getting_str_literals_from_type which calls map_instance_to_supertype. It's crucial to ensure that this combination doesn't lead to infinite recursion, especially with complex type hierarchies. The code should be reviewed for potential edge cases that could trigger this.
  • Inconsistent error message: In check_for_extra_actual_arguments, the code generates an 'unexpected keyword argument' error. However, the logic for handling this error seems a bit convoluted. Consider simplifying the error reporting logic to make it more readable and maintainable.

Merge Readiness

The pull request introduces important checks for keyword arguments and literal types. However, there are some medium and high severity issues that need to be addressed before merging. I recommend addressing these issues before merging, and also having others review and approve this code before merging. I am unable to directly approve this pull request.

Comment on lines +4057 to +4059
is_subtype(typ, self.chk.named_type('typing.Mapping')) and
try_getting_str_literals_from_type(map_instance_to_supertype(
typ, mapping_type.type).args[0]) is not None) or

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Consider adding a check to ensure that typ is not None before calling map_instance_to_supertype. This would prevent a potential AttributeError if typ is unexpectedly None.

            (isinstance(typ, Instance) and typ is not None and
                is_subtype(typ, self.chk.named_type('typing.Mapping')) and
                try_getting_str_literals_from_type(map_instance_to_supertype(
                    typ, mapping_type.type).args[0]) is not None) or

Comment on lines +1505 to +1511
elif (args and nodes.ARG_POS in callee.arg_kinds and
not all(arg in callee.arg_names for arg in args) and
isinstance(actual_names, Iterable)):
act_names = [name for name, kind in
zip(iter(actual_names), actual_kinds)
if kind != nodes.ARG_STAR2]
messages.too_few_arguments(callee, context, act_names)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block seems to duplicate the logic from lines 1501-1504. It might be beneficial to refactor this into a single, more general block to reduce redundancy and improve maintainability.

Comment on lines +4056 to +4059
(isinstance(typ, Instance) and
is_subtype(typ, self.chk.named_type('typing.Mapping')) and
try_getting_str_literals_from_type(map_instance_to_supertype(
typ, mapping_type.type).args[0]) is not None) or

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This condition checks if typ is an instance and a subtype of typing.Mapping, and then calls try_getting_str_literals_from_type. It might be beneficial to add a comment explaining why this specific check is necessary and what kind of types it's intended to handle.

Comment on lines +364 to +366
u: Mapping[Literal['c'], int] = {'b':2} \
# E: Dict entry 0 has incompatible type "Literal['b']": "int"; expected "Literal['c']": "int"
f(**u)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It seems like the type annotation for d is not being used, and mypy is inferring the type as Dict[str, Any]. Consider adding a test case that specifically checks the interaction of Any and Dict with keyword arguments.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 380cb8d and 7c9090a.

📒 Files selected for processing (2)
  • mypy/checkexpr.py (4 hunks)
  • test-data/unit/check-kwargs.test (13 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
mypy/checkexpr.py

1499-1499: Undefined name messages

(F821)


1502-1502: Undefined name messages

(F821)


1511-1511: Undefined name messages

(F821)

🔇 Additional comments (4)
test-data/unit/check-kwargs.test (4)

138-138: Good modernization of type annotations.

Updated from comment-style type hints to inline variable annotations, which follows modern Python typing practices.


145-145: Consistent modernization of type annotations throughout the file.

All type annotations have been updated from comment-style type hints to inline variable annotations, improving code readability and maintainability. This follows modern Python typing best practices and makes the type information more explicit.

Also applies to: 215-216, 240-240, 265-267, 274-276, 296-296, 299-299, 332-334, 345-345, 385-385, 396-396, 398-398, 407-408, 419-420, 431-431, 519-519, 523-523


438-438: Function signature annotations improved.

Function signatures have been updated from comment-style type hints to inline parameter and return type annotations, which is the preferred modern approach.

Also applies to: 447-447, 455-455


350-379: Well-designed test case for mapping literals as keyword arguments.

The new test case testPassingMappingLiteralsForKeywordVarArg thoroughly tests:

  1. Detection of non-string literal keys (triggering appropriate errors)
  2. Support for string literal keys in mappings
  3. Validation of literal type mismatches
  4. Handling of multiple literal key options
  5. Error reporting for unexpected or missing arguments

This implementation aligns with the PR objective to improve handling of literals passed as keyword arguments and covers both positive and negative test cases.

Comment on lines +1493 to +1513
elif (isinstance(actual_type, Instance) and
actual_type.type.has_base('typing.Mapping')):
any_type = AnyType(TypeOfAny.special_form)
mapping_info = self.chk.named_generic_type('typing.Mapping',
[any_type, any_type]).type
supertype = map_instance_to_supertype(actual_type, mapping_info)
if messages and supertype.args:
args = try_getting_str_literals_from_type(supertype.args[0])
if args and nodes.ARG_STAR2 not in callee.arg_kinds:
messages.unexpected_keyword_argument(
callee, args[0], supertype.args[0], context)
is_unexpected_arg_error = True
elif (args and nodes.ARG_POS in callee.arg_kinds and
not all(arg in callee.arg_names for arg in args) and
isinstance(actual_names, Iterable)):
act_names = [name for name, kind in
zip(iter(actual_names), actual_kinds)
if kind != nodes.ARG_STAR2]
messages.too_few_arguments(callee, context, act_names)
ok = False

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

messages is undefined – will raise NameError at runtime

The new branch inside check_for_extra_actual_arguments() invokes messages.* methods, but the local name messages is never defined in this scope (nor passed as a parameter).
During real execution this path will therefore crash with:

NameError: name 'messages' is not defined

Use the existing MessageBuilder available as self.msg (or add a parameter) to avoid the failure:

-                    if messages and supertype.args:
+                    if supertype.args:
                         args = try_getting_str_literals_from_type(supertype.args[0])
-                        if args and nodes.ARG_STAR2 not in callee.arg_kinds:
-                            messages.unexpected_keyword_argument(
+                        if args and nodes.ARG_STAR2 not in callee.arg_kinds:
+                            self.msg.unexpected_keyword_argument(
                                 callee, args[0], supertype.args[0], context)
                             is_unexpected_arg_error = True
-                        elif (args and nodes.ARG_POS in callee.arg_kinds and
+                        elif (args and nodes.ARG_POS in callee.arg_kinds and
                                 not all(arg in callee.arg_names for arg in args) and
-                                isinstance(actual_names, Iterable)):
+                                isinstance(actual_names, Iterable)):
                             act_names = [name for name, kind in
                                          zip(iter(actual_names), actual_kinds)
                                          if kind != nodes.ARG_STAR2]
-                            messages.too_few_arguments(callee, context, act_names)
+                            self.msg.too_few_arguments(callee, context, act_names)
                         ok = False

Without this change every project exercising this branch (see primer failures in the PR description) will encounter internal errors, masking the intended functionality.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
elif (isinstance(actual_type, Instance) and
actual_type.type.has_base('typing.Mapping')):
any_type = AnyType(TypeOfAny.special_form)
mapping_info = self.chk.named_generic_type('typing.Mapping',
[any_type, any_type]).type
supertype = map_instance_to_supertype(actual_type, mapping_info)
if messages and supertype.args:
args = try_getting_str_literals_from_type(supertype.args[0])
if args and nodes.ARG_STAR2 not in callee.arg_kinds:
messages.unexpected_keyword_argument(
callee, args[0], supertype.args[0], context)
is_unexpected_arg_error = True
elif (args and nodes.ARG_POS in callee.arg_kinds and
not all(arg in callee.arg_names for arg in args) and
isinstance(actual_names, Iterable)):
act_names = [name for name, kind in
zip(iter(actual_names), actual_kinds)
if kind != nodes.ARG_STAR2]
messages.too_few_arguments(callee, context, act_names)
ok = False
elif (isinstance(actual_type, Instance) and
actual_type.type.has_base('typing.Mapping')):
any_type = AnyType(TypeOfAny.special_form)
mapping_info = self.chk.named_generic_type('typing.Mapping',
[any_type, any_type]).type
supertype = map_instance_to_supertype(actual_type, mapping_info)
if supertype.args:
args = try_getting_str_literals_from_type(supertype.args[0])
if args and nodes.ARG_STAR2 not in callee.arg_kinds:
self.msg.unexpected_keyword_argument(
callee, args[0], supertype.args[0], context)
is_unexpected_arg_error = True
elif (args and nodes.ARG_POS in callee.arg_kinds and
not all(arg in callee.arg_names for arg in args) and
isinstance(actual_names, Iterable)):
act_names = [name for name, kind in
zip(iter(actual_names), actual_kinds)
if kind != nodes.ARG_STAR2]
self.msg.too_few_arguments(callee, context, act_names)
ok = False
🧰 Tools
🪛 Ruff (0.8.2)

1499-1499: Undefined name messages

(F821)


1502-1502: Undefined name messages

(F821)


1511-1511: Undefined name messages

(F821)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mypy incorrectly warns when passing literals as kwarg keywords
3 participants