Merge branch 'develop' into asyncio

# Conflicts:
#	pyrogram/client/client.py
#	pyrogram/client/ext/base_client.py
#	pyrogram/client/methods/bots/request_callback_answer.py
#	pyrogram/session/session.py
This commit is contained in:
Dan 2018-06-24 19:27:37 +02:00
commit 5f727cb5a2
8 changed files with 165 additions and 100 deletions

View file

@ -1,5 +1,5 @@
## Include ## Include
include COPYING COPYING.lesser NOTICE requirements.txt requirements_extras.txt include COPYING COPYING.lesser NOTICE requirements.txt
recursive-include compiler *.py *.tl *.tsv *.txt recursive-include compiler *.py *.tl *.tsv *.txt
## Exclude ## Exclude

View file

@ -1,7 +1,7 @@
|header| |header|
Pyrogram |twitter| Pyrogram
================== ========
.. code-block:: python .. code-block:: python
@ -26,8 +26,8 @@ Features
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away. - **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
- **High-level**: The low-level details of MTProto are abstracted and automatically handled. - **High-level**: The low-level details of MTProto are abstracted and automatically handled.
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C. - **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Updated** to the latest Telegram API version, currently Layer 76 running on MTProto 2.0. - **Updated** to the latest Telegram API version, currently Layer 79 on top of MTProto 2.0.
- **Documented**: Pyrogram API methods are documented and resemble the Telegram Bot API. - **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
- **Full API**, allowing to execute any advanced action an official client is able to do, and more. - **Full API**, allowing to execute any advanced action an official client is able to do, and more.
Requirements Requirements
@ -54,7 +54,7 @@ Getting Started
Contributing Contributing
------------ ------------
Pyrogram is brand new! **You are welcome to try it and help make it better** by either submitting pull Pyrogram is brand new, and **you are welcome to try it and help make it even better** by either submitting pull
requests or reporting issues/bugs as well as suggesting best practices, ideas, enhancements on both code requests or reporting issues/bugs as well as suggesting best practices, ideas, enhancements on both code
and documentation. Any help is appreciated! and documentation. Any help is appreciated!
@ -100,28 +100,25 @@ Copyright & License
</a> </a>
<br><br> <br><br>
<a href="compiler/api/source/main_api.tl"> <a href="compiler/api/source/main_api.tl">
<img src="https://media.pyrogram.ml/images/scheme.svg" <img src="https://img.shields.io/badge/SCHEME-LAYER%2079-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
alt="Scheme Layer 76"> alt="Scheme Layer">
</a> </a>
<a href="https://github.com/pyrogram/tgcrypto"> <a href="https://github.com/pyrogram/tgcrypto">
<img src="https://media.pyrogram.ml/images/tgcrypto.svg" <img src="https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
alt="TgCrypto"> alt="TgCrypto">
</a> </a>
</p> </p>
.. |twitter| image:: https://media.pyrogram.ml/images/twitter.svg
:target: https://twitter.com/intent/tweet?text=Build%20custom%20Telegram%20applications%20with%20Pyrogram&url=https://github.com/pyrogram/pyrogram&hashtags=Telegram,MTProto,Python
.. |logo| image:: https://pyrogram.ml/images/logo.png .. |logo| image:: https://pyrogram.ml/images/logo.png
:target: https://pyrogram.ml :target: https://pyrogram.ml
:alt: Pyrogram :alt: Pyrogram
.. |description| replace:: **Telegram MTProto API Client Library for Python** .. |description| replace:: **Telegram MTProto API Client Library for Python**
.. |scheme| image:: https://www.pyrogram.ml/images/scheme.svg .. |scheme| image:: "https://img.shields.io/badge/SCHEME-LAYER%2079-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
:target: compiler/api/source/main_api.tl :target: compiler/api/source/main_api.tl
:alt: Scheme Layer 76 :alt: Scheme Layer
.. |tgcrypto| image:: https://www.pyrogram.ml/images/tgcrypto.svg .. |tgcrypto| image:: "https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
:target: https://github.com/pyrogram/tgcrypto :target: https://github.com/pyrogram/tgcrypto
:alt: TgCrypto :alt: TgCrypto

View file

@ -14,27 +14,31 @@ Log In
To automate the **Log In** process, pass your ``phone_number`` and ``password`` (if you have one) in the Client parameters. To automate the **Log In** process, pass your ``phone_number`` and ``password`` (if you have one) in the Client parameters.
If you want to retrieve the phone code programmatically, pass a callback function in the ``phone_code`` field — this If you want to retrieve the phone code programmatically, pass a callback function in the ``phone_code`` field — this
function must return the correct phone code as string (e.g., "12345") — otherwise, ignore this parameter, Pyrogram will function accepts a single positional argument (phone_number) and must return the correct phone code (e.g., "12345")
ask you to input the phone code manually. — otherwise, ignore this parameter, Pyrogram will ask you to input the phone code manually.
Example:
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
def phone_code_callback(): def phone_code_callback(phone_number):
code = ... # Get your code programmatically code = ... # Get your code programmatically
return code # Must be string, e.g., "12345" return code # e.g., "12345"
app = Client( app = Client(
session_name="example", session_name="example",
phone_number="39**********", phone_number="39**********",
phone_code=phone_code_callback, phone_code=phone_code_callback, # Note the missing parentheses
password="password" # (if you have one) password="password" # (if you have one)
) )
app.start() app.start()
print(app.get_me()) print(app.get_me())
app.stop() app.stop()
Sign Up Sign Up
@ -44,23 +48,27 @@ To automate the **Sign Up** process (i.e., automatically create a new Telegram a
``first_name`` and ``last_name`` fields alongside the other parameters; they will be used to automatically create a new ``first_name`` and ``last_name`` fields alongside the other parameters; they will be used to automatically create a new
Telegram account in case the phone number you passed is not registered yet. Telegram account in case the phone number you passed is not registered yet.
Example:
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
def phone_code_callback(): def phone_code_callback(phone_number):
code = ... # Get your code programmatically code = ... # Get your code programmatically
return code # Must be string, e.g., "12345" return code # e.g., "12345"
app = Client( app = Client(
session_name="example", session_name="example",
phone_number="39**********", phone_number="39**********",
phone_code=phone_code_callback, phone_code=phone_code_callback, # Note the missing parentheses
first_name="Pyrogram", first_name="Pyrogram",
last_name="" # Can be an empty string last_name="" # Can be an empty string
) )
app.start() app.start()
print(app.get_me()) print(app.get_me())
app.stop() app.stop()

View file

@ -31,7 +31,7 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
"e" if sys.getfilesystemencoding() != "utf-8" else "\xe8" "e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
) )
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)" __license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.7.5.dev2" __version__ = "0.7.5.dev3"
from .api.errors import Error from .api.errors import Error
from .client.types import ( from .client.types import (

View file

@ -50,9 +50,6 @@ from .ext import BaseClient, Syncer, utils
from .handlers import DisconnectHandler from .handlers import DisconnectHandler
from .methods import Methods from .methods import Methods
# Custom format for nice looking log lines
LOG_FORMAT = "[%(asctime)s.%(msecs)03d] %(filename)s:%(lineno)s %(levelname)s: %(message)s"
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -76,6 +73,26 @@ class Client(Methods, BaseClient):
The *api_hash* part of your Telegram API Key, as string. E.g.: "0123456789abcdef0123456789abcdef" The *api_hash* part of your Telegram API Key, as string. E.g.: "0123456789abcdef0123456789abcdef"
This is an alternative way to pass it if you don't want to use the *config.ini* file. This is an alternative way to pass it if you don't want to use the *config.ini* file.
app_version (``str``, *optional*):
Application version. Defaults to "Pyrogram \U0001f525 vX.Y.Z"
This is an alternative way to set it if you don't want to use the *config.ini* file.
device_model (``str``, *optional*):
Device model. Defaults to *platform.python_implementation() + " " + platform.python_version()*
This is an alternative way to set it if you don't want to use the *config.ini* file.
system_version (``str``, *optional*):
Operating System version. Defaults to *platform.system() + " " + platform.release()*
This is an alternative way to set it if you don't want to use the *config.ini* file.
system_lang_code (``str``, *optional*):
Code of the language used on the system, in ISO 639-1 standard. Defaults to "en".
This is an alternative way to set it if you don't want to use the *config.ini* file.
lang_code (``str``, *optional*):
Code of the language used on the client, in ISO 639-1 standard. Defaults to "en".
This is an alternative way to set it if you don't want to use the *config.ini* file.
proxy (``dict``, *optional*): proxy (``dict``, *optional*):
Your SOCKS5 Proxy settings as dict, Your SOCKS5 Proxy settings as dict,
e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*. e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*.
@ -92,8 +109,8 @@ class Client(Methods, BaseClient):
entering it manually. Only applicable for new sessions. entering it manually. Only applicable for new sessions.
phone_code (``str`` | ``callable``, *optional*): phone_code (``str`` | ``callable``, *optional*):
Pass the phone code as string (for test numbers only), or pass a callback function Pass the phone code as string (for test numbers only), or pass a callback function which accepts
which must return the correct phone code as string (e.g., "12345"). a single positional argument *(phone_number)* and must return the correct phone code (e.g., "12345").
Only applicable for new sessions. Only applicable for new sessions.
password (``str``, *optional*): password (``str``, *optional*):
@ -128,6 +145,11 @@ class Client(Methods, BaseClient):
session_name: str, session_name: str,
api_id: int or str = None, api_id: int or str = None,
api_hash: str = None, api_hash: str = None,
app_version: str = None,
device_model: str = None,
system_version: str = None,
system_lang_code: str = None,
lang_code: str = None,
proxy: dict = None, proxy: dict = None,
test_mode: bool = False, test_mode: bool = False,
phone_number: str = None, phone_number: str = None,
@ -144,6 +166,11 @@ class Client(Methods, BaseClient):
self.session_name = session_name self.session_name = session_name
self.api_id = int(api_id) if api_id else None self.api_id = int(api_id) if api_id else None
self.api_hash = api_hash self.api_hash = api_hash
self.app_version = app_version
self.device_model = device_model
self.system_version = system_version
self.system_lang_code = system_lang_code
self.lang_code = lang_code
# TODO: Make code consistent, use underscore for private/protected fields # TODO: Make code consistent, use underscore for private/protected fields
self._proxy = proxy self._proxy = proxy
self.test_mode = test_mode self.test_mode = test_mode
@ -186,12 +213,9 @@ class Client(Methods, BaseClient):
await self.load_session() await self.load_session()
self.session = Session( self.session = Session(
self,
self.dc_id, self.dc_id,
self.test_mode, self.auth_key
self._proxy,
self.auth_key,
self.api_id,
client=self
) )
await self.session.start() await self.session.start()
@ -274,6 +298,7 @@ class Client(Methods, BaseClient):
Iterable containing signals the signal handler will listen to. Iterable containing signals the signal handler will listen to.
Defaults to (SIGINT, SIGTERM, SIGABRT). Defaults to (SIGINT, SIGTERM, SIGABRT).
""" """
def signal_handler(*args): def signal_handler(*args):
log.info("Stop signal received ({}). Exiting...".format(args[0])) log.info("Stop signal received ({}). Exiting...".format(args[0]))
self.is_idle = False self.is_idle = False
@ -368,12 +393,9 @@ class Client(Methods, BaseClient):
self.auth_key = await Auth(self.dc_id, self.test_mode, self._proxy).create() self.auth_key = await Auth(self.dc_id, self.test_mode, self._proxy).create()
self.session = Session( self.session = Session(
self,
self.dc_id, self.dc_id,
self.test_mode, self.auth_key
self._proxy,
self.auth_key,
self.api_id,
client=self
) )
await self.session.start() await self.session.start()
@ -416,12 +438,9 @@ class Client(Methods, BaseClient):
self.auth_key = await Auth(self.dc_id, self.test_mode, self._proxy).create() self.auth_key = await Auth(self.dc_id, self.test_mode, self._proxy).create()
self.session = Session( self.session = Session(
self,
self.dc_id, self.dc_id,
self.test_mode, self.auth_key
self._proxy,
self.auth_key,
self.api_id,
client=self
) )
await self.session.start() await self.session.start()
@ -465,7 +484,7 @@ class Client(Methods, BaseClient):
self.phone_code = ( self.phone_code = (
input("Enter phone code: ") if self.phone_code is None input("Enter phone code: ") if self.phone_code is None
else self.phone_code if type(self.phone_code) is str else self.phone_code if type(self.phone_code) is str
else str(self.phone_code()) else str(self.phone_code(self.phone_number))
) )
try: try:
@ -807,7 +826,7 @@ class Client(Methods, BaseClient):
log.info("UpdatesWorkerTask stopped") log.info("UpdatesWorkerTask stopped")
async def send(self, data: Object): async def send(self, data: Object, retries: int = Session.MAX_RETRIES, timeout: float = Session.WAIT_TIMEOUT):
"""Use this method to send Raw Function queries. """Use this method to send Raw Function queries.
This method makes possible to manually call every single Telegram API method in a low-level manner. This method makes possible to manually call every single Telegram API method in a low-level manner.
@ -818,13 +837,19 @@ class Client(Methods, BaseClient):
data (``Object``): data (``Object``):
The API Scheme function filled with proper arguments. The API Scheme function filled with proper arguments.
retries (``int``):
Number of retries.
timeout (``float``):
Timeout in seconds.
Raises: Raises:
:class:`Error <pyrogram.Error>` :class:`Error <pyrogram.Error>`
""" """
if not self.is_started: if not self.is_started:
raise ConnectionError("Client has not been started") raise ConnectionError("Client has not been started")
r = await self.session.send(data) r = await self.session.send(data, retries, timeout)
self.fetch_peers(getattr(r, "users", [])) self.fetch_peers(getattr(r, "users", []))
self.fetch_peers(getattr(r, "chats", [])) self.fetch_peers(getattr(r, "chats", []))
@ -847,6 +872,31 @@ class Client(Methods, BaseClient):
"More info: https://docs.pyrogram.ml/start/ProjectSetup#configuration" "More info: https://docs.pyrogram.ml/start/ProjectSetup#configuration"
) )
for option in {"app_version", "device_model", "system_version", "system_lang_code", "lang_code"}:
if getattr(self, option):
pass
else:
setattr(self, option, Client.APP_VERSION)
if parser.has_section("pyrogram"):
setattr(self, option, parser.get(
"pyrogram",
option,
fallback=getattr(Client, option.upper())
))
if self.lang_code:
pass
else:
self.lang_code = Client.LANG_CODE
if parser.has_section("pyrogram"):
self.lang_code = parser.get(
"pyrogram",
"lang_code",
fallback=Client.LANG_CODE
)
if self._proxy: if self._proxy:
self._proxy["enabled"] = True self._proxy["enabled"] = True
else: else:
@ -1017,7 +1067,7 @@ class Client(Methods, BaseClient):
file_id = file_id or self.rnd_id() file_id = file_id or self.rnd_id()
md5_sum = md5() if not is_big and not is_missing_part else None md5_sum = md5() if not is_big and not is_missing_part else None
session = Session(self.dc_id, self.test_mode, self._proxy, self.auth_key, self.api_id) session = Session(self, self.dc_id, self.auth_key, is_media=True)
await session.start() await session.start()
try: try:
@ -1101,11 +1151,10 @@ class Client(Methods, BaseClient):
) )
session = Session( session = Session(
self,
dc_id, dc_id,
self.test_mode,
self._proxy,
await Auth(dc_id, self.test_mode, self._proxy).create(), await Auth(dc_id, self.test_mode, self._proxy).create(),
self.api_id is_media=True
) )
await session.start() await session.start()
@ -1120,11 +1169,10 @@ class Client(Methods, BaseClient):
) )
else: else:
session = Session( session = Session(
self,
dc_id, dc_id,
self.test_mode,
self._proxy,
self.auth_key, self.auth_key,
self.api_id is_media=True
) )
await session.start() await session.start()
@ -1190,11 +1238,10 @@ class Client(Methods, BaseClient):
if cdn_session is None: if cdn_session is None:
cdn_session = Session( cdn_session = Session(
self,
r.dc_id, r.dc_id,
self.test_mode,
self._proxy,
await Auth(r.dc_id, self.test_mode, self._proxy).create(), await Auth(r.dc_id, self.test_mode, self._proxy).create(),
self.api_id, is_media=True,
is_cdn=True is_cdn=True
) )

View file

@ -17,14 +17,32 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import asyncio import asyncio
import platform
import re import re
from pyrogram import __version__
from ..style import Markdown, HTML from ..style import Markdown, HTML
from ...api.core import Object from ...api.core import Object
from ...session import Session
from ...session.internals import MsgId from ...session.internals import MsgId
class BaseClient: class BaseClient:
APP_VERSION = "Pyrogram \U0001f525 {}".format(__version__)
DEVICE_MODEL = "{} {}".format(
platform.python_implementation(),
platform.python_version()
)
SYSTEM_VERSION = "{} {}".format(
platform.system(),
platform.release()
)
SYSTEM_LANG_CODE = "en"
LANG_CODE = "en"
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$") INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$") BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$")
DIALOGS_AT_ONCE = 100 DIALOGS_AT_ONCE = 100
@ -76,7 +94,7 @@ class BaseClient:
self.disconnect_handler = None self.disconnect_handler = None
def send(self, data: Object): def send(self, data: Object, retries: int = Session.MAX_RETRIES, timeout: float = Session.WAIT_TIMEOUT):
pass pass
def resolve_peer(self, peer_id: int or str): def resolve_peer(self, peer_id: int or str):

View file

@ -25,9 +25,8 @@ class RequestCallbackAnswer(BaseClient):
chat_id: int or str, chat_id: int or str,
message_id: int, message_id: int,
callback_data: str): callback_data: str):
"""Use this method to request a callback answer from bots. This is the equivalent of clicking an inline button """Use this method to request a callback answer from bots. This is the equivalent of clicking an
containing callback data. The answer contains info useful for clients to display a notification at the top of inline button containing callback data.
the chat screen or as an alert.
Args: Args:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
@ -41,11 +40,21 @@ class RequestCallbackAnswer(BaseClient):
callback_data (``str``): callback_data (``str``):
Callback data associated with the inline button you want to get the answer from. Callback data associated with the inline button you want to get the answer from.
Returns:
The answer containing info useful for clients to display a notification at the top of the chat screen
or as an alert.
Raises:
:class:`Error <pyrogram.Error>`
``TimeoutError``: If the bot fails to answer within 10 seconds
""" """
return await self.send( return await self.send(
functions.messages.GetBotCallbackAnswer( functions.messages.GetBotCallbackAnswer(
peer=self.resolve_peer(chat_id), peer=self.resolve_peer(chat_id),
msg_id=message_id, msg_id=message_id,
data=callback_data.encode() data=callback_data.encode()
) ),
retries=0,
timeout=10
) )

View file

@ -18,7 +18,6 @@
import asyncio import asyncio
import logging import logging
import platform
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import sha1 from hashlib import sha1
from io import BytesIO from io import BytesIO
@ -43,19 +42,8 @@ class Result:
class Session: class Session:
VERSION = __version__ INITIAL_SALT = 0x616e67656c696361
APP_VERSION = "Pyrogram \U0001f525 {}".format(VERSION) NET_WORKERS = 1
DEVICE_MODEL = "{} {}".format(
platform.python_implementation(),
platform.python_version()
)
SYSTEM_VERSION = "{} {}".format(
platform.system(),
platform.release()
)
WAIT_TIMEOUT = 15 WAIT_TIMEOUT = 15
MAX_RETRIES = 5 MAX_RETRIES = 5
ACKS_THRESHOLD = 8 ACKS_THRESHOLD = 8
@ -78,28 +66,24 @@ class Session:
} }
def __init__(self, def __init__(self,
client: pyrogram,
dc_id: int, dc_id: int,
test_mode: bool,
proxy: dict,
auth_key: bytes, auth_key: bytes,
api_id: int, is_media: bool = False,
is_cdn: bool = False, is_cdn: bool = False):
client: pyrogram = None):
if not Session.notice_displayed: if not Session.notice_displayed:
print("Pyrogram v{}, {}".format(__version__, __copyright__)) print("Pyrogram v{}, {}".format(__version__, __copyright__))
print("Licensed under the terms of the " + __license__, end="\n\n") print("Licensed under the terms of the " + __license__, end="\n\n")
Session.notice_displayed = True Session.notice_displayed = True
self.dc_id = dc_id
self.test_mode = test_mode
self.proxy = proxy
self.api_id = api_id
self.is_cdn = is_cdn
self.client = client self.client = client
self.dc_id = dc_id
self.auth_key = auth_key
self.is_media = is_media
self.is_cdn = is_cdn
self.connection = None self.connection = None
self.auth_key = auth_key
self.auth_key_id = sha1(auth_key).digest()[-8:] self.auth_key_id = sha1(auth_key).digest()[-8:]
self.session_id = Long(MsgId()) self.session_id = Long(MsgId())
@ -125,7 +109,7 @@ class Session:
async def start(self): async def start(self):
while True: while True:
self.connection = Connection(DataCenter(self.dc_id, self.test_mode), self.proxy) self.connection = Connection(DataCenter(self.dc_id, self.client.test_mode), self.client.proxy)
try: try:
await self.connection.connect() await self.connection.connect()
@ -144,12 +128,14 @@ class Session:
functions.InvokeWithLayer( functions.InvokeWithLayer(
layer, layer,
functions.InitConnection( functions.InitConnection(
self.api_id, api_id=self.client.api_id,
self.DEVICE_MODEL, app_version=self.client.app_version,
self.SYSTEM_VERSION, device_model=self.client.device_model,
self.APP_VERSION, system_version=self.client.system_version,
"en", "", "en", system_lang_code=self.client.system_lang_code,
functions.help.GetConfig(), lang_code=self.client.lang_code,
lang_pack="",
query=functions.help.GetConfig(),
) )
) )
) )
@ -349,7 +335,7 @@ class Session:
log.info("RecvTask stopped") log.info("RecvTask stopped")
async def _send(self, data: Object, wait_response: bool = True): async def _send(self, data: Object, wait_response: bool = True, timeout: float = WAIT_TIMEOUT):
message = self.msg_factory(data) message = self.msg_factory(data)
msg_id = message.msg_id msg_id = message.msg_id
@ -372,7 +358,7 @@ class Session:
if wait_response: if wait_response:
try: try:
await asyncio.wait_for(self.results[msg_id].event.wait(), self.WAIT_TIMEOUT) await asyncio.wait_for(self.results[msg_id].event.wait(), timeout)
except asyncio.TimeoutError: except asyncio.TimeoutError:
pass pass
@ -390,14 +376,14 @@ class Session:
else: else:
return result return result
async def send(self, data: Object, retries: int = MAX_RETRIES): async def send(self, data: Object, retries: int = MAX_RETRIES, timeout: float = WAIT_TIMEOUT):
try: try:
await asyncio.wait_for(self.is_connected.wait(), self.WAIT_TIMEOUT) await asyncio.wait_for(self.is_connected.wait(), self.WAIT_TIMEOUT)
except asyncio.TimeoutError: except asyncio.TimeoutError:
pass pass
try: try:
return await self._send(data) return await self._send(data, timeout=timeout)
except (OSError, TimeoutError, InternalServerError) as e: except (OSError, TimeoutError, InternalServerError) as e:
if retries == 0: if retries == 0:
raise e from None raise e from None
@ -408,7 +394,7 @@ class Session:
datetime.now(), type(data))) datetime.now(), type(data)))
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
return await self.send(data, retries - 1) return await self.send(data, retries - 1, timeout)
# class Result: # class Result:
# def __init__(self): # def __init__(self):
@ -807,4 +793,4 @@ class Session:
# datetime.now(), type(data))) # datetime.now(), type(data)))
# #
# time.sleep(0.5) # time.sleep(0.5)
# return self.send(data, retries - 1) # return self.send(data, retries - 1, timeout)