From 3c60d3b1ca492ba77d64111f0378892acaadf36b Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 31 Aug 2014 15:22:00 -0400 Subject: - 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 --- lib/sqlalchemy/util/langhelpers.py | 51 ++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 10 deletions(-) (limited to 'lib/sqlalchemy/util/langhelpers.py') 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): -- cgit v1.2.1