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

Cannot resolve recursive/cyclic definitions (v0.991, Python 3.8/3.9) #14219

Closed
bartfeenstra opened this issue Nov 29, 2022 · 3 comments
Closed
Labels
bug mypy got something wrong

Comments

@bartfeenstra
Copy link

Bug Report

Mypy fails to check recursive/cyclic type aliases, even with #731 being part of v0.991. However, this happens on Python 3.8 and 3.9 only (and possibly below), but not on 3.10 or 3.11.
Example:

RecursiveTypeAlias: TypeAlias = Union[bool, int, float, str, Sequence['RecursiveTypeAlias']]

for which mypy raises error: Cannot resolve name "RecursiveTypeAlias" (possible cyclic definition) [misc].

To Reproduce

Alternatively, clone https://github.com/bartfeenstra/mypy-type-alias and run tox.

Expected Behavior
Mypy not raising any errors for recursive/cyclig type aliases and correctly parsing them as it does on Python 3.10 and 3.11.

Actual Behavior
Mypy fails on recursive/cyclic type aliases with the error error: Cannot resolve name "RecursiveTypeAlias" (possible cyclic definition) [misc].

@bartfeenstra bartfeenstra added the bug mypy got something wrong label Nov 29, 2022
@JukkaL
Copy link
Collaborator

JukkaL commented Nov 29, 2022

On a recent master this works on Python 3.8:

from typing_extensions import TypeAlias
from typing import Union, Sequence

RecursiveTypeAlias: TypeAlias = Union[bool, int, float, str, Sequence['RecursiveTypeAlias']]

The issue is with imports. Mypy doesn't support this on Python 3.8:

try:
    from typing import TypeAlias  # type: ignore
except ImportError:
    from typing_extensions import TypeAlias

As a workaround, this should work:

try:
    from typing_extensions import TypeAlias
except ImportError:
    from typing import TypeAlias  # type: ignore

@bartfeenstra
Copy link
Author

I can confirm this works with mypy commit 3a3cf412b278ee7bf710742b168beed41c1c02f2 in combination with the imports turned around. Thanks!

@ssbarnea
Copy link
Contributor

ssbarnea commented Apr 30, 2023

For reference, this is the full solution that seems to currently work with py39+ and also pass mypy and ruff:

from __future__ import annotations
from collections.abc import Mapping, Sequence
from typing import Any, Union

try:
    from typing_extensions import TypeAlias
except ImportError:
    from typing import TypeAlias  # type: ignore[no-redef,attr-defined]

JSON: TypeAlias = Union[dict[str, "JSON"], list["JSON"], str, int, float, bool, None]
JSON_ro: TypeAlias = Union[
    Mapping[str, "JSON_ro"],
    Sequence["JSON_ro"],
    str,
    int,
    float,
    bool,
    None,
]

Note the awkward import fallback is needed because doing it the opposite does not work py39 or py38 either, only importing first from typing_extensions seems to be working well.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

4 participants