Skip to content

Commit 9afd244

Browse files
authored
Merge pull request pyrogram#331 from pyrogram/I329
Closes #329
2 parents e74521b + 8e9e8b4 commit 9afd244

File tree

2 files changed

+88
-40
lines changed

2 files changed

+88
-40
lines changed

pyrogram/client/client.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,18 @@ def initialize(self):
328328

329329
self.is_initialized = True
330330

331-
def terminate(self):
331+
def terminate(self, block: bool = True):
332332
"""Terminate the client by shutting down workers.
333333
334334
This method does the opposite of :meth:`~Client.initialize`.
335335
It will stop the dispatcher and shut down updates and download workers.
336336
337+
Parameters:
338+
block (``bool``, *optional*):
339+
Blocks the code execution until the client has been terminated. It is useful with ``block=False`` in
340+
case you want to terminate the own client *within* an handler in order not to cause a deadlock.
341+
Defaults to True.
342+
337343
Raises:
338344
ConnectionError: In case you try to terminate a client that is already terminated.
339345
"""
@@ -345,7 +351,7 @@ def terminate(self):
345351
log.warning("Takeout session {} finished".format(self.takeout_id))
346352

347353
Syncer.remove(self)
348-
self.dispatcher.stop()
354+
self.dispatcher.stop(block)
349355

350356
for _ in range(self.DOWNLOAD_WORKERS):
351357
self.download_queue.put(None)
@@ -840,11 +846,17 @@ def start(self):
840846
self.initialize()
841847
return self
842848

843-
def stop(self):
849+
def stop(self, block: bool = True):
844850
"""Stop the Client.
845851
846852
This method disconnects the client from Telegram and stops the underlying tasks.
847853
854+
Parameters:
855+
block (``bool``, *optional*):
856+
Blocks the code execution until the client has been stopped. It is useful with ``block=False`` in case
857+
you want to stop the own client *within* an handler in order not to cause a deadlock.
858+
Defaults to True.
859+
848860
Returns:
849861
:obj:`Client`: The stopped client itself.
850862
@@ -864,17 +876,23 @@ def stop(self):
864876
865877
app.stop()
866878
"""
867-
self.terminate()
879+
self.terminate(block)
868880
self.disconnect()
869881

870882
return self
871883

872-
def restart(self):
884+
def restart(self, block: bool = True):
873885
"""Restart the Client.
874886
875887
This method will first call :meth:`~Client.stop` and then :meth:`~Client.start` in a row in order to restart
876888
a client using a single method.
877889
890+
Parameters:
891+
block (``bool``, *optional*):
892+
Blocks the code execution until the client has been restarted. It is useful with ``block=False`` in case
893+
you want to restart the own client *within* an handler in order not to cause a deadlock.
894+
Defaults to True.
895+
878896
Returns:
879897
:obj:`Client`: The restarted client itself.
880898
@@ -898,7 +916,7 @@ def restart(self):
898916
899917
app.stop()
900918
"""
901-
self.stop()
919+
self.stop(block)
902920
self.start()
903921

904922
return self
@@ -985,7 +1003,7 @@ def run(self):
9851003
Client.idle()
9861004
self.stop()
9871005

988-
def add_handler(self, handler: Handler, group: int = 0):
1006+
def add_handler(self, handler: Handler, group: int = 0, block: bool = True):
9891007
"""Register an update handler.
9901008
9911009
You can register multiple handlers, but at most one handler within a group will be used for a single update.
@@ -1000,6 +1018,11 @@ def add_handler(self, handler: Handler, group: int = 0):
10001018
group (``int``, *optional*):
10011019
The group identifier, defaults to 0.
10021020
1021+
block (``bool``, *optional*):
1022+
Blocks the code execution until the handler has been added. It is useful with ``block=False`` in case
1023+
you want to register a new handler *within* another handler in order not to cause a deadlock.
1024+
Defaults to True.
1025+
10031026
Returns:
10041027
``tuple``: A tuple consisting of *(handler, group)*.
10051028
@@ -1021,11 +1044,11 @@ def dump(client, message):
10211044
if isinstance(handler, DisconnectHandler):
10221045
self.disconnect_handler = handler.callback
10231046
else:
1024-
self.dispatcher.add_handler(handler, group)
1047+
self.dispatcher.add_handler(handler, group, block)
10251048

10261049
return handler, group
10271050

1028-
def remove_handler(self, handler: Handler, group: int = 0):
1051+
def remove_handler(self, handler: Handler, group: int = 0, block: bool = True):
10291052
"""Remove a previously-registered update handler.
10301053
10311054
Make sure to provide the right group where the handler was added in. You can use the return value of the
@@ -1038,6 +1061,13 @@ def remove_handler(self, handler: Handler, group: int = 0):
10381061
group (``int``, *optional*):
10391062
The group identifier, defaults to 0.
10401063
1064+
block (``bool``, *optional*):
1065+
Blocks the code execution until the handler has been removed. It is useful with ``block=False`` in case
1066+
you want to remove a previously registered handler *within* another handler in order not to cause a
1067+
deadlock.
1068+
Defaults to True.
1069+
1070+
10411071
Example:
10421072
.. code-block:: python
10431073
:emphasize-lines: 11
@@ -1059,7 +1089,7 @@ def dump(client, message):
10591089
if isinstance(handler, DisconnectHandler):
10601090
self.disconnect_handler = None
10611091
else:
1062-
self.dispatcher.remove_handler(handler, group)
1092+
self.dispatcher.remove_handler(handler, group, block)
10631093

10641094
def stop_transmission(self):
10651095
"""Stop downloading or uploading a file.

pyrogram/client/ext/dispatcher.py

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -118,43 +118,61 @@ def start(self):
118118

119119
self.workers_list[-1].start()
120120

121-
def stop(self):
122-
for _ in range(self.workers):
123-
self.updates_queue.put(None)
121+
def stop(self, block: bool = True):
122+
def do_it():
123+
for _ in range(self.workers):
124+
self.updates_queue.put(None)
124125

125-
for worker in self.workers_list:
126-
worker.join()
126+
for worker in self.workers_list:
127+
worker.join()
127128

128-
self.workers_list.clear()
129-
self.locks_list.clear()
130-
self.groups.clear()
129+
self.workers_list.clear()
130+
self.locks_list.clear()
131+
self.groups.clear()
131132

132-
def add_handler(self, handler, group: int):
133-
for lock in self.locks_list:
134-
lock.acquire()
133+
if block:
134+
do_it()
135+
else:
136+
Thread(target=do_it).start()
135137

136-
try:
137-
if group not in self.groups:
138-
self.groups[group] = []
139-
self.groups = OrderedDict(sorted(self.groups.items()))
140-
141-
self.groups[group].append(handler)
142-
finally:
138+
def add_handler(self, handler, group: int, block: bool = True):
139+
def do_it():
143140
for lock in self.locks_list:
144-
lock.release()
145-
146-
def remove_handler(self, handler, group: int):
147-
for lock in self.locks_list:
148-
lock.acquire()
149-
150-
try:
151-
if group not in self.groups:
152-
raise ValueError("Group {} does not exist. Handler was not removed.".format(group))
141+
lock.acquire()
153142

154-
self.groups[group].remove(handler)
155-
finally:
143+
try:
144+
if group not in self.groups:
145+
self.groups[group] = []
146+
self.groups = OrderedDict(sorted(self.groups.items()))
147+
148+
self.groups[group].append(handler)
149+
finally:
150+
for lock in self.locks_list:
151+
lock.release()
152+
153+
if block:
154+
do_it()
155+
else:
156+
Thread(target=do_it).start()
157+
158+
def remove_handler(self, handler, group: int, block: bool = True):
159+
def do_it():
156160
for lock in self.locks_list:
157-
lock.release()
161+
lock.acquire()
162+
163+
try:
164+
if group not in self.groups:
165+
raise ValueError("Group {} does not exist. Handler was not removed.".format(group))
166+
167+
self.groups[group].remove(handler)
168+
finally:
169+
for lock in self.locks_list:
170+
lock.release()
171+
172+
if block:
173+
do_it()
174+
else:
175+
Thread(target=do_it).start()
158176

159177
def update_worker(self, lock):
160178
name = threading.current_thread().name

0 commit comments

Comments
 (0)