Last active
September 13, 2019 08:13
-
-
Save rfezzani/002181c8667ec4c671421a4d938167eb to your computer and use it in GitHub Desktop.
Python decorator to ensure backward compatibility if function's argument name is modified
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Decorator ensuring backward compatibility when an argument name is | |
modified in a function definition. | |
# Use case | |
Let's suppose that in a previous version, the function `func` was defined | |
with the `old_arg` argument name: | |
```python | |
def func(old_arg, arg=0): | |
print(old_arg, arg) | |
``` | |
For any reason, if a developer decides to use more appropriate argument | |
name `new_name`. This may introduce backward compatibility issues if a | |
user has in his code `func(old_arg=1)`. | |
The `arg_fix` decorator takes a dictionary as argument for mapping | |
between old argument names and new ones, and check if the patched | |
function is called with an old argument name as keyword argument. In this | |
case, a deprecation warning is thrown and the old argument name is | |
replaced with the new one. | |
By decorating `func` with `arg_fix` like this: | |
```python | |
@arg_fix(arg_mapping={'old_arg': 'new_arg'}) | |
def func(new_arg, arg=0): | |
print(new_arg, arg) | |
``` | |
From now on, any code using`func` with `old_arg` explicitely (`func(old_arg=1)`) | |
will throw a deprecation warning. | |
""" | |
import functools | |
import warnings | |
class arg_fix: | |
"""Decorator ensuring backward compatibility when an argument name is | |
modified in a function definition. | |
""" | |
def __init__(self, arg_mapping): | |
""" | |
Args: | |
arg_mapping (dict): mapping between the function's old argument | |
names and the new ones. | |
""" | |
self.arg_mapping = arg_mapping | |
self.warning_msg = ("'%s' is a deprecated argument name " + | |
"for the function '%s', use '%s' instead.") | |
def __call__(self, f): | |
@functools.wraps(f) | |
def fixed_f(*args, **kwargs): | |
for old_arg, new_arg in self.arg_mapping.items(): | |
if old_arg in kwargs: | |
# warn that the function interface has changed: | |
warnings.warn(self.warning_msg % | |
(old_arg, f.__name__, new_arg), DeprecationWarning) | |
# Substitute new_arg to old_arg | |
kwargs[new_arg] = kwargs.pop(old_arg) | |
# Call the function with the fixed arguments | |
return f(*args, **kwargs) | |
return fixed_f |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment