pyright
pyright copied to clipboard
Some types cannot be assigned to TypeAlias when TypeAlias is imported from anywhere other than typing
Describe the bug
When TypeAlias
is imported from anywhere other than typing
, Tuple
cannot be assigned to a TypeAlias
.
To Reproduce
- Create a module that exports
TypeAlias
- Import
TypeAlias
from the module created in the previous step - 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.
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.
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.
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.
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.
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.,
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.
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.
This will be addressed in the next release.
This is addressed in pyright 1.1.297, which I just published. It will also be included in a future release of pylance.