Merge branch 'aiogram:dev-2.x' into patch-1

This commit is contained in:
Wendirad Demelash 2022-03-25 14:13:12 +03:00 committed by GitHub
commit d180b47869
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 147 additions and 78 deletions

View file

@ -277,7 +277,13 @@ class BaseBot:
dest = destination if isinstance(destination, io.IOBase) else open(destination, 'wb')
session = await self.get_session()
async with session.get(url, timeout=timeout, proxy=self.proxy, proxy_auth=self.proxy_auth) as response:
async with session.get(
url,
timeout=timeout,
proxy=self.proxy,
proxy_auth=self.proxy_auth,
raise_for_status=True,
) as response:
while True:
chunk = await response.content.read(chunk_size)
if not chunk:

View file

@ -50,7 +50,7 @@ class MongoStorage(BaseStorage):
self._uri = uri
self._username = username
self._password = password
self._kwargs = kwargs
self._kwargs = kwargs # custom client options like SSL configuration, etc.
self._mongo: Optional[AsyncIOMotorClient] = None
self._db: Optional[AsyncIOMotorDatabase] = None
@ -63,7 +63,7 @@ class MongoStorage(BaseStorage):
if self._uri:
try:
self._mongo = AsyncIOMotorClient(self._uri)
self._mongo = AsyncIOMotorClient(self._uri, **self._kwargs)
except pymongo.errors.ConfigurationError as e:
if "query() got an unexpected keyword argument 'lifetime'" in e.args[0]:
import logging

View file

@ -216,11 +216,11 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
async def skip_updates(self):
"""
You can skip old incoming updates from queue.
This method is not recommended to use if you use payments or you bot has high-load.
This method is not recommended for using in production.
:return: None
Note that the webhook will be deleted!
"""
await self.bot.get_updates(offset=-1, timeout=1)
await self.bot.delete_webhook(drop_pending_updates=True)
async def process_updates(self, updates, fast: bool = True):
"""
@ -768,7 +768,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
.. code-block:: python3
dp.register_chosen_inline_handler(some_chosen_inline_handler, lambda chosen_inline_query: True)
dp.register_chosen_inline_handler(some_chosen_inline_handler, lambda chosen_inline_result: True)
:param callback:
:param state:
@ -793,8 +793,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
.. code-block:: python3
@dp.chosen_inline_handler(lambda chosen_inline_query: True)
async def some_chosen_inline_handler(chosen_inline_query: types.ChosenInlineResult)
@dp.chosen_inline_handler(lambda chosen_inline_result: True)
async def some_chosen_inline_handler(chosen_inline_result: types.ChosenInlineResult)
:param state:
:param custom_filters:

View file

@ -240,7 +240,7 @@ class TelegramObject(ContextInstanceMixin, metaclass=MetaTelegramObject):
:return:
"""
if key in self.props:
return self.props[key].set_value(self, value, self.conf.get('parent', None))
return self.props[key].set_value(self, value, self.conf.get('parent', self))
self.values[key] = value
# Log warning when Telegram silently adds new Fields

View file

@ -145,7 +145,7 @@ class InputTextMessageContent(InputMessageContent):
"""
message_text: base.String = fields.Field()
parse_mode: typing.Optional[base.String] = fields.Field()
caption_entities: typing.Optional[typing.List[MessageEntity]] = fields.Field()
entities: typing.Optional[typing.List[MessageEntity]] = fields.Field()
disable_web_page_preview: base.Boolean = fields.Field()
def safe_get_parse_mode(self):
@ -164,7 +164,7 @@ class InputTextMessageContent(InputMessageContent):
self,
message_text: base.String,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
entities: typing.Optional[typing.List[MessageEntity]] = None,
disable_web_page_preview: typing.Optional[base.Boolean] = None,
):
if parse_mode is None:
@ -175,7 +175,7 @@ class InputTextMessageContent(InputMessageContent):
super().__init__(
message_text=message_text,
parse_mode=parse_mode,
caption_entities=caption_entities,
entities=entities,
disable_web_page_preview=disable_web_page_preview,
)

View file

@ -11,7 +11,7 @@ from .poll import Poll, PollAnswer
from .pre_checkout_query import PreCheckoutQuery
from .shipping_query import ShippingQuery
from .chat_join_request import ChatJoinRequest
from ..utils import helper, deprecated
from ..utils import helper
class Update(base.TelegramObject):
@ -70,12 +70,6 @@ class AllowedUpdates(helper.Helper):
CHAT_MEMBER = helper.ListItem() # chat_member
CHAT_JOIN_REQUEST = helper.ListItem() # chat_join_request
CHOSEN_INLINE_QUERY = deprecated.DeprecatedReadOnlyClassVar(
"`CHOSEN_INLINE_QUERY` is a deprecated value for allowed update. "
"Use `CHOSEN_INLINE_RESULT`",
new_value_getter=lambda cls: cls.CHOSEN_INLINE_RESULT,
)
@classmethod
def default(cls):
return []

View file

@ -44,7 +44,7 @@ setup(
url='https://github.com/aiogram/aiogram',
license='MIT',
author='Alex Root Junior',
requires_python='>=3.7',
python_requires='>=3.7',
author_email='jroot.junior@gmail.com',
description='Is a pretty simple and fully asynchronous framework for Telegram Bot API',
long_description=get_description(),

View file

@ -1,7 +1,12 @@
import asyncio
import aioredis
import pytest
from _pytest.config import UsageError
from aiogram import Bot
from . import TOKEN
try:
import aioredis.util
except ImportError:
@ -72,3 +77,14 @@ def redis_options(request):
raise UsageError(f"Invalid redis URI {redis_uri!r}: {e}")
raise UsageError("Unsupported aioredis version")
@pytest.fixture(name='bot')
async def bot_fixture():
"""Bot fixture."""
bot = Bot(TOKEN)
yield bot
session = await bot.get_session()
if session and not session.closed:
await session.close()
await asyncio.sleep(0.2)

View file

@ -6,14 +6,6 @@ from . import FakeTelegram, TOKEN, BOT_ID
pytestmark = pytest.mark.asyncio
@pytest.fixture(name='bot')
async def bot_fixture():
""" Bot fixture """
_bot = Bot(TOKEN, parse_mode=types.ParseMode.MARKDOWN_V2)
yield _bot
await _bot.close()
async def test_get_me(bot: Bot):
""" getMe method test """
from .types.dataset import USER

View file

@ -1,11 +1,14 @@
import os
from io import BytesIO
from pathlib import Path
from unittest.mock import AsyncMock
import pytest
from aiohttp import ClientResponseError
from aiogram import Bot
from aiogram.types import File
from aiogram.utils.json import json
from tests import TOKEN
from tests.types.dataset import FILE
@ -14,12 +17,9 @@ pytestmark = pytest.mark.asyncio
@pytest.fixture(name='bot')
async def bot_fixture():
async def get_file():
return File(**FILE)
""" Bot fixture """
_bot = Bot(TOKEN)
_bot.get_file = get_file
_bot.get_file = AsyncMock(return_value=File(**FILE))
yield _bot
session = await _bot.get_session()
await session.close()
@ -37,43 +37,54 @@ def tmppath(tmpdir, request):
os.chdir(request.config.invocation_dir)
@pytest.fixture()
def get_file_response(aresponses):
aresponses.add(response=aresponses.Response(body=json.dumps(FILE)))
class TestBotDownload:
async def test_download_file(self, tmppath, bot, file):
async def test_download_file(self, tmppath, bot, file, get_file_response):
f = await bot.download_file(file_path=file.file_path)
assert len(f.read()) != 0
async def test_download_file_destination(self, tmppath, bot, file):
async def test_download_file_destination(self, tmppath, bot, file, get_file_response):
await bot.download_file(file_path=file.file_path, destination="test.file")
assert os.path.isfile(tmppath.joinpath('test.file'))
async def test_download_file_destination_with_dir(self, tmppath, bot, file):
async def test_download_file_destination_with_dir(self, tmppath, bot, file, get_file_response):
await bot.download_file(file_path=file.file_path,
destination=os.path.join('dir_name', 'file_name'))
assert os.path.isfile(tmppath.joinpath('dir_name', 'file_name'))
async def test_download_file_destination_raise_file_not_found(self, tmppath, bot, file):
async def test_download_file_destination_raise_file_not_found(self, tmppath, bot, file, get_file_response):
with pytest.raises(FileNotFoundError):
await bot.download_file(file_path=file.file_path,
destination=os.path.join('dir_name', 'file_name'),
make_dirs=False)
async def test_download_file_destination_io_bytes(self, tmppath, bot, file):
async def test_download_file_destination_io_bytes(self, tmppath, bot, file, get_file_response):
f = BytesIO()
await bot.download_file(file_path=file.file_path,
destination=f)
assert len(f.read()) != 0
async def test_download_file_raise_value_error(self, tmppath, bot, file):
async def test_download_file_raise_value_error(self, tmppath, bot, file, get_file_response):
with pytest.raises(ValueError):
await bot.download_file(file_path=file.file_path, destination="a", destination_dir="b")
async def test_download_file_destination_dir(self, tmppath, bot, file):
async def test_download_file_destination_dir(self, tmppath, bot, file, get_file_response):
await bot.download_file(file_path=file.file_path, destination_dir='test_dir')
assert os.path.isfile(tmppath.joinpath('test_dir', file.file_path))
async def test_download_file_destination_dir_raise_file_not_found(self, tmppath, bot, file):
async def test_download_file_destination_dir_raise_file_not_found(self, tmppath, bot, file, get_file_response):
with pytest.raises(FileNotFoundError):
await bot.download_file(file_path=file.file_path,
destination_dir='test_dir',
make_dirs=False)
assert os.path.isfile(tmppath.joinpath('test_dir', file.file_path))
async def test_download_file_404(self, tmppath, bot, file):
with pytest.raises(ClientResponseError) as exc_info:
await bot.download_file(file_path=file.file_path)
assert exc_info.value.status == 404

View file

@ -5,14 +5,6 @@ from aiogram import Dispatcher, Bot
pytestmark = pytest.mark.asyncio
@pytest.fixture(name='bot')
async def bot_fixture():
""" Bot fixture """
_bot = Bot(token='123456789:AABBCCDDEEFFaabbccddeeff-1234567890')
yield _bot
await _bot.close()
class TestDispatcherInit:
async def test_successful_init(self, bot):
"""

View file

@ -8,16 +8,8 @@ from . import FakeTelegram, TOKEN
pytestmark = pytest.mark.asyncio
@pytest.fixture(name='bot')
async def bot_fixture():
""" Bot fixture """
_bot = Bot(TOKEN, parse_mode=types.ParseMode.HTML)
yield _bot
await _bot.close()
@pytest.fixture()
async def message(bot):
async def message(bot: Bot):
"""
Message fixture
:param bot: Telegram bot fixture

View file

@ -19,6 +19,14 @@ CHAT = {
"type": "private",
}
CHAT_PHOTO = {
"small_file_id": "small_file_id",
"small_file_unique_id": "small_file_unique_id",
"big_file_id": "big_file_id",
"big_file_unique_id": "big_file_unique_id",
}
PHOTO = {
"file_id": "AgADBAADFak0G88YZAf8OAug7bHyS9x2ZxkABHVfpJywcloRAAGAAQABAg",
"file_size": 1101,
@ -485,3 +493,37 @@ REPLY_KEYBOARD_MARKUP = {
"keyboard": [[{"text": "something here"}]],
"resize_keyboard": True,
}
CHAT_PERMISSIONS = {
"can_send_messages": True,
"can_send_media_messages": True,
"can_send_polls": True,
"can_send_other_messages": True,
"can_add_web_page_previews": True,
"can_change_info": True,
"can_invite_users": True,
"can_pin_messages": True,
}
CHAT_LOCATION = {
"location": LOCATION,
"address": "address",
}
FULL_CHAT = {
**CHAT,
"photo": CHAT_PHOTO,
"bio": "bio",
"has_private_forwards": False,
"description": "description",
"invite_link": "invite_link",
"pinned_message": MESSAGE,
"permissions": CHAT_PERMISSIONS,
"slow_mode_delay": 10,
"message_auto_delete_time": 60,
"has_protected_content": True,
"sticker_set_name": "sticker_set_name",
"can_set_sticker_set": True,
"linked_chat_id": -1234567890,
"location": CHAT_LOCATION,
}

View file

@ -1,5 +1,10 @@
from aiogram import types
from .dataset import CHAT
import pytest
from aiogram import Bot, types
from .dataset import CHAT, FULL_CHAT
from .. import FakeTelegram
pytestmark = pytest.mark.asyncio
chat = types.Chat(**CHAT)
@ -59,3 +64,10 @@ def test_chat_actions():
assert types.ChatActions.FIND_LOCATION == 'find_location'
assert types.ChatActions.RECORD_VIDEO_NOTE == 'record_video_note'
assert types.ChatActions.UPLOAD_VIDEO_NOTE == 'upload_video_note'
async def test_update_chat(bot: Bot):
Bot.set_current(bot)
async with FakeTelegram(message_data=FULL_CHAT):
await chat.update_chat()
assert chat.to_python() == types.Chat(**FULL_CHAT).to_python()

View file

@ -1,12 +1,15 @@
import os
from io import BytesIO
from pathlib import Path
from unittest.mock import AsyncMock
import pytest
from aiohttp import ClientResponseError
from aiogram import Bot
from aiogram.types import File
from aiogram.types.mixins import Downloadable
from aiogram.utils.json import json
from tests import TOKEN
from tests.types.dataset import FILE
@ -18,7 +21,8 @@ async def bot_fixture():
""" Bot fixture """
_bot = Bot(TOKEN)
yield _bot
await (await _bot.get_session()).close()
session = await _bot.get_session()
await session.close()
@pytest.fixture
@ -30,73 +34,81 @@ def tmppath(tmpdir, request):
@pytest.fixture
def downloadable(bot):
async def get_file():
return File(**FILE)
downloadable = Downloadable()
downloadable.get_file = get_file
downloadable.get_file = AsyncMock(return_value=File(**FILE))
downloadable.bot = bot
return downloadable
@pytest.fixture()
def get_file_response(aresponses):
aresponses.add(response=aresponses.Response(body=json.dumps(FILE)))
class TestDownloadable:
async def test_download_make_dirs_false_nodir(self, tmppath, downloadable):
async def test_download_make_dirs_false_nodir(self, tmppath, downloadable, get_file_response):
with pytest.raises(FileNotFoundError):
await downloadable.download(make_dirs=False)
async def test_download_make_dirs_false_mkdir(self, tmppath, downloadable):
async def test_download_make_dirs_false_mkdir(self, tmppath, downloadable, get_file_response):
os.mkdir('voice')
await downloadable.download(make_dirs=False)
assert os.path.isfile(tmppath.joinpath(FILE["file_path"]))
async def test_download_make_dirs_true(self, tmppath, downloadable):
async def test_download_make_dirs_true(self, tmppath, downloadable, get_file_response):
await downloadable.download(make_dirs=True)
assert os.path.isfile(tmppath.joinpath(FILE["file_path"]))
async def test_download_deprecation_warning(self, tmppath, downloadable):
async def test_download_deprecation_warning(self, tmppath, downloadable, get_file_response):
with pytest.deprecated_call():
await downloadable.download("test.file")
async def test_download_destination(self, tmppath, downloadable):
async def test_download_destination(self, tmppath, downloadable, get_file_response):
with pytest.deprecated_call():
await downloadable.download("test.file")
assert os.path.isfile(tmppath.joinpath('test.file'))
async def test_download_destination_dir_exist(self, tmppath, downloadable):
async def test_download_destination_dir_exist(self, tmppath, downloadable, get_file_response):
os.mkdir("test_folder")
with pytest.deprecated_call():
await downloadable.download("test_folder")
assert os.path.isfile(tmppath.joinpath('test_folder', FILE["file_path"]))
async def test_download_destination_with_dir(self, tmppath, downloadable):
async def test_download_destination_with_dir(self, tmppath, downloadable, get_file_response):
with pytest.deprecated_call():
await downloadable.download(os.path.join('dir_name', 'file_name'))
assert os.path.isfile(tmppath.joinpath('dir_name', 'file_name'))
async def test_download_destination_io_bytes(self, tmppath, downloadable):
async def test_download_destination_io_bytes(self, tmppath, downloadable, get_file_response):
file = BytesIO()
with pytest.deprecated_call():
await downloadable.download(file)
assert len(file.read()) != 0
async def test_download_raise_value_error(self, tmppath, downloadable):
async def test_download_raise_value_error(self, tmppath, downloadable, get_file_response):
with pytest.raises(ValueError):
await downloadable.download(destination_dir="a", destination_file="b")
async def test_download_destination_dir(self, tmppath, downloadable):
async def test_download_destination_dir(self, tmppath, downloadable, get_file_response):
await downloadable.download(destination_dir='test_dir')
assert os.path.isfile(tmppath.joinpath('test_dir', FILE["file_path"]))
async def test_download_destination_file(self, tmppath, downloadable):
async def test_download_destination_file(self, tmppath, downloadable, get_file_response):
await downloadable.download(destination_file='file_name')
assert os.path.isfile(tmppath.joinpath('file_name'))
async def test_download_destination_file_with_dir(self, tmppath, downloadable):
async def test_download_destination_file_with_dir(self, tmppath, downloadable, get_file_response):
await downloadable.download(destination_file=os.path.join('dir_name', 'file_name'))
assert os.path.isfile(tmppath.joinpath('dir_name', 'file_name'))
async def test_download_io_bytes(self, tmppath, downloadable):
async def test_download_io_bytes(self, tmppath, downloadable, get_file_response):
file = BytesIO()
await downloadable.download(destination_file=file)
assert len(file.read()) != 0
async def test_download_404(self, tmppath, downloadable):
with pytest.raises(ClientResponseError) as exc_info:
await downloadable.download(destination_file='file_name')
assert exc_info.value.status == 404