summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-08-31 15:22:00 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-08-31 15:22:00 -0400
commit3c60d3b1ca492ba77d64111f0378892acaadf36b (patch)
tree5e2df552a7142cd3bb2ef9e5236db85e5c2859f4 /lib/sqlalchemy/util
parent903b0a42e71c81ff99494352760c0f92fa7a486d (diff)
downloadsqlalchemy-3c60d3b1ca492ba77d64111f0378892acaadf36b.tar.gz
- A new style of warning can be emitted which will "filter" up to
N occurrences of a parameterized string. This allows parameterized warnings that can refer to their arguments to be delivered a fixed number of times until allowing Python warning filters to squelch them, and prevents memory from growing unbounded within Python's warning registries. fixes #3178
Diffstat (limited to 'lib/sqlalchemy/util')
-rw-r--r--lib/sqlalchemy/util/__init__.py3
-rw-r--r--lib/sqlalchemy/util/deprecations.py2
-rw-r--r--lib/sqlalchemy/util/langhelpers.py51
3 files changed, 44 insertions, 12 deletions
diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py
index b92beac96..c963b18c3 100644
--- a/lib/sqlalchemy/util/__init__.py
+++ b/lib/sqlalchemy/util/__init__.py
@@ -34,7 +34,8 @@ from .langhelpers import iterate_attributes, class_hierarchy, \
classproperty, set_creation_order, warn_exception, warn, NoneType,\
constructor_copy, methods_equivalent, chop_traceback, asint,\
generic_repr, counter, PluginLoader, hybridmethod, safe_reraise,\
- get_callable_argspec, only_once, attrsetter
+ get_callable_argspec, only_once, attrsetter, ellipses_string, \
+ warn_limited
from .deprecations import warn_deprecated, warn_pending_deprecation, \
deprecated, pending_deprecation, inject_docstring_text
diff --git a/lib/sqlalchemy/util/deprecations.py b/lib/sqlalchemy/util/deprecations.py
index d48efbaaa..124f304fc 100644
--- a/lib/sqlalchemy/util/deprecations.py
+++ b/lib/sqlalchemy/util/deprecations.py
@@ -102,7 +102,7 @@ def _decorate_with_warning(func, wtype, message, docstring_header=None):
@decorator
def warned(fn, *args, **kwargs):
- warnings.warn(wtype(message), stacklevel=3)
+ warnings.warn(message, wtype, stacklevel=3)
return fn(*args, **kwargs)
doc = func.__doc__ is not None and func.__doc__ or ''
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index f0dd7a08e..c260e0931 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -1189,24 +1189,55 @@ def warn_exception(func, *args, **kwargs):
warn("%s('%s') ignored" % sys.exc_info()[0:2])
-def warn(msg, stacklevel=3):
+def ellipses_string(value, len_=25):
+ if len(value) > len_:
+ return "%s..." % value[0:len_]
+ else:
+ return value
+
+
+class _hash_limit_string(compat.text_type):
+ """A string subclass that can only be hashed on a maximum amount
+ of unique values.
+
+ This is used for warnings so that we can send out parameterized warnings
+ without the __warningregistry__ of the module, or the non-overridable
+ "once" registry within warnings.py, overloading memory,
+
+
+ """
+ def __new__(cls, value, args, num):
+ interpolated = value % args + \
+ (" (this warning may be suppressed after %d occurrences)" % num)
+ self = super(_hash_limit_string, cls).__new__(cls, interpolated)
+ self._hash = hash("%s_%d" % (value, hash(interpolated) % num))
+ return self
+
+ def __hash__(self):
+ return self._hash
+
+ def __eq__(self, other):
+ return hash(self) == hash(other)
+
+
+def warn(msg):
"""Issue a warning.
If msg is a string, :class:`.exc.SAWarning` is used as
the category.
- .. note::
+ """
+ warnings.warn(msg, exc.SAWarning, stacklevel=2)
+
- This function is swapped out when the test suite
- runs, with a compatible version that uses
- warnings.warn_explicit, so that the warnings registry can
- be controlled.
+def warn_limited(msg, *args):
+ """Issue a warning with a paramterized string, limiting the number
+ of registrations.
"""
- if isinstance(msg, compat.string_types):
- warnings.warn(msg, exc.SAWarning, stacklevel=stacklevel)
- else:
- warnings.warn(msg, stacklevel=stacklevel)
+ if args:
+ msg = _hash_limit_string(msg, args, 10)
+ warnings.warn(msg, exc.SAWarning, stacklevel=2)
def only_once(fn):