dbapi_proxy.py 4.3 KB

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