Issue
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 ?
Solution
You could write a decorator:
from typing import Callable, Dict
import functools
import warnings
def deprecated_alias(**aliases: str) -> Callable:
"""Decorator for deprecated function and method arguments.
Use as follows:
@deprecated_alias(old_arg='new_arg')
def myfunc(new_arg):
...
"""
def deco(f: Callable):
@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: str, kwargs: Dict[str, str], aliases: Dict[str, str]):
"""Helper function for deprecating function arguments."""
for alias, new in aliases.items():
if alias in kwargs:
if new in kwargs:
raise TypeError(
f"{func_name} received both {alias} and {new} as arguments!"
f" {alias} is deprecated, use {new} instead."
)
warnings.warn(
message=(
f"`{alias}` is deprecated as an argument to `{func_name}`; use"
f" `{new}` instead."
),
category=DeprecationWarning,
stacklevel=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")
Answered By - user2357112
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.