python / python/typing

Annotate sync/async code via Generic | generic TypeVar / Type aliases in Generic

Aperta
#1,183 6 commenti 0 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

Consider this case:
import asyncio
from typing import Any, Coroutine, Generic, TypeVar

from httpx import AsyncClient, Client, Response

ClientT = TypeVar("ClientT", bound=Client | AsyncClient)



class API(Generic[ClientT]):
    client: ClientT

    def __init__(self, client: ClientT) -> None:
        self.client = client

    def get_items(self) -> Response | Coroutine[Any, Any, Response]:
        return self.client.request('get', 'https://example.com/api/v1/get_items')


response = API[Client](Client()).get_items()
response.json()  # Item "Coroutine[Any, Any, Response]" of "Union[Response, Coroutine[Any, Any, Response]]" has no attribute "json"  [union-attr]

response_coroutine = API[AsyncClient](AsyncClient()).get_items()
asyncio.run(response_coroutine)  # Argument 1 to "run" has incompatible type "Union[Response, Coroutine[Any, Any, Response]]"; expected "Awaitable[Response]"  [arg-type]mypy(error)

How can I express bound between ClientT and return type of API().get_items()?

How it could look like if Generic would support type aliases or TypeVar supported another type variables:

import asyncio
from typing import Annotated, Any, Awaitable, Coroutine, Generic, TypeVar
from typing_extensions import reveal_type

from httpx import AsyncClient, Client, Response

ClientT = TypeVar("ClientT", bound=Client | AsyncClient)
T = TypeVar("T")

# Using Annotated just as generic type that returns its argument as is
Sync = Annotated[T, ...]
MightBeAwaitable = TypeVar("MightBeAwaitable", bound=Awaitable | Sync)
# or MightBeAwaitable = Awaitable | Sync

class API(Generic[ClientT, MightBeAwaitable):
    client: ClientT

    def __init__(self, client: ClientT) -> None:
        self.client = client

    def get_items(self) -> MightBeAwaitable[Response]:  # TypeError: 'TypeVar' object is not subscriptable
        return self.client.request('get', 'https://example.com/api/v1/get_items')


response = API[Client, Sync](Client()).get_items()
reveal_type(response)  # Revealed type is "Response"

response_coroutine = API[AsyncClient, Awaitable](AsyncClient()).get_items()
reveal_type(response_coroutine)  # Revealed type is "typing.Awaitable[Any]"

Sorry if it's the wrong place/type for this issue.

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

Inizia dagli esempi Python di questo issue e dai costrutti di tipizzazione che utilizzano: Generic, TypeVar, alias di tipo, Annotated, Awaitable e Coroutine. Determina la relazione prevista tra il tipo del client e il tipo restituito da get_items(), quindi documenta un design di tipizzazione supportato e i tipi rivelati attesi.

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

Valutazione

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

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.