Skip to content

Commit

Permalink
Stop using asyncio.get_event_loop()
Browse files Browse the repository at this point in the history
It is deprecated in newer Python versions.
Closes #4013.
  • Loading branch information
Lonami committed Jan 11, 2023
1 parent fb97a8a commit 83bafa2
Show file tree
Hide file tree
Showing 14 changed files with 82 additions and 99 deletions.
56 changes: 29 additions & 27 deletions readthedocs/concepts/asyncio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,22 @@ because tasks are smaller than threads, which are smaller than processes.
What are asyncio basics?
========================

The code samples below assume that you have Python 3.7 or greater installed.

.. code-block:: python
# First we need the asyncio library
import asyncio
# Then we need a loop to work with
loop = asyncio.get_event_loop()
# We also need something to run
async def main():
for char in 'Hello, world!\n':
print(char, end='', flush=True)
await asyncio.sleep(0.2)
# Then, we need to run the loop with a task
loop.run_until_complete(main())
# Then, we can create a new asyncio loop and use it to run our coroutine.
# The creation and tear-down of the loop is hidden away from us.
asyncio.run(main())
What does telethon.sync do?
Expand Down Expand Up @@ -101,7 +101,7 @@ Instead of this:
# or, using asyncio's default loop (it's the same)
import asyncio
loop = asyncio.get_event_loop() # == client.loop
loop = asyncio.get_running_loop() # == client.loop
me = loop.run_until_complete(client.get_me())
print(me.username)
Expand Down Expand Up @@ -158,13 +158,10 @@ loops or use ``async with``:
print(message.sender.username)
loop = asyncio.get_event_loop()
# ^ this assigns the default event loop from the main thread to a variable
loop.run_until_complete(main())
# ^ this runs the *entire* loop until the main() function finishes.
# While the main() function does not finish, the loop will be running.
# While the loop is running, you can't run it again.
asyncio.run(main())
# ^ this will create a new asyncio loop behind the scenes and tear it down
# once the function returns. It will run the loop untiil main finishes.
# You should only use this function if there is no other loop running.
The ``await`` keyword blocks the *current* task, and the loop can run
Expand All @@ -184,14 +181,14 @@ concurrently:
await asyncio.sleep(delay) # await tells the loop this task is "busy"
print('world') # eventually the loop finishes all tasks
loop = asyncio.get_event_loop() # get the default loop for the main thread
loop.create_task(world(2)) # create the world task, passing 2 as delay
loop.create_task(hello(delay=1)) # another task, but with delay 1
async def main():
asyncio.create_task(world(2)) # create the world task, passing 2 as delay
asyncio.create_task(hello(delay=1)) # another task, but with delay 1
await asyncio.sleep(3) # wait for three seconds before exiting
try:
# run the event loop forever; ctrl+c to stop it
# we could also run the loop for three seconds:
# loop.run_until_complete(asyncio.sleep(3))
loop.run_forever()
# create a new temporary asyncio loop and use it to run main
asyncio.run(main())
except KeyboardInterrupt:
pass
Expand All @@ -209,10 +206,15 @@ The same example, but without the comment noise:
await asyncio.sleep(delay)
print('world')
loop = asyncio.get_event_loop()
loop.create_task(world(2))
loop.create_task(hello(1))
loop.run_until_complete(asyncio.sleep(3))
async def main():
asyncio.create_task(world(2))
asyncio.create_task(hello(delay=1))
await asyncio.sleep(3)
try:
asyncio.run(main())
except KeyboardInterrupt:
pass
Can I use threads?
Expand Down Expand Up @@ -250,9 +252,9 @@ You may have seen this error:
RuntimeError: There is no current event loop in thread 'Thread-1'.
It just means you didn't create a loop for that thread, and if you don't
pass a loop when creating the client, it uses ``asyncio.get_event_loop()``,
which only works in the main thread.
It just means you didn't create a loop for that thread. Please refer to
the ``asyncio`` documentation to correctly learn how to set the event loop
for non-main threads.


client.run_until_disconnected() blocks!
Expand Down
3 changes: 1 addition & 2 deletions readthedocs/concepts/updates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@ so the code above and the following are equivalent:
async def main():
await client.disconnected
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
asyncio.run(main())
You could also run `client.disconnected
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/misc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2088,7 +2088,7 @@ the scenes! This means you're now able to do both of the following:
async def main():
await client.send_message('me', 'Hello!')
asyncio.get_event_loop().run_until_complete(main())
asyncio.run(main())
# ...can be rewritten as:
Expand Down
12 changes: 5 additions & 7 deletions readthedocs/misc/compatibility-and-convenience.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,19 +161,17 @@ just get rid of ``telethon.sync`` and work inside an ``async def``:
await client.run_until_disconnected()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
asyncio.run(main())
The ``telethon.sync`` magic module simply wraps every method behind:
The ``telethon.sync`` magic module essentially wraps every method behind:

.. code-block:: python
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
asyncio.run(main())
So that you don't have to write it yourself every time. That's the
overhead you pay if you import it, and what you save if you don't.
With some other tricks, so that you don't have to write it yourself every time.
That's the overhead you pay if you import it, and what you save if you don't.

Learning
========
Expand Down
4 changes: 2 additions & 2 deletions telethon/client/telegrambaseclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class TelegramBaseClient(abc.ABC):
Defaults to `lang_code`.
loop (`asyncio.AbstractEventLoop`, optional):
Asyncio event loop to use. Defaults to `asyncio.get_event_loop()`.
Asyncio event loop to use. Defaults to `asyncio.get_running_loop()`.
This argument is ignored.
base_logger (`str` | `logging.Logger`, optional):
Expand Down Expand Up @@ -470,7 +470,7 @@ def loop(self: 'TelegramClient') -> asyncio.AbstractEventLoop:
# Join the task (wait for it to complete)
await task
"""
return asyncio.get_event_loop()
return helpers.get_running_loop()

@property
def disconnected(self: 'TelegramClient') -> asyncio.Future:
Expand Down
4 changes: 2 additions & 2 deletions telethon/events/inlinequery.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import asyncio

from .common import EventBuilder, EventCommon, name_inner_event
from .. import utils
from .. import utils, helpers
from ..tl import types, functions, custom
from ..tl.custom.sendergetter import SenderGetter

Expand Down Expand Up @@ -242,6 +242,6 @@ def _as_future(obj):
if inspect.isawaitable(obj):
return asyncio.ensure_future(obj)

f = asyncio.get_event_loop().create_future()
f = helpers.get_running_loop().create_future()
f.set_result(obj)
return f
13 changes: 8 additions & 5 deletions telethon/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def retry_range(retries, force_retry=True):
while attempt != retries:
attempt += 1
yield attempt



async def _maybe_await(value):
Expand Down Expand Up @@ -426,7 +426,10 @@ def close(self, *args, **kwargs):
# endregion

def get_running_loop():
if sys.version_info[:2] <= (3, 6):
return asyncio._get_running_loop()

return asyncio.get_running_loop()
if sys.version_info >= (3, 7):
try:
return asyncio.get_running_loop()
except RuntimeError:
return asyncio.get_event_loop_policy().get_event_loop()
else:
return asyncio.get_event_loop()
6 changes: 3 additions & 3 deletions telethon/network/connection/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ async def _proxy_connect(self, timeout=None, local_addr=None):

# Actual TCP connection is performed here.
await asyncio.wait_for(
asyncio.get_event_loop().sock_connect(sock=sock, address=address),
helpers.get_running_loop().sock_connect(sock=sock, address=address),
timeout=timeout
)

Expand Down Expand Up @@ -190,7 +190,7 @@ async def _proxy_connect(self, timeout=None, local_addr=None):

# Actual TCP connection and negotiation performed here.
await asyncio.wait_for(
asyncio.get_event_loop().sock_connect(sock=sock, address=address),
helpers.get_running_loop().sock_connect(sock=sock, address=address),
timeout=timeout
)

Expand Down Expand Up @@ -244,7 +244,7 @@ async def connect(self, timeout=None, ssl=None):
await self._connect(timeout=timeout, ssl=ssl)
self._connected = True

loop = asyncio.get_event_loop()
loop = helpers.get_running_loop()
self._send_task = loop.create_task(self._send_loop())
self._recv_task = loop.create_task(self._recv_loop())

Expand Down
8 changes: 4 additions & 4 deletions telethon/network/mtprotosender.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def __init__(self, auth_key, *, loggers,
# pending futures should be cancelled.
self._user_connected = False
self._reconnecting = False
self._disconnected = asyncio.get_event_loop().create_future()
self._disconnected = helpers.get_running_loop().create_future()
self._disconnected.set_result(None)

# We need to join the loops upon disconnection
Expand Down Expand Up @@ -261,7 +261,7 @@ async def _connect(self):
await self._disconnect(error=e)
raise e

loop = asyncio.get_event_loop()
loop = helpers.get_running_loop()
self._log.debug('Starting send loop')
self._send_loop_handle = loop.create_task(self._send_loop())

Expand Down Expand Up @@ -400,7 +400,7 @@ async def _reconnect(self, last_error):
self._pending_state.clear()

if self._auto_reconnect_callback:
asyncio.get_event_loop().create_task(self._auto_reconnect_callback())
helpers.get_running_loop().create_task(self._auto_reconnect_callback())

break
else:
Expand All @@ -425,7 +425,7 @@ def _start_reconnect(self, error):
# gets stuck.
# TODO It still gets stuck? Investigate where and why.
self._reconnecting = True
asyncio.get_event_loop().create_task(self._reconnect(error))
helpers.get_running_loop().create_task(self._reconnect(error))

def _keepalive_ping(self, rnd_id):
"""
Expand Down
4 changes: 2 additions & 2 deletions telethon/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import functools
import inspect

from . import events, errors, utils, connection
from . import events, errors, utils, connection, helpers
from .client.account import _TakeoutClient
from .client.telegramclient import TelegramClient
from .tl import types, functions, custom
Expand All @@ -32,7 +32,7 @@ def _syncify_wrap(t, method_name):
@functools.wraps(method)
def syncified(*args, **kwargs):
coro = method(*args, **kwargs)
loop = asyncio.get_event_loop()
loop = helpers.get_running_loop()
if loop.is_running():
return coro
else:
Expand Down
10 changes: 2 additions & 8 deletions telethon_examples/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def callback(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
if inspect.iscoroutine(result):
aio_loop.create_task(result)
asyncio.create_task(result)

return wrapped

Expand Down Expand Up @@ -369,10 +369,4 @@ async def main(interval=0.05):


if __name__ == "__main__":
# Some boilerplate code to set up the main method
aio_loop = asyncio.get_event_loop()
try:
aio_loop.run_until_complete(main())
finally:
if not aio_loop.is_closed():
aio_loop.close()
asyncio.run(main())
Loading

0 comments on commit 83bafa2

Please sign in to comment.