1# engine/create.py 
    2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# 
    5# This module is part of SQLAlchemy and is released under 
    6# the MIT License: https://www.opensource.org/licenses/mit-license.php 
    7 
    8from __future__ import annotations 
    9 
    10import inspect 
    11import typing 
    12from typing import Any 
    13from typing import Callable 
    14from typing import cast 
    15from typing import Dict 
    16from typing import List 
    17from typing import Optional 
    18from typing import overload 
    19from typing import Type 
    20from typing import Union 
    21 
    22from . import base 
    23from . import url as _url 
    24from .interfaces import DBAPIConnection 
    25from .mock import create_mock_engine 
    26from .. import event 
    27from .. import exc 
    28from .. import util 
    29from ..pool import _AdhocProxiedConnection 
    30from ..pool import ConnectionPoolEntry 
    31from ..sql import compiler 
    32from ..util import immutabledict 
    33 
    34if typing.TYPE_CHECKING: 
    35    from typing import Literal 
    36 
    37    from .base import Engine 
    38    from .interfaces import _ExecuteOptions 
    39    from .interfaces import _ParamStyle 
    40    from .interfaces import IsolationLevel 
    41    from .url import URL 
    42    from ..log import _EchoFlagType 
    43    from ..pool import _CreatorFnType 
    44    from ..pool import _CreatorWRecFnType 
    45    from ..pool import _ResetStyleArgType 
    46    from ..pool import Pool 
    47 
    48 
    49@overload 
    50def create_engine( 
    51    url: Union[str, URL], 
    52    *, 
    53    connect_args: Dict[Any, Any] = ..., 
    54    convert_unicode: bool = ..., 
    55    creator: Union[_CreatorFnType, _CreatorWRecFnType] = ..., 
    56    echo: _EchoFlagType = ..., 
    57    echo_pool: _EchoFlagType = ..., 
    58    enable_from_linting: bool = ..., 
    59    execution_options: _ExecuteOptions = ..., 
    60    future: Literal[True], 
    61    hide_parameters: bool = ..., 
    62    implicit_returning: Literal[True] = ..., 
    63    insertmanyvalues_page_size: int = ..., 
    64    isolation_level: IsolationLevel = ..., 
    65    json_deserializer: Callable[..., Any] = ..., 
    66    json_serializer: Callable[..., Any] = ..., 
    67    label_length: Optional[int] = ..., 
    68    logging_name: str = ..., 
    69    max_identifier_length: Optional[int] = ..., 
    70    max_overflow: int = ..., 
    71    module: Optional[Any] = ..., 
    72    paramstyle: Optional[_ParamStyle] = ..., 
    73    pool: Optional[Pool] = ..., 
    74    poolclass: Optional[Type[Pool]] = ..., 
    75    pool_logging_name: str = ..., 
    76    pool_pre_ping: bool = ..., 
    77    pool_size: int = ..., 
    78    pool_recycle: int = ..., 
    79    pool_reset_on_return: Optional[_ResetStyleArgType] = ..., 
    80    pool_timeout: float = ..., 
    81    pool_use_lifo: bool = ..., 
    82    plugins: List[str] = ..., 
    83    query_cache_size: int = ..., 
    84    use_insertmanyvalues: bool = ..., 
    85    **kwargs: Any, 
    86) -> Engine: ... 
    87 
    88 
    89@overload 
    90def create_engine(url: Union[str, URL], **kwargs: Any) -> Engine: ... 
    91 
    92 
    93@util.deprecated_params( 
    94    strategy=( 
    95        "1.4", 
    96        "The :paramref:`_sa.create_engine.strategy` keyword is deprecated, " 
    97        "and the only argument accepted is 'mock'; please use " 
    98        ":func:`.create_mock_engine` going forward.  For general " 
    99        "customization of create_engine which may have been accomplished " 
    100        "using strategies, see :class:`.CreateEnginePlugin`.", 
    101    ), 
    102    empty_in_strategy=( 
    103        "1.4", 
    104        "The :paramref:`_sa.create_engine.empty_in_strategy` keyword is " 
    105        "deprecated, and no longer has any effect.  All IN expressions " 
    106        "are now rendered using " 
    107        'the "expanding parameter" strategy which renders a set of bound' 
    108        'expressions, or an "empty set" SELECT, at statement execution' 
    109        "time.", 
    110    ), 
    111    implicit_returning=( 
    112        "2.0", 
    113        "The :paramref:`_sa.create_engine.implicit_returning` parameter " 
    114        "is deprecated and will be removed in a future release. ", 
    115    ), 
    116) 
    117def create_engine(url: Union[str, _url.URL], **kwargs: Any) -> Engine: 
    118    """Create a new :class:`_engine.Engine` instance. 
    119 
    120    The standard calling form is to send the :ref:`URL <database_urls>` as the 
    121    first positional argument, usually a string 
    122    that indicates database dialect and connection arguments:: 
    123 
    124        engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test") 
    125 
    126    .. note:: 
    127 
    128        Please review :ref:`database_urls` for general guidelines in composing 
    129        URL strings.  In particular, special characters, such as those often 
    130        part of passwords, must be URL encoded to be properly parsed. 
    131 
    132    Additional keyword arguments may then follow it which 
    133    establish various options on the resulting :class:`_engine.Engine` 
    134    and its underlying :class:`.Dialect` and :class:`_pool.Pool` 
    135    constructs:: 
    136 
    137        engine = create_engine( 
    138            "mysql+mysqldb://scott:tiger@hostname/dbname", 
    139            pool_recycle=3600, 
    140            echo=True, 
    141        ) 
    142 
    143    The string form of the URL is 
    144    ``dialect[+driver]://user:password@host/dbname[?key=value..]``, where 
    145    ``dialect`` is a database name such as ``mysql``, ``oracle``, 
    146    ``postgresql``, etc., and ``driver`` the name of a DBAPI, such as 
    147    ``psycopg2``, ``pyodbc``, ``cx_oracle``, etc.  Alternatively, 
    148    the URL can be an instance of :class:`~sqlalchemy.engine.url.URL`. 
    149 
    150    ``**kwargs`` takes a wide variety of options which are routed 
    151    towards their appropriate components.  Arguments may be specific to 
    152    the :class:`_engine.Engine`, the underlying :class:`.Dialect`, 
    153    as well as the 
    154    :class:`_pool.Pool`.  Specific dialects also accept keyword arguments that 
    155    are unique to that dialect.   Here, we describe the parameters 
    156    that are common to most :func:`_sa.create_engine()` usage. 
    157 
    158    Once established, the newly resulting :class:`_engine.Engine` will 
    159    request a connection from the underlying :class:`_pool.Pool` once 
    160    :meth:`_engine.Engine.connect` is called, or a method which depends on it 
    161    such as :meth:`_engine.Engine.execute` is invoked.   The 
    162    :class:`_pool.Pool` in turn 
    163    will establish the first actual DBAPI connection when this request 
    164    is received.   The :func:`_sa.create_engine` call itself does **not** 
    165    establish any actual DBAPI connections directly. 
    166 
    167    .. seealso:: 
    168 
    169        :doc:`/core/engines` 
    170 
    171        :doc:`/dialects/index` 
    172 
    173        :ref:`connections_toplevel` 
    174 
    175    :param connect_args: a dictionary of options which will be 
    176        passed directly to the DBAPI's ``connect()`` method as 
    177        additional keyword arguments.  See the example 
    178        at :ref:`custom_dbapi_args`. 
    179 
    180    :param creator: a callable which returns a DBAPI connection. 
    181        This creation function will be passed to the underlying 
    182        connection pool and will be used to create all new database 
    183        connections. Usage of this function causes connection 
    184        parameters specified in the URL argument to be bypassed. 
    185 
    186        This hook is not as flexible as the newer 
    187        :meth:`_events.DialectEvents.do_connect` hook which allows complete 
    188        control over how a connection is made to the database, given the full 
    189        set of URL arguments and state beforehand. 
    190 
    191        .. seealso:: 
    192 
    193            :meth:`_events.DialectEvents.do_connect` - event hook that allows 
    194            full control over DBAPI connection mechanics. 
    195 
    196            :ref:`custom_dbapi_args` 
    197 
    198    :param echo=False: if True, the Engine will log all statements 
    199        as well as a ``repr()`` of their parameter lists to the default log 
    200        handler, which defaults to ``sys.stdout`` for output.   If set to the 
    201        string ``"debug"``, result rows will be printed to the standard output 
    202        as well. The ``echo`` attribute of ``Engine`` can be modified at any 
    203        time to turn logging on and off; direct control of logging is also 
    204        available using the standard Python ``logging`` module. 
    205 
    206        .. seealso:: 
    207 
    208            :ref:`dbengine_logging` - further detail on how to configure 
    209            logging. 
    210 
    211 
    212    :param echo_pool=False: if True, the connection pool will log 
    213        informational output such as when connections are invalidated 
    214        as well as when connections are recycled to the default log handler, 
    215        which defaults to ``sys.stdout`` for output.   If set to the string 
    216        ``"debug"``, the logging will include pool checkouts and checkins. 
    217        Direct control of logging is also available using the standard Python 
    218        ``logging`` module. 
    219 
    220        .. seealso:: 
    221 
    222            :ref:`dbengine_logging` - further detail on how to configure 
    223            logging. 
    224 
    225 
    226    :param empty_in_strategy:   No longer used; SQLAlchemy now uses 
    227        "empty set" behavior for IN in all cases. 
    228 
    229    :param enable_from_linting: defaults to True.  Will emit a warning 
    230        if a given SELECT statement is found to have un-linked FROM elements 
    231        which would cause a cartesian product. 
    232 
    233        .. versionadded:: 1.4 
    234 
    235        .. seealso:: 
    236 
    237            :ref:`change_4737` 
    238 
    239    :param execution_options: Dictionary execution options which will 
    240        be applied to all connections.  See 
    241        :meth:`~sqlalchemy.engine.Connection.execution_options` 
    242 
    243    :param future: Use the 2.0 style :class:`_engine.Engine` and 
    244        :class:`_engine.Connection` API. 
    245 
    246        As of SQLAlchemy 2.0, this parameter is present for backwards 
    247        compatibility only and must remain at its default value of ``True``. 
    248 
    249        The :paramref:`_sa.create_engine.future` parameter will be 
    250        deprecated in a subsequent 2.x release and eventually removed. 
    251 
    252        .. versionadded:: 1.4 
    253 
    254        .. versionchanged:: 2.0 All :class:`_engine.Engine` objects are 
    255           "future" style engines and there is no longer a ``future=False`` 
    256           mode of operation. 
    257 
    258        .. seealso:: 
    259 
    260            :ref:`migration_20_toplevel` 
    261 
    262    :param hide_parameters: Boolean, when set to True, SQL statement parameters 
    263        will not be displayed in INFO logging nor will they be formatted into 
    264        the string representation of :class:`.StatementError` objects. 
    265 
    266        .. seealso:: 
    267 
    268            :ref:`dbengine_logging` - further detail on how to configure 
    269            logging. 
    270 
    271    :param implicit_returning=True:  Legacy parameter that may only be set 
    272        to True. In SQLAlchemy 2.0, this parameter does nothing. In order to 
    273        disable "implicit returning" for statements invoked by the ORM, 
    274        configure this on a per-table basis using the 
    275        :paramref:`.Table.implicit_returning` parameter. 
    276 
    277 
    278    :param insertmanyvalues_page_size: number of rows to format into an 
    279     INSERT statement when the statement uses "insertmanyvalues" mode, which is 
    280     a paged form of bulk insert that is used for many backends when using 
    281     :term:`executemany` execution typically in conjunction with RETURNING. 
    282     Defaults to 1000, but may also be subject to dialect-specific limiting 
    283     factors which may override this value on a per-statement basis. 
    284 
    285     .. versionadded:: 2.0 
    286 
    287     .. seealso:: 
    288 
    289        :ref:`engine_insertmanyvalues` 
    290 
    291        :ref:`engine_insertmanyvalues_page_size` 
    292 
    293        :paramref:`_engine.Connection.execution_options.insertmanyvalues_page_size` 
    294 
    295    :param isolation_level: optional string name of an isolation level 
    296        which will be set on all new connections unconditionally. 
    297        Isolation levels are typically some subset of the string names 
    298        ``"SERIALIZABLE"``, ``"REPEATABLE READ"``, 
    299        ``"READ COMMITTED"``, ``"READ UNCOMMITTED"`` and ``"AUTOCOMMIT"`` 
    300        based on backend. 
    301 
    302        The :paramref:`_sa.create_engine.isolation_level` parameter is 
    303        in contrast to the 
    304        :paramref:`.Connection.execution_options.isolation_level` 
    305        execution option, which may be set on an individual 
    306        :class:`.Connection`, as well as the same parameter passed to 
    307        :meth:`.Engine.execution_options`, where it may be used to create 
    308        multiple engines with different isolation levels that share a common 
    309        connection pool and dialect. 
    310 
    311        .. versionchanged:: 2.0 The 
    312           :paramref:`_sa.create_engine.isolation_level` 
    313           parameter has been generalized to work on all dialects which support 
    314           the concept of isolation level, and is provided as a more succinct, 
    315           up front configuration switch in contrast to the execution option 
    316           which is more of an ad-hoc programmatic option. 
    317 
    318        .. seealso:: 
    319 
    320            :ref:`dbapi_autocommit` 
    321 
    322    :param json_deserializer: for dialects that support the 
    323        :class:`_types.JSON` 
    324        datatype, this is a Python callable that will convert a JSON string 
    325        to a Python object.  By default, the Python ``json.loads`` function is 
    326        used. 
    327 
    328    :param json_serializer: for dialects that support the :class:`_types.JSON` 
    329        datatype, this is a Python callable that will render a given object 
    330        as JSON.   By default, the Python ``json.dumps`` function is used. 
    331 
    332    :param label_length=None: optional integer value which limits 
    333        the size of dynamically generated column labels to that many 
    334        characters. If less than 6, labels are generated as 
    335        "_(counter)". If ``None``, the value of 
    336        ``dialect.max_identifier_length``, which may be affected via the 
    337        :paramref:`_sa.create_engine.max_identifier_length` parameter, 
    338        is used instead.   The value of 
    339        :paramref:`_sa.create_engine.label_length` 
    340        may not be larger than that of 
    341        :paramref:`_sa.create_engine.max_identfier_length`. 
    342 
    343        .. seealso:: 
    344 
    345            :paramref:`_sa.create_engine.max_identifier_length` 
    346 
    347    :param logging_name:  String identifier which will be used within 
    348        the "name" field of logging records generated within the 
    349        "sqlalchemy.engine" logger. Defaults to a hexstring of the 
    350        object's id. 
    351 
    352        .. seealso:: 
    353 
    354            :ref:`dbengine_logging` - further detail on how to configure 
    355            logging. 
    356 
    357            :paramref:`_engine.Connection.execution_options.logging_token` 
    358 
    359    :param max_identifier_length: integer; override the max_identifier_length 
    360        determined by the dialect.  if ``None`` or zero, has no effect.  This 
    361        is the database's configured maximum number of characters that may be 
    362        used in a SQL identifier such as a table name, column name, or label 
    363        name. All dialects determine this value automatically, however in the 
    364        case of a new database version for which this value has changed but 
    365        SQLAlchemy's dialect has not been adjusted, the value may be passed 
    366        here. 
    367 
    368        .. seealso:: 
    369 
    370            :paramref:`_sa.create_engine.label_length` 
    371 
    372    :param max_overflow=10: the number of connections to allow in 
    373        connection pool "overflow", that is connections that can be 
    374        opened above and beyond the pool_size setting, which defaults 
    375        to five. this is only used with :class:`~sqlalchemy.pool.QueuePool`. 
    376 
    377    :param module=None: reference to a Python module object (the module 
    378        itself, not its string name).  Specifies an alternate DBAPI module to 
    379        be used by the engine's dialect.  Each sub-dialect references a 
    380        specific DBAPI which will be imported before first connect.  This 
    381        parameter causes the import to be bypassed, and the given module to 
    382        be used instead. Can be used for testing of DBAPIs as well as to 
    383        inject "mock" DBAPI implementations into the :class:`_engine.Engine`. 
    384 
    385    :param paramstyle=None: The `paramstyle <https://legacy.python.org/dev/peps/pep-0249/#paramstyle>`_ 
    386        to use when rendering bound parameters.  This style defaults to the 
    387        one recommended by the DBAPI itself, which is retrieved from the 
    388        ``.paramstyle`` attribute of the DBAPI.  However, most DBAPIs accept 
    389        more than one paramstyle, and in particular it may be desirable 
    390        to change a "named" paramstyle into a "positional" one, or vice versa. 
    391        When this attribute is passed, it should be one of the values 
    392        ``"qmark"``, ``"numeric"``, ``"named"``, ``"format"`` or 
    393        ``"pyformat"``, and should correspond to a parameter style known 
    394        to be supported by the DBAPI in use. 
    395 
    396    :param pool=None: an already-constructed instance of 
    397        :class:`~sqlalchemy.pool.Pool`, such as a 
    398        :class:`~sqlalchemy.pool.QueuePool` instance. If non-None, this 
    399        pool will be used directly as the underlying connection pool 
    400        for the engine, bypassing whatever connection parameters are 
    401        present in the URL argument. For information on constructing 
    402        connection pools manually, see :ref:`pooling_toplevel`. 
    403 
    404    :param poolclass=None: a :class:`~sqlalchemy.pool.Pool` 
    405        subclass, which will be used to create a connection pool 
    406        instance using the connection parameters given in the URL. Note 
    407        this differs from ``pool`` in that you don't actually 
    408        instantiate the pool in this case, you just indicate what type 
    409        of pool to be used. 
    410 
    411    :param pool_logging_name:  String identifier which will be used within 
    412       the "name" field of logging records generated within the 
    413       "sqlalchemy.pool" logger. Defaults to a hexstring of the object's 
    414       id. 
    415 
    416       .. seealso:: 
    417 
    418            :ref:`dbengine_logging` - further detail on how to configure 
    419            logging. 
    420 
    421    :param pool_pre_ping: boolean, if True will enable the connection pool 
    422        "pre-ping" feature that tests connections for liveness upon 
    423        each checkout. 
    424 
    425        .. seealso:: 
    426 
    427            :ref:`pool_disconnects_pessimistic` 
    428 
    429    :param pool_size=5: the number of connections to keep open 
    430        inside the connection pool. This used with 
    431        :class:`~sqlalchemy.pool.QueuePool` as 
    432        well as :class:`~sqlalchemy.pool.SingletonThreadPool`.  With 
    433        :class:`~sqlalchemy.pool.QueuePool`, a ``pool_size`` setting 
    434        of 0 indicates no limit; to disable pooling, set ``poolclass`` to 
    435        :class:`~sqlalchemy.pool.NullPool` instead. 
    436 
    437    :param pool_recycle=-1: this setting causes the pool to recycle 
    438        connections after the given number of seconds has passed. It 
    439        defaults to -1, or no timeout. For example, setting to 3600 
    440        means connections will be recycled after one hour. Note that 
    441        MySQL in particular will disconnect automatically if no 
    442        activity is detected on a connection for eight hours (although 
    443        this is configurable with the MySQLDB connection itself and the 
    444        server configuration as well). 
    445 
    446        .. seealso:: 
    447 
    448            :ref:`pool_setting_recycle` 
    449 
    450    :param pool_reset_on_return='rollback': set the 
    451        :paramref:`_pool.Pool.reset_on_return` parameter of the underlying 
    452        :class:`_pool.Pool` object, which can be set to the values 
    453        ``"rollback"``, ``"commit"``, or ``None``. 
    454 
    455        .. seealso:: 
    456 
    457            :ref:`pool_reset_on_return` 
    458 
    459            :ref:`dbapi_autocommit_skip_rollback` - a more modern approach 
    460            to using connections with no transactional instructions 
    461 
    462    :param pool_timeout=30: number of seconds to wait before giving 
    463        up on getting a connection from the pool. This is only used 
    464        with :class:`~sqlalchemy.pool.QueuePool`. This can be a float but is 
    465        subject to the limitations of Python time functions which may not be 
    466        reliable in the tens of milliseconds. 
    467 
    468        .. note: don't use 30.0 above, it seems to break with the :param tag 
    469 
    470    :param pool_use_lifo=False: use LIFO (last-in-first-out) when retrieving 
    471        connections from :class:`.QueuePool` instead of FIFO 
    472        (first-in-first-out). Using LIFO, a server-side timeout scheme can 
    473        reduce the number of connections used during non- peak   periods of 
    474        use.   When planning for server-side timeouts, ensure that a recycle or 
    475        pre-ping strategy is in use to gracefully   handle stale connections. 
    476 
    477          .. seealso:: 
    478 
    479            :ref:`pool_use_lifo` 
    480 
    481            :ref:`pool_disconnects` 
    482 
    483    :param plugins: string list of plugin names to load.  See 
    484        :class:`.CreateEnginePlugin` for background. 
    485 
    486    :param query_cache_size: size of the cache used to cache the SQL string 
    487     form of queries.  Set to zero to disable caching. 
    488 
    489     The cache is pruned of its least recently used items when its size reaches 
    490     N * 1.5.  Defaults to 500, meaning the cache will always store at least 
    491     500 SQL statements when filled, and will grow up to 750 items at which 
    492     point it is pruned back down to 500 by removing the 250 least recently 
    493     used items. 
    494 
    495     Caching is accomplished on a per-statement basis by generating a 
    496     cache key that represents the statement's structure, then generating 
    497     string SQL for the current dialect only if that key is not present 
    498     in the cache.   All statements support caching, however some features 
    499     such as an INSERT with a large set of parameters will intentionally 
    500     bypass the cache.   SQL logging will indicate statistics for each 
    501     statement whether or not it were pull from the cache. 
    502 
    503     .. note:: some ORM functions related to unit-of-work persistence as well 
    504        as some attribute loading strategies will make use of individual 
    505        per-mapper caches outside of the main cache. 
    506 
    507 
    508     .. seealso:: 
    509 
    510        :ref:`sql_caching` 
    511 
    512     .. versionadded:: 1.4 
    513 
    514    :param skip_autocommit_rollback: When True, the dialect will 
    515       unconditionally skip all calls to the DBAPI ``connection.rollback()`` 
    516       method if the DBAPI connection is confirmed to be in "autocommit" mode. 
    517       The availability of this feature is dialect specific; if not available, 
    518       a ``NotImplementedError`` is raised by the dialect when rollback occurs. 
    519 
    520       .. seealso:: 
    521 
    522            :ref:`dbapi_autocommit_skip_rollback` 
    523 
    524       .. versionadded:: 2.0.43 
    525 
    526    :param use_insertmanyvalues: True by default, use the "insertmanyvalues" 
    527     execution style for INSERT..RETURNING statements by default. 
    528 
    529     .. versionadded:: 2.0 
    530 
    531     .. seealso:: 
    532 
    533        :ref:`engine_insertmanyvalues` 
    534 
    535    """  # noqa 
    536 
    537    if "strategy" in kwargs: 
    538        strat = kwargs.pop("strategy") 
    539        if strat == "mock": 
    540            # this case is deprecated 
    541            return create_mock_engine(url, **kwargs)  # type: ignore 
    542        else: 
    543            raise exc.ArgumentError("unknown strategy: %r" % strat) 
    544 
    545    kwargs.pop("empty_in_strategy", None) 
    546 
    547    # create url.URL object 
    548    u = _url.make_url(url) 
    549 
    550    u, plugins, kwargs = u._instantiate_plugins(kwargs) 
    551 
    552    entrypoint = u._get_entrypoint() 
    553    _is_async = kwargs.pop("_is_async", False) 
    554    if _is_async: 
    555        dialect_cls = entrypoint.get_async_dialect_cls(u) 
    556    else: 
    557        dialect_cls = entrypoint.get_dialect_cls(u) 
    558 
    559    if kwargs.pop("_coerce_config", False): 
    560 
    561        def pop_kwarg(key: str, default: Optional[Any] = None) -> Any: 
    562            value = kwargs.pop(key, default) 
    563            if key in dialect_cls.engine_config_types: 
    564                value = dialect_cls.engine_config_types[key](value) 
    565            return value 
    566 
    567    else: 
    568        pop_kwarg = kwargs.pop  # type: ignore 
    569 
    570    dialect_args = {} 
    571    # consume dialect arguments from kwargs 
    572    for k in util.get_cls_kwargs(dialect_cls): 
    573        if k in kwargs: 
    574            dialect_args[k] = pop_kwarg(k) 
    575 
    576    dbapi = kwargs.pop("module", None) 
    577    if dbapi is None: 
    578        dbapi_args = {} 
    579 
    580        if "import_dbapi" in dialect_cls.__dict__: 
    581            dbapi_meth = dialect_cls.import_dbapi 
    582 
    583        elif hasattr(dialect_cls, "dbapi") and inspect.ismethod( 
    584            dialect_cls.dbapi 
    585        ): 
    586            util.warn_deprecated( 
    587                "The dbapi() classmethod on dialect classes has been " 
    588                "renamed to import_dbapi().  Implement an import_dbapi() " 
    589                f"classmethod directly on class {dialect_cls} to remove this " 
    590                "warning; the old .dbapi() classmethod may be maintained for " 
    591                "backwards compatibility.", 
    592                "2.0", 
    593            ) 
    594            dbapi_meth = dialect_cls.dbapi 
    595        else: 
    596            dbapi_meth = dialect_cls.import_dbapi 
    597 
    598        for k in util.get_func_kwargs(dbapi_meth): 
    599            if k in kwargs: 
    600                dbapi_args[k] = pop_kwarg(k) 
    601        dbapi = dbapi_meth(**dbapi_args) 
    602 
    603    dialect_args["dbapi"] = dbapi 
    604 
    605    dialect_args.setdefault("compiler_linting", compiler.NO_LINTING) 
    606    enable_from_linting = kwargs.pop("enable_from_linting", True) 
    607    if enable_from_linting: 
    608        dialect_args["compiler_linting"] ^= compiler.COLLECT_CARTESIAN_PRODUCTS 
    609 
    610    for plugin in plugins: 
    611        plugin.handle_dialect_kwargs(dialect_cls, dialect_args) 
    612 
    613    # create dialect 
    614    dialect = dialect_cls(**dialect_args) 
    615 
    616    # assemble connection arguments 
    617    (cargs_tup, cparams) = dialect.create_connect_args(u) 
    618    cparams.update(pop_kwarg("connect_args", {})) 
    619    cargs = list(cargs_tup)  # allow mutability 
    620 
    621    # look for existing pool or create 
    622    pool = pop_kwarg("pool", None) 
    623    if pool is None: 
    624 
    625        def connect( 
    626            connection_record: Optional[ConnectionPoolEntry] = None, 
    627        ) -> DBAPIConnection: 
    628            if dialect._has_events: 
    629                for fn in dialect.dispatch.do_connect: 
    630                    connection = cast( 
    631                        DBAPIConnection, 
    632                        fn(dialect, connection_record, cargs, cparams), 
    633                    ) 
    634                    if connection is not None: 
    635                        return connection 
    636 
    637            return dialect.connect(*cargs, **cparams) 
    638 
    639        creator = pop_kwarg("creator", connect) 
    640 
    641        poolclass = pop_kwarg("poolclass", None) 
    642        if poolclass is None: 
    643            poolclass = dialect.get_dialect_pool_class(u) 
    644        pool_args = {"dialect": dialect} 
    645 
    646        # consume pool arguments from kwargs, translating a few of 
    647        # the arguments 
    648        for k in util.get_cls_kwargs(poolclass): 
    649            tk = _pool_translate_kwargs.get(k, k) 
    650            if tk in kwargs: 
    651                pool_args[k] = pop_kwarg(tk) 
    652 
    653        for plugin in plugins: 
    654            plugin.handle_pool_kwargs(poolclass, pool_args) 
    655 
    656        pool = poolclass(creator, **pool_args) 
    657    else: 
    658        pool._dialect = dialect 
    659 
    660    if ( 
    661        hasattr(pool, "_is_asyncio") 
    662        and pool._is_asyncio is not dialect.is_async 
    663    ): 
    664        raise exc.ArgumentError( 
    665            f"Pool class {pool.__class__.__name__} cannot be " 
    666            f"used with {'non-' if not dialect.is_async else ''}" 
    667            "asyncio engine", 
    668            code="pcls", 
    669        ) 
    670 
    671    # create engine. 
    672    if not pop_kwarg("future", True): 
    673        raise exc.ArgumentError( 
    674            "The 'future' parameter passed to " 
    675            "create_engine() may only be set to True." 
    676        ) 
    677 
    678    engineclass = base.Engine 
    679 
    680    engine_args = {} 
    681    for k in util.get_cls_kwargs(engineclass): 
    682        if k in kwargs: 
    683            engine_args[k] = pop_kwarg(k) 
    684 
    685    # internal flags used by the test suite for instrumenting / proxying 
    686    # engines with mocks etc. 
    687    _initialize = kwargs.pop("_initialize", True) 
    688 
    689    # all kwargs should be consumed 
    690    if kwargs: 
    691        raise TypeError( 
    692            "Invalid argument(s) %s sent to create_engine(), " 
    693            "using configuration %s/%s/%s.  Please check that the " 
    694            "keyword arguments are appropriate for this combination " 
    695            "of components." 
    696            % ( 
    697                ",".join("'%s'" % k for k in kwargs), 
    698                dialect.__class__.__name__, 
    699                pool.__class__.__name__, 
    700                engineclass.__name__, 
    701            ) 
    702        ) 
    703 
    704    engine = engineclass(pool, dialect, u, **engine_args) 
    705 
    706    if _initialize: 
    707        do_on_connect = dialect.on_connect_url(u) 
    708        if do_on_connect: 
    709 
    710            def on_connect( 
    711                dbapi_connection: DBAPIConnection, 
    712                connection_record: ConnectionPoolEntry, 
    713            ) -> None: 
    714                assert do_on_connect is not None 
    715                do_on_connect(dbapi_connection) 
    716 
    717            event.listen(pool, "connect", on_connect) 
    718 
    719        builtin_on_connect = dialect._builtin_onconnect() 
    720        if builtin_on_connect: 
    721            event.listen(pool, "connect", builtin_on_connect) 
    722 
    723        def first_connect( 
    724            dbapi_connection: DBAPIConnection, 
    725            connection_record: ConnectionPoolEntry, 
    726        ) -> None: 
    727            c = base.Connection( 
    728                engine, 
    729                connection=_AdhocProxiedConnection( 
    730                    dbapi_connection, connection_record 
    731                ), 
    732                _has_events=False, 
    733                # reconnecting will be a reentrant condition, so if the 
    734                # connection goes away, Connection is then closed 
    735                _allow_revalidate=False, 
    736                # dont trigger the autobegin sequence 
    737                # within the up front dialect checks 
    738                _allow_autobegin=False, 
    739            ) 
    740            c._execution_options = util.EMPTY_DICT 
    741 
    742            try: 
    743                dialect.initialize(c) 
    744            finally: 
    745                # note that "invalidated" and "closed" are mutually 
    746                # exclusive in 1.4 Connection. 
    747                if not c.invalidated and not c.closed: 
    748                    # transaction is rolled back otherwise, tested by 
    749                    # test/dialect/postgresql/test_dialect.py 
    750                    # ::MiscBackendTest::test_initial_transaction_state 
    751                    dialect.do_rollback(c.connection) 
    752 
    753        # previously, the "first_connect" event was used here, which was then 
    754        # scaled back if the "on_connect" handler were present.  now, 
    755        # since "on_connect" is virtually always present, just use 
    756        # "connect" event with once_unless_exception in all cases so that 
    757        # the connection event flow is consistent in all cases. 
    758        event.listen( 
    759            pool, "connect", first_connect, _once_unless_exception=True 
    760        ) 
    761 
    762    dialect_cls.engine_created(engine) 
    763    if entrypoint is not dialect_cls: 
    764        entrypoint.engine_created(engine) 
    765 
    766    for plugin in plugins: 
    767        plugin.engine_created(engine) 
    768 
    769    return engine 
    770 
    771 
    772def engine_from_config( 
    773    configuration: Dict[str, Any], prefix: str = "sqlalchemy.", **kwargs: Any 
    774) -> Engine: 
    775    """Create a new Engine instance using a configuration dictionary. 
    776 
    777    The dictionary is typically produced from a config file. 
    778 
    779    The keys of interest to ``engine_from_config()`` should be prefixed, e.g. 
    780    ``sqlalchemy.url``, ``sqlalchemy.echo``, etc.  The 'prefix' argument 
    781    indicates the prefix to be searched for.  Each matching key (after the 
    782    prefix is stripped) is treated as though it were the corresponding keyword 
    783    argument to a :func:`_sa.create_engine` call. 
    784 
    785    The only required key is (assuming the default prefix) ``sqlalchemy.url``, 
    786    which provides the :ref:`database URL <database_urls>`. 
    787 
    788    A select set of keyword arguments will be "coerced" to their 
    789    expected type based on string values.    The set of arguments 
    790    is extensible per-dialect using the ``engine_config_types`` accessor. 
    791 
    792    :param configuration: A dictionary (typically produced from a config file, 
    793        but this is not a requirement).  Items whose keys start with the value 
    794        of 'prefix' will have that prefix stripped, and will then be passed to 
    795        :func:`_sa.create_engine`. 
    796 
    797    :param prefix: Prefix to match and then strip from keys 
    798        in 'configuration'. 
    799 
    800    :param kwargs: Each keyword argument to ``engine_from_config()`` itself 
    801        overrides the corresponding item taken from the 'configuration' 
    802        dictionary.  Keyword arguments should *not* be prefixed. 
    803 
    804    """ 
    805 
    806    options = { 
    807        key[len(prefix) :]: configuration[key] 
    808        for key in configuration 
    809        if key.startswith(prefix) 
    810    } 
    811    options["_coerce_config"] = True 
    812    options.update(kwargs) 
    813    url = options.pop("url") 
    814    return create_engine(url, **options) 
    815 
    816 
    817@overload 
    818def create_pool_from_url( 
    819    url: Union[str, URL], 
    820    *, 
    821    poolclass: Optional[Type[Pool]] = ..., 
    822    logging_name: str = ..., 
    823    pre_ping: bool = ..., 
    824    size: int = ..., 
    825    recycle: int = ..., 
    826    reset_on_return: Optional[_ResetStyleArgType] = ..., 
    827    timeout: float = ..., 
    828    use_lifo: bool = ..., 
    829    **kwargs: Any, 
    830) -> Pool: ... 
    831 
    832 
    833@overload 
    834def create_pool_from_url(url: Union[str, URL], **kwargs: Any) -> Pool: ... 
    835 
    836 
    837def create_pool_from_url(url: Union[str, URL], **kwargs: Any) -> Pool: 
    838    """Create a pool instance from the given url. 
    839 
    840    If ``poolclass`` is not provided the pool class used 
    841    is selected using the dialect specified in the URL. 
    842 
    843    The arguments passed to :func:`_sa.create_pool_from_url` are 
    844    identical to the pool argument passed to the :func:`_sa.create_engine` 
    845    function. 
    846 
    847    .. versionadded:: 2.0.10 
    848    """ 
    849 
    850    for key in _pool_translate_kwargs: 
    851        if key in kwargs: 
    852            kwargs[_pool_translate_kwargs[key]] = kwargs.pop(key) 
    853 
    854    engine = create_engine(url, **kwargs, _initialize=False) 
    855    return engine.pool 
    856 
    857 
    858_pool_translate_kwargs = immutabledict( 
    859    { 
    860        "logging_name": "pool_logging_name", 
    861        "echo": "echo_pool", 
    862        "timeout": "pool_timeout", 
    863        "recycle": "pool_recycle", 
    864        "events": "pool_events",  # deprecated 
    865        "reset_on_return": "pool_reset_on_return", 
    866        "pre_ping": "pool_pre_ping", 
    867        "use_lifo": "pool_use_lifo", 
    868    } 
    869)