We are developing a python library and would like to change the way some function arguments are named in some functions.
We would like to keep backward compatibility and thus we would like to find a way to create alias for function arguments.
Here is an example:
Old Version:
class MyClass(object):
def __init__(self, object_id):
self.id = object_id
New Version:
class MyClass(object):
def __init__(self, id_object):
self.id = id_object
How can we make the class to be compatible with both calling ways:
object1 = MyClass(object_id=1234)
object2 = MyClass(id_object=1234)
I could of course create something like this:
class MyClass(object):
def __init__(self, object_id=None, id_object=None):
if id_object is not None:
self.id = id_object
else:
self.id = object_id
However, it would change the number of arguments and we strictly want to avoid this.
Is there any way to declare a method alias or an argument alias ?
You could write a decorator:
import functools
import warnings
def deprecated_alias(**aliases):
def deco(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
rename_kwargs(f.__name__, kwargs, aliases)
return f(*args, **kwargs)
return wrapper
return deco
def rename_kwargs(func_name, kwargs, aliases):
for alias, new in aliases.items():
if alias in kwargs:
if new in kwargs:
raise TypeError('{} received both {} and {}'.format(
func_name, alias, new))
warnings.warn('{} is deprecated; use {}'.format(alias, new),
DeprecationWarning,
3)
kwargs[new] = kwargs.pop(alias)
class MyClass(object):
@deprecated_alias(object_id='id_object')
def __init__(self, id_object):
self.id = id_object
Alternatively, since you're on Python 3, you can make object_id
a keyword-only argument:
import warnings
class MyClass(object):
# v Look here
def __init__(self, id_object=None, *, object_id=None):
if id_object is not None and object_id is not None:
raise TypeError("MyClass received both object_id and id_object")
elif id_object is not None:
self.id = id_object
elif object_id is not None:
warnings.warn("object_id is deprecated; use id_object", DeprecationWarning, 2)
self.id = object_id
else:
raise TypeError("MyClass missing id_object argument")
Another way, less elegant than user2357112's answer, would be to use a metaclass factory:
import warnings
def Deprecation(deprec):
class DeprecMeta(type):
def __call__(cls, *args, **kwargs):
new_kwargs = {k : v for k, v in kwargs.items() if k not in deprec}
for k in deprec:
if k in kwargs:
warnings.warn("{0}: Deprecated call with '{0}'. Use '{1}' instead".format(
cls.__name__, k, deprec[k]))
if deprec[k] not in kwargs:
new_kwargs[deprec[k]] = kwargs[k]
else:
raise TypeError('{} received both {} and {}'.format(
cls.__name__, k, deprec[k]))
obj = cls.__new__(cls, *args, **new_kwargs)
obj.__init__(*args, **new_kwargs)
return obj
return DeprecMeta
class MyClass(object, metaclass = Deprecation({'object_id': 'id_object'})):
def __init__(self, id_object):
self.id = id_object
object2 = MyClass(object_id=1234)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With