From 85089353f2c1e1c9cb3925583beb629051ff2415 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 30 May 2018 18:36:37 +0200 Subject: [PATCH] Fix asyncio docs --- .../advanced-usage/accessing-the-full-api.rst | 17 +-- .../extra/advanced-usage/update-modes.rst | 124 +++--------------- readthedocs/extra/basic/creating-a-client.rst | 18 +-- readthedocs/extra/basic/entities.rst | 2 +- readthedocs/extra/basic/getting-started.rst | 11 +- readthedocs/extra/basic/telegram-client.rst | 5 +- .../extra/basic/working-with-updates.rst | 17 ++- telethon/telegram_client.py | 7 +- 8 files changed, 61 insertions(+), 140 deletions(-) diff --git a/readthedocs/extra/advanced-usage/accessing-the-full-api.rst b/readthedocs/extra/advanced-usage/accessing-the-full-api.rst index 659af4b7..5774f7ee 100644 --- a/readthedocs/extra/advanced-usage/accessing-the-full-api.rst +++ b/readthedocs/extra/advanced-usage/accessing-the-full-api.rst @@ -67,7 +67,7 @@ Or we call ``.get_input_entity()``: .. code-block:: python - peer = client.get_input_entity('someone') + peer = await client.get_input_entity('someone') When you're going to invoke an API method, most require you to pass an :tl:`InputUser`, :tl:`InputChat`, or so on, this is why using @@ -78,7 +78,7 @@ If you also need to have information about the whole user, use .. code-block:: python - entity = client.get_entity('someone') + entity = await client.get_entity('someone') In the later case, when you use the entity, the library will cast it to its "input" version for you. If you already have the complete user and @@ -104,7 +104,7 @@ request we do: .. code-block:: python - result = client(SendMessageRequest(peer, 'Hello there!')) + result = await client(SendMessageRequest(peer, 'Hello there!')) # __call__ is an alias for client.invoke(request). Both will work Message sent! Of course, this is only an example. There are nearly 250 @@ -113,18 +113,19 @@ as you wish. Remember to use the right types! To sum up: .. code-block:: python - result = client(SendMessageRequest( - client.get_input_entity('username'), 'Hello there!' - )) + async def method(): + result = await client(SendMessageRequest( + client.get_input_entity('username'), 'Hello there!' + )) This can further be simplified to: .. code-block:: python - result = client(SendMessageRequest('username', 'Hello there!')) + result = await client(SendMessageRequest('username', 'Hello there!')) # Or even - result = client(SendMessageRequest(PeerChannel(id), 'Hello there!')) + result = await client(SendMessageRequest(PeerChannel(id), 'Hello there!')) .. note:: diff --git a/readthedocs/extra/advanced-usage/update-modes.rst b/readthedocs/extra/advanced-usage/update-modes.rst index 942af9c9..5e613bb7 100644 --- a/readthedocs/extra/advanced-usage/update-modes.rst +++ b/readthedocs/extra/advanced-usage/update-modes.rst @@ -4,41 +4,23 @@ Update Modes ============ +Using ``asyncio`` simplifies the way you can work with updates. The library +will always ensure the future of a loop that will poll updates for you, so +you can do other things in the mean time. -The library can run in four distinguishable modes: - -- With no extra threads at all. -- With an extra thread that receives everything as soon as possible (default). -- With several worker threads that run your update handlers. -- A mix of the above. - -Since this section is about updates, we'll describe the simplest way to -work with them. - - -Using multiple workers -********************** - -When you create your client, simply pass a number to the -``update_workers`` parameter: - - ``client = TelegramClient('session', api_id, api_hash, update_workers=2)`` - -You can set any amount of workers you want. The more you put, the more -update handlers that can be called "at the same time". One or two should -suffice most of the time, since setting more will not make things run -faster most of the times (actually, it could slow things down). - -The next thing you want to do is to add a method that will be called when -an `Update`__ arrives: +Once you have your client ready, the next thing you want to do is to add a +method that will be called when an `Update`__ arrives: .. code-block:: python - def callback(update): + import asyncio + loop = asyncio.get_event_loop() + + async def callback(update): print('I received', update) - client.add_event_handler(callback) - # do more work here, or simply sleep! + loop.run_until_complete(client.add_event_handler(callback)) + loop.run_forever() # this blocks forever, don't let the script end! That's it! This is the old way to listen for raw updates, with no further processing. If this feels annoying for you, remember that you can always @@ -51,94 +33,18 @@ let's reply to them with the same text reversed: from telethon.tl.types import UpdateShortMessage, PeerUser - def replier(update): + async def replier(update): if isinstance(update, UpdateShortMessage) and not update.out: - client.send_message(PeerUser(update.user_id), update.message[::-1]) + await client.send_message(PeerUser(update.user_id), update.message[::-1]) - client.add_event_handler(replier) - input('Press enter to stop this!') - client.disconnect() + loop.run_until_complete(client.add_event_handler(replier)) + loop.run_forever() We only ask you one thing: don't keep this running for too long, or your contacts will go mad. -Spawning no worker at all -************************* - -All the workers do is loop forever and poll updates from a queue that is -filled from the ``ReadThread``, responsible for reading every item off -the network. If you only need a worker and the ``MainThread`` would be -doing no other job, this is the preferred way. You can easily do the same -as the workers like so: - - .. code-block:: python - - while True: - try: - update = client.updates.poll() - if not update: - continue - - print('I received', update) - except KeyboardInterrupt: - break - - client.disconnect() - -Note that ``poll`` accepts a ``timeout=`` parameter, and it will return -``None`` if other thread got the update before you could or if the timeout -expired, so it's important to check ``if not update``. - -This can coexist with the rest of ``N`` workers, or you can set it to ``0`` -additional workers: - - ``client = TelegramClient('session', api_id, api_hash, update_workers=0)`` - -You **must** set it to ``0`` (or higher), as it defaults to ``None`` and that -has a different meaning. ``None`` workers means updates won't be processed -*at all*, so you must set it to some integer value if you want -``client.updates.poll()`` to work. - - -Using the main thread instead the ``ReadThread`` -************************************************ - -If you have no work to do on the ``MainThread`` and you were planning to have -a ``while True: sleep(1)``, don't do that. Instead, don't spawn the secondary -``ReadThread`` at all like so: - - .. code-block:: python - - client = TelegramClient( - ... - spawn_read_thread=False - ) - -And then ``.idle()`` from the ``MainThread``: - - ``client.idle()`` - -You can stop it with :kbd:`Control+C`, and you can configure the signals -to be used in a similar fashion to `Python Telegram Bot`__. - -As a complete example: - - .. code-block:: python - - def callback(update): - print('I received', update) - - client = TelegramClient('session', api_id, api_hash, - update_workers=1, spawn_read_thread=False) - - client.connect() - client.add_event_handler(callback) - client.idle() # ends with Ctrl+C - - This is the preferred way to use if you're simply going to listen for updates. __ https://lonamiwebs.github.io/Telethon/types/update.html -__ https://github.com/python-telegram-bot/python-telegram-bot/blob/4b3315db6feebafb94edcaa803df52bb49999ced/telegram/ext/updater.py#L460 diff --git a/readthedocs/extra/basic/creating-a-client.rst b/readthedocs/extra/basic/creating-a-client.rst index 6ebd9f27..132e53ac 100644 --- a/readthedocs/extra/basic/creating-a-client.rst +++ b/readthedocs/extra/basic/creating-a-client.rst @@ -84,19 +84,21 @@ As a full example: .. code-block:: python - client = TelegramClient('anon', api_id, api_hash) - assert await client.connect() - if not client.is_user_authorized(): - await client.send_code_request(phone_number) - me = await client.sign_in(phone_number, input('Enter code: ')) + async def main(): + client = TelegramClient('anon', api_id, api_hash) + assert await client.connect() + if not client.is_user_authorized(): + await client.send_code_request(phone_number) + me = await client.sign_in(phone_number, input('Enter code: ')) All of this, however, can be done through a call to ``.start()``: .. code-block:: python - client = TelegramClient('anon', api_id, api_hash) - await client.start() + async def main(): + client = TelegramClient('anon', api_id, api_hash) + await client.start() The code shown is just what ``.start()`` will be doing behind the scenes @@ -159,7 +161,7 @@ account, calling :meth:`telethon.TelegramClient.sign_in` will raise a import getpass from telethon.errors import SessionPasswordNeededError - client.sign_in(phone) + await client.sign_in(phone) try: await client.sign_in(code=input('Enter code: ')) except SessionPasswordNeededError: diff --git a/readthedocs/extra/basic/entities.rst b/readthedocs/extra/basic/entities.rst index 6f9a8775..205c1d0d 100644 --- a/readthedocs/extra/basic/entities.rst +++ b/readthedocs/extra/basic/entities.rst @@ -38,7 +38,7 @@ Getting entities Through the use of the :ref:`sessions`, the library will automatically remember the ID and hash pair, along with some extra information, so -you're able to just do this: +you're able to just do this (inside an ``async def``): .. code-block:: python diff --git a/readthedocs/extra/basic/getting-started.rst b/readthedocs/extra/basic/getting-started.rst index 49d95dd5..72f02619 100644 --- a/readthedocs/extra/basic/getting-started.rst +++ b/readthedocs/extra/basic/getting-started.rst @@ -19,15 +19,19 @@ Creating a client .. code-block:: python + import asyncio + loop = asyncio.get_event_loop() + from telethon import TelegramClient + # These example values won't work. You must get your own api_id and # api_hash from https://my.telegram.org, under API Development. api_id = 12345 api_hash = '0123456789abcdef0123456789abcdef' client = TelegramClient('session_name', api_id, api_hash) - await client.start() + loop.run_until_complete(client.start()) **More details**: :ref:`creating-a-client` @@ -37,6 +41,8 @@ Basic Usage .. code-block:: python + # You should write all this inside of an async def. + # # Getting information about yourself print((await client.get_me()).stringify()) @@ -76,9 +82,6 @@ Handling Updates import asyncio from telethon import events - # We need to have some worker running - client.updates.workers = 1 - @client.on(events.NewMessage(incoming=True, pattern='(?i)hi')) async def handler(event): await event.reply('Hello!') diff --git a/readthedocs/extra/basic/telegram-client.rst b/readthedocs/extra/basic/telegram-client.rst index 266a0305..4646637a 100644 --- a/readthedocs/extra/basic/telegram-client.rst +++ b/readthedocs/extra/basic/telegram-client.rst @@ -32,7 +32,7 @@ need of manually importing the requests you need. For instance, retrieving your own user can be done in a single line: - ``myself = client.get_me()`` + ``myself = await client.get_me()`` Internally, this method has sent a request to Telegram, who replied with the information about your own user, and then the desired information @@ -53,7 +53,8 @@ The so called "entities" are another important whole concept on its own, but for now you don't need to worry about it. Simply know that they are a good way to get information about an user, chat or channel. -Many other common methods for quick scripts are also available: +Many other common methods for quick scripts are also available. +Note that you should be writing this inside of an ``async def``: .. code-block:: python diff --git a/readthedocs/extra/basic/working-with-updates.rst b/readthedocs/extra/basic/working-with-updates.rst index 858a472d..24c22512 100644 --- a/readthedocs/extra/basic/working-with-updates.rst +++ b/readthedocs/extra/basic/working-with-updates.rst @@ -31,27 +31,32 @@ Getting Started .. code-block:: python import asyncio + loop = asyncio.get_event_loop() + from telethon import TelegramClient, events - client = TelegramClient(..., update_workers=1, spawn_read_thread=False) - await client.start() + client = TelegramClient(...) + loop.run_until_complete(client.start()) @client.on(events.NewMessage) async def my_event_handler(event): if 'hello' in event.raw_text: await event.reply('hi!') - asyncio.get_event_loop().run_forever() + loop.run_forever() Not much, but there might be some things unclear. What does this code do? .. code-block:: python + import asyncio + loop = asyncio.get_event_loop() + from telethon import TelegramClient, events - client = TelegramClient(..., update_workers=1, spawn_read_thread=False) - await client.start() + client = TelegramClient(...) + loop.run_until_complete(client.start()) This is normal initialization (of course, pass session name, API ID and hash). @@ -78,7 +83,7 @@ message, we ``reply`` to the event with a ``'hi!'`` message. .. code-block:: python - asyncio.get_event_loop().run_forever() + loop.run_forever() Finally, this tells the script that we're done with our code, and want diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 5d5b3b69..20a64b23 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -256,7 +256,9 @@ class TelegramClient(TelegramBareClient): also taking into consideration that 2FA may be enabled in the account. Example usage: - >>> client = await TelegramClient(session, api_id, api_hash).start(phone) + >>> import asyncio + >>> rc = asyncio.get_event_loop().run_until_complete + >>> client = rc(TelegramClient(session, api_id, api_hash).start(phone)) Please enter the code you received: 12345 Please enter your password: ******* (You are now logged in) @@ -945,6 +947,7 @@ class TelegramClient(TelegramBareClient): Examples: + >>> import asyncio >>> async def main(): ... client = await TelegramClient(...).start() ... message = await client.send_message('username', 'hello') @@ -955,7 +958,7 @@ class TelegramClient(TelegramBareClient): ... # or ... await client.edit_message(message, 'Hello!') ... - >>> loop = ... + >>> loop = asyncio.get_event_loop() >>> loop.run_until_complete(main()) Raises: