1# sqlalchemy/pool/dbapi_proxy.py 
    2# Copyright (C) 2005-2021 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: http://www.opensource.org/licenses/mit-license.php 
    7 
    8 
    9"""DBAPI proxy utility. 
    10 
    11Provides transparent connection pooling on top of a Python DBAPI. 
    12 
    13This is legacy SQLAlchemy functionality that is not typically used 
    14today. 
    15 
    16""" 
    17 
    18from .impl import QueuePool 
    19from .. import util 
    20from ..util import threading 
    21 
    22proxies = {} 
    23 
    24 
    25@util.deprecated( 
    26    "1.3", 
    27    "The :func:`.pool.manage` function is deprecated, and will be " 
    28    "removed in a future release.", 
    29) 
    30def manage(module, **params): 
    31    r"""Return a proxy for a DB-API module that automatically 
    32    pools connections. 
    33 
    34    Given a DB-API 2.0 module and pool management parameters, returns 
    35    a proxy for the module that will automatically pool connections, 
    36    creating new connection pools for each distinct set of connection 
    37    arguments sent to the decorated module's connect() function. 
    38 
    39    :param module: a DB-API 2.0 database module 
    40 
    41    :param poolclass: the class used by the pool module to provide 
    42      pooling.  Defaults to :class:`.QueuePool`. 
    43 
    44    :param \**params: will be passed through to *poolclass* 
    45 
    46    """ 
    47    try: 
    48        return proxies[module] 
    49    except KeyError: 
    50        return proxies.setdefault(module, _DBProxy(module, **params)) 
    51 
    52 
    53def clear_managers(): 
    54    """Remove all current DB-API 2.0 managers. 
    55 
    56    All pools and connections are disposed. 
    57    """ 
    58 
    59    for manager in proxies.values(): 
    60        manager.close() 
    61    proxies.clear() 
    62 
    63 
    64class _DBProxy(object): 
    65 
    66    """Layers connection pooling behavior on top of a standard DB-API module. 
    67 
    68    Proxies a DB-API 2.0 connect() call to a connection pool keyed to the 
    69    specific connect parameters. Other functions and attributes are delegated 
    70    to the underlying DB-API module. 
    71    """ 
    72 
    73    def __init__(self, module, poolclass=QueuePool, **kw): 
    74        """Initializes a new proxy. 
    75 
    76        module 
    77          a DB-API 2.0 module 
    78 
    79        poolclass 
    80          a Pool class, defaulting to QueuePool 
    81 
    82        Other parameters are sent to the Pool object's constructor. 
    83 
    84        """ 
    85 
    86        self.module = module 
    87        self.kw = kw 
    88        self.poolclass = poolclass 
    89        self.pools = {} 
    90        self._create_pool_mutex = threading.Lock() 
    91 
    92    def close(self): 
    93        for key in list(self.pools): 
    94            del self.pools[key] 
    95 
    96    def __del__(self): 
    97        self.close() 
    98 
    99    def __getattr__(self, key): 
    100        return getattr(self.module, key) 
    101 
    102    def get_pool(self, *args, **kw): 
    103        key = self._serialize(*args, **kw) 
    104        try: 
    105            return self.pools[key] 
    106        except KeyError: 
    107            self._create_pool_mutex.acquire() 
    108            try: 
    109                if key not in self.pools: 
    110                    kw.pop("sa_pool_key", None) 
    111                    pool = self.poolclass( 
    112                        lambda: self.module.connect(*args, **kw), **self.kw 
    113                    ) 
    114                    self.pools[key] = pool 
    115                    return pool 
    116                else: 
    117                    return self.pools[key] 
    118            finally: 
    119                self._create_pool_mutex.release() 
    120 
    121    def connect(self, *args, **kw): 
    122        """Activate a connection to the database. 
    123 
    124        Connect to the database using this DBProxy's module and the given 
    125        connect arguments.  If the arguments match an existing pool, the 
    126        connection will be returned from the pool's current thread-local 
    127        connection instance, or if there is no thread-local connection 
    128        instance it will be checked out from the set of pooled connections. 
    129 
    130        If the pool has no available connections and allows new connections 
    131        to be created, a new database connection will be made. 
    132 
    133        """ 
    134 
    135        return self.get_pool(*args, **kw).connect() 
    136 
    137    def dispose(self, *args, **kw): 
    138        """Dispose the pool referenced by the given connect arguments.""" 
    139 
    140        key = self._serialize(*args, **kw) 
    141        try: 
    142            del self.pools[key] 
    143        except KeyError: 
    144            pass 
    145 
    146    def _serialize(self, *args, **kw): 
    147        if "sa_pool_key" in kw: 
    148            return kw["sa_pool_key"] 
    149 
    150        return tuple(list(args) + [(k, kw[k]) for k in sorted(kw)])