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.
This commit is contained in:
Lonami Exo 2018-08-21 11:08:08 +02:00
parent 9f237cc928
commit d474458136
6 changed files with 26 additions and 31 deletions

View File

@ -270,8 +270,6 @@ class TelegramBaseClient(abc.ABC):
# Some further state for subclasses # Some further state for subclasses
self._event_builders = [] self._event_builders = []
self._events_pending_resolve = []
self._event_resolve_lock = asyncio.Lock()
self._conversations = {} self._conversations = {}
# Default parse mode # Default parse mode

View File

@ -89,7 +89,7 @@ class UpdateMethods(UserMethods):
elif not event: elif not event:
event = events.Raw() event = events.Raw()
self._events_pending_resolve.append(event) self._loop.create_task(event.resolve(self))
self._event_builders.append((event, callback)) self._event_builders.append((event, callback))
def remove_event_handler(self, callback, event=None): def remove_event_handler(self, callback, event=None):
@ -249,17 +249,6 @@ class UpdateMethods(UserMethods):
self._dispatching_updates_queue.clear() self._dispatching_updates_queue.clear()
async def _dispatch_update(self, update): 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) built = EventBuilderDict(self, update)
if self._conversations: if self._conversations:
for conv in self._conversations.values(): for conv in self._conversations.values():
@ -274,7 +263,15 @@ class UpdateMethods(UserMethods):
for builder, callback in self._event_builders: for builder, callback in self._event_builders:
event = built[type(builder)] 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 continue
try: try:

View File

@ -57,7 +57,7 @@ class EventBuilder(abc.ABC):
def __init__(self, chats=None, blacklist_chats=False): def __init__(self, chats=None, blacklist_chats=False):
self.chats = chats self.chats = chats
self.blacklist_chats = blacklist_chats self.blacklist_chats = blacklist_chats
self._self_id = None self.resolved = False
@classmethod @classmethod
@abc.abstractmethod @abc.abstractmethod
@ -66,6 +66,8 @@ class EventBuilder(abc.ABC):
async def resolve(self, client): async def resolve(self, client):
"""Helper method to allow event builders to be resolved before usage""" """Helper method to allow event builders to be resolved before usage"""
if not self.resolved:
self.resolved = True
self.chats = await _into_id_set(client, self.chats) self.chats = await _into_id_set(client, self.chats)
if not EventBuilder.self_id: if not EventBuilder.self_id:
EventBuilder.self_id = await client.get_peer_id('me') EventBuilder.self_id = await client.get_peer_id('me')

View File

@ -71,6 +71,7 @@ class NewMessage(EventBuilder):
)) ))
async def resolve(self, client): async def resolve(self, client):
if not self.resolved:
await super().resolve(client) await super().resolve(client)
self.from_users = await _into_id_set(client, self.from_users) self.from_users = await _into_id_set(client, self.from_users)

View File

@ -27,7 +27,7 @@ class Raw(EventBuilder):
self.types = tuple(types) self.types = tuple(types)
async def resolve(self, client): async def resolve(self, client):
pass self.resolved = True
@classmethod @classmethod
def build(cls, update): def build(cls, update):

View File

@ -260,6 +260,8 @@ class Conversation(ChatGetter):
if isinstance(event, type): if isinstance(event, type):
event = event() event = event()
await event.resolve()
counter = Conversation._custom_counter counter = Conversation._custom_counter
Conversation._custom_counter += 1 Conversation._custom_counter += 1
@ -270,21 +272,16 @@ class Conversation(ChatGetter):
finally: finally:
del self._custom[counter] del self._custom[counter]
self._custom[counter] = (event, future, False) self._custom[counter] = (event, future)
return await result() return await result()
async def _check_custom(self, built): async def _check_custom(self, built):
# TODO This code is quite much a copy paste of registering events # TODO This code is quite much a copy paste of registering events
# in the client, resolving them and setting the client; perhaps # in the client, resolving them and setting the client; perhaps
# there is a better way? # 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) ev_type = type(ev)
if built[ev_type]: if built[ev_type] and ev.filter(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]) fut.set_result(built[ev_type])
def _on_new_message(self, response): def _on_new_message(self, response):