diff options
Diffstat (limited to 'lib/sqlalchemy/util/typing.py')
-rw-r--r-- | lib/sqlalchemy/util/typing.py | 77 |
1 files changed, 68 insertions, 9 deletions
diff --git a/lib/sqlalchemy/util/typing.py b/lib/sqlalchemy/util/typing.py index ebcae28a7..44e26f609 100644 --- a/lib/sqlalchemy/util/typing.py +++ b/lib/sqlalchemy/util/typing.py @@ -11,7 +11,9 @@ from typing import Dict from typing import ForwardRef from typing import Generic from typing import Iterable +from typing import NoReturn from typing import Optional +from typing import overload from typing import Tuple from typing import Type from typing import TypeVar @@ -33,7 +35,7 @@ Self = TypeVar("Self", bound=Any) if compat.py310: # why they took until py310 to put this in stdlib is beyond me, # I've been wanting it since py27 - from types import NoneType + from types import NoneType as NoneType else: NoneType = type(None) # type: ignore @@ -68,6 +70,8 @@ else: # copied from TypeShed, required in order to implement # MutableMapping.update() +_AnnotationScanType = Union[Type[Any], str] + class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): def keys(self) -> Iterable[_KT]: @@ -90,9 +94,9 @@ else: def de_stringify_annotation( cls: Type[Any], - annotation: Union[str, Type[Any]], + annotation: _AnnotationScanType, str_cleanup_fn: Optional[Callable[[str], str]] = None, -) -> Union[str, Type[Any]]: +) -> Type[Any]: """Resolve annotations that may be string based into real objects. This is particularly important if a module defines "from __future__ import @@ -125,20 +129,32 @@ def de_stringify_annotation( annotation = eval(annotation, base_globals, None) except NameError: pass - return annotation + return annotation # type: ignore -def is_fwd_ref(type_): +def is_fwd_ref(type_: _AnnotationScanType) -> bool: return isinstance(type_, ForwardRef) -def de_optionalize_union_types(type_): +@overload +def de_optionalize_union_types(type_: str) -> str: + ... + + +@overload +def de_optionalize_union_types(type_: Type[Any]) -> Type[Any]: + ... + + +def de_optionalize_union_types( + type_: _AnnotationScanType, +) -> _AnnotationScanType: """Given a type, filter out ``Union`` types that include ``NoneType`` to not include the ``NoneType``. """ if is_optional(type_): - typ = set(type_.__args__) + typ = set(type_.__args__) # type: ignore typ.discard(NoneType) @@ -148,14 +164,14 @@ def de_optionalize_union_types(type_): return type_ -def make_union_type(*types): +def make_union_type(*types: _AnnotationScanType) -> Type[Any]: """Make a Union type. This is needed by :func:`.de_optionalize_union_types` which removes ``NoneType`` from a ``Union``. """ - return cast(Any, Union).__getitem__(types) + return cast(Any, Union).__getitem__(types) # type: ignore def expand_unions( @@ -251,4 +267,47 @@ class DescriptorReference(Generic[_DESC]): ... +_DESC_co = TypeVar("_DESC_co", bound=DescriptorProto, covariant=True) + + +class RODescriptorReference(Generic[_DESC_co]): + """a descriptor that refers to a descriptor. + + same as :class:`.DescriptorReference` but is read-only, so that subclasses + can define a subtype as the generically contained element + + """ + + def __get__(self, instance: object, owner: Any) -> _DESC_co: + ... + + def __set__(self, instance: Any, value: Any) -> NoReturn: + ... + + def __delete__(self, instance: Any) -> NoReturn: + ... + + +_FN = TypeVar("_FN", bound=Optional[Callable[..., Any]]) + + +class CallableReference(Generic[_FN]): + """a descriptor that refers to a callable. + + works around mypy's limitation of not allowing callables assigned + as instance variables + + + """ + + def __get__(self, instance: object, owner: Any) -> _FN: + ... + + def __set__(self, instance: Any, value: _FN) -> None: + ... + + def __delete__(self, instance: Any) -> None: + ... + + # $def ro_descriptor_reference(fn: Callable[]) |