python / python/mypy

dmypy Suggest assertion error in `node_type_from_base`

Offen
#16,708 6 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

crash topic-daemon
Vorherrschende Sprache
Python
Sterne
20.6k
Forks
3.3k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Crash Report

Mypy Daemon crashed attempting to generate type suggestions for __reduce__ function in class with nonstandard __new__. (re-creating #15823)

Traceback

dmypy suggest crash.py:64: 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: 1.8.0
dmypy suggest crash.py: 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: 1.8.0
Daemon crashed!
Traceback (most recent call last):
  File "mypy/dmypy_server.py", line 236, in serve
  File "mypy/dmypy_server.py", line 285, in run_command
  File "mypy/dmypy_server.py", line 972, in cmd_suggest
  File "mypy/suggestions.py", line 267, in suggest
  File "mypy/suggestions.py", line 502, in get_suggestion
  File "mypy/suggestions.py", line 444, in find_best
  File "mypy/suggestions.py", line 675, in try_type
  File "mypy/server/update.py", line 314, in trigger
  File "mypy/server/update.py", line 881, in propagate_changes_using_dependencies
  File "mypy/server/update.py", line 1010, in reprocess_nodes
  File "mypy/semanal_main.py", line 144, in semantic_analysis_for_targets
  File "mypy/semanal_main.py", line 291, in process_top_level_function
  File "mypy/semanal_main.py", line 349, in semantic_analyze_target
  File "mypy/semanal.py", line 600, in refresh_partial
  File "mypy/semanal.py", line 6541, in accept
  File "mypy/errors.py", line 1261, in report_internal_error
  File "mypy/semanal.py", line 6539, in accept
  File "mypy/nodes.py", line 787, in accept
  File "mypy/semanal.py", line 833, in visit_func_def
  File "mypy/semanal.py", line 865, in analyze_func_def
  File "mypy/typeanal.py", line 1031, in visit_callable_type
  File "mypy/typeanal.py", line 1684, in anal_type
  File "mypy/types.py", line 2400, in accept
  File "mypy/typeanal.py", line 1133, in visit_tuple_type
  File "mypy/typeanal.py", line 1659, in anal_array
  File "mypy/typeanal.py", line 1684, in anal_type
  File "mypy/types.py", line 1970, in accept
  File "mypy/typeanal.py", line 980, in visit_callable_type
  File "mypy/typeanal.py", line 1614, in bind_function_type_variables
AssertionError

To Reproduce

dmypy suggest crash.py:

from __future__ import annotations

from collections.abc import Iterable, Callable
from typing import TYPE_CHECKING, TypeVar, Any, cast
import sys

if sys.version_info < (3, 11):
    from exceptiongroup import ExceptionGroup, BaseExceptionGroup

if TYPE_CHECKING:
    from types import TracebackType
    from typing_extensions import Self

NonBaseMultiError = ExceptionGroup


class MultiError(BaseExceptionGroup[BaseException]):
    def __init__(
        self, exceptions: list[BaseException], *, _collapse: bool = True
    ) -> None:
        self.collapse = _collapse

        # Avoid double initialization when _collapse is True and exceptions[0] returned
        # by __new__() happens to be a MultiError and subsequently __init__() is called.
        if _collapse and getattr(self, "exceptions", None) is not None:
            # This exception was already initialized.
            return

        super().__init__("multiple tasks failed", exceptions)

    def __new__(
        cls, exceptions: Iterable[BaseException], *, _collapse: bool = True
    ) -> MultiError | NonBaseMultiError[Exception] | Self:
        exceptions_union: list[BaseException | Exception] = list(exceptions)
        for exc in exceptions_union:
            if not isinstance(exc, BaseException):
                raise TypeError(f"Expected an exception object, not {exc!r}")
        class_sub: type[Self | NonBaseMultiError[Exception]] = cls
        if _collapse and len(exceptions_union) == 1:
            # If this lone object happens to itself be a MultiError, then
            # Python will implicitly call our __init__ on it again.  See
            # special handling in __init__.
            single = exceptions_union[0]
            assert isinstance(single, MultiError)
            return single
        else:
            # The base class __new__() implicitly invokes our __init__, which
            # is what we want.
            #
            # In an earlier version of the code, we didn't define __init__ and
            # simply set the `exceptions` attribute directly on the new object.
            # However, linters expect attributes to be initialized in __init__.
            if all(isinstance(exc, Exception) for exc in exceptions_union):
                exceptions_arg_one = cast("list[Exception]", exceptions_union)

                return super().__new__(
                    NonBaseMultiError, "multiple tasks failed", exceptions_arg_one
                )
            else:
                exceptions_arg_two = cast("list[BaseException]", exceptions_union)

                return super().__new__(cls, "multiple tasks failed", exceptions_arg_two)

    def __reduce__(self):
        return (
            self.__new__,
            (self.__class__, list(self.exceptions)),
            {"collapse": self.collapse},
        )

Run:

dmypy --status-file="<user path>/dmypy.json" run --timeout=1800 --log-file="<user path>/log.txt" --export-types "<user path>/dmypy suggest crash.py" -- --no-implicit-reexport --disallow-untyped-defs --show-error-codes --strict --warn-redundant-casts --cache-dir="<user path>" --show-error-end --no-color-output --no-error-summary --show-column-numbers --warn-unreachable --disallow-untyped-calls --warn-unused-ignores --cache-fine-grained --soft-error-limit=-1 --show-traceback --hide-error-context --show-absolute-path
dmypy --status-file="<user path>/dmypy.json" suggest "<user path>/dmypy suggest crash.py:65"

Your Environment

  • Mypy version used: dmypy 1.8.0
  • Mypy command-line flags: See above
  • Mypy configuration options from mypy.ini (and other config files): See above
  • Python version used: Python 3.12.0 (main, Oct 4 2023, 06:27:34) [GCC 13.2.0] on linux
  • Operating system and version: 64 bit Linux Ubuntu Budgie 10.8 (Ubuntu 23.10) Linux 6.5.0-14-generic

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne mit der im Issue beschriebenen Reproduktion von dmypy suggest und verfolge dann den gemeldeten Aufrufpfad durch mypy/suggestions.py und mypy/typeanal.py, insbesondere bind_function_type_variables. Bestätige das Verhalten mit der bereitgestellten crash.py und ermittle, wie die Assertion während der Typanalyse erreicht wird. Erledigt ist die Aufgabe, wenn dmypy suggest diesen Fall ohne einen INTERNAL ERROR oder einen Absturz des Daemons verarbeitet.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
devtools
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Ruhig
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
55/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.