-
towards trying to finalize all the typing approaches we want to use with SQLAlchemy, we can come up with things that work but sometimes it's not clear if it's a good idea based on the intent of the constructs in use. Ill present the API in a neutral way, suppose a function that is given a type, that is, a Python class, and returns some kind of object (here from typing import Any
from typing import Generic
from typing import List
from typing import Type
from typing import TypeVar
_T = TypeVar("_T", bound=Any)
class CollectionThing(Generic[_T]):
...
def collection_of_objects_one(cls: Type[_T]) -> CollectionThing[List[_T]]:
...
class SomeClass:
pass
# (variable) thing: CollectionThing[List[SomeClass]]
thing = collection_of_objects_one(SomeClass) however, def collection_of_objects_two(
cls: Type[_T], collection_cls: Type = None
) -> CollectionThing[Collection[_T]]:
...
# (variable) thing: CollectionThing[Collection[SomeClass]]
thing = collection_of_objects_two(SomeClass, collection_cls=set) I'm using _T = TypeVar("_T", bound=Any)
_TC = TypeVar("_TC", bound=Any)
# not possible w/ current typing
def collection_of_objects_three(
cls: Type[_T], collection_cls: Type[_TC] = None
) -> CollectionThing[_TC[_T]]:
... so without the # use a cast. will not be popular
foo = cast(
CollectionThing[Set[MyClass]],
collection_of_objects_two(MyClass, collection_class=set)
) So as the title implies, there's a neato nifty way to make a new API here that is totally succinct, eliminates the need for the user to be aware of the
that is, the typing API is just this: def collection_of_objects_four(cls: Type[_T]) -> CollectionThing[_T]:
...
# (variable) foo3: CollectionThing[set[SomeClass]]
foo3 = collection_of_objects_four(set[SomeClass])
# (variable) bar3: CollectionThing[List[SomeClass]]
bar3 = collection_of_objects_four(List[SomeClass]) so above, we just forget about "collection_class" and consider that since When i prototyped that, I noticed that it's not super friendly to detect Basically we're looking at I would be publishing an API that's either like, "hey now you can do other ways to go would be the pydantic / dataclasses thing, where we get the typing information almost exclusively from the left hand "typed" side, but for us that is a more dramatic API change and while I might look into that, I dont want that to be the "canonical" way this works. I want folks to stick as much with the same SQLAlchemy API they always had, but introduce small syntactical changes they can make so that their mapped classes are fully typed without any plugins. So basically the Q is am I going against the spirit of GenericAlias by using them at runtime in this way. thanks for reading! |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 12 replies
-
If most of the time people are going to pass one of a few classes for from collections import deque
from typing import TypeVar, Generic, overload, Any
_T = TypeVar("_T")
class CollectionThing(Generic[_T]):
pass
@overload
def collection_of_objects(
cls: type[_T], collection_cls: type[set]
) -> CollectionThing[set[_T]]: ...
@overload
def collection_of_objects(
cls: type[_T], collection_cls: type[list]
) -> CollectionThing[list[_T]]: ...
@overload
def collection_of_objects(
cls: type[_T], collection_cls: type[tuple]
) -> CollectionThing[tuple[_T, ...]]: ...
@overload
def collection_of_objects(
cls: type, collection_cls: type
) -> CollectionThing[Any]: ...
def collection_of_objects(cls: type, collection_cls: type) -> CollectionThing[Any]:
return CollectionThing()
reveal_type(collection_of_objects(str, collection_cls=set)) # CollectionThing[set[str]]
reveal_type(collection_of_objects(str, collection_cls=list)) # CollectionThing[list[str]]
reveal_type(collection_of_objects(str, collection_cls=tuple)) # CollectionThing[tuple[str, ...]]
reveal_type(collection_of_objects(str, collection_cls=deque)) # CollectionThing[Any] This isn't ideal, because passing in thing: CollectionThing[deque[str]] = collection_of_objects(str, collection_cls=deque)
reveal_type(thing) # Revealed type is "a.CollectionThing[collections.deque[builtins.str]]" Also, |
Beta Was this translation helpful? Give feedback.
-
pep 593 directly talks about consuming annotation data at runtime, though doesn't mention if it's in the annotated side or not. certainly what pydantic is doing here is completely a runtime thing. certainly I should try to get the overloads for collection_class working also as that's the traditional API. but im going to poke around Annotated a bit |
Beta Was this translation helpful? Give feedback.
-
OK folks, since I know this discussion forum is very active, and while @Akuli has been generous in their help for which I thank them, I think the answer I'm getting for my ultimate question about using generics on the right hand side for things is: ** awkward silence ** and that is a very good answer! it means everyone's more or less on the same page as I am , which is, "well, that's just not what we had in mind, though who can say...." and as Ive spent today thinking about this I will be doing it in some more traditional ways. for relationship() we will stick with the |
Beta Was this translation helpful? Give feedback.
-
My perspective is that using GenericAlias at runtime works right now, and CPython cares enough about backward compatibility for this API that if you don't do anything too crazy, it will continue to work in the foreseeable future. However, static type checkers are probably not going to like it for a while. |
Beta Was this translation helpful? Give feedback.
OK folks, since I know this discussion forum is very active, and while @Akuli has been generous in their help for which I thank them, I think the answer I'm getting for my ultimate question about using generics on the right hand side for things is:
** awkward silence **
and that is a very good answer! it means everyone's more or less on the same page as I am , which is, "well, that's just not what we had in mind, though who can say...." and as Ive spent today thinking about this I will be doing it in some more traditional ways. for relationship() we will stick with the
@overload
thing and judicious use ofAny
to make typing it simple (thanks @Akuli for theAny
tip) and for the "zippy new …