Skip to content
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

[INTERNAL ERROR] IndexError: list index out of range #13903

Closed
supersergiy opened this issue Oct 15, 2022 · 3 comments · Fixed by #14118
Closed

[INTERNAL ERROR] IndexError: list index out of range #13903

supersergiy opened this issue Oct 15, 2022 · 3 comments · Fixed by #14118
Labels
crash topic-paramspec PEP 612, ParamSpec, Concatenate

Comments

@supersergiy
Copy link

supersergiy commented Oct 15, 2022

Crash Report

An internal error, possibly caused by some interaction with pytest.

To Reproduce

from __future__ import annotations

from typing import Any, Callable, Generic
from typing_extensions import ParamSpec
import pytest

P = ParamSpec("P")

class C(Generic[P]):
    def __init__(
        self,
        fn: Callable[P, Any],
        *args: P.args,
        **kwargs: P.kwargs,
    ):
        pass

def foo(x):
    return x

# No Error
c: C = C(
    fn=foo,
    x=1
)

# Internal Error
# Using a dummy decorator instead of `pytest.mark.parametrize`resolves the error.
@pytest.mark.parametrize(
    "c",
    [
        [
            C(
                fn=foo,
                x=1
            )
        ],
    ],
)
def dummy_test(
    c: C,
):
    pass

Mypy playground doesn't have pytest installed, and it's not obvious to me how to reproduce without pytest.

Traceback
version: 0.990+dev.abc9d155ffbd9ea160eec0b57c450cdf7e53ce39:

Traceback (most recent call last):
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/bin/mypy", line 8, in <module>
    sys.exit(console_entry())
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/__main__.py", line 15, in console_entry
    main()
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/main.py", line 95, in main
    res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/main.py", line 174, in run_build
    res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/build.py", line 193, in build
    result = _build(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/build.py", line 276, in _build
    graph = dispatch(sources, manager, stdout)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/build.py", line 2903, in dispatch
    process_graph(graph, manager)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/build.py", line 3287, in process_graph
    process_stale_scc(graph, scc, manager)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/build.py", line 3388, in process_stale_scc
    graph[id].type_check_first_pass()
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/build.py", line 2309, in type_check_first_pass
    self.type_checker().check_first_pass()
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checker.py", line 464, in check_first_pass
    self.accept(d)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checker.py", line 572, in accept
    stmt.accept(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/nodes.py", line 920, in accept
    return visitor.visit_decorator(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checker.py", line 4505, in visit_decorator
    dec = self.expr_checker.accept(d)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 4662, in accept
    typ = node.accept(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/nodes.py", line 1838, in accept
    return visitor.visit_call_expr(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 409, in visit_call_expr
    return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 529, in visit_call_expr_inner
    ret_type = self.check_call_expr_with_callee_type(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1180, in check_call_expr_with_callee_type
    ret_type, callee_type = self.check_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1299, in check_call
    result = self.check_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1263, in check_call
    return self.check_callable_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1431, in check_callable_call
    arg_types = self.infer_arg_types_in_context(callee, args, arg_kinds, formal_to_actual)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1597, in infer_arg_types_in_context
    res[ai] = self.accept(args[ai], callee.arg_types[i])
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 4662, in accept
    typ = node.accept(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/nodes.py", line 2118, in accept
    return visitor.visit_list_expr(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 3900, in visit_list_expr
    return self.check_lst_expr(e, "builtins.list", "<list>")
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 3959, in check_lst_expr
    out = self.check_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1263, in check_call
    return self.check_callable_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1403, in check_callable_call
    callee = self.infer_function_type_arguments(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1702, in infer_function_type_arguments
    arg_types = self.infer_arg_types_in_context(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1597, in infer_arg_types_in_context
    res[ai] = self.accept(args[ai], callee.arg_types[i])
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 4662, in accept
    typ = node.accept(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/nodes.py", line 2118, in accept
    return visitor.visit_list_expr(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 3900, in visit_list_expr
    return self.check_lst_expr(e, "builtins.list", "<list>")
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 3959, in check_lst_expr
    out = self.check_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1263, in check_call
    return self.check_callable_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1431, in check_callable_call
    arg_types = self.infer_arg_types_in_context(callee, args, arg_kinds, formal_to_actual)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1597, in infer_arg_types_in_context
    res[ai] = self.accept(args[ai], callee.arg_types[i])
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 4662, in accept
    typ = node.accept(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/nodes.py", line 1838, in accept
    return visitor.visit_call_expr(self)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 409, in visit_call_expr
    return self.visit_call_expr_inner(e, allow_none_return=allow_none_return)
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 529, in visit_call_expr_inner
    ret_type = self.check_call_expr_with_callee_type(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1180, in check_call_expr_with_callee_type
    ret_type, callee_type = self.check_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1263, in check_call
    return self.check_callable_call(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1403, in check_callable_call
    callee = self.infer_function_type_arguments(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1702, in infer_function_type_arguments
    arg_types = self.infer_arg_types_in_context(
  File "/mnt/disks/sergiy-x0/envs/zetta-x3/lib/python3.8/site-packages/mypy/checkexpr.py", line 1596, in infer_arg_types_in_context
    with self.allow_unions(callee.arg_types[i]):
IndexError: list index out of range

version: 0.971:

Traceback (most recent call last):
  File "mypy/checkexpr.py", line 3999, in accept
  File "mypy/nodes.py", line 1771, in accept
  File "mypy/checkexpr.py", line 306, in visit_call_expr
  File "mypy/checkexpr.py", line 384, in visit_call_expr_inner
  File "mypy/checkexpr.py", line 893, in check_call_expr_with_callee_type
  File "mypy/checkexpr.py", line 958, in check_call
  File "mypy/checkexpr.py", line 1043, in check_callable_call
  File "mypy/checkexpr.py", line 1264, in infer_function_type_arguments
  File "mypy/checkexpr.py", line 1164, in infer_arg_types_in_context
IndexError: list index out of range

Your Environment

  • Mypy version used: 0.971, 0.990+dev.abc9d155ffbd9ea160eec0b57c450cdf7e53ce39
  • Mypy command-line flags: --show-traceback
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.8.14
@supersergiy supersergiy added the bug mypy got something wrong label Oct 15, 2022
@AlexWaygood AlexWaygood added crash and removed bug mypy got something wrong labels Oct 15, 2022
@nnrepos
Copy link

nnrepos commented Oct 17, 2022

i tried figuring this out but i'm not familiar enough with mypy to understand the issue.
that's what i've found so far:
in the normal definition c: C = C(...), we call check_callable_call just once, and therefore we don't convert the paramspec arguments (e.g. P`1) into the actual arguments (e.g. x: Any) in callee.arg_types.
on the other hand, in the parameterized definition parametrize(...C(...)), we call check_callable_call twice, and on the 2nd iteration it converts the paramspec arguments into actual arguments.
this is why we get:

(Pdb) p callee.arg_types
[def (*P.args, **P.kwargs) -> Any, P.args`1, P.kwargs`1]

during the normal definition (c: C = C(...)), and:

(Pdb) p callee.arg_types
[def (x: Any) -> Any, Any]

during the parameterized definition (parametrize(...C(...))).
then, we get index out of range inside infer_arg_types_in_context, because we have formal_to_actual == [[0], [], [1]], which is longer than [def (x: Any) -> Any, Any]. even though they should be the same length.

this is where i got stuck :/

@hauntsaninja
Copy link
Collaborator

@nnrepos Thanks for investigating! I'll take a look and see if I can provide any help.

For what it's worth, here's a self contained and somewhat minimised repro:

from typing import Callable, Generic, ParamSpec

def foo(x):
    return x

P = ParamSpec("P")

class C(Generic[P]):
    def __init__(self, fn: Callable[P, int], *args: P.args, **kwargs: P.kwargs): ...

class MarkDecorator:
    def __call__(self, *args: object, **kwargs: object) -> "MarkDecorator": ...

class MarkGenerator:
    parametrize: MarkDecorator
    
pytest_mark = MarkGenerator()

@pytest_mark.parametrize([C(fn=foo, x=1)])
def dummy_test(): ...

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Oct 19, 2022

Okay, actually here's a simpler one:

from typing import Callable, Generic, ParamSpec

def foo(x): ...
def bar(x: object): ...

P = ParamSpec("P")

class C(Generic[P]):
    def __init__(self, fn: Callable[P, int], *args: P.args, **kwargs: P.kwargs): ...

bar([C(fn=foo, x=1)])

@hauntsaninja hauntsaninja added the topic-paramspec PEP 612, ParamSpec, Concatenate label Oct 19, 2022
ilevkivskyi added a commit that referenced this issue Nov 17, 2022
Fixes #13903 

The fix is straightforward, the formal to actual map needs to be
refreshed twice, after both using external _and_ internal type context.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crash topic-paramspec PEP 612, ParamSpec, Concatenate
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants