diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-31 15:22:00 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-31 15:22:00 -0400 |
commit | 3c60d3b1ca492ba77d64111f0378892acaadf36b (patch) | |
tree | 5e2df552a7142cd3bb2ef9e5236db85e5c2859f4 /lib/sqlalchemy/util/langhelpers.py | |
parent | 903b0a42e71c81ff99494352760c0f92fa7a486d (diff) | |
download | sqlalchemy-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/langhelpers.py')
-rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 51 |
1 files changed, 41 insertions, 10 deletions
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): |