From 189753cf6747be7303d547e41dc05e3518c43bd3 Mon Sep 17 00:00:00 2001 From: birdi Date: Sat, 10 Aug 2019 23:42:49 +0300 Subject: [PATCH 1/7] Add renamed_argument decorator for convenient warnings about deprecated arguments --- aiogram/utils/deprecated.py | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index 1ea2561d..792a4d17 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -5,6 +5,7 @@ Source: https://stackoverflow.com/questions/2536307/decorators-in-the-python-sta import functools import inspect import warnings +import asyncio def deprecated(reason): @@ -73,3 +74,57 @@ def warn_deprecated(message, warning=DeprecationWarning, stacklevel=2): warnings.simplefilter('always', warning) warnings.warn(message, category=warning, stacklevel=stacklevel) warnings.simplefilter('default', warning) + + +def renamed_argument(old_name: str, new_name: str, until_version: str): + """ + A meta-decorator to mark some arguments in function as deprecated. + + .. code-block:: python3 + + @renamed_argument("user", "user_id", "3.0") + def some_function(user_id): + print(f"user_id={user_id}") + + some_function(user=123) # prints 'user_id=123' with warning + some_function(123) # prints 'user_id=123' without warning + some_function(user_id=123) # prints 'user_id=123' without warning + + + :param old_name: + :param new_name: + :param until_version: the version in which the argument is scheduled to be removed + :return: decorator + """ + + def decorator(func): + if asyncio.iscoroutinefunction(func): + @functools.wraps(func) + async def wrapped(*args, **kwargs): + if old_name in kwargs: + warn_deprecated(f"In coroutine '{func.__name__}' argument '{old_name}' is renamed to '{new_name}' " + f"and will be removed in aiogram {until_version}", stacklevel=3) + kwargs.update( + { + new_name: kwargs[old_name], + } + ) + kwargs.pop(old_name) + await func(*args, **kwargs) + else: + @functools.wraps(func) + def wrapped(*args, **kwargs): + if old_name in kwargs: + warn_deprecated(f"In function '{func.__name__}' argument '{old_name}' is renamed to '{new_name}' " + f"and will be removed in aiogram {until_version}", stacklevel=3) + kwargs.update( + { + new_name: kwargs[old_name], + } + ) + kwargs.pop(old_name) + func(*args, **kwargs) + + return wrapped + + return decorator From 05bfb37e18d4afd3082c0356063e29bc5e505f30 Mon Sep 17 00:00:00 2001 From: birdi Date: Sat, 10 Aug 2019 23:44:45 +0300 Subject: [PATCH 2/7] Add docs for deprecated --- docs/source/utils/deprecated.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/utils/deprecated.rst b/docs/source/utils/deprecated.rst index 0a7b4089..ce80bb0e 100644 --- a/docs/source/utils/deprecated.rst +++ b/docs/source/utils/deprecated.rst @@ -1,4 +1,8 @@ ========== Deprecated ========== -Coming soon... +.. literalinclude:: ../../../aiogram/utils/deprecated.py + :caption: deprecated.py + :language: python3 + :linenos: + From cc601a7e0d1152ce0de543f45c663df785856937 Mon Sep 17 00:00:00 2001 From: birdi Date: Sun, 11 Aug 2019 00:07:30 +0300 Subject: [PATCH 3/7] add support of stacklevel parameter in renamed_argument decorator --- aiogram/utils/deprecated.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index 792a4d17..4b954c56 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -76,24 +76,27 @@ def warn_deprecated(message, warning=DeprecationWarning, stacklevel=2): warnings.simplefilter('default', warning) -def renamed_argument(old_name: str, new_name: str, until_version: str): +def renamed_argument(old_name: str, new_name: str, until_version: str, stacklevel: int = 3): """ A meta-decorator to mark some arguments in function as deprecated. .. code-block:: python3 - @renamed_argument("user", "user_id", "3.0") - def some_function(user_id): - print(f"user_id={user_id}") + @renamed_argument("chat", "chat_id", "3.0") # stacklevel=3 by default + @renamed_argument("user", "user_id", "3.0", stacklevel=4) + def some_function(user_id, chat_id=None): + print(f"user_id={user_id}, chat_id={chat_id}") - some_function(user=123) # prints 'user_id=123' with warning - some_function(123) # prints 'user_id=123' without warning - some_function(user_id=123) # prints 'user_id=123' without warning + some_function(user=123) # prints 'user_id=123, chat_id=None' with warning + some_function(123) # prints 'user_id=123, chat_id=None' without warning + some_function(user_id=123) # prints 'user_id=123, chat_id=None' without warning :param old_name: :param new_name: :param until_version: the version in which the argument is scheduled to be removed + :param stacklevel: leave it to default if it's the first decorator used. + Increment with any new decorator used. :return: decorator """ @@ -102,8 +105,10 @@ def renamed_argument(old_name: str, new_name: str, until_version: str): @functools.wraps(func) async def wrapped(*args, **kwargs): if old_name in kwargs: - warn_deprecated(f"In coroutine '{func.__name__}' argument '{old_name}' is renamed to '{new_name}' " - f"and will be removed in aiogram {until_version}", stacklevel=3) + warn_deprecated(f"In coroutine '{func.__name__}' argument '{old_name}' " + f"is renamed to '{new_name}' " + f"and will be removed in aiogram {until_version}", + stacklevel=stacklevel) kwargs.update( { new_name: kwargs[old_name], @@ -115,8 +120,10 @@ def renamed_argument(old_name: str, new_name: str, until_version: str): @functools.wraps(func) def wrapped(*args, **kwargs): if old_name in kwargs: - warn_deprecated(f"In function '{func.__name__}' argument '{old_name}' is renamed to '{new_name}' " - f"and will be removed in aiogram {until_version}", stacklevel=3) + warn_deprecated(f"In function '{func.__name__}' argument '{old_name}' " + f"is renamed to '{new_name}' " + f"and will be removed in aiogram {until_version}", + stacklevel=stacklevel) kwargs.update( { new_name: kwargs[old_name], From f750ea13f5660889747a1f887e2849878739304b Mon Sep 17 00:00:00 2001 From: birdi Date: Sun, 11 Aug 2019 00:09:44 +0300 Subject: [PATCH 4/7] fix typo --- aiogram/utils/deprecated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index 4b954c56..3917c72c 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -78,7 +78,7 @@ def warn_deprecated(message, warning=DeprecationWarning, stacklevel=2): def renamed_argument(old_name: str, new_name: str, until_version: str, stacklevel: int = 3): """ - A meta-decorator to mark some arguments in function as deprecated. + A meta-decorator to mark an argument as deprecated. .. code-block:: python3 From e1cd68d4d3a4ab286398df2a2450073f27593920 Mon Sep 17 00:00:00 2001 From: birdi Date: Sun, 11 Aug 2019 01:15:03 +0300 Subject: [PATCH 5/7] fix quotes --- aiogram/utils/deprecated.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index 3917c72c..89081c8b 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -120,8 +120,8 @@ def renamed_argument(old_name: str, new_name: str, until_version: str, stackleve @functools.wraps(func) def wrapped(*args, **kwargs): if old_name in kwargs: - warn_deprecated(f"In function '{func.__name__}' argument '{old_name}' " - f"is renamed to '{new_name}' " + warn_deprecated(f"In function `{func.__name__}` argument `{old_name}` " + f"is renamed to `{new_name}` " f"and will be removed in aiogram {until_version}", stacklevel=stacklevel) kwargs.update( From 9ea22a29fc3a14beedeafd22d7f250a1aa45368a Mon Sep 17 00:00:00 2001 From: birdi Date: Mon, 12 Aug 2019 14:16:05 +0300 Subject: [PATCH 6/7] fix docs --- aiogram/utils/deprecated.py | 6 ++---- docs/source/utils/deprecated.rst | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index 89081c8b..ecb66e6a 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -1,7 +1,3 @@ -""" -Source: https://stackoverflow.com/questions/2536307/decorators-in-the-python-standard-lib-deprecated-specifically -""" - import functools import inspect import warnings @@ -13,6 +9,8 @@ def deprecated(reason): This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used. + + Source: https://stackoverflow.com/questions/2536307/decorators-in-the-python-standard-lib-deprecated-specifically """ if isinstance(reason, str): diff --git a/docs/source/utils/deprecated.rst b/docs/source/utils/deprecated.rst index ce80bb0e..7f2f07cc 100644 --- a/docs/source/utils/deprecated.rst +++ b/docs/source/utils/deprecated.rst @@ -1,8 +1,5 @@ ========== Deprecated ========== -.. literalinclude:: ../../../aiogram/utils/deprecated.py - :caption: deprecated.py - :language: python3 - :linenos: - +.. automodule:: aiogram.utils.deprecated + :members: From 026416a66882ed445f71918df1b12872deb15e25 Mon Sep 17 00:00:00 2001 From: birdi Date: Mon, 12 Aug 2019 14:16:38 +0300 Subject: [PATCH 7/7] refactoring --- aiogram/utils/deprecated.py | 5 ++--- examples/webhook_example.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index ecb66e6a..8a527420 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -40,7 +40,7 @@ def deprecated(reason): return decorator - elif inspect.isclass(reason) or inspect.isfunction(reason): + if inspect.isclass(reason) or inspect.isfunction(reason): # The @deprecated is used without any 'reason'. # @@ -64,8 +64,7 @@ def deprecated(reason): return wrapper1 - else: - raise TypeError(repr(type(reason))) + raise TypeError(repr(type(reason))) def warn_deprecated(message, warning=DeprecationWarning, stacklevel=2): diff --git a/examples/webhook_example.py b/examples/webhook_example.py index 0f6ae3cd..ca8028fd 100644 --- a/examples/webhook_example.py +++ b/examples/webhook_example.py @@ -62,7 +62,7 @@ async def cmd_about(message: types.Message): async def cancel(message: types.Message): # Get current state context - state = dp.current_state(chat=message.chat.id, user=message.from_user.id) + state = dp.current_state(chat_id=message.chat.id, user_id=message.from_user.id) # If current user in any state - cancel it. if await state.get_state() is not None: