summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util/langhelpers.py
diff options
context:
space:
mode:
authorFederico Caselli <cfederico87@gmail.com>2021-12-22 21:45:45 +0100
committerMike Bayer <mike_mp@zzzcomputing.com>2021-12-30 18:07:26 -0500
commite913ec8155b64e055f3a88ca9c1bb7f112202c76 (patch)
treeb0847baac43de628cd74bc2ff70cc352bbcb0270 /lib/sqlalchemy/util/langhelpers.py
parent54875c21601eaca01e3217d5b22fab6f6cf50992 (diff)
downloadsqlalchemy-e913ec8155b64e055f3a88ca9c1bb7f112202c76.tar.gz
Properly type _generative, decorator, public_factory
Good new is that pylance likes it and copies over the singature and everything. Bad news is that mypy does not support this yet https://github.com/python/mypy/issues/8645 Other minor bad news is that non_generative is not typed. I've tried using a protocol like the one in the comment but the signature is not ported over by pylance, so it's probably best to just live without it to have the correct signature. notes from mike: these three decorators are at the core of getting the library to be typed, more good news is that pylance will do all the things we like re: public_factory, see https://github.com/microsoft/pyright/issues/2758#issuecomment-1002788656 . For @_generative, we will likely move to using pep 673 once mypy supports it which may be soon. but overall having the explicit "return self" in the methods, while a little inconvenient, makes the typing more straightforward and locally present in the files rather than being decided at a distance. having "return self" present, or not, both have problems, so maybe we will be able to change it again if things change as far as decorator support. As it is, I feel like we are barely squeaking by with our decorators, the typing is already pretty out there. Change-Id: Ic77e13fc861def76a5925331df85c0aa48d77807 References: #6810
Diffstat (limited to 'lib/sqlalchemy/util/langhelpers.py')
-rw-r--r--lib/sqlalchemy/util/langhelpers.py38
1 files changed, 32 insertions, 6 deletions
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index ca64296c1..93caa0ee5 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -20,6 +20,7 @@ import re
import sys
import textwrap
import types
+import typing
from typing import Any
from typing import Callable
from typing import Generic
@@ -31,10 +32,10 @@ import warnings
from . import _collections
from . import compat
+from . import typing as compat_typing
from .. import exc
_T = TypeVar("_T")
-_MP = TypeVar("_MP", bound="memoized_property[Any]")
def md5_hex(x):
@@ -166,7 +167,13 @@ def map_bits(fn, n):
n ^= b
-def decorator(target):
+_Fn = typing.TypeVar("_Fn", bound=typing.Callable)
+_Args = compat_typing.ParamSpec("_Args")
+
+
+def decorator(
+ target: typing.Callable[compat_typing.Concatenate[_Fn, _Args], typing.Any]
+) -> _Fn:
"""A signature-matching decorator factory."""
def decorate(fn):
@@ -198,7 +205,7 @@ def %(name)s%(grouped_args)s:
decorated.__wrapped__ = fn
return update_wrapper(decorated, fn)
- return update_wrapper(decorate, target)
+ return typing.cast(_Fn, update_wrapper(decorate, target))
def _update_argspec_defaults_into_env(spec, env):
@@ -227,7 +234,16 @@ def _exec_code_in_env(code, env, fn_name):
return env[fn_name]
-def public_factory(target, location, class_location=None):
+_TE = TypeVar("_TE")
+
+_P = compat_typing.ParamSpec("_P")
+
+
+def public_factory(
+ target: typing.Callable[_P, _TE],
+ location: str,
+ class_location: Optional[str] = None,
+) -> typing.Callable[_P, _TE]:
"""Produce a wrapping function for the given cls or classmethod.
Rationale here is so that the __init__ method of the
@@ -273,6 +289,7 @@ def %(name)s%(grouped_args)s:
"__name__": callable_.__module__,
}
exec(code, env)
+
decorated = env[location_name]
if hasattr(fn, "_linked_to"):
@@ -1077,6 +1094,11 @@ def as_interface(obj, cls=None, methods=None, required=None):
)
+Selfmemoized_property = TypeVar(
+ "Selfmemoized_property", bound="memoized_property[Any]"
+)
+
+
class memoized_property(Generic[_T]):
"""A read-only @property that is only evaluated once."""
@@ -1090,14 +1112,18 @@ class memoized_property(Generic[_T]):
self.__name__ = fget.__name__
@overload
- def __get__(self: _MP, obj: None, cls: Any) -> _MP:
+ def __get__(
+ self: Selfmemoized_property, obj: None, cls: Any
+ ) -> Selfmemoized_property:
...
@overload
def __get__(self, obj: Any, cls: Any) -> _T:
...
- def __get__(self: _MP, obj: Any, cls: Any) -> Union[_MP, _T]:
+ def __get__(
+ self: Selfmemoized_property, obj: Any, cls: Any
+ ) -> Union[Selfmemoized_property, _T]:
if obj is None:
return self
obj.__dict__[self.__name__] = result = self.fget(obj)