Skip to content

Commit 29266d7

Browse files
committed
Introduce replace_bots argument
1 parent 4698bc4 commit 29266d7

File tree

4 files changed

+195
-101
lines changed

4 files changed

+195
-101
lines changed

telegram/ext/_basepersistence.py

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,19 @@ class BasePersistence(Generic[UD, CD, BD], ABC):
8282
:meth:`refresh_bot_data`.
8383
8484
Warning:
85-
Persistence will try to replace :class:`telegram.Bot` instances by :attr:`REPLACED_BOT` and
86-
insert the bot set with :meth:`set_bot` upon loading of the data. This is to ensure that
87-
changes to the bot apply to the saved objects, too. If you change the bots token, this may
88-
lead to e.g. ``Chat not found`` errors. For the limitations on replacing bots see
89-
:meth:`replace_bot` and :meth:`insert_bot`.
85+
By default, persistence will try to replace :class:`telegram.Bot` instances in your data by
86+
:attr:`REPLACED_BOT` and insert the bot set with :meth:`set_bot` upon loading of the data.
87+
This is to ensure that changes to the bot apply to the saved objects, too.
88+
89+
For the limitations on replacing bots see :meth:`replace_bot` and :meth:`insert_bot`.
90+
91+
This as also relevant for all objects from the :mod:`telegram` package that have shortcuts
92+
to bot methods (e.g. :meth:`telegram.Message.reply_text`), since they contain a reference
93+
to a :class:`telegram.Bot` object (see :meth:`telegram.TelegramObject.get_bot`).
94+
95+
Because this replacing & insertion needs to go through all your data, it may add
96+
considerable overhead. If you data does not contain any references to :class:`telegram.Bot`
97+
instances, you can deactivate this behavior via the ``replace_bots`` parameter.
9098
9199
Note:
92100
:meth:`replace_bot` and :meth:`insert_bot` are used *independently* of the implementation
@@ -100,84 +108,95 @@ class BasePersistence(Generic[UD, CD, BD], ABC):
100108
store_data (:class:`PersistenceInput`, optional): Specifies which kinds of data will be
101109
saved by this persistence instance. By default, all available kinds of data will be
102110
saved.
111+
replace_bots (:class:`PersistenceInput`, optional): Specifies for which kinds of data
112+
:meth:`replace_bot` and :meth:`insert_bot` will be called. By default, this is the case
113+
for all available kinds of data.
114+
115+
.. versionadded:: 14.0
103116
104117
Attributes:
105118
store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this
106119
persistence instance.
120+
replace_bots (:class:`PersistenceInput`, optional): Specifies for which kinds of data
121+
:meth:`replace_bot` and :meth:`insert_bot` will be called.
122+
123+
.. versionadded:: 14.0
107124
"""
108125

109126
__slots__ = (
110127
'bot',
111128
'store_data',
112-
'__dict__', # __dict__ is included because we replace methods in the __new__
129+
'replace_bots',
130+
# __dict__ is included because we replace methods in __inject_replace_insert_bot
131+
'__dict__',
113132
)
114133

115-
def __new__(
116-
cls, *args: object, **kwargs: object # pylint: disable=unused-argument
117-
) -> 'BasePersistence':
134+
def __init__(self, store_data: PersistenceInput = None, replace_bots: PersistenceInput = None):
135+
self.store_data = store_data or PersistenceInput()
136+
self.replace_bots = replace_bots or PersistenceInput()
137+
self.__inject_replace_insert_bot()
138+
139+
self.bot: Bot = None # type: ignore[assignment]
140+
141+
def __inject_replace_insert_bot(self):
118142
"""This overrides the get_* and update_* methods to use insert/replace_bot.
119-
That has the side effect that we always pass deepcopied data to those methods, so in
120-
Pickle/DictPersistence we don't have to worry about copying the data again.
121143
122-
Note: This doesn't hold for second tuple-entry of callback_data. That's a Dict[str, str],
144+
Note: Depending on `self.replace_bots`, we pass the exact objects to the update_* methods,
145+
so in Pickle/DictPersistence we need to make sure not to modify such data in place!
146+
147+
Note: The second tuple-entry of callback_data is never affected. That's a Dict[str, str],
123148
so no bots to replace anyway.
124149
"""
125-
instance = super().__new__(cls)
126-
get_user_data = instance.get_user_data
127-
get_chat_data = instance.get_chat_data
128-
get_bot_data = instance.get_bot_data
129-
get_callback_data = instance.get_callback_data
130-
update_user_data = instance.update_user_data
131-
update_chat_data = instance.update_chat_data
132-
update_bot_data = instance.update_bot_data
133-
update_callback_data = instance.update_callback_data
150+
get_user_data = self.get_user_data
151+
get_chat_data = self.get_chat_data
152+
get_bot_data = self.get_bot_data
153+
get_callback_data = self.get_callback_data
154+
update_user_data = self.update_user_data
155+
update_chat_data = self.update_chat_data
156+
update_bot_data = self.update_bot_data
157+
update_callback_data = self.update_callback_data
134158

135159
def get_user_data_insert_bot() -> DefaultDict[int, UD]:
136-
return instance.insert_bot(get_user_data())
160+
return self.insert_bot(get_user_data())
137161

138162
def get_chat_data_insert_bot() -> DefaultDict[int, CD]:
139-
return instance.insert_bot(get_chat_data())
163+
return self.insert_bot(get_chat_data())
140164

141165
def get_bot_data_insert_bot() -> BD:
142-
return instance.insert_bot(get_bot_data())
166+
return self.insert_bot(get_bot_data())
143167

144168
def get_callback_data_insert_bot() -> Optional[CDCData]:
145169
cdc_data = get_callback_data()
146170
if cdc_data is None:
147171
return None
148-
return instance.insert_bot(cdc_data[0]), cdc_data[1]
172+
return self.insert_bot(cdc_data[0]), cdc_data[1]
149173

150174
def update_user_data_replace_bot(user_id: int, data: UD) -> None:
151-
return update_user_data(user_id, instance.replace_bot(data))
175+
return update_user_data(user_id, self.replace_bot(data))
152176

153177
def update_chat_data_replace_bot(chat_id: int, data: CD) -> None:
154-
return update_chat_data(chat_id, instance.replace_bot(data))
178+
return update_chat_data(chat_id, self.replace_bot(data))
155179

156180
def update_bot_data_replace_bot(data: BD) -> None:
157-
return update_bot_data(instance.replace_bot(data))
181+
return update_bot_data(self.replace_bot(data))
158182

159183
def update_callback_data_replace_bot(data: CDCData) -> None:
160184
obj_data, queue = data
161-
return update_callback_data((instance.replace_bot(obj_data), queue))
185+
return update_callback_data((self.replace_bot(obj_data), queue))
162186

163187
# Adds to __dict__
164-
setattr(instance, 'get_user_data', get_user_data_insert_bot)
165-
setattr(instance, 'get_chat_data', get_chat_data_insert_bot)
166-
setattr(instance, 'get_bot_data', get_bot_data_insert_bot)
167-
setattr(instance, 'get_callback_data', get_callback_data_insert_bot)
168-
setattr(instance, 'update_user_data', update_user_data_replace_bot)
169-
setattr(instance, 'update_chat_data', update_chat_data_replace_bot)
170-
setattr(instance, 'update_bot_data', update_bot_data_replace_bot)
171-
setattr(instance, 'update_callback_data', update_callback_data_replace_bot)
172-
return instance
173-
174-
def __init__(
175-
self,
176-
store_data: PersistenceInput = None,
177-
):
178-
self.store_data = store_data or PersistenceInput()
179-
180-
self.bot: Bot = None # type: ignore[assignment]
188+
if self.replace_bots.bot_data:
189+
self.get_bot_data = get_bot_data_insert_bot
190+
self.update_bot_data = update_bot_data_replace_bot
191+
if self.replace_bots.chat_data:
192+
self.get_chat_data = get_chat_data_insert_bot
193+
self.update_chat_data = update_chat_data_replace_bot
194+
if self.replace_bots.user_data:
195+
self.get_user_data = get_user_data_insert_bot
196+
self.update_user_data = update_user_data_replace_bot
197+
if self.replace_bots.callback_data:
198+
self.get_callback_data = get_callback_data_insert_bot
199+
self.update_callback_data = update_callback_data_replace_bot
181200

182201
def set_bot(self, bot: Bot) -> None:
183202
"""Set the Bot to be used by this persistence instance.

telegram/ext/_dictpersistence.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,19 @@ class DictPersistence(BasePersistence):
4141
classes that need to JSON-serialize the stored data before writing them to file/database.
4242
4343
Warning:
44-
:class:`DictPersistence` will try to replace :class:`telegram.Bot` instances by
45-
:attr:`REPLACED_BOT` and insert the bot set with
46-
:meth:`telegram.ext.BasePersistence.set_bot` upon loading of the data. This is to ensure
47-
that changes to the bot apply to the saved objects, too. If you change the bots token, this
48-
may lead to e.g. ``Chat not found`` errors. For the limitations on replacing bots see
49-
:meth:`telegram.ext.BasePersistence.replace_bot` and
50-
:meth:`telegram.ext.BasePersistence.insert_bot`.
44+
By default, persistence will try to replace :class:`telegram.Bot` instances in your data by
45+
:attr:`REPLACED_BOT` and insert the bot set with :meth:`set_bot` upon loading of the data.
46+
This is to ensure that changes to the bot apply to the saved objects, too.
47+
48+
For the limitations on replacing bots see :meth:`replace_bot` and :meth:`insert_bot`.
49+
50+
This as also relevant for all objects from the :mod:`telegram` package that have shortcuts
51+
to bot methods (e.g. :meth:`telegram.Message.reply_text`), since they contain a reference
52+
to a :class:`telegram.Bot` object (see :meth:`telegram.TelegramObject.get_bot`).
53+
54+
Because this replacing & insertion needs to go through all your data, it may add
55+
considerable overhead. If you data does not contain any references to :class:`telegram.Bot`
56+
instances, you can deactivate this behavior via the ``replace_bots`` parameter.
5157
5258
.. versionchanged:: 14.0
5359
The parameters and attributes ``store_*_data`` were replaced by :attr:`store_data`.
@@ -56,6 +62,11 @@ class DictPersistence(BasePersistence):
5662
store_data (:class:`PersistenceInput`, optional): Specifies which kinds of data will be
5763
saved by this persistence instance. By default, all available kinds of data will be
5864
saved.
65+
replace_bots (:class:`PersistenceInput`, optional): Specifies for which kinds of data
66+
:meth:`replace_bot` and :meth:`insert_bot` will be called. By default, this is the case
67+
for all available kinds of data.
68+
69+
.. versionadded:: 14.0
5970
user_data_json (:obj:`str`, optional): JSON string that will be used to reconstruct
6071
user_data on creating this persistence. Default is ``""``.
6172
chat_data_json (:obj:`str`, optional): JSON string that will be used to reconstruct
@@ -72,6 +83,10 @@ class DictPersistence(BasePersistence):
7283
Attributes:
7384
store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this
7485
persistence instance.
86+
replace_bots (:class:`PersistenceInput`, optional): Specifies for which kinds of data
87+
:meth:`replace_bot` and :meth:`insert_bot` will be called.
88+
89+
.. versionadded:: 14.0
7590
"""
7691

7792
__slots__ = (
@@ -90,13 +105,14 @@ class DictPersistence(BasePersistence):
90105
def __init__(
91106
self,
92107
store_data: PersistenceInput = None,
108+
replace_bots: PersistenceInput = None,
93109
user_data_json: str = '',
94110
chat_data_json: str = '',
95111
bot_data_json: str = '',
96112
conversations_json: str = '',
97113
callback_data_json: str = '',
98114
):
99-
super().__init__(store_data=store_data)
115+
super().__init__(store_data=store_data, replace_bots=replace_bots)
100116
self._user_data = None
101117
self._chat_data = None
102118
self._bot_data = None

telegram/ext/_picklepersistence.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,19 @@ class PicklePersistence(BasePersistence[UD, CD, BD]):
4040
"""Using python's builtin pickle for making your bot persistent.
4141
4242
Warning:
43-
:class:`PicklePersistence` will try to replace :class:`telegram.Bot` instances by
44-
:attr:`REPLACED_BOT` and insert the bot set with
45-
:meth:`telegram.ext.BasePersistence.set_bot` upon loading of the data. This is to ensure
46-
that changes to the bot apply to the saved objects, too. If you change the bots token, this
47-
may lead to e.g. ``Chat not found`` errors. For the limitations on replacing bots see
48-
:meth:`telegram.ext.BasePersistence.replace_bot` and
49-
:meth:`telegram.ext.BasePersistence.insert_bot`.
43+
By default, persistence will try to replace :class:`telegram.Bot` instances in your data by
44+
:attr:`REPLACED_BOT` and insert the bot set with :meth:`set_bot` upon loading of the data.
45+
This is to ensure that changes to the bot apply to the saved objects, too.
46+
47+
For the limitations on replacing bots see :meth:`replace_bot` and :meth:`insert_bot`.
48+
49+
This as also relevant for all objects from the :mod:`telegram` package that have shortcuts
50+
to bot methods (e.g. :meth:`telegram.Message.reply_text`), since they contain a reference
51+
to a :class:`telegram.Bot` object (see :meth:`telegram.TelegramObject.get_bot`).
52+
53+
Because this replacing & insertion needs to go through all your data, it may add
54+
considerable overhead. If you data does not contain any references to :class:`telegram.Bot`
55+
instances, you can deactivate this behavior via the ``replace_bots`` parameter.
5056
5157
.. versionchanged:: 14.0
5258
@@ -61,6 +67,11 @@ class PicklePersistence(BasePersistence[UD, CD, BD]):
6167
store_data (:class:`PersistenceInput`, optional): Specifies which kinds of data will be
6268
saved by this persistence instance. By default, all available kinds of data will be
6369
saved.
70+
replace_bots (:class:`PersistenceInput`, optional): Specifies for which kinds of data
71+
:meth:`replace_bot` and :meth:`insert_bot` will be called. By default, this is the case
72+
for all available kinds of data.
73+
74+
.. versionadded:: 14.0
6475
single_file (:obj:`bool`, optional): When :obj:`False` will store 5 separate files of
6576
`filename_user_data`, `filename_bot_data`, `filename_chat_data`,
6677
`filename_callback_data` and `filename_conversations`. Default is :obj:`True`.
@@ -80,6 +91,10 @@ class PicklePersistence(BasePersistence[UD, CD, BD]):
8091
When :attr:`single_file` is :obj:`False` this will be used as a prefix.
8192
store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this
8293
persistence instance.
94+
replace_bots (:class:`PersistenceInput`, optional): Specifies for which kinds of data
95+
:meth:`replace_bot` and :meth:`insert_bot` will be called.
96+
97+
.. versionadded:: 14.0
8398
single_file (:obj:`bool`): Optional. When :obj:`False` will store 5 separate files of
8499
`filename_user_data`, `filename_bot_data`, `filename_chat_data`,
85100
`filename_callback_data` and `filename_conversations`. Default is :obj:`True`.
@@ -110,6 +125,7 @@ def __init__(
110125
self: 'PicklePersistence[Dict, Dict, Dict]',
111126
filepath: FilePathInput,
112127
store_data: PersistenceInput = None,
128+
replace_bots: PersistenceInput = None,
113129
single_file: bool = True,
114130
on_flush: bool = False,
115131
):
@@ -120,6 +136,7 @@ def __init__(
120136
self: 'PicklePersistence[UD, CD, BD]',
121137
filepath: FilePathInput,
122138
store_data: PersistenceInput = None,
139+
replace_bots: PersistenceInput = None,
123140
single_file: bool = True,
124141
on_flush: bool = False,
125142
context_types: ContextTypes[Any, UD, CD, BD] = None,
@@ -130,11 +147,12 @@ def __init__(
130147
self,
131148
filepath: FilePathInput,
132149
store_data: PersistenceInput = None,
150+
replace_bots: PersistenceInput = None,
133151
single_file: bool = True,
134152
on_flush: bool = False,
135153
context_types: ContextTypes[Any, UD, CD, BD] = None,
136154
):
137-
super().__init__(store_data=store_data)
155+
super().__init__(store_data=store_data, replace_bots=replace_bots)
138156
self.filepath = Path(filepath)
139157
self.single_file = single_file
140158
self.on_flush = on_flush

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy