[master 2/3] Add a decorator to mark deprecated functions/methods.

dwlehman installerbot-noreply at redhat.com
Mon Jun 29 18:21:41 UTC 2015


From: David Lehman <dlehman at redhat.com>

The function's docstring is modified to include the sphinx 'deprecated'
directive and a wrapper ensures that every call to the function emits a
warning using warnings.warn.
---
 blivet/util.py | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 112 insertions(+), 1 deletion(-)

diff --git a/blivet/util.py b/blivet/util.py
index db1887b..4a6a4d6 100644
--- a/blivet/util.py
+++ b/blivet/util.py
@@ -11,9 +11,11 @@
 import tempfile
 import uuid
 import hashlib
+import warnings
 from decimal import Decimal
 from contextlib import contextmanager
 from gi.repository import BlockDev as blockdev
+from functools import wraps
 
 import six
 
@@ -25,7 +27,6 @@
 # this will get set to anaconda's program_log_lock in enable_installer_mode
 program_log_lock = Lock()
 
-
 def _run_program(argv, root='/', stdin=None, env_prune=None, stderr_to_stdout=False, binary_output=False):
     if env_prune is None:
         env_prune = []
@@ -661,3 +662,113 @@ def eintr_ignore(func, *args, **kwargs):
 def open(*args, **kwargs):  # pylint: disable=redefined-builtin
     """Open a file, and retry on EINTR."""
     return eintr_retry_call(_open, *args, **kwargs)
+
+#
+# Deprecation decorator.
+#
+_DEPRECATION_MESSAGE = "will be removed in a future version."
+def _default_deprecation_msg(func):
+    return "%s %s" % (func.__name__, _DEPRECATION_MESSAGE)
+
+def indent(text, spaces=4):
+    """ Indent text by a specified number of spaces.
+
+        :param str text: the text to indent
+        :keyword int spaces: the number of spaces to indent text
+
+        It would be nice if we could use textwrap.indent for this but, since it
+        does not exist in python2, I prefer to just use this.
+    """
+    if not text or not text.strip():
+        return text
+
+    indentation = " " * spaces
+    indented = []
+    for line in text.splitlines():
+        indented.append("%s%s" % (indentation, line))
+
+    return "\n".join(indented)
+
+_SPHINX_DEPRECATE = """.. deprecated:: %(version)s
+    %(message)s
+"""
+def _add_deprecation_doc_text(func, version=None, message=None):
+    """ Add sphinx 'deprecated' markup to a function's docstring.
+
+        :param :class:`function` func: the function
+        :param str version: version in which the deprecation is effective
+        :param str message: message suggesting a preferred alternative
+
+        If your doctext is indented with something other than spaces the added
+        doctext's indentation will probably not match. That'd be your fault.
+    """
+    base_text = func.__doc__
+    if base_text is None:
+        base_text = " " # They contain leading and trailing spaces. *shrug*
+    else:
+        base_text = base_text[:-1] # Trim the trailing space.
+
+    if ".. deprecated::" in base_text:
+        # Don't add multiple deprecation directives.
+        return
+
+    # Figure out the number of spaces to indent docstring text. We are looking
+    # for the minimum indentation, not including the first line or empty lines.
+    indent_spaces = None
+    for l in base_text.splitlines()[1:]:
+        if not l.strip():
+            continue
+
+        spaces = 0
+        _l = l[:]
+        while _l and _l.startswith(" "):
+            spaces += 1
+            _l = _l[1:]
+
+        if indent_spaces is None or indent_spaces > spaces:
+            indent_spaces = spaces
+
+    if indent_spaces is None:
+        indent_spaces = 0
+
+    text = ""
+    if not re.search(r'\n\s*$', base_text):
+        # Make sure there's a newline after the last text.
+        text = "\n"
+
+    message = message or ""
+    text += _SPHINX_DEPRECATE % {"version": version, "message": message}
+    func.__doc__ = base_text + "\n" + indent(text, indent_spaces)
+
+def deprecated(version, message):
+    """ Decorator to deprecate a function or method via warning and docstring.
+
+        :param str version: version in which the deprecation is effective
+        :param str message: message suggesting a preferred alternative
+
+        .. note::
+            At the point this decorator gets applied to a method in a class the
+            method is just a function. It becomes a method later.
+
+        The docstring manipulation is performed only once for each decorated
+        function/method, but the warning is issued every time the decorated
+        function is called.
+    """
+    def deprecate_func(func):
+        @wraps(func)
+        def the_func(*args, **kwargs):
+            """ Issue a deprecation warning for, then call, a function. """
+            # Warnings look much better with default warning text than with
+            # no text. The sphinx doesn't benefit from it, so don't use it
+            # there.
+            warn_msg = _default_deprecation_msg(func)
+            if message:
+                warn_msg += " %s" % message
+
+            warnings.warn(warn_msg, DeprecationWarning, stacklevel=2)
+            return func(*args, **kwargs)
+
+        _add_deprecation_doc_text(the_func, message=message, version=version)
+        return the_func
+
+    return deprecate_func


-- 
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/867b359fd6313f59c0738f6806a260c335116fc9


More information about the anaconda-patches mailing list