From: Mike Bayer Date: Wed, 30 Mar 2011 16:30:54 +0000 (-0400) Subject: - AssertionPool now stores the traceback indicating X-Git-Tag: rel_0_7b4~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=218c3cb365e7354a312bc3e24e1cb21a2487459c;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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] --- diff --git a/CHANGES b/CHANGES index 2562f69fda..3765a6ed9e 100644 --- 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 diff --git a/lib/sqlalchemy/pool.py b/lib/sqlalchemy/pool.py index a5b06f63ae..6d7542fb58 100644 --- a/lib/sqlalchemy/pool.py +++ b/lib/sqlalchemy/pool.py @@ -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): diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index 96026c952f..7e8ce59c47 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -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 diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index 64997706c4..cbe0d2a4f6 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -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)