feat(typing): Make IntoBackend generic#3002
feat(typing): Make IntoBackend generic#3002FBruzzesi merged 16 commits intochore/expose-into-backendfrom
IntoBackend generic#3002Conversation
- New `SCREAMING_SNAKE_CASE` group for `Implementation` - Used only in `_compliant` (directly) - Everything is a runtime typing symbol
@FBruzzesi mentioned a skill issue in #2971 (comment)
…o into-backend-generic
These examples will show up everywhere, without needing repeating in `Into<...>Backend`
Just a single method
…o into-backend-generic
| Backend: TypeAlias = Literal[EagerAllowed, LazyAllowed] | ||
| """Ooh look, a description!""" | ||
|
|
||
|
|
||
| BackendT = TypeVar("BackendT", bound=Backend) | ||
| IntoBackend: TypeAlias = Union[BackendT, ModuleType] | ||
| """Anything that can be converted into a Narwhals Implementation. |
There was a problem hiding this comment.
So my question is: how come that
BackendT = TypeVar("BackendT", bound=Backend)is a generic alias?
Short answer
BackendT is not a generic alias.
It is a TypeVar used in a generic alias named IntoBackend
Longer answer
I think this concept is easier to understand using TypeAliasType
I'll build up the equivalent of IntoBackend using that and more explicit names:
from __future__ import annotations
from types import ModuleType
from typing import TYPE_CHECKING, Literal, Union
from typing_extensions import TypeAliasType
from narwhals._typing import EagerAllowed, LazyAllowed
from narwhals._typing_compat import TypeVar
if TYPE_CHECKING:
from typing_extensions import TypeAlias
# `Backend` (equivalent to one or more of the members in `Literal`)
BackendAlias: TypeAlias = Literal[EagerAllowed, LazyAllowed]
# `BackendT` (as above, but *remembers* (narrows to) what we passed in)
BackendTypeVar = TypeVar("BackendTypeVar", bound=BackendAlias)
# `IntoBackend` (as above, and expands into `BackendTypeVar | ModuleType`)
IntoBackendGenericAlias = TypeAliasType(
"IntoBackendGenericAlias",
Union[BackendTypeVar, ModuleType],
type_params=(BackendTypeVar,), # <--- Parameterizing an alias makes the alias generic
)
# `IntoBackendAny` (similar concept, but no narrowing)
IntoBackendConcreteAlias: TypeAlias = Union[BackendAlias, ModuleType]
# narrowed to `Literal["polars"] | ModuleType`
IntoBackendSubscribedAlias1: TypeAlias = IntoBackendGenericAlias[Literal["polars"]]
# `IntoBackendEager` (narrowed to `<everything in `EagerAllowed`> | ModuleType`)
IntoBackendSubscribedAlias2: TypeAlias = IntoBackendGenericAlias[EagerAllowed]
# `IntoBackendBad` (narrowed to `Unknown | ModuleType`, because "bad" is not assignable to `BackendAlias`)
IntoBackendSubscribedAlias3: TypeAlias = IntoBackendGenericAlias[Literal["bad"]] # type: ignore[type-var]There was a problem hiding this comment.
The syntax for this from 3.12 is a lot nicer https://docs.python.org/3/library/typing.html#type-aliases
from __future__ import annotations
from types import ModuleType
from narwhals._typing import EagerAllowed, LazyAllowed
type IntoBackend[T: EagerAllowed | LazyAllowed] = T | ModuleType
type IntoBackendAny = IntoBackend[EagerAllowed | LazyAllowed]
type IntoBackendEager = IntoBackend[EagerAllowed]#3002 (comment) Co-authored-by: Francesco Bruzzesi <42817048+FBruzzesi@users.noreply.github.com>
Final round of bikesheddingLines 1612 to 1630 in f2932a6 The conventions I've been trying to use in
|
Also creates a diff for a thread #3002 (comment)
|
|
||
|
|
||
| IntoBackendAny: TypeAlias = IntoBackend[Backend] | ||
| IntoBackendEager: TypeAlias = IntoBackend[EagerAllowed] | ||
| IntoBackendLazy: TypeAlias = IntoBackend[LazyAllowed] |
There was a problem hiding this comment.
I haven't used these yet.
The downside to them is the need to repeat (or condense) the docs from IntoBackend.
| IntoBackendAny: TypeAlias = IntoBackend[Backend] | |
| IntoBackendEager: TypeAlias = IntoBackend[EagerAllowed] | |
| IntoBackendLazy: TypeAlias = IntoBackend[LazyAllowed] |
If the additional length is an issue, we could alias *Allowed like this to get the benefits of both:
Eager: TypeAlias = EagerAllowed
Lazy: TypeAlias = LazyAllowed
def some_function(backend: IntoBackend[Eager]): ...But honestly all of this is shorter than main anyway 😄:
def some_function(backend: ModuleType | Implementation | str): ...wow pretty big one I missed there 😅
|
@dangotbanned I think this is ready to merge? What's left? If it's just variable naming we can chat about it to make it the loop quicker |
Yeah I'm happy with it! I just wanted to make sure you were before merging back into your PR 😊 |
Yep! Let's do it! |
What type of PR is this? (check all applicable)
Related issues
IntoBackendalias #2971IntoBackendalias #2971 (comment)IntoBackendalias #2971 (comment)Tasks
Implementationalias names (feat(typing): MakeIntoBackendgeneric #3002 (comment))IntoBackendalias #2971 (comment))_typing.py(77c707f)