From d4744581366828c6948f9eab066db78c7dd267eb Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Tue, 21 Aug 2018 11:08:08 +0200 Subject: [PATCH] Simplify event resolving logic Although this commit introduces a race condition since an event may only be half-resolved. A lock is thus needed, but it depends on an event-loop to which we don't have access in the class-level. --- telethon/client/telegrambaseclient.py | 2 -- telethon/client/updates.py | 23 ++++++++++------------- telethon/events/common.py | 10 ++++++---- telethon/events/newmessage.py | 5 +++-- telethon/events/raw.py | 2 +- telethon/tl/custom/conversation.py | 15 ++++++--------- 6 files changed, 26 insertions(+), 31 deletions(-) diff --git a/telethon/client/telegrambaseclient.py b/telethon/client/telegrambaseclient.py index e4ae156a..65c20177 100644 --- a/telethon/client/telegrambaseclient.py +++ b/telethon/client/telegrambaseclient.py @@ -270,8 +270,6 @@ class TelegramBaseClient(abc.ABC): # Some further state for subclasses self._event_builders = [] - self._events_pending_resolve = [] - self._event_resolve_lock = asyncio.Lock() self._conversations = {} # Default parse mode diff --git a/telethon/client/updates.py b/telethon/client/updates.py index 30a7410f..0dff677a 100644 --- a/telethon/client/updates.py +++ b/telethon/client/updates.py @@ -89,7 +89,7 @@ class UpdateMethods(UserMethods): elif not event: event = events.Raw() - self._events_pending_resolve.append(event) + self._loop.create_task(event.resolve(self)) self._event_builders.append((event, callback)) def remove_event_handler(self, callback, event=None): @@ -249,17 +249,6 @@ class UpdateMethods(UserMethods): self._dispatching_updates_queue.clear() async def _dispatch_update(self, update): - if self._events_pending_resolve: - if self._event_resolve_lock.locked(): - async with self._event_resolve_lock: - pass - else: - async with self._event_resolve_lock: - for event in self._events_pending_resolve: - await event.resolve(self) - - self._events_pending_resolve.clear() - built = EventBuilderDict(self, update) if self._conversations: for conv in self._conversations.values(): @@ -274,7 +263,15 @@ class UpdateMethods(UserMethods): for builder, callback in self._event_builders: event = built[type(builder)] - if not event or not builder.filter(event): + if not event: + continue + + # TODO Lock until it's resolved; the task for resolving + # was already created when adding the event handler. + if not builder.resolved: + await builder.resolve() + + if not builder.filter(event): continue try: diff --git a/telethon/events/common.py b/telethon/events/common.py index 23b018e1..e1eb2ee6 100644 --- a/telethon/events/common.py +++ b/telethon/events/common.py @@ -57,7 +57,7 @@ class EventBuilder(abc.ABC): def __init__(self, chats=None, blacklist_chats=False): self.chats = chats self.blacklist_chats = blacklist_chats - self._self_id = None + self.resolved = False @classmethod @abc.abstractmethod @@ -66,9 +66,11 @@ class EventBuilder(abc.ABC): async def resolve(self, client): """Helper method to allow event builders to be resolved before usage""" - self.chats = await _into_id_set(client, self.chats) - if not EventBuilder.self_id: - EventBuilder.self_id = await client.get_peer_id('me') + if not self.resolved: + self.resolved = True + self.chats = await _into_id_set(client, self.chats) + if not EventBuilder.self_id: + EventBuilder.self_id = await client.get_peer_id('me') def filter(self, event): """ diff --git a/telethon/events/newmessage.py b/telethon/events/newmessage.py index e270c094..902a6f73 100644 --- a/telethon/events/newmessage.py +++ b/telethon/events/newmessage.py @@ -71,8 +71,9 @@ class NewMessage(EventBuilder): )) async def resolve(self, client): - await super().resolve(client) - self.from_users = await _into_id_set(client, self.from_users) + if not self.resolved: + await super().resolve(client) + self.from_users = await _into_id_set(client, self.from_users) @classmethod def build(cls, update): diff --git a/telethon/events/raw.py b/telethon/events/raw.py index 67232292..5bd11721 100644 --- a/telethon/events/raw.py +++ b/telethon/events/raw.py @@ -27,7 +27,7 @@ class Raw(EventBuilder): self.types = tuple(types) async def resolve(self, client): - pass + self.resolved = True @classmethod def build(cls, update): diff --git a/telethon/tl/custom/conversation.py b/telethon/tl/custom/conversation.py index 7318fe63..577220ca 100644 --- a/telethon/tl/custom/conversation.py +++ b/telethon/tl/custom/conversation.py @@ -260,6 +260,8 @@ class Conversation(ChatGetter): if isinstance(event, type): event = event() + await event.resolve() + counter = Conversation._custom_counter Conversation._custom_counter += 1 @@ -270,22 +272,17 @@ class Conversation(ChatGetter): finally: del self._custom[counter] - self._custom[counter] = (event, future, False) + self._custom[counter] = (event, future) return await result() async def _check_custom(self, built): # TODO This code is quite much a copy paste of registering events # in the client, resolving them and setting the client; perhaps # there is a better way? - for i, (ev, fut, resolved) in self._custom.items(): + for i, (ev, fut) in self._custom.items(): ev_type = type(ev) - if built[ev_type]: - if not resolved: - await ev.resolve(self._client) - self._custom[i] = (ev, fut, True) - - if ev.filter(built[ev_type]): - fut.set_result(built[ev_type]) + if built[ev_type] and ev.filter(built[ev_type]): + fut.set_result(built[ev_type]) def _on_new_message(self, response): response = response.message