pyright icon indicating copy to clipboard operation
pyright copied to clipboard

Some types cannot be assigned to TypeAlias when TypeAlias is imported from anywhere other than typing

Open rpmrmartin opened this issue 2 years ago • 7 comments

Describe the bug When TypeAlias is imported from anywhere other than typing, Tuple cannot be assigned to a TypeAlias.

To Reproduce

  1. Create a module that exports TypeAlias
  2. Import TypeAlias from the module created in the previous step
  3. Create an explicit type alias of Tuple

Expected behavior No error where an alias of Tuple is created

Screenshots or Code If applicable, add screenshots or the text of the code (surrounded by triple back ticks) to help explain your problem. Compat.py

from typing import TypeAlias

__all__ = ['TypeAlias']

Repro.py

from typing import Tuple

from Compat import TypeAlias

Alias: TypeAlias = Tuple
error: Expression of type "Type[Tuple[_T_co@tuple]]" cannot be assigned to declared type "TypeAlias"
   "Type[type]" is incompatible with "Type[TypeAlias]" (reportGeneralTypeIssues)

VS Code extension or command-line Pylance extension v2022.11.20 Command-line v1.1.279

Additional context This is a simplified reproduction of an issue I am facing upgrading from Psycopg 3.0.15 to 3.1.4. In 3.1, some implicit type aliases were made explicit.

rpmrmartin avatar Nov 16 '22 15:11 rpmrmartin

Yes, this is intended. TypeAlias is a special form that needs to be understood by early stages of the static analyzer as a precursor for type evaluation. Therefore, you cannot alias it as you've done here. You need to import it directly from typing or typing_extensions if you want it to work with pyright.

erictraut avatar Nov 16 '22 16:11 erictraut

Hello,

FYI, this makes pyright not compatible with psycopg 3.

Pyright requirement (import directly from one of two magic places) is not a PEP-613 requirement and is not required by Mypy. If you need to special-case TypeAlias, you can have the analyzer recognising it just by its name, not by where it is imported from.

dvarrazzo avatar Nov 17 '22 19:11 dvarrazzo

It won't work to simply recognize it by name because a name alone is not sufficient to know the type of a symbol.

If you want psycopg to work with pyright and pylance, you'll need to make a change here.

Out of curiosity, why do you have a compatibility abstraction layer that conditionally imports from typing and typing_extensions? The typing_extensions module is already such an abstraction layer. Is there a reason not to import directly from typing_extensions? This is what other libraries do.

erictraut avatar Nov 17 '22 19:11 erictraut

Psycopg's goal is not to be compatible with pyright, no. If it happens as a side effect of pyright being compatible with Mypy I'm happy for pyright users, otherwise I don't wish to receive this additional maintenance burden without a sponsorship.

We have an abstraction layer that takes care of all the version-guarded imports - that's the reason for _compat. Because typing_extension is not needed for all Python versions (it's currently guarded as python_version < 3.11 in setup.py metadata), TypeAlias was imported from there.

As mentioned in the ticket on our side, I think that we can drop the dependency guard and import TypeAlias directly from typing_extensions, instead of from _compat. This is not a big deal and we can do it in the next bugfix release.

dvarrazzo avatar Nov 17 '22 20:11 dvarrazzo

From Gentoo perspective, I find this very bad. typing-extensions is a backport module, i.e. a module that should only be necessary on old Python implementations by design. The design of pyright now means that we'll have to be stuck with it indefinitely, even if the Python we're using doesn't need anything from it at all.,

mgorny avatar Dec 14 '22 05:12 mgorny

I hit this too with a library I maintain which is also type-checked by mypy, where this bug does not occur. Surprised to see this is intended behavior of Pyright.

Is there a reason not to import directly from typing_extensions? This is what other libraries do.

Not mine. :) My library depends only on the Python standard library, and currently supports all non-EOL Python versions, including those where TypeAlias is not in the standard library (3.7, 3.8, 3.9).

Any library in this situation should not add a runtime dependency on typing_extensions just for TypeAlias, and just to be able to use Pyright in development. Forcing all such libraries' <3.10 dependents to transitively pull in typing_extensions as a runtime dependency is bad engineering. (Remember, deferred evaluation of type hints allows such libraries to use types provided by typing_extensions without having to include it as a runtime dependency, but rather with just a guard like if typing.TYPE_CHECKING.)

On the other hand, forcing all such libraries to repeat the guard if typing.TYPE_CHECKING around every from typing_extensions import TypeAlias in each module where TypeAlias is used, rather than being able to put this guard in a single internal module and do from _internal_module import TypeAlias, is also bad, and increases Pyright users' maintenance burden.

To turn this around:

Is there a reason not to import directly from typing_extensions? This is what other libraries do.

Is there a reason Pyright can't support this? This is what other type checkers do.

Hope this helps clarify our perspective here at least a little bit.

jab avatar Dec 29 '22 01:12 jab

Here is full example code that hits this bug, to make my previous comment more concrete:

foolib/_typing.py:

if typing.TYPE_CHECKING:
    from typing_extensions import TypeAlias as TypeAlias
else:
    TypeAlias = 'TypeAlias'

foolib/foo.py

from __future__ import annotations
from ._typing import TypeAlias

IntList: TypeAlias = "list[int]"

x: IntList = []
x.append(42)

With the above, foolib both runs and type-checks successfully with mypy, even on Python 3.7, even with no typing_extensions declared as a runtime dependency of foolib.

I think it should be a goal of all type checkers to support this, rather than forcing users to either bloat their runtime dependencies, or their own code with extra noisy if TYPE_CHECKING guards.

jab avatar Dec 29 '22 02:12 jab

This will be addressed in the next release.

erictraut avatar Mar 03 '23 07:03 erictraut

This is addressed in pyright 1.1.297, which I just published. It will also be included in a future release of pylance.

erictraut avatar Mar 08 '23 01:03 erictraut