File: compatibility-and-convenience.rst

package info (click to toggle)
python-telethon 1.40.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,508 kB
  • sloc: python: 16,125; javascript: 200; makefile: 16; sh: 11
file content (185 lines) | stat: -rw-r--r-- 6,228 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
.. _compatibility-and-convenience:

=============================
Compatibility and Convenience
=============================

Telethon is an `asyncio` library. Compatibility is an important concern,
and while it can't always be kept and mistakes happens, the :ref:`changelog`
is there to tell you when these important changes happen.

.. contents::


Compatibility
=============

Some decisions when developing will inevitable be proven wrong in the future.
One of these decisions was using threads. Now that Python 3.4 is reaching EOL
and using `asyncio` is usable as of Python 3.5 it makes sense for a library
like Telethon to make a good use of it.

If you have old code, **just use old versions** of the library! There is
nothing wrong with that other than not getting new updates or fixes, but
using a fixed version with ``pip install telethon==0.19.1.6`` is easy
enough to do.

You might want to consider using `Virtual Environments
<https://docs.python.org/3/tutorial/venv.html>`_ in your projects.

There's no point in maintaining a synchronous version because the whole point
is that people don't have time to upgrade, and there has been several changes
and clean-ups. Using an older version is the right way to go.

Sometimes, other small decisions are made. These all will be reflected in the
:ref:`changelog` which you should read when upgrading.

If you want to jump the `asyncio` boat, here are some of the things you will
need to start migrating really old code:

.. code-block:: python

    # 1. Import the client from telethon.sync
    from telethon.sync import TelegramClient

    # 2. Change this monster...
    try:
        assert client.connect()
        if not client.is_user_authorized():
            client.send_code_request(phone_number)
            me = client.sign_in(phone_number, input('Enter code: '))

        ...  # REST OF YOUR CODE
    finally:
        client.disconnect()

    # ...for this:
    with client:
        ...  # REST OF YOUR CODE

    # 3. client.idle() no longer exists.
    # Change this...
    client.idle()
    # ...to this:
    client.run_until_disconnected()

    # 4. client.add_update_handler no longer exists.
    # Change this...
    client.add_update_handler(handler)
    # ...to this:
    client.add_event_handler(handler)


In addition, all the update handlers must be ``async def``, and you need
to ``await`` method calls that rely on network requests, such as getting
the chat or sender. If you don't use updates, you're done!


Convenience
===========

.. note::

    The entire documentation assumes you have done one of the following:

    .. code-block:: python

        from telethon import TelegramClient, sync
        # or
        from telethon.sync import TelegramClient

    This makes the examples shorter and easier to think about.

For quick scripts that don't need updates, it's a lot more convenient to
forget about `asyncio` and just work with sequential code. This can prove
to be a powerful hybrid for running under the Python REPL too.

.. code-block:: python

    from telethon.sync import TelegramClient
    #            ^~~~~ note this part; it will manage the asyncio loop for you

    with TelegramClient(...) as client:
        print(client.get_me().username)
        #     ^ notice the lack of await, or loop.run_until_complete().
        #       Since there is no loop running, this is done behind the scenes.
        #
        message = client.send_message('me', 'Hi!')
        import time
        time.sleep(5)
        message.delete()

        # You can also have an hybrid between a synchronous
        # part and asynchronous event handlers.
        #
        from telethon import events
        @client.on(events.NewMessage(pattern='(?i)hi|hello'))
        async def handler(event):
            await event.reply('hey')

        client.run_until_disconnected()


Some methods, such as ``with``, ``start``, ``disconnect`` and
``run_until_disconnected`` work both in synchronous and asynchronous
contexts by default for convenience, and to avoid the little overhead
it has when using methods like sending a message, getting messages, etc.
This keeps the best of both worlds as a sane default.

.. note::

    As a rule of thumb, if you're inside an ``async def`` and you need
    the client, you need to ``await`` calls to the API. If you call other
    functions that also need API calls, make them ``async def`` and ``await``
    them too. Otherwise, there is no need to do so with this mode.

Speed
=====

When you're ready to micro-optimize your application, or if you simply
don't need to call any non-basic methods from a synchronous context,
just get rid of ``telethon.sync`` and work inside an ``async def``:

.. code-block:: python

    import asyncio
    from telethon import TelegramClient, events

    async def main():
        async with TelegramClient(...) as client:
            print((await client.get_me()).username)
            #     ^_____________________^ notice these parenthesis
            #     You want to ``await`` the call, not the username.
            #
            message = await client.send_message('me', 'Hi!')
            await asyncio.sleep(5)
            await message.delete()

            @client.on(events.NewMessage(pattern='(?i)hi|hello'))
            async def handler(event):
                await event.reply('hey')

            await client.run_until_disconnected()

    asyncio.run(main())


The ``telethon.sync`` magic module essentially wraps every method behind:

.. code-block:: python

    asyncio.run(main())

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
========

You know the library uses `asyncio` everywhere, and you want to learn
how to do things right. Even though `asyncio` is its own topic, the
documentation wants you to learn how to use Telethon correctly, and for
that, you need to use `asyncio` correctly too. For this reason, there
is a section called :ref:`mastering-asyncio` that will introduce you to
the `asyncio` world, with links to more resources for learning how to
use it. Feel free to check that section out once you have read the rest.