diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-02-17 13:43:04 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-03-01 09:09:02 -0500 |
commit | a4bb502cf95ea3523e4d383c4377e50f402d7d52 (patch) | |
tree | 124400f741b6b91f0e9e582b510268607394dfaa /lib/sqlalchemy/engine/create.py | |
parent | 60fca2ac8cf44bdaf68552ab5c69854a6776c73c (diff) | |
download | sqlalchemy-a4bb502cf95ea3523e4d383c4377e50f402d7d52.tar.gz |
pep-484 for engine
All modules in sqlalchemy.engine are strictly
typed with the exception of cursor, default, and
reflection. cursor and default pass with non-strict
typing, reflection is waiting on the multi-reflection
refactor.
Behavioral changes:
* create_connect_args() methods return a tuple of list,
dict, rather than a list of list, dict
* removed allow_chars parameter from
pyodbc connector ._get_server_version_info()
method
* the parameter list passed to do_executemany is now
a list in all cases. previously, this was being run
through dialect.execute_sequence_format, which
defaults to tuple and was only intended for individual
tuple params.
* broke up dialect.dbapi into dialect.import_dbapi
class method and dialect.dbapi module object. added
a deprecation path for legacy dialects. it's not
really feasible to type a single attr as a classmethod
vs. module type. The "type_compiler" attribute also
has this problem with greater ability to work around,
left that one for now.
* lots of constants changing to be Enum, so that we can
type them. for fixed tuple-position constants in
cursor.py / compiler.py (which are used to avoid the
speed overhead of namedtuple), using Literal[value]
which seems to work well
* some tightening up in Row regarding __getitem__, which
we can do since we are on full 2.0 style result use
* altered the set_connection_execution_options and
set_engine_execution_options event flows so that the
dictionary of options may be mutated within the event
hook, where it will then take effect as the actual
options used. Previously, changing the dict would
be silently ignored which seems counter-intuitive
and not very useful.
* A lot of DefaultDialect/DefaultExecutionContext
methods and attributes, including underscored ones, move
to interfaces. This is not fully ideal as it means
the Dialect/ExecutionContext interfaces aren't publicly
subclassable directly, but their current purpose
is more of documentation for dialect authors who should
(and certainly are) still be subclassing the DefaultXYZ
versions in all cases
Overall, Result was the most extremely difficult class
hierarchy to type here as this hierarchy passes through
largely amorphous "row" datatypes throughout, which
can in fact by all kinds of different things, like
raw DBAPI rows, or Row objects, or "scalar"/Any, but
at the same time these types have meaning so I tried still
maintaining some level of semantic markings for these,
it highlights how complex Result is now, as it's trying
to be extremely efficient and inlined while also being
very open-ended and extensible.
Change-Id: I98b75c0c09eab5355fc7a33ba41dd9874274f12a
Diffstat (limited to 'lib/sqlalchemy/engine/create.py')
-rw-r--r-- | lib/sqlalchemy/engine/create.py | 65 |
1 files changed, 52 insertions, 13 deletions
diff --git a/lib/sqlalchemy/engine/create.py b/lib/sqlalchemy/engine/create.py index ac3d6a2d8..cb5219396 100644 --- a/lib/sqlalchemy/engine/create.py +++ b/lib/sqlalchemy/engine/create.py @@ -7,7 +7,12 @@ from __future__ import annotations +import inspect +import typing from typing import Any +from typing import cast +from typing import Dict +from typing import Optional from typing import Union from . import base @@ -21,6 +26,9 @@ from ..pool import _AdhocProxiedConnection from ..pool import ConnectionPoolEntry from ..sql import compiler +if typing.TYPE_CHECKING: + from .base import Engine + @util.deprecated_params( strategy=( @@ -46,7 +54,7 @@ from ..sql import compiler "is deprecated and will be removed in a future release. ", ), ) -def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": +def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> Engine: """Create a new :class:`_engine.Engine` instance. The standard calling form is to send the :ref:`URL <database_urls>` as the @@ -452,7 +460,8 @@ def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": if "strategy" in kwargs: strat = kwargs.pop("strategy") if strat == "mock": - return create_mock_engine(url, **kwargs) + # this case is deprecated + return create_mock_engine(url, **kwargs) # type: ignore else: raise exc.ArgumentError("unknown strategy: %r" % strat) @@ -472,14 +481,14 @@ def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": if kwargs.pop("_coerce_config", False): - def pop_kwarg(key, default=None): + def pop_kwarg(key: str, default: Optional[Any] = None) -> Any: value = kwargs.pop(key, default) if key in dialect_cls.engine_config_types: value = dialect_cls.engine_config_types[key](value) return value else: - pop_kwarg = kwargs.pop + pop_kwarg = kwargs.pop # type: ignore dialect_args = {} # consume dialect arguments from kwargs @@ -490,10 +499,29 @@ def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": dbapi = kwargs.pop("module", None) if dbapi is None: dbapi_args = {} - for k in util.get_func_kwargs(dialect_cls.dbapi): + + if "import_dbapi" in dialect_cls.__dict__: + dbapi_meth = dialect_cls.import_dbapi + + elif hasattr(dialect_cls, "dbapi") and inspect.ismethod( + dialect_cls.dbapi + ): + util.warn_deprecated( + "The dbapi() classmethod on dialect classes has been " + "renamed to import_dbapi(). Implement an import_dbapi() " + f"classmethod directly on class {dialect_cls} to remove this " + "warning; the old .dbapi() classmethod may be maintained for " + "backwards compatibility.", + "2.0", + ) + dbapi_meth = dialect_cls.dbapi + else: + dbapi_meth = dialect_cls.import_dbapi + + for k in util.get_func_kwargs(dbapi_meth): if k in kwargs: dbapi_args[k] = pop_kwarg(k) - dbapi = dialect_cls.dbapi(**dbapi_args) + dbapi = dbapi_meth(**dbapi_args) dialect_args["dbapi"] = dbapi @@ -509,18 +537,23 @@ def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": dialect = dialect_cls(**dialect_args) # assemble connection arguments - (cargs, cparams) = dialect.create_connect_args(u) + (cargs_tup, cparams) = dialect.create_connect_args(u) cparams.update(pop_kwarg("connect_args", {})) - cargs = list(cargs) # allow mutability + cargs = list(cargs_tup) # allow mutability # look for existing pool or create pool = pop_kwarg("pool", None) if pool is None: - def connect(connection_record=None): + def connect( + connection_record: Optional[ConnectionPoolEntry] = None, + ) -> DBAPIConnection: if dialect._has_events: for fn in dialect.dispatch.do_connect: - connection = fn(dialect, connection_record, cargs, cparams) + connection = cast( + DBAPIConnection, + fn(dialect, connection_record, cargs, cparams), + ) if connection is not None: return connection return dialect.connect(*cargs, **cparams) @@ -596,7 +629,11 @@ def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": do_on_connect = dialect.on_connect_url(u) if do_on_connect: - def on_connect(dbapi_connection, connection_record): + def on_connect( + dbapi_connection: DBAPIConnection, + connection_record: ConnectionPoolEntry, + ) -> None: + assert do_on_connect is not None do_on_connect(dbapi_connection) event.listen(pool, "connect", on_connect) @@ -608,7 +645,7 @@ def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": def first_connect( dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry, - ): + ) -> None: c = base.Connection( engine, connection=_AdhocProxiedConnection( @@ -654,7 +691,9 @@ def create_engine(url: Union[str, "_url.URL"], **kwargs: Any) -> "base.Engine": return engine -def engine_from_config(configuration, prefix="sqlalchemy.", **kwargs): +def engine_from_config( + configuration: Dict[str, Any], prefix: str = "sqlalchemy.", **kwargs: Any +) -> Engine: """Create a new Engine instance using a configuration dictionary. The dictionary is typically produced from a config file. |