python / python/typing

Overhaul the python typing system to make typing a 'first class citizen' with a dedicated typing syntax, like match patterns

Aperta
#953 7 commenti 8 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

topic: feature
Lingua principale
Python
Stelle
1.8k
Fork
302
Merge medio
23h
PR unite (30g)
8

Descrizione

Pythons implementation of typing at the language level has been very limited, with only the absolute minimal changes to the grammar being implemented:

  • type annotations: a: int
  • return type annotations: def () -> int:

Both of these syntaxes are just normal expressions that are evaluated (or are just strings if __future__.annotations is on).

That has meant that the entirety of the typing landscape has been implemented 'in the language' using existing syntax and functionality to try and emulate typing as a first class language feature.

This has lead to modifications to the standard library to attempt to better support these usages (eg: special casing type[...], defining __or__ on type and the introduction of __class_getitem__ among others). So all these objects now have a dual nature, they have functions to support their 'normal' usages, and also a bunch of functions to support building type annotations.

The authors of PEP-586 have commented on this problem(thanks @sobolevn):

we feel overhauling the syntax of types in Python is not within the scope of this PEP: it would be best to have that discussion in a separate PEP

Due to the nature of typing being 'implemented in the language' instead of in the grammar/interpreter has lead to a myriad of complexities, edge cases, limitations and confusion. I will list a few of these side effect here. also showing a more optimal syntax that would be possible if typing were to be overhauled:

Fake types and type alias

Fake types such as None and ForwardRefs lead to runtime errors(either in annotations or upon calling get_type_hints):

from typing import TypeAlias

Str: TypeAlias = "str"
Int: TypeAlias = "int"

a: Str | Int = 0  # SUS ALERT

There could be a dedicated typealias keyword like other languages

typealias foo = int | str

(https://github.com/python/mypy/issues/11582)

Shout out to NewType
MyStr = NewType("MyStr", str)

Could be

newtype MyStr = str
Typing imports

A large amount of types need to be imported from typing, leading to a boilerplate of imports in every module:

from typing import NoReturn, TypedDict, TypeAlias, TypeVar, Generic, Protocol, etc, etc, etc, etc

There could be a 'builtins' for type annotations that are usable by default.

Also there is no way to import a type statically, see #928

Type parameters

Are one of the worst incantations imaginable:

from typing import TypeVar, Generic

T_cont = TypeVar("T_cont", contravariant=True, bound=str)

class A(Generic[T_cont]):
    ...

Could be

class A[in T_cont: str]:
    ...

Also you can't specify type parameters explicitly on function call sites:

foo_result: int | str = foo(1, "a")
bar(foo_result)

Could be:

bar(foo[int | str](1, "a"))
Literal syntax
a: Literal[1, 2] = 1

Could be

a: 1 | 2 = 1
cast

Yet another import, and very clunky to boot.
And why is the type first? everywhere else(isinstance, issubclass, type annotations) types go second.

cast(int, a)

Could be as or similar like other languages:

a as int
Callable

In error messages mypy shows this as (int, str) -> bool

a: Callable[[int, str], bool]

Could be

a: (int, str) -> bool
Confusion

Because all these type machineries exist at runtime, it creates very confusing situations where an annotation is used in a value position:

A = int | str
A()  # is this valid?
foo(A)  # is this valid?
B: type[int | str] = int | str  # is this valid?

https://github.com/microsoft/pyright/issues/2522 shows that it's very easy to overlook this situation.

Match Patterns

Enter match/case statements, which have their own unique language level syntax for dealing with all the concepts needed in a comprehensive way. For example matching a union of strings: "a" | "b" which in a typing annotation would just be evaluated as a normal expression and raise a TypeError.

match "a":
    case "a" | "b":  # 😳😳😳😳😳😳😳😳😳😳😳😳😳😳😳😳
          print("AMONGUS")

Instead of endlessly implementing special casing and workarounds, please overhaul the entire implementation of typing in the language from the ground up. Currently typing in python is very rough around the edges and could be vastly improved based upon the experience and usages gathered over the years since it's inception.

Proposal

Add the concept of a 'type context' that has it's own unique semantic meaning, separating the type realm functionality from the value(value context) realm functionality. This would make things such as the type union | much simpler and consistent (see issue where some types are not actually types at all, and don't have __or__). These operations would still produce type machinery representations:

class A:
    a: int | str
A.__annotations__  # {'a': UnionType[int, str]}

And these machinereies could be constructed manually in a 'value context' when needed:

a = UnionType(int, str)
Example
Bar = 1 | 2
    # ^ value context
typealias Foo = 1 | 2
              # ^ type context
Evaluating type syntax
from __future__ import annotations
from typing import get_type_hints

class A:
    x: "a" | "b"

get_type_hints(A)  # Currently will raise a TypeError

Under the hood, get_type_hints simply evals the annotation as a module level expression, I suggest adding a eval_type function that will evaluate it as a type context expression, not a value context

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Non vengono indicati file di implementazione né test. Inizia leggendo il modello di contesto dei tipi proposto insieme a PEP-586, PEP-622, issue #928 e alle discussioni collegate di mypy e pyright; un prossimo passo praticabile richiederebbe di concordare una sintassi e un design prima dell’implementazione nella grammatica e negli strumenti di typing.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
compilers
Tipo di issue
Funzionalità
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Da chiarire
Idoneità per principianti
15/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.