-
Notifications
You must be signed in to change notification settings - Fork 6k
Remove @_log decorator by logging in Bot._do_post
#4122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hi. Thanks for the PR! I see that you explicitly use Your changes also lead to a number of problems reported by mypy (see the pre-commit check). I haven't yet checked if they are genuine problems that surface due to improved type hinting or if some of them are due to problems in your changes. Have you checked also the other options mentioned in microsoft/pyright#6669 (comment), i.e. using a typevar or no annotation at all, and how those behave with VSCode & PyCharm? |
I agree with you,
All of them seem to be genuine, and were quite easy to fix. The changes required are just in a few spots, so you can make sure everything is correct just by looking at the commits. |
|
Is there a specific reason why python-telegram-bot/tests/test_message.py Line 457 in 1cf63c2
is an object instead of the expected string or int? |
Bibo-Joshi
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your analysis! I had a look at the follow-up changes in the other places and left some comments about them.
is an object instead of the expected string or int?
oh, that's just for testing. This makes it easier to check that we received the actualy object that we passed without having to compare a bunch of strings :)
Now to the acutal topic :D The typing of the log decorator seems to be a realy problem. The definiton place alone apparently is a problem: Note that it was originally placed outside the class bot #2686 moved it into the class (IIRC that was actually to improve the typing).
Looking at the function itself, what we acually want it to do is to do some logging when
- we start calling a specific TG endpoint
- we got the result
- we we finish calling the TG endpoint
Instead of doing this via a decorator, we could that instead in a centralized place in _do_post.
Pros:
- no need to add the
@_logdecorator everywhere (DRY) - no typing problems
Cons:
- only
endpointis available, not the function name (i.e.sendMessageinstead ofsend_message) - result available only as json data, not as
TelegramObject
Both are acceptable for debug logging IMO.
I propose that we go forward in that direction instead.
| return await self.get_bot().set_chat_administrator_custom_title( | ||
| chat_id=self.id, | ||
| user_id=user_id, | ||
| user_id=int(user_id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of casting to int, we should adapt the type hint of the argument to match the one of Bot.set_admin_title. I see that in test_chat.py, the corresponding test test_set_administrator_custom_title and actually a few other tests are missing the assertions for check_shortcut_signature and check_shortcut_call. I expect that those would have already caught that …
We should add them. If not done within the scope of this PR. we should open an issue about that.
telegram/_message.py
Outdated
| ) | ||
| return await self.get_bot().send_game( | ||
| chat_id=chat_id, | ||
| chat_id=int(chat_id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would vote for a # type: ignore[arg-type] instead, here. My reasoning:
chat_idis annotated here asstr | intas return value of the internal_parse_quote_arguments, which is supposed to be used by all shortcut methods, even those that accept strings forchat_id. So forcing_parse_quote_argumentsto return an integer doesn't make sense- However, we generally do not enforce type checks before passing values to Telegram. If a user would pass a string here, Telegram will through a descriptive error message if it can't parse it. That should be enough
telegram/ext/_extbot.py
Outdated
| reply_markup=cast( | ||
| Optional["InlineKeyboardMarkup"], self._replace_keyboard(reply_markup) | ||
| ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we can instead improve the typing of _replace_keyboard here via a typevar, i.e.
_replace_keyboard(self, reply_markup: Optional[KT]) -> Optional[KT]:
telegram/ext/_updater.py
Outdated
| _LOGGER.debug("Dropping pending updates from Telegram server") | ||
| await self.bot.set_webhook( | ||
| url=webhook_url, | ||
| url=webhook_url if webhook_url is not None else "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
passing an empty string wouldn't make sense. Since this callback function is used only within an if webhook_url cause (see line 813), a type: ignore would be enough IMO.
|
@Bibo-Joshi yeah I am fine with the massively reduced complexity in favour of better type hinting. If people need more info about the called method they should just debug step into or write their own log lines in user level code |
also agree with dropping the decorator. |
|
@TendTo would you be interested in working on the alternative approach? |
I thought this would have been a matter of a few targeted changes, but I now see it is a bit more complex than that. I still want to give it a try, but since I am a bit busy, if someone can solve the issue faster, they are free to do so. |
|
So, I tried following your advice of centralizing the logging in the method async def _do_post(self, endpoint: str, ....):
self._LOGGER.debug("Calling: %s", endpoint)
...
result = await request.post(
url=f"{self._base_url}/{endpoint}",
request_data=request_data,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
)
self._LOGGER.debug("Result from: %s", endpoint)
self._LOGGER.debug(result)
return resultor using the traceback provided by python: async def _do_post(self, endpoint: str, ....):
# is this -4 always correct?
self._LOGGER.debug("Calling: %s", traceback.extract_stack(limit=4)[-4].name)
...
result = await request.post(
url=f"{self._base_url}/{endpoint}",
request_data=request_data,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
)
self._LOGGER.debug("Result from: %s", traceback.extract_stack(limit=4)[-4].name)
self._LOGGER.debug(result)
return resultBased on the approach you prefer, there are a lot of tests that may need to be updated. E.g. python-telegram-bot/tests/test_bot.py Line 421 in 277031c
|
|
Definitely simply go with logging the endpoint. Trying to extract the traceback stack is a bit too complicated for such little benefit. |
@_log decorator by logging in Bot._do_post
closes #4010
Trying to fix the annoying absence of type hinting for every function wrapped in the _log decorator.
This solution should also be compatible with older versions of Python.
I have opted to put all the imports inside the TYPE_CHEKING branch. This requires the use of strings in the type annotations.
It is easy to go with the opposite approach.
Changes should be transparent to the end-user and not affect them in any way, except for the added support of static typing.