summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/inspection.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-01-09 11:49:02 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2022-01-24 15:14:01 -0500
commitff1ab665cb1694b85085680d1a02c7c11fa2a6d4 (patch)
treebb8414b44946d9cb96361d7dcd4a4541d8254672 /lib/sqlalchemy/inspection.py
parentaba3ab247da4628e4e7baf993702e2efaccbc547 (diff)
downloadsqlalchemy-ff1ab665cb1694b85085680d1a02c7c11fa2a6d4.tar.gz
mypy: sqlalchemy.util
Starting to set up practices and conventions to get the library typed. Key goals for typing are: 1. whole library can pass mypy without any strict turned on. 2. we can incrementally turn on some strict flags on a per-package/ module basis, as here we turn on more strictness for sqlalchemy.util, exc, and log 3. mypy ORM plugin tests work fully without sqlalchemy2-stubs installed 4. public facing methods all have return types, major parameter signatures filled in also 5. Foundational elements like util etc. are typed enough so that we can use them in fully typed internals higher up the stack. Conventions set up here: 1. we can use lots of config in setup.cfg to limit where mypy is throwing errors and how detailed it should be in different packages / modules. We can use this to push up gerrits that will pass tests fully without everything being typed. 2. a new tox target pep484 is added. this links to a new jenkins pep484 job that works across all projects (alembic, dogpile, etc.) We've worked around some mypy bugs that will likely be around for awhile, and also set up some core practices for how to deal with certain things such as public_factory modules (mypy won't accept a module from a callable at all, so need to use simple type checking conditionals). References: #6810 Change-Id: I80be58029896a29fd9f491aa3215422a8b705e12
Diffstat (limited to 'lib/sqlalchemy/inspection.py')
-rw-r--r--lib/sqlalchemy/inspection.py54
1 files changed, 44 insertions, 10 deletions
diff --git a/lib/sqlalchemy/inspection.py b/lib/sqlalchemy/inspection.py
index 7f9822d02..c6e9ca69a 100644
--- a/lib/sqlalchemy/inspection.py
+++ b/lib/sqlalchemy/inspection.py
@@ -28,15 +28,43 @@ tools which build on top of SQLAlchemy configurations to be constructed
in a forwards-compatible way.
"""
+from typing import Any
+from typing import Callable
+from typing import Dict
+from typing import Generic
+from typing import overload
+from typing import Type
+from typing import TypeVar
+from typing import Union
from . import exc
-from . import util
+from .util.typing import Literal
+_T = TypeVar("_T", bound=Any)
-_registrars = util.defaultdict(list)
+_registrars: Dict[type, Union[Literal[True], Callable[[Any], Any]]] = {}
-def inspect(subject, raiseerr=True):
+class Inspectable(Generic[_T]):
+ """define a class as inspectable.
+
+ This allows typing to set up a linkage between an object that
+ can be inspected and the type of inspection it returns.
+
+ """
+
+
+@overload
+def inspect(subject: Inspectable[_T], raiseerr: bool = True) -> _T:
+ ...
+
+
+@overload
+def inspect(subject: Any, raiseerr: bool = True) -> Any:
+ ...
+
+
+def inspect(subject: Any, raiseerr: bool = True) -> Any:
"""Produce an inspection object for the given target.
The returned value in some cases may be the
@@ -58,12 +86,14 @@ def inspect(subject, raiseerr=True):
type_ = type(subject)
for cls in type_.__mro__:
if cls in _registrars:
- reg = _registrars[cls]
- if reg is True:
+ reg = _registrars.get(cls, None)
+ if reg is None:
+ continue
+ elif reg is True:
return subject
ret = reg(subject)
if ret is not None:
- break
+ return ret
else:
reg = ret = None
@@ -75,8 +105,10 @@ def inspect(subject, raiseerr=True):
return ret
-def _inspects(*types):
- def decorate(fn_or_cls):
+def _inspects(
+ *types: type,
+) -> Callable[[Callable[[Any], Any]], Callable[[Any], Any]]:
+ def decorate(fn_or_cls: Callable[[Any], Any]) -> Callable[[Any], Any]:
for type_ in types:
if type_ in _registrars:
raise AssertionError(
@@ -88,6 +120,8 @@ def _inspects(*types):
return decorate
-def _self_inspects(cls):
- _inspects(cls)(True)
+def _self_inspects(cls: Type[_T]) -> Type[_T]:
+ if cls in _registrars:
+ raise AssertionError("Type %s is already " "registered" % cls)
+ _registrars[cls] = True
return cls