From 99686d8e13dc974f5b58be75fed75701f5bf2e35 Mon Sep 17 00:00:00 2001
From: Alexandru Fikl <alexfikl@gmail.com>
Date: Wed, 14 Oct 2020 10:34:21 -0500
Subject: [PATCH] add deprecate_keyword decorator

---
 pytools/__init__.py | 85 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 64 insertions(+), 21 deletions(-)

diff --git a/pytools/__init__.py b/pytools/__init__.py
index 950e001..7c1052a 100644
--- a/pytools/__init__.py
+++ b/pytools/__init__.py
@@ -26,7 +26,7 @@ THE SOFTWARE.
 """
 
 
-from functools import reduce
+from functools import reduce, wraps
 import operator
 import sys
 import logging
@@ -174,6 +174,69 @@ F = TypeVar("F", bound=Callable[..., Any])
 # }}}
 
 
+# {{{ code maintenance
+
+class MovedFunctionDeprecationWrapper:
+    def __init__(self, f, deadline=None):
+        if deadline is None:
+            deadline = "the future"
+
+        self.f = f
+        self.deadline = deadline
+
+    def __call__(self, *args, **kwargs):
+        from warnings import warn
+        warn(f"This function is deprecated and will go away in {self.deadline}. "
+            f"Use {self.f.__module}.{self.f.__name__} instead.",
+            DeprecationWarning, stacklevel=2)
+
+        return self.f(*args, **kwargs)
+
+
+def deprecate_keyword(oldkey: str,
+        newkey: Optional[str] = None, *,
+        deadline: Optional[str] = None):
+    """Decorator used to deprecate function keyword arguments.
+
+    :arg oldkey: deprecated argument name.
+    :arg newkey: new argument name that serves the same purpose, if any.
+    :arg deadline: expected time frame for the removal of the deprecated argument.
+    """
+    from warnings import warn
+
+    if deadline is None:
+        deadline = "the future"
+
+    def wrapper(func):
+        @wraps(func)
+        def inner_wrapper(*args, **kwargs):
+            if oldkey in kwargs:
+                if newkey is None:
+                    warn(f"The '{oldkey}' keyword is deprecated and will "
+                            f"go away in {deadline}.",
+                            DeprecationWarning, stacklevel=2)
+                else:
+                    warn(f"The '{oldkey}' keyword is deprecated and will "
+                            f"go away in {deadline}. "
+                            f"Use '{newkey}' instead.",
+                            DeprecationWarning, stacklevel=2)
+
+                    if newkey in kwargs:
+                        raise ValueError(f"Cannot use '{oldkey}' "
+                                f"and '{newkey}' in the same call.")
+
+                    kwargs[newkey] = kwargs[oldkey]
+                    del kwargs[oldkey]
+
+            return func(*args, **kwargs)
+
+        return inner_wrapper
+
+    return wrapper
+
+# }}}
+
+
 # {{{ math --------------------------------------------------------------------
 
 def delta(x, y):
@@ -778,7 +841,6 @@ def memoize_method_nested(inner):
             "Use @memoize_in(self, 'identifier') instead", DeprecationWarning,
             stacklevel=2)
 
-    from functools import wraps
     cache_dict_name = intern("_memoize_inner_dic_%s_%s_%d"
             % (inner.__name__, inner.__code__.co_filename,
                 inner.__code__.co_firstlineno))
@@ -827,8 +889,6 @@ class memoize_in(object):  # noqa
         self.cache_dict = memoize_in_dict.setdefault(identifier, {})
 
     def __call__(self, inner):
-        from functools import wraps
-
         @wraps(inner)
         def new_inner(*args):
             try:
@@ -1465,23 +1525,6 @@ def get_write_to_map_from_permutation(original, permuted):
 # }}}
 
 
-# {{{ code maintenance
-
-class MovedFunctionDeprecationWrapper:
-    def __init__(self, f):
-        self.f = f
-
-    def __call__(self, *args, **kwargs):
-        from warnings import warn
-        warn("This function is deprecated. Use %s.%s instead." % (
-            self.f.__module__, self.f.__name__),
-            DeprecationWarning, stacklevel=2)
-
-        return self.f(*args, **kwargs)
-
-# }}}
-
-
 # {{{ graph algorithms
 
 from pytools.graph import a_star as a_star_moved
-- 
GitLab