From 7df42bede7df0a9eae63f12ee396b748274c2aaa Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 26 Apr 2019 19:55:16 +0100 Subject: [PATCH] ENH: New deprecated.warn function, for directly raising a deprecationwarning. --- fsl/utils/deprecated.py | 87 ++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/fsl/utils/deprecated.py b/fsl/utils/deprecated.py index c79dddfeb..64a0ad908 100644 --- a/fsl/utils/deprecated.py +++ b/fsl/utils/deprecated.py @@ -6,8 +6,10 @@ # """This module provides the :func:`deprecated` function, a simple decorator for deprecating functions and methods. -""" +The :func:`warn` function can also be called directly, to emit a +``DeprecationWarning`` +""" import functools as ft @@ -16,14 +18,20 @@ import warnings _warned_cache = set() -"""Used by the :func:`deprecated` function to keep track of whether a warning -has already been emitted for the use of a deprecated item. +"""Used by to keep track of whether a warning has already been emitted for the +use of a deprecated item. """ -def deprecated(vin=None, rin=None, msg=None): - """Decorator to mark a function or method as deprecated. A - ``DeprecationWarning`` is raised via the standard ``warnings`` module. +def resetWarningCache(): + """Clears the internal warning cache, so that the same line of code + may emit another deprecation warning. + """ + _warned_cache.clear() + + +def _buildMessageFormat(vin=None, rin=None, msg=None): + """Builds a deprecation warning message from the arguments. :arg vin: Optional version - the warning message will mention that the function is deprecated from this version. @@ -32,9 +40,9 @@ def deprecated(vin=None, rin=None, msg=None): function will be removed in this version. :arg msg: Optional message to use in the warning. - """ - + :returns: A format string which needs to be formatted with a ``{name}``. + """ if vin is not None and rin is not None: msgfmt = '{{name}} is deprecated from version {vin} and will be ' \ 'removed in {rin}.'.format(vin=vin, rin=rin) @@ -49,21 +57,62 @@ def deprecated(vin=None, rin=None, msg=None): if msg is not None: msgfmt = msgfmt + ' ' + msg - def wrapper(thing): - name = thing.__name__ + return msgfmt - def decorator(*args, **kwargs): - frame = inspect.stack()[1] - ident = '{}:{}'.format(frame.filename, frame.lineno) +def _buildWarningSourceIdentity(stacklevel=2): + """Creates a string to be used as an identifier for the calling code. + + :arg stacklevel: How far up the calling stack the calling code is. + :returns: A string which can be used as an identifier for the + calling code. + """ + frame = inspect.stack()[stacklevel] + ident = '{}:{}'.format(frame.filename, frame.lineno) + return ident + + +def warn(name, vin=None, rin=None, msg=None, stacklevel=1): + """Emit a deprecation warning. + + :arg name: Name of the thing (class, function, module, etc) that is + deprecated. + + :arg vin: Optional version - the warning message will mention that + the function is deprecated from this version. + + :arg rin: Optional version - the warning message will mention that + the function will be removed in this version. - if ident not in _warned_cache: - warnings.warn(msgfmt.format(name=name), - category=DeprecationWarning, - stacklevel=2) - _warned_cache.add(ident) + :arg msg: Optional message to use in the warning. + :arg stacklevel: How far up the stack the calling code is. + """ + + msgfmt = _buildMessageFormat(vin=vin, rin=rin, msg=msg) + ident = _buildWarningSourceIdentity() + if ident not in _warned_cache: + warnings.warn(msgfmt.format(name=name), + category=DeprecationWarning, + stacklevel=stacklevel + 1) + _warned_cache.add(ident) + + +def deprecated(vin=None, rin=None, msg=None): + """Decorator to mark a function or method as deprecated. A + ``DeprecationWarning`` is raised via the standard ``warnings`` module. + + :arg vin: Optional version - the warning message will mention that the + function is deprecated from this version. + + :arg rin: Optional version - the warning message will mention that the + function will be removed in this version. + + :arg msg: Optional message to use in the warning. + """ + def wrapper(thing): + def decorator(*args, **kwargs): + warn(thing.__name__, vin=vin, rin=rin, msg=msg, stacklevel=2) return thing(*args, **kwargs) return ft.update_wrapper(decorator, thing) - return wrapper -- GitLab