mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-29 04:43:45 +03:00
Remove sync hack
This commit is contained in:
parent
34e7b7cc9f
commit
2a933ac3bd
2
.github/ISSUE_TEMPLATE/bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/bug-report.md
vendored
|
@ -14,7 +14,7 @@ assignees: ''
|
|||
|
||||
**Code that causes the issue**
|
||||
```python
|
||||
from telethon.sync import TelegramClient
|
||||
from telethon import TelegramClient
|
||||
...
|
||||
|
||||
```
|
||||
|
|
22
README.rst
22
README.rst
|
@ -35,15 +35,19 @@ Creating a client
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon import TelegramClient, events, sync
|
||||
import asyncio
|
||||
from telethon import TelegramClient, events
|
||||
|
||||
# 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)
|
||||
client.start()
|
||||
async def main():
|
||||
client = TelegramClient('session_name', api_id, api_hash)
|
||||
await client.start()
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
Doing stuff
|
||||
|
@ -51,14 +55,14 @@ Doing stuff
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
print(client.get_me().stringify())
|
||||
print((await client.get_me()).stringify())
|
||||
|
||||
client.send_message('username', 'Hello! Talking to you from Telethon')
|
||||
client.send_file('username', '/home/myself/Pictures/holidays.jpg')
|
||||
await client.send_message('username', 'Hello! Talking to you from Telethon')
|
||||
await client.send_file('username', '/home/myself/Pictures/holidays.jpg')
|
||||
|
||||
client.download_profile_photo('me')
|
||||
messages = client.get_messages('username')
|
||||
messages[0].download_media()
|
||||
await client.download_profile_photo('me')
|
||||
messages = await client.get_messages('username')
|
||||
await messages[0].download_media()
|
||||
|
||||
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
|
||||
async def handler(event):
|
||||
|
|
|
@ -100,12 +100,8 @@ proceeding. We will see all the available methods later on.
|
|||
# Most of your code should go here.
|
||||
# You can of course make and use your own async def (do_something).
|
||||
# They only need to be async if they need to await things.
|
||||
me = await client.get_me()
|
||||
await do_something(me)
|
||||
async with client:
|
||||
me = await client.get_me()
|
||||
await do_something(me)
|
||||
|
||||
with client:
|
||||
client.loop.run_until_complete(main())
|
||||
|
||||
After you understand this, you may use the ``telethon.sync`` hack if you
|
||||
want do so (see :ref:`compatibility-and-convenience`), but note you may
|
||||
run into other issues (iPython, Anaconda, etc. have some issues with it).
|
||||
client.loop.run_until_complete(main())
|
||||
|
|
|
@ -55,9 +55,12 @@ We can finally write some code to log into our account!
|
|||
api_id = 12345
|
||||
api_hash = '0123456789abcdef0123456789abcdef'
|
||||
|
||||
# The first parameter is the .session file name (absolute paths allowed)
|
||||
with TelegramClient('anon', api_id, api_hash) as client:
|
||||
client.loop.run_until_complete(client.send_message('me', 'Hello, myself!'))
|
||||
async def main():
|
||||
# The first parameter is the .session file name (absolute paths allowed)
|
||||
async with TelegramClient('anon', api_id, api_hash) as client:
|
||||
await client.send_message('me', 'Hello, myself!')
|
||||
|
||||
client.loop.run_until_complete(main())
|
||||
|
||||
|
||||
In the first line, we import the class name so we can create an instance
|
||||
|
@ -95,7 +98,7 @@ You will still need an API ID and hash, but the process is very similar:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon.sync import TelegramClient
|
||||
from telethon import TelegramClient
|
||||
|
||||
api_id = 12345
|
||||
api_hash = '0123456789abcdef0123456789abcdef'
|
||||
|
@ -104,9 +107,12 @@ You will still need an API ID and hash, but the process is very similar:
|
|||
# We have to manually call "start" if we want an explicit bot token
|
||||
bot = TelegramClient('bot', api_id, api_hash).start(bot_token=bot_token)
|
||||
|
||||
# But then we can use the client instance as usual
|
||||
with bot:
|
||||
...
|
||||
async def main():
|
||||
# But then we can use the client instance as usual
|
||||
async with bot:
|
||||
...
|
||||
|
||||
client.loop.run_until_complete(main())
|
||||
|
||||
|
||||
To get a bot account, you need to talk
|
||||
|
|
|
@ -58,84 +58,6 @@ What are asyncio basics?
|
|||
loop.run_until_complete(main())
|
||||
|
||||
|
||||
What does telethon.sync do?
|
||||
===========================
|
||||
|
||||
The moment you import any of these:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon import sync, ...
|
||||
# or
|
||||
from telethon.sync import ...
|
||||
# or
|
||||
import telethon.sync
|
||||
|
||||
The ``sync`` module rewrites most ``async def``
|
||||
methods in Telethon to something similar to this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def new_method():
|
||||
result = original_method()
|
||||
if loop.is_running():
|
||||
# the loop is already running, return the await-able to the user
|
||||
return result
|
||||
else:
|
||||
# the loop is not running yet, so we can run it for the user
|
||||
return loop.run_until_complete(result)
|
||||
|
||||
|
||||
That means you can do this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
print(client.get_me().username)
|
||||
|
||||
Instead of this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
me = client.loop.run_until_complete(client.get_me())
|
||||
print(me.username)
|
||||
|
||||
# or, using asyncio's default loop (it's the same)
|
||||
import asyncio
|
||||
loop = asyncio.get_event_loop() # == client.loop
|
||||
me = loop.run_until_complete(client.get_me())
|
||||
print(me.username)
|
||||
|
||||
|
||||
As you can see, it's a lot of boilerplate and noise having to type
|
||||
``run_until_complete`` all the time, so you can let the magic module
|
||||
to rewrite it for you. But notice the comment above: it won't run
|
||||
the loop if it's already running, because it can't. That means this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def main():
|
||||
# 3. the loop is running here
|
||||
print(
|
||||
client.get_me() # 4. this will return a coroutine!
|
||||
.username # 5. this fails, coroutines don't have usernames
|
||||
)
|
||||
|
||||
loop.run_until_complete( # 2. run the loop and the ``main()`` coroutine
|
||||
main() # 1. calling ``async def`` "returns" a coroutine
|
||||
)
|
||||
|
||||
|
||||
Will fail. So if you're inside an ``async def``, then the loop is
|
||||
running, and if the loop is running, you must ``await`` things yourself:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def main():
|
||||
print((await client.get_me()).username)
|
||||
|
||||
loop.run_until_complete(main())
|
||||
|
||||
|
||||
What are async, await and coroutines?
|
||||
=====================================
|
||||
|
||||
|
@ -275,7 +197,7 @@ in it. So if you want to run *other* code, create tasks for it:
|
|||
|
||||
loop.create_task(clock())
|
||||
...
|
||||
client.run_until_disconnected()
|
||||
await client.run_until_disconnected()
|
||||
|
||||
This creates a task for a clock that prints the time every second.
|
||||
You don't need to use `client.run_until_disconnected()
|
||||
|
@ -344,19 +266,6 @@ When you use a library, you're not limited to use only its methods. You can
|
|||
combine all the libraries you want. People seem to forget this simple fact!
|
||||
|
||||
|
||||
Why does client.start() work outside async?
|
||||
===========================================
|
||||
|
||||
Because it's so common that it's really convenient to offer said
|
||||
functionality by default. This means you can set up all your event
|
||||
handlers and start the client without worrying about loops at all.
|
||||
|
||||
Using the client in a ``with`` block, `start
|
||||
<telethon.client.auth.AuthMethods.start>`, `run_until_disconnected
|
||||
<telethon.client.updates.UpdateMethods.run_until_disconnected>`, and
|
||||
`disconnect <telethon.client.telegrambaseclient.TelegramBaseClient.disconnect>`
|
||||
all support this.
|
||||
|
||||
Where can I read more?
|
||||
======================
|
||||
|
||||
|
|
|
@ -73,10 +73,10 @@ You can import these ``from telethon.sessions``. For example, using the
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon.sync import TelegramClient
|
||||
from telethon import TelegramClient
|
||||
from telethon.sessions import StringSession
|
||||
|
||||
with TelegramClient(StringSession(string), api_id, api_hash) as client:
|
||||
async with TelegramClient(StringSession(string), api_id, api_hash) as client:
|
||||
... # use the client
|
||||
|
||||
# Save the string session as a string; you should decide how
|
||||
|
@ -129,10 +129,10 @@ The easiest way to generate a string session is as follows:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon.sync import TelegramClient
|
||||
from telethon import TelegramClient
|
||||
from telethon.sessions import StringSession
|
||||
|
||||
with TelegramClient(StringSession(), api_id, api_hash) as client:
|
||||
async with TelegramClient(StringSession(), api_id, api_hash) as client:
|
||||
print(client.session.save())
|
||||
|
||||
|
||||
|
|
|
@ -4,17 +4,21 @@ Telethon's Documentation
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from telethon.sync import TelegramClient, events
|
||||
import asyncio
|
||||
from telethon import TelegramClient, events
|
||||
|
||||
with TelegramClient('name', api_id, api_hash) as client:
|
||||
client.send_message('me', 'Hello, myself!')
|
||||
print(client.download_profile_photo('me'))
|
||||
async def main():
|
||||
async with TelegramClient('name', api_id, api_hash) as client:
|
||||
await client.send_message('me', 'Hello, myself!')
|
||||
print(await client.download_profile_photo('me'))
|
||||
|
||||
@client.on(events.NewMessage(pattern='(?i).*Hello'))
|
||||
async def handler(event):
|
||||
await event.reply('Hey!')
|
||||
@client.on(events.NewMessage(pattern='(?i).*Hello'))
|
||||
async def handler(event):
|
||||
await event.reply('Hey!')
|
||||
|
||||
client.run_until_disconnected()
|
||||
await client.run_until_disconnected()
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
* Are you new here? Jump straight into :ref:`installation`!
|
||||
|
|
|
@ -32,3 +32,22 @@ change even across minor version changes, and thus have your code break.
|
|||
|
||||
* The ``telethon.client`` module is now ``telethon._client``, meaning you should stop relying on
|
||||
anything inside of it. This includes all of the subclasses that used to exist (like ``UserMethods``).
|
||||
|
||||
|
||||
Synchronous compatibility mode has been removed
|
||||
-----------------------------------------------
|
||||
|
||||
The "sync hack" (which kicked in as soon as anything from ``telethon.sync`` was imported) has been
|
||||
removed. This implies:
|
||||
|
||||
* The ``telethon.sync`` module is gone.
|
||||
* Synchronous context-managers (``with`` as opposed to ``async with``) are no longer supported.
|
||||
Most notably, you can no longer do ``with client``. It must be ``async with client`` now.
|
||||
* The "smart" behaviour of the following methods has been removed and now they no longer work in
|
||||
a synchronous context when the ``asyncio`` event loop was not running. This means they now need
|
||||
to be used with ``await`` (or, alternatively, manually used with ``loop.run_until_complete``):
|
||||
* ``start``
|
||||
* ``disconnect``
|
||||
* ``run_until_disconnected``
|
||||
|
||||
// TODO provide standalone alternative for this?
|
||||
|
|
|
@ -127,14 +127,7 @@ This is basic Python knowledge. You should use the dot operator:
|
|||
AttributeError: 'coroutine' object has no attribute 'id'
|
||||
========================================================
|
||||
|
||||
You either forgot to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import telethon.sync
|
||||
# ^^^^^ import sync
|
||||
|
||||
Or:
|
||||
Telethon is an asynchronous library. This means you need to ``await`` most methods:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -218,19 +211,7 @@ Check out `quart_login.py`_ for an example web-application based on Quart.
|
|||
Can I use Anaconda/Spyder/IPython with the library?
|
||||
===================================================
|
||||
|
||||
Yes, but these interpreters run the asyncio event loop implicitly,
|
||||
which interferes with the ``telethon.sync`` magic module.
|
||||
|
||||
If you use them, you should **not** import ``sync``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Change any of these...:
|
||||
from telethon import TelegramClient, sync, ...
|
||||
from telethon.sync import TelegramClient, ...
|
||||
|
||||
# ...with this:
|
||||
from telethon import TelegramClient, ...
|
||||
Yes, but these interpreters run the asyncio event loop implicitly, so be wary of that.
|
||||
|
||||
You are also more likely to get "sqlite3.OperationalError: database is locked"
|
||||
with them. If they cause too much trouble, just write your code in a ``.py``
|
||||
|
|
|
@ -56,9 +56,6 @@ class _TakeoutClient:
|
|||
raise ValueError("Failed to finish the takeout.")
|
||||
self.session.takeout_id = None
|
||||
|
||||
__enter__ = helpers._sync_enter
|
||||
__exit__ = helpers._sync_exit
|
||||
|
||||
async def __call__(self, request, ordered=False):
|
||||
takeout_id = self.__client.session.takeout_id
|
||||
if takeout_id is None:
|
||||
|
|
|
@ -12,7 +12,7 @@ if typing.TYPE_CHECKING:
|
|||
from .telegramclient import TelegramClient
|
||||
|
||||
|
||||
def start(
|
||||
async def start(
|
||||
self: 'TelegramClient',
|
||||
phone: typing.Callable[[], str] = lambda: input('Please enter your phone (or bot token): '),
|
||||
password: typing.Callable[[], str] = lambda: getpass.getpass('Please enter your password: '),
|
||||
|
@ -39,7 +39,7 @@ def start(
|
|||
raise ValueError('Both a phone and a bot token provided, '
|
||||
'must only provide one of either')
|
||||
|
||||
coro = self._start(
|
||||
return await self._start(
|
||||
phone=phone,
|
||||
password=password,
|
||||
bot_token=bot_token,
|
||||
|
@ -49,10 +49,6 @@ def start(
|
|||
last_name=last_name,
|
||||
max_attempts=max_attempts
|
||||
)
|
||||
return (
|
||||
coro if self.loop.is_running()
|
||||
else self.loop.run_until_complete(coro)
|
||||
)
|
||||
|
||||
async def _start(
|
||||
self: 'TelegramClient', phone, password, bot_token, force_sms,
|
||||
|
|
|
@ -76,9 +76,6 @@ class _ChatAction:
|
|||
|
||||
self._task = None
|
||||
|
||||
__enter__ = helpers._sync_enter
|
||||
__exit__ = helpers._sync_exit
|
||||
|
||||
async def _update(self):
|
||||
try:
|
||||
while self._running:
|
||||
|
|
|
@ -137,9 +137,6 @@ class _DirectDownloadIter(RequestIter):
|
|||
async def __aexit__(self, *args):
|
||||
await self.close()
|
||||
|
||||
__enter__ = helpers._sync_enter
|
||||
__exit__ = helpers._sync_exit
|
||||
|
||||
|
||||
class _GenericDownloadIter(_DirectDownloadIter):
|
||||
async def _load_next_chunk(self):
|
||||
|
|
|
@ -337,19 +337,8 @@ def is_connected(self: 'TelegramClient') -> bool:
|
|||
sender = getattr(self, '_sender', None)
|
||||
return sender and sender.is_connected()
|
||||
|
||||
def disconnect(self: 'TelegramClient'):
|
||||
if self.loop.is_running():
|
||||
return self._disconnect_coro()
|
||||
else:
|
||||
try:
|
||||
self.loop.run_until_complete(self._disconnect_coro())
|
||||
except RuntimeError:
|
||||
# Python 3.5.x complains when called from
|
||||
# `__aexit__` and there were pending updates with:
|
||||
# "Event loop stopped before Future completed."
|
||||
#
|
||||
# However, it doesn't really make a lot of sense.
|
||||
pass
|
||||
async def disconnect(self: 'TelegramClient'):
|
||||
return await self._disconnect_coro()
|
||||
|
||||
def set_proxy(self: 'TelegramClient', proxy: typing.Union[tuple, dict]):
|
||||
init_proxy = None if not issubclass(self._connection, TcpMTProxy) else \
|
||||
|
|
|
@ -619,9 +619,6 @@ class TelegramClient:
|
|||
async def __aexit__(self, *args):
|
||||
await self.disconnect()
|
||||
|
||||
__enter__ = helpers._sync_enter
|
||||
__exit__ = helpers._sync_exit
|
||||
|
||||
# endregion Auth
|
||||
|
||||
# region Bots
|
||||
|
|
|
@ -40,7 +40,7 @@ async def set_receive_updates(self: 'TelegramClient', receive_updates):
|
|||
if receive_updates:
|
||||
await self(functions.updates.GetStateRequest())
|
||||
|
||||
def run_until_disconnected(self: 'TelegramClient'):
|
||||
async def run_until_disconnected(self: 'TelegramClient'):
|
||||
"""
|
||||
Runs the event loop until the library is disconnected.
|
||||
|
||||
|
@ -75,15 +75,7 @@ def run_until_disconnected(self: 'TelegramClient'):
|
|||
# script from exiting.
|
||||
await client.run_until_disconnected()
|
||||
"""
|
||||
if self.loop.is_running():
|
||||
return self._run_until_disconnected()
|
||||
try:
|
||||
return self.loop.run_until_complete(self._run_until_disconnected())
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
# No loop.run_until_complete; it's already syncified
|
||||
self.disconnect()
|
||||
return await self._run_until_disconnected()
|
||||
|
||||
def on(self: 'TelegramClient', event: EventBuilder):
|
||||
"""
|
||||
|
|
|
@ -118,7 +118,7 @@ def retry_range(retries, force_retry=True):
|
|||
while attempt != retries:
|
||||
attempt += 1
|
||||
yield attempt
|
||||
|
||||
|
||||
|
||||
|
||||
async def _maybe_await(value):
|
||||
|
@ -165,34 +165,6 @@ async def _cancel(log, **tasks):
|
|||
'%s (%s)', name, type(task), task)
|
||||
|
||||
|
||||
def _sync_enter(self):
|
||||
"""
|
||||
Helps to cut boilerplate on async context
|
||||
managers that offer synchronous variants.
|
||||
"""
|
||||
if hasattr(self, 'loop'):
|
||||
loop = self.loop
|
||||
else:
|
||||
loop = self._client.loop
|
||||
|
||||
if loop.is_running():
|
||||
raise RuntimeError(
|
||||
'You must use "async with" if the event loop '
|
||||
'is running (i.e. you are inside an "async def")'
|
||||
)
|
||||
|
||||
return loop.run_until_complete(self.__aenter__())
|
||||
|
||||
|
||||
def _sync_exit(self, *args):
|
||||
if hasattr(self, 'loop'):
|
||||
loop = self.loop
|
||||
else:
|
||||
loop = self._client.loop
|
||||
|
||||
return loop.run_until_complete(self.__aexit__(*args))
|
||||
|
||||
|
||||
def _entity_type(entity):
|
||||
# This could be a `utils` method that just ran a few `isinstance` on
|
||||
# `utils.get_peer(...)`'s result. However, there are *a lot* of auto
|
||||
|
|
|
@ -12,9 +12,6 @@ class RequestIter(abc.ABC):
|
|||
It has some facilities, such as automatically sleeping a desired
|
||||
amount of time between requests if needed (but not more).
|
||||
|
||||
Can be used synchronously if the event loop is not running and
|
||||
as an asynchronous iterator otherwise.
|
||||
|
||||
`limit` is the total amount of items that the iterator should return.
|
||||
This is handled on this base class, and will be always ``>= 0``.
|
||||
|
||||
|
@ -82,12 +79,6 @@ class RequestIter(abc.ABC):
|
|||
self.index += 1
|
||||
return result
|
||||
|
||||
def __next__(self):
|
||||
try:
|
||||
return self.client.loop.run_until_complete(self.__anext__())
|
||||
except StopAsyncIteration:
|
||||
raise StopIteration
|
||||
|
||||
def __aiter__(self):
|
||||
self.buffer = None
|
||||
self.index = 0
|
||||
|
@ -95,15 +86,6 @@ class RequestIter(abc.ABC):
|
|||
self.left = self.limit
|
||||
return self
|
||||
|
||||
def __iter__(self):
|
||||
if self.client.loop.is_running():
|
||||
raise RuntimeError(
|
||||
'You must use "async for" if the event loop '
|
||||
'is running (i.e. you are inside an "async def")'
|
||||
)
|
||||
|
||||
return self.__aiter__()
|
||||
|
||||
async def collect(self):
|
||||
"""
|
||||
Create a `self` iterator and collect it into a `TotalList`
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
"""
|
||||
This magical module will rewrite all public methods in the public interface
|
||||
of the library so they can run the loop on their own if it's not already
|
||||
running. This rewrite may not be desirable if the end user always uses the
|
||||
methods they way they should be ran, but it's incredibly useful for quick
|
||||
scripts and the runtime overhead is relatively low.
|
||||
|
||||
Some really common methods which are hardly used offer this ability by
|
||||
default, such as ``.start()`` and ``.run_until_disconnected()`` (since
|
||||
you may want to start, and then run until disconnected while using async
|
||||
event handlers).
|
||||
"""
|
||||
import asyncio
|
||||
import functools
|
||||
import inspect
|
||||
|
||||
from . import events, errors, utils, connection
|
||||
from .client.account import _TakeoutClient
|
||||
from .client.telegramclient import TelegramClient
|
||||
from .tl import types, functions, custom
|
||||
from .tl.custom import (
|
||||
Draft, Dialog, MessageButton, Forward, Button,
|
||||
Message, InlineResult, Conversation
|
||||
)
|
||||
from .tl.custom.chatgetter import ChatGetter
|
||||
from .tl.custom.sendergetter import SenderGetter
|
||||
|
||||
|
||||
def _syncify_wrap(t, method_name):
|
||||
method = getattr(t, method_name)
|
||||
|
||||
@functools.wraps(method)
|
||||
def syncified(*args, **kwargs):
|
||||
coro = method(*args, **kwargs)
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_running():
|
||||
return coro
|
||||
else:
|
||||
return loop.run_until_complete(coro)
|
||||
|
||||
# Save an accessible reference to the original method
|
||||
setattr(syncified, '__tl.sync', method)
|
||||
setattr(t, method_name, syncified)
|
||||
|
||||
|
||||
def syncify(*types):
|
||||
"""
|
||||
Converts all the methods in the given types (class definitions)
|
||||
into synchronous, which return either the coroutine or the result
|
||||
based on whether ``asyncio's`` event loop is running.
|
||||
"""
|
||||
# Our asynchronous generators all are `RequestIter`, which already
|
||||
# provide a synchronous iterator variant, so we don't need to worry
|
||||
# about asyncgenfunction's here.
|
||||
for t in types:
|
||||
for name in dir(t):
|
||||
if not name.startswith('_') or name == '__call__':
|
||||
if inspect.iscoroutinefunction(getattr(t, name)):
|
||||
_syncify_wrap(t, name)
|
||||
|
||||
|
||||
syncify(TelegramClient, _TakeoutClient, Draft, Dialog, MessageButton,
|
||||
ChatGetter, SenderGetter, Forward, Message, InlineResult, Conversation)
|
||||
|
||||
|
||||
# Private special case, since a conversation's methods return
|
||||
# futures (but the public function themselves are synchronous).
|
||||
_syncify_wrap(Conversation, '_get_result')
|
||||
|
||||
__all__ = [
|
||||
'TelegramClient', 'Button',
|
||||
'types', 'functions', 'custom', 'errors',
|
||||
'events', 'utils', 'connection'
|
||||
]
|
|
@ -524,6 +524,3 @@ class Conversation(ChatGetter):
|
|||
del self._client._conversations[chat_id]
|
||||
|
||||
self._cancel_all()
|
||||
|
||||
__enter__ = helpers._sync_enter
|
||||
__exit__ = helpers._sync_exit
|
||||
|
|
|
@ -396,7 +396,7 @@ def _write_html_pages(tlobjects, methods, layer, input_res):
|
|||
docs.write('<details>')
|
||||
|
||||
docs.write('''<pre>\
|
||||
<strong>from</strong> telethon.sync <strong>import</strong> TelegramClient
|
||||
<strong>from</strong> telethon <strong>import</strong> TelegramClient
|
||||
<strong>from</strong> telethon <strong>import</strong> functions, types
|
||||
|
||||
<strong>with</strong> TelegramClient(name, api_id, api_hash) <strong>as</strong> client:
|
||||
|
|
|
@ -14,43 +14,6 @@ def test_strip_text():
|
|||
# I can't interpret the rest of the code well enough yet
|
||||
|
||||
|
||||
class TestSyncifyAsyncContext:
|
||||
class NoopContextManager:
|
||||
def __init__(self, loop):
|
||||
self.count = 0
|
||||
self.loop = loop
|
||||
|
||||
async def __aenter__(self):
|
||||
self.count += 1
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, *args):
|
||||
assert exc_type is None
|
||||
self.count -= 1
|
||||
|
||||
__enter__ = helpers._sync_enter
|
||||
__exit__ = helpers._sync_exit
|
||||
|
||||
def test_sync_acontext(self, event_loop):
|
||||
contm = self.NoopContextManager(event_loop)
|
||||
assert contm.count == 0
|
||||
|
||||
with contm:
|
||||
assert contm.count == 1
|
||||
|
||||
assert contm.count == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_acontext(self, event_loop):
|
||||
contm = self.NoopContextManager(event_loop)
|
||||
assert contm.count == 0
|
||||
|
||||
async with contm:
|
||||
assert contm.count == 1
|
||||
|
||||
assert contm.count == 0
|
||||
|
||||
|
||||
def test_generate_key_data_from_nonce():
|
||||
gkdfn = helpers.generate_key_data_from_nonce
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user