python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.18k stars 2.77k forks source link

IndexError when using Unpack #17518

Closed qi55wyqu closed 1 month ago

qi55wyqu commented 1 month ago

Crash Report

Mypy crashes when using Unpack and TypedDict.

Traceback

Traceback (most recent call last):
  File ".../bin/mypy", line 8, in <module>
    sys.exit(console_entry())
  File ".../mypy/mypy/__main__.py", line 15, in console_entry
    main()
  File ".../mypy/mypy/main.py", line 100, in main
    res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
  File ".../mypy/mypy/main.py", line 182, in run_build
    res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
  File ".../mypy/mypy/build.py", line 192, in build
    result = _build(
  File ".../mypy/mypy/build.py", line 266, in _build
    graph = dispatch(sources, manager, stdout)
  File ".../mypy/mypy/build.py", line 2942, in dispatch
    process_graph(graph, manager)
  File ".../mypy/mypy/build.py", line 3340, in process_graph
    process_stale_scc(graph, scc, manager)
  File ".../mypy/mypy/build.py", line 3441, in process_stale_scc
    graph[id].type_check_first_pass()
  File ".../mypy/mypy/build.py", line 2310, in type_check_first_pass
    self.type_checker().check_first_pass()
  File ".../mypy/mypy/checker.py", line 481, in check_first_pass
    self.accept(d)
  File ".../mypy/mypy/checker.py", line 589, in accept
    stmt.accept(self)
  File ".../mypy/mypy/nodes.py", line 1142, in accept
    return visitor.visit_class_def(self)
  File ".../mypy/mypy/checker.py", line 2340, in visit_class_def
    self.accept(defn.defs)
  File ".../mypy/mypy/checker.py", line 589, in accept
    stmt.accept(self)
  File ".../mypy/mypy/nodes.py", line 1223, in accept
    return visitor.visit_block(self)
  File ".../mypy/mypy/checker.py", line 2802, in visit_block
    self.accept(s)
  File ".../mypy/mypy/checker.py", line 589, in accept
    stmt.accept(self)
  File ".../mypy/mypy/nodes.py", line 1310, in accept
    return visitor.visit_assignment_stmt(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/checker.py", line 2850, in visit_assignment_stmt
    self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax)
  File ".../mypy/mypy/checker.py", line 2944, in check_assignment
    if self.check_compatibility_all_supers(lvalue, lvalue_type, rvalue):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/checker.py", line 3181, in check_compatibility_all_supers
    if not self.check_compatibility_super(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/checker.py", line 3248, in check_compatibility_super
    ok = self.check_subtype(
         ^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/checker.py", line 6705, in check_subtype
    if is_subtype(subtype, supertype, options=self.options):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/subtypes.py", line 179, in is_subtype
    return _is_subtype(left, right, subtype_context, proper_subtype=False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/subtypes.py", line 352, in _is_subtype
    return left.accept(SubtypeVisitor(orig_right, subtype_context, proper_subtype))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/types.py", line 1984, in accept
    return visitor.visit_callable_type(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/subtypes.py", line 703, in visit_callable_type
    return is_callable_compatible(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/subtypes.py", line 1495, in is_callable_compatible
    left = left.with_unpacked_kwargs().with_normalized_var_args()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../mypy/mypy/types.py", line 2105, in with_unpacked_kwargs
    last_type = get_proper_type(self.arg_types[-1])
                                ~~~~~~~~~~~~~~^^^^
IndexError: list index out of range

To Reproduce

Unfortunately I cannot narrow down problem to a small example. The crash happens in an inherited class which does not overload init.

class Kwargs(TypedDict):
    name: str

class C:
    def __init__(self, **kwargs: Unpack[Kwargs]) -> None:
        pass

Your Environment

ilevkivskyi commented 1 month ago

Just by chance: are you missing self in either base class or in the override?

qi55wyqu commented 1 month ago

Thank you for taking a look at this!

I dont' think I'm missing self. In that case I would expect an exception at runtime but the code works fine otherwise.

svisser commented 1 month ago

The following code generates the IndexError with mypy 1.11.0 and Python 3.12.4:

from typing import Callable, TypedDict, Unpack

class Kwargs(TypedDict):
    name: str

def f(**kwargs: Kwargs) -> None:
    pass

class C:
    d: Callable[[Unpack[Kwargs]], None]

class D(C):
    d = f
ilevkivskyi commented 1 month ago

OK, interesting, so it is essentially what I guessed, but it looks like mypy is wrong, IIUC it shouldn't try to bind self in d. Probably an easy fix, but I will be busy next few days.