Rewrite the first part of the docs for asyncio

This commit is contained in:
Lonami Exo
2018-06-21 21:54:54 +02:00
parent 5e322a6ca9
commit f733f8e565
11 changed files with 389 additions and 302 deletions

View File

@@ -69,7 +69,9 @@ Or we call `client.get_input_entity
.. code-block:: python
peer = client.get_input_entity('someone')
import asyncio
loop = asyncio.get_event_loop()
peer = loop.run_until_complete(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
@@ -81,7 +83,7 @@ instead:
.. code-block:: python
entity = client.get_entity('someone')
entity = loop.run_until_complete(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
@@ -110,7 +112,9 @@ request we do:
.. code-block:: python
result = client(SendMessageRequest(peer, 'Hello there!'))
result = loop.run_until_complete(
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
@@ -119,19 +123,21 @@ as you wish. Remember to use the right types! To sum up:
.. code-block:: python
result = client(SendMessageRequest(
result = loop.run_until_complete(client(SendMessageRequest(
client.get_input_entity('username'), 'Hello there!'
))
)))
This can further be simplified to:
.. code-block:: python
result = client(SendMessageRequest('username', 'Hello there!'))
# Or even
result = client(SendMessageRequest(PeerChannel(id), 'Hello there!'))
async def main():
result = await client(SendMessageRequest('username', 'Hello there!'))
# Or even
result = await client(SendMessageRequest(PeerChannel(id), 'Hello there!'))
loop.run_until_complete(main())
.. note::

View File

@@ -4,141 +4,46 @@
Update Modes
============
With ``asyncio``, the library has several tasks running in the background.
One task is used for sending requests, another task is used to receive them,
and a third one is used to handle updates.
The library can run in four distinguishable modes:
To handle updates, you must keep your script running. You can do this in
several ways. For instance, if you are *not* running ``asyncio``'s event
loop, you should use `client.run_until_disconnected
<telethon.client.updates.UpdateMethods.run_until_disconnected>`:
- 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.
.. code-block:: python
Since this section is about updates, we'll describe the simplest way to
work with them.
import asyncio
from telethon import TelegramClient
client = TelegramClient(...)
...
client.run_until_disconnected()
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:
.. code-block:: python
def callback(update):
print('I received', update)
client.add_event_handler(callback)
# do more work here, or simply sleep!
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
use :ref:`working-with-updates` but maybe use this for some other cases.
Now let's do something more interesting. Every time an user talks to us,
let's reply to them with the same text reversed:
.. code-block:: python
from telethon.tl.types import UpdateShortMessage, PeerUser
def replier(update):
if isinstance(update, UpdateShortMessage) and not update.out:
client.send_message(PeerUser(update.user_id), update.message[::-1])
Behind the scenes, this method is ``await``'ing on the `client.disconnected
<telethon.client.telegrambaseclient.disconnected>` property, so the code above
and the following are equivalent:
client.add_event_handler(replier)
input('Press enter to stop this!')
client.disconnect()
.. code-block:: python
We only ask you one thing: don't keep this running for too long, or your
contacts will go mad.
import asyncio
from telethon import TelegramClient
client = TelegramClient(...)
async def main():
await client.disconnected
asyncio.get_event_loop().run_until_complete(main())
Spawning no worker at all
*************************
You could also run `client.disconnected
<telethon.client.telegrambaseclient.disconnected>` until it completed.
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
But if you don't want to ``await``, then you should know what you want
to be doing instead! What matters is that you shouldn't let your script
die. If you don't care about updates, you don't need any of this.