diff --git a/docs/source/telegram.bot.rst b/docs/source/telegram.bot.rst index 6b620b5fb21..5f78d37781d 100644 --- a/docs/source/telegram.bot.rst +++ b/docs/source/telegram.bot.rst @@ -3,4 +3,5 @@ telegram.Bot .. autoclass:: telegram.Bot :members: - :show-inheritance: \ No newline at end of file + :show-inheritance: + :special-members: __reduce__, __deepcopy__ \ No newline at end of file diff --git a/telegram/_bot.py b/telegram/_bot.py index 35dc629bdb3..332b8785e3a 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -141,7 +141,8 @@ class Bot(TelegramObject, AbstractAsyncContextManager): passing files. * Bots should not be serialized since if you for e.g. change the bots token, then your serialized instance will not reflect that change. Trying to pickle a bot instance will - raise :exc:`pickle.PicklingError`. + raise :exc:`pickle.PicklingError`. Trying to deepcopy a bot instance will raise + :exc:`TypeError`. Examples: :any:`Raw API Bot ` @@ -167,6 +168,7 @@ class Bot(TelegramObject, AbstractAsyncContextManager): :class:`telegram.ext.Defaults`, please use the subclass :class:`telegram.ext.ExtBot` instead. * Attempting to pickle a bot instance will now raise :exc:`pickle.PicklingError`. + * Attempting to deepcopy a bot instance will now raise :exc:`TypeError`. * The following are now keyword-only arguments in Bot methods: ``location``, ``filename``, ``venue``, ``contact``, ``{read, write, connect, pool}_timeout``, ``api_kwargs``. Use a named argument for those, @@ -302,9 +304,27 @@ def private_key(self) -> Optional[Any]: return self._private_key def __reduce__(self) -> NoReturn: - """Called by pickle.dumps(). Serializing bots is unadvisable, so we forbid pickling.""" + """Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not + be pickled and this method will always raise an exception. + + .. versionadded:: 20.0 + + Raises: + :exc:`pickle.PicklingError` + """ raise pickle.PicklingError("Bot objects cannot be pickled!") + def __deepcopy__(self, memodict: dict) -> NoReturn: + """Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not + be deepcopied and this method will always raise an exception. + + .. versionadded:: 20.0 + + Raises: + :exc:`TypeError` + """ + raise TypeError("Bot objects cannot be deepcopied!") + # TODO: After https://youtrack.jetbrains.com/issue/PY-50952 is fixed, we can revisit this and # consider adding Paramspec from typing_extensions to properly fix this. Currently a workaround def _log(func: Any): # type: ignore[no-untyped-def] # skipcq: PY-D0003 diff --git a/tests/test_bot.py b/tests/test_bot.py index cbbb0696c29..916461bccf0 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -436,6 +436,10 @@ def test_bot_pickling_error(self, bot): with pytest.raises(pickle.PicklingError, match="Bot objects cannot be pickled"): pickle.dumps(bot) + def test_bot_deepcopy_error(self, bot): + with pytest.raises(TypeError, match="Bot objects cannot be deepcopied"): + copy.deepcopy(bot) + @bot_methods(ext_bot=False) async def test_defaults_handling( self, bot_class, bot_method_name, bot_method, bot, raw_bot, monkeypatch