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)])