]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- AssertionPool now stores the traceback indicating
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Mar 2011 16:30:54 +0000 (12:30 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Mar 2011 16:30:54 +0000 (12:30 -0400)
where the currently checked out connection was
acquired; this traceback is reported within
the assertion raised upon a second concurrent
checkout; courtesy Gunnlaugur Briem
[ticket:2103]

CHANGES
lib/sqlalchemy/pool.py
lib/sqlalchemy/util/__init__.py
lib/sqlalchemy/util/langhelpers.py

diff --git a/CHANGES b/CHANGES
index 2562f69fda18cb736c656ec6873705af02d7d11d..3765a6ed9eed32db3457543f3ba68dad9e46e76c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -46,6 +46,14 @@ CHANGES
     collection of Sequence objects, list
     of schema names.  [ticket:2104]
 
+- pool
+  - AssertionPool now stores the traceback indicating
+    where the currently checked out connection was
+    acquired; this traceback is reported within
+    the assertion raised upon a second concurrent 
+    checkout; courtesy Gunnlaugur Briem 
+    [ticket:2103]
+
 - oracle
   - Using column names that would require quotes
     for the column itself or for a name-generated
index a5b06f63aeacdcad86281cba0243036c9f48c858..6d7542fb587e9f2359943a1eb9e10680c9b4cd2a 100644 (file)
@@ -16,11 +16,12 @@ regular DB-API connect() methods to be transparently managed by a
 SQLAlchemy connection pool.
 """
 
-import weakref, time, threading
+import weakref, time, traceback
 
 from sqlalchemy import exc, log, event, events, interfaces, util
 from sqlalchemy.util import queue as sqla_queue
-from sqlalchemy.util import threading, pickle, memoized_property
+from sqlalchemy.util import threading, pickle, memoized_property, \
+    chop_traceback
 
 proxies = {}
 
@@ -815,17 +816,23 @@ class StaticPool(Pool):
         return self.connection
 
 class AssertionPool(Pool):
-    """A Pool that allows at most one checked out connection at any given
+    """A :class:`.Pool` that allows at most one checked out connection at any given
     time.
 
     This will raise an exception if more than one connection is checked out
     at a time.  Useful for debugging code that is using more connections
     than desired.
+    
+    :class:`.AssertionPool` also logs a traceback of where
+    the original connection was checked out, and reports
+    this in the assertion error raised (new in 0.7).
 
     """
     def __init__(self, *args, **kw):
         self._conn = None
         self._checked_out = False
+        self._store_traceback = kw.pop('store_traceback', True)
+        self._checkout_traceback = None
         Pool.__init__(self, *args, **kw)
 
     def status(self):
@@ -850,12 +857,19 @@ class AssertionPool(Pool):
 
     def _do_get(self):
         if self._checked_out:
-            raise AssertionError("connection is already checked out")
+            if self._checkout_traceback:
+                suffix = ' at:\n%s' % ''.join(
+                    chop_traceback(self._checkout_traceback))
+            else:
+                suffix = ''
+            raise AssertionError("connection is already checked out" + suffix)
 
         if not self._conn:
             self._conn = self._create_connection()
 
         self._checked_out = True
+        if self._store_traceback:
+            self._checkout_traceback = traceback.format_stack()
         return self._conn
 
 class _DBProxy(object):
index 96026c952fc40f85379542f5c0408ece100a9bdd..7e8ce59c4745c1c8773b627e1fb7b99c255cd301 100644 (file)
@@ -25,7 +25,7 @@ from langhelpers import iterate_attributes, class_hierarchy, \
     monkeypatch_proxied_specials, asbool, bool_or_str, coerce_kw_type,\
     duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\
     classproperty, set_creation_order, warn_exception, warn, NoneType,\
-    constructor_copy, methods_equivalent
+    constructor_copy, methods_equivalent, chop_traceback
 
 from deprecations import warn_deprecated, warn_pending_deprecation, \
     deprecated, pending_deprecation
index 64997706c45c7c80cebe256e7e7f7b871da9b064..cbe0d2a4f6022b47e2a1b5d1162bcc8bc9f55111 100644 (file)
@@ -11,6 +11,7 @@ modules, classes, hierarchies, attributes, functions, and methods.
 import itertools
 import inspect
 import operator
+import re
 import sys
 import types
 import warnings
@@ -757,4 +758,26 @@ def warn(msg, stacklevel=3):
     else:
         warnings.warn(msg, stacklevel=stacklevel)
 
+_SQLA_RE = re.compile(r'sqlalchemy/([a-z_]+/){0,2}[a-z_]+\.py')
+_UNITTEST_RE = re.compile(r'unit(?:2|test2?/)')
+def chop_traceback(tb, exclude_prefix=_UNITTEST_RE, exclude_suffix=_SQLA_RE):
+    """Chop extraneous lines off beginning and end of a traceback.
+
+    :param tb:
+      a list of traceback lines as returned by ``traceback.format_stack()``
+
+    :param exclude_prefix:
+      a regular expression object matching lines to skip at beginning of ``tb``
+
+    :param exclude_suffix:
+      a regular expression object matching lines to skip at end of ``tb``
+    """
+    start = 0
+    end = len(tb) - 1
+    while start <= end and exclude_prefix.search(tb[start]):
+        start += 1
+    while start <= end and exclude_suffix.search(tb[end]):
+        end -= 1
+    return tb[start:end+1]
+
 NoneType = type(None)