Compare commits

...

9 Commits

Author SHA1 Message Date
Bibo-Joshi e314e78d06 Bump Version to v21.6 (#4486) 2024-09-19 20:17:08 +02:00
Harshil 67a97ae5a7 API 7.10 (#4461, #4460, #4463, #4464)
Co-authored-by: aelkheir <90580077+aelkheir@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
2024-09-17 18:09:19 +02:00
dependabot[bot] 9248c539d0 Bump pytest from 8.3.2 to 8.3.3 (#4475)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-14 08:17:31 +02:00
Bibo-Joshi 6b5e46cc08 Improve Type Completeness (#4466) 2024-09-14 08:16:39 +02:00
Harshil b3155b2e55 Update Python 3.13 Test Suite to RC2 (#4471) 2024-09-13 19:32:22 +02:00
Bibo-Joshi ec909e62cf Enforce the offline_bot Fixture in Test*WithoutRequest (#4465) 2024-09-13 19:10:09 +02:00
Bibo-Joshi 1223e851c3 Add Parameter httpx_kwargs to HTTPXRequest (#4451) 2024-09-11 22:34:18 +02:00
Bibo-Joshi 0b352b043e Make Tests for telegram.ext Independent of Networking (#4454) 2024-09-09 07:32:32 +02:00
Bibo-Joshi b9d2efdec5 Rename Testing Base Classes (#4453) 2024-09-03 05:24:25 +02:00
183 changed files with 3006 additions and 2165 deletions
@@ -0,0 +1,33 @@
name: Check Type Completeness Monthly Run
on:
schedule:
# Run first friday of the month at 03:17 - odd time to spread load on GitHub Actions
- cron: '17 3 1-7 * 5'
jobs:
test-type-completeness:
name: test-type-completeness
runs-on: ubuntu-latest
steps:
- uses: Bibo-Joshi/pyright-type-completeness@1.0.0
id: pyright-type-completeness
with:
package-name: telegram
python-version: 3.12
pyright-version: ~=1.1.367
- name: Check Output
uses: jannekem/run-python-script-action@v1
env:
TYPE_COMPLETENESS: ${{ steps.pyright-type-completeness.outputs.base-completeness-score }}
with:
script: |
import os
completeness = float(os.getenv("TYPE_COMPLETENESS"))
if completeness >= 1:
exit(0)
text = f"Type Completeness Decreased to {completeness}. ❌"
error(text)
set_summary(text)
exit(1)
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ${{matrix.os}}
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13.0-rc.1']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13.0-rc.2']
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: False
steps:
+31
View File
@@ -4,6 +4,37 @@
Changelog
=========
Version 21.6
============
*Released 2024-09-19*
This is the technical changelog for version 21.6. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
New Features
------------
- Full Support for Bot API 7.10 (:pr:`4461` closes :issue:`4459`, :pr:`4460`, :pr:`4463` by `aelkheir <https://github.com/aelkheir>`_, :pr:`4464`)
- Add Parameter ``httpx_kwargs`` to ``HTTPXRequest`` (:pr:`4451` closes :issue:`4424`)
Minor Changes
-------------
- Improve Type Completeness (:pr:`4466`)
Internal Changes
----------------
- Update Python 3.13 Test Suite to RC2 (:pr:`4471`)
- Enforce the ``offline_bot`` Fixture in ``Test*WithoutRequest`` (:pr:`4465`)
- Make Tests for ``telegram.ext`` Independent of Networking (:pr:`4454`)
- Rename Testing Base Classes (:pr:`4453`)
Dependency Updates
------------------
- Bump ``pytest`` from 8.3.2 to 8.3.3 (:pr:`4475`)
Version 21.5
============
+2 -2
View File
@@ -11,7 +11,7 @@
:target: https://pypi.org/project/python-telegram-bot/
:alt: Supported Python versions
.. image:: https://img.shields.io/badge/Bot%20API-7.9-blue?logo=telegram
.. image:: https://img.shields.io/badge/Bot%20API-7.10-blue?logo=telegram
:target: https://core.telegram.org/bots/api-changelog
:alt: Supported Bot API version
@@ -81,7 +81,7 @@ After installing_ the library, be sure to check out the section on `working with
Telegram API support
~~~~~~~~~~~~~~~~~~~~
All types and methods of the Telegram Bot API **7.9** are natively supported by this library.
All types and methods of the Telegram Bot API **7.10** are natively supported by this library.
In addition, Bot API functionality not yet natively included can still be used as described `in our wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Bot-API-Forward-Compatibility>`_.
Notable Features
+1
View File
@@ -120,6 +120,7 @@ Available Types
telegram.paidmediainfo
telegram.paidmediaphoto
telegram.paidmediapreview
telegram.paidmediapurchased
telegram.paidmediavideo
telegram.photosize
telegram.poll
@@ -18,6 +18,7 @@ Handlers
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.messagereactionhandler
telegram.ext.paidmediapurchasedhandler
telegram.ext.pollanswerhandler
telegram.ext.pollhandler
telegram.ext.precheckoutqueryhandler
@@ -0,0 +1,6 @@
PaidMediaPurchasedHandler
=========================
.. autoclass:: telegram.ext.PaidMediaPurchasedHandler
:members:
:show-inheritance:
@@ -0,0 +1,6 @@
PaidMediaPurchased
==================
.. autoclass:: telegram.PaidMediaPurchased
:members:
:show-inheritance:
+2 -2
View File
@@ -371,8 +371,8 @@ def main() -> None:
entry_points=[CommandHandler("start", start)],
states={
SHOWING: [CallbackQueryHandler(start, pattern="^" + str(END) + "$")],
SELECTING_ACTION: selection_handlers,
SELECTING_LEVEL: selection_handlers,
SELECTING_ACTION: selection_handlers, # type: ignore[dict-item]
SELECTING_LEVEL: selection_handlers, # type: ignore[dict-item]
DESCRIBING_SELF: [description_conv],
STOPPING: [CommandHandler("start", start)],
},
+1 -1
View File
@@ -4,7 +4,7 @@
build
# For the test suite
pytest==8.3.2
pytest==8.3.3
# needed because pytest doesn't come with native support for coroutines as tests
pytest-asyncio==0.21.2
+9 -1
View File
@@ -180,6 +180,7 @@ __all__ = (
"PaidMediaInfo",
"PaidMediaPhoto",
"PaidMediaPreview",
"PaidMediaPurchased",
"PaidMediaVideo",
"PassportData",
"PassportElementError",
@@ -419,7 +420,14 @@ from ._messageorigin import (
MessageOriginUser,
)
from ._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
from ._paidmedia import PaidMedia, PaidMediaInfo, PaidMediaPhoto, PaidMediaPreview, PaidMediaVideo
from ._paidmedia import (
PaidMedia,
PaidMediaInfo,
PaidMediaPhoto,
PaidMediaPreview,
PaidMediaPurchased,
PaidMediaVideo,
)
from ._passport.credentials import (
Credentials,
DataCredentials,
+9 -1
View File
@@ -9193,6 +9193,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
payload: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -9211,9 +9212,15 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
Telegram Star proceeds from this media will be credited to the chat's balance.
Otherwise, they will be credited to the bot's balance.
star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access
to the media.
to the media; :tg-const:`telegram.constants.InvoiceLimit.MIN_STAR_COUNT` -
:tg-const:`telegram.constants.InvoiceLimit.MAX_STAR_COUNT`.
media (Sequence[:class:`telegram.InputPaidMedia`]): A list describing the media to be
sent; up to :tg-const:`telegram.constants.MediaGroupLimit.MAX_MEDIA_LENGTH` items.
payload (:obj:`str`, optional): Bot-defined paid media payload,
0-:tg-const:`telegram.constants.InvoiceLimit.MAX_PAYLOAD_LENGTH` bytes. This will
not be displayed to the user, use it for your internal processes.
.. versionadded:: 21.6
caption (:obj:`str`, optional): Caption of the media to be sent,
0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters.
parse_mode (:obj:`str`, optional): |parse_mode|
@@ -9252,6 +9259,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
"star_count": star_count,
"media": media,
"show_caption_above_media": show_caption_above_media,
"payload": payload,
}
return await self._send_message(
+2
View File
@@ -3350,6 +3350,7 @@ class _ChatBase(TelegramObject):
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
payload: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -3391,6 +3392,7 @@ class _ChatBase(TelegramObject):
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
business_connection_id=business_connection_id,
payload=payload,
)
+17 -4
View File
@@ -187,15 +187,22 @@ class ChatBoostSourceGiftCode(ChatBoostSource):
class ChatBoostSourceGiveaway(ChatBoostSource):
"""
The boost was obtained by the creation of a Telegram Premium giveaway. This boosts the chat 4
times for the duration of the corresponding Telegram Premium subscription.
The boost was obtained by the creation of a Telegram Premium giveaway or a Telegram Star.
This boosts the chat 4 times for the duration of the corresponding Telegram Premium
subscription for Telegram Premium giveaways and :attr:`prize_star_count` / 500 times for
one year for Telegram Star giveaways.
.. versionadded:: 20.8
Args:
giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway;
the message could have been deleted already. May be 0 if the message isn't sent yet.
user (:class:`telegram.User`, optional): User that won the prize in the giveaway if any.
user (:class:`telegram.User`, optional): User that won the prize in the giveaway if any;
for Telegram Premium giveaways only.
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
is_unclaimed (:obj:`bool`, optional): :obj:`True`, if the giveaway was completed, but
there was no user to win the prize.
@@ -205,17 +212,22 @@ class ChatBoostSourceGiveaway(ChatBoostSource):
giveaway_message_id (:obj:`int`): Identifier of a message in the chat with the giveaway;
the message could have been deleted already. May be 0 if the message isn't sent yet.
user (:class:`telegram.User`): Optional. User that won the prize in the giveaway if any.
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
is_unclaimed (:obj:`bool`): Optional. :obj:`True`, if the giveaway was completed, but
there was no user to win the prize.
"""
__slots__ = ("giveaway_message_id", "is_unclaimed", "user")
__slots__ = ("giveaway_message_id", "is_unclaimed", "prize_star_count", "user")
def __init__(
self,
giveaway_message_id: int,
user: Optional[User] = None,
is_unclaimed: Optional[bool] = None,
prize_star_count: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -224,6 +236,7 @@ class ChatBoostSourceGiveaway(ChatBoostSource):
with self._unfrozen():
self.giveaway_message_id: int = giveaway_message_id
self.user: Optional[User] = user
self.prize_star_count: Optional[int] = prize_star_count
self.is_unclaimed: Optional[bool] = is_unclaimed
+54 -6
View File
@@ -56,8 +56,13 @@ class Giveaway(TelegramObject):
country codes indicating the countries from which eligible users for the giveaway must
come. If empty, then all users can participate in the giveaway. Users with a phone
number that was bought on Fragment can always participate in giveaways.
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram
Premium subscription won from the giveaway will be active for.
Premium subscription won from the giveaway will be active for; for Telegram Premium
giveaways only.
Attributes:
chats (Sequence[:class:`telegram.Chat`]): The list of chats which the user must join to
@@ -75,8 +80,13 @@ class Giveaway(TelegramObject):
country codes indicating the countries from which eligible users for the giveaway must
come. If empty, then all users can participate in the giveaway. Users with a phone
number that was bought on Fragment can always participate in giveaways.
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram
Premium subscription won from the giveaway will be active for.
Premium subscription won from the giveaway will be active for; for Telegram Premium
giveaways only.
"""
__slots__ = (
@@ -86,6 +96,7 @@ class Giveaway(TelegramObject):
"only_new_members",
"premium_subscription_month_count",
"prize_description",
"prize_star_count",
"winner_count",
"winners_selection_date",
)
@@ -100,6 +111,7 @@ class Giveaway(TelegramObject):
prize_description: Optional[str] = None,
country_codes: Optional[Sequence[str]] = None,
premium_subscription_month_count: Optional[int] = None,
prize_star_count: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -113,6 +125,7 @@ class Giveaway(TelegramObject):
self.prize_description: Optional[str] = prize_description
self.country_codes: Tuple[str, ...] = parse_sequence_arg(country_codes)
self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count
self.prize_star_count: Optional[int] = prize_star_count
self._id_attrs = (
self.chats,
@@ -145,13 +158,28 @@ class Giveaway(TelegramObject):
class GiveawayCreated(TelegramObject):
"""This object represents a service message about the creation of a scheduled giveaway.
Currently holds no information.
Args:
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be
split between giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
Attributes:
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be
split between giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
"""
__slots__ = ()
__slots__ = ("prize_star_count",)
def __init__(self, *, api_kwargs: Optional[JSONDict] = None):
def __init__(
self, prize_star_count: Optional[int] = None, *, api_kwargs: Optional[JSONDict] = None
):
super().__init__(api_kwargs=api_kwargs)
self.prize_star_count: Optional[int] = prize_star_count
self._freeze()
@@ -173,6 +201,10 @@ class GiveawayWinners(TelegramObject):
winner_count (:obj:`int`): Total number of winners in the giveaway
winners (Sequence[:class:`telegram.User`]): List of up to
:tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway
prize_star_count (:obj:`int`, optional): The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
additional_chat_count (:obj:`int`, optional): The number of other chats the user had to
join in order to be eligible for the giveaway
premium_subscription_month_count (:obj:`int`, optional): The number of months the Telegram
@@ -194,6 +226,10 @@ class GiveawayWinners(TelegramObject):
:tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway
additional_chat_count (:obj:`int`): Optional. The number of other chats the user had to
join in order to be eligible for the giveaway
prize_star_count (:obj:`int`): Optional. The number of Telegram Stars to be split between
giveaway winners; for Telegram Star giveaways only.
.. versionadded:: 21.6
premium_subscription_month_count (:obj:`int`): Optional. The number of months the Telegram
Premium subscription won from the giveaway will be active for
unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes
@@ -211,6 +247,7 @@ class GiveawayWinners(TelegramObject):
"only_new_members",
"premium_subscription_month_count",
"prize_description",
"prize_star_count",
"unclaimed_prize_count",
"was_refunded",
"winner_count",
@@ -231,6 +268,7 @@ class GiveawayWinners(TelegramObject):
only_new_members: Optional[bool] = None,
was_refunded: Optional[bool] = None,
prize_description: Optional[str] = None,
prize_star_count: Optional[int] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -247,6 +285,7 @@ class GiveawayWinners(TelegramObject):
self.only_new_members: Optional[bool] = only_new_members
self.was_refunded: Optional[bool] = was_refunded
self.prize_description: Optional[str] = prize_description
self.prize_star_count: Optional[int] = prize_star_count
self._id_attrs = (
self.chat,
@@ -295,21 +334,29 @@ class GiveawayCompleted(TelegramObject):
unclaimed_prize_count (:obj:`int`, optional): Number of undistributed prizes
giveaway_message (:class:`telegram.Message`, optional): Message with the giveaway that was
completed, if it wasn't deleted
is_star_giveaway (:obj:`bool`, optional): :obj:`True`, if the giveaway is a Telegram Star
giveaway. Otherwise, currently, the giveaway is a Telegram Premium giveaway.
.. versionadded:: 21.6
Attributes:
winner_count (:obj:`int`): Number of winners in the giveaway
unclaimed_prize_count (:obj:`int`): Optional. Number of undistributed prizes
giveaway_message (:class:`telegram.Message`): Optional. Message with the giveaway that was
completed, if it wasn't deleted
is_star_giveaway (:obj:`bool`): Optional. :obj:`True`, if the giveaway is a Telegram Star
giveaway. Otherwise, currently, the giveaway is a Telegram Premium giveaway.
.. versionadded:: 21.6
"""
__slots__ = ("giveaway_message", "unclaimed_prize_count", "winner_count")
__slots__ = ("giveaway_message", "is_star_giveaway", "unclaimed_prize_count", "winner_count")
def __init__(
self,
winner_count: int,
unclaimed_prize_count: Optional[int] = None,
giveaway_message: Optional["Message"] = None,
is_star_giveaway: Optional[bool] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -318,6 +365,7 @@ class GiveawayCompleted(TelegramObject):
self.winner_count: int = winner_count
self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count
self.giveaway_message: Optional[Message] = giveaway_message
self.is_star_giveaway: Optional[bool] = is_star_giveaway
self._id_attrs = (
self.winner_count,
+50
View File
@@ -24,6 +24,7 @@ from telegram import constants
from telegram._files.photosize import PhotoSize
from telegram._files.video import Video
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils import enum
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.types import JSONDict
@@ -288,3 +289,52 @@ class PaidMediaInfo(TelegramObject):
data["paid_media"] = PaidMedia.de_list(data.get("paid_media"), bot=bot)
return super().de_json(data=data, bot=bot)
class PaidMediaPurchased(TelegramObject):
"""This object contains information about a paid media purchase.
Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`from_user` and :attr:`paid_media_payload` are equal.
Note:
In Python :keyword:`from` is a reserved word. Use :paramref:`from_user` instead.
.. versionadded:: 21.6
Args:
from_user (:class:`telegram.User`): User who purchased the media.
paid_media_payload (:obj:`str`): Bot-specified paid media payload.
Attributes:
from_user (:class:`telegram.User`): User who purchased the media.
paid_media_payload (:obj:`str`): Bot-specified paid media payload.
"""
__slots__ = ("from_user", "paid_media_payload")
def __init__(
self,
from_user: "User",
paid_media_payload: str,
*,
api_kwargs: Optional[JSONDict] = None,
) -> None:
super().__init__(api_kwargs=api_kwargs)
self.from_user: User = from_user
self.paid_media_payload: str = paid_media_payload
self._id_attrs = (self.from_user, self.paid_media_payload)
self._freeze()
@classmethod
def de_json(
cls, data: Optional[JSONDict], bot: Optional["Bot"] = None
) -> Optional["PaidMediaPurchased"]:
data = cls._parse_data(data)
if not data:
return None
data["from_user"] = User.de_json(data=data.pop("from"), bot=bot)
return super().de_json(data=data, bot=bot)
+10 -5
View File
@@ -328,6 +328,9 @@ class TransactionPartnerUser(TransactionPartner):
media bought by the user.
.. versionadded:: 21.5
paid_media_payload (:obj:`str`, optional): Optional. Bot-specified paid media payload.
.. versionadded:: 21.6
Attributes:
type (:obj:`str`): The type of the transaction partner,
@@ -338,19 +341,20 @@ class TransactionPartnerUser(TransactionPartner):
media bought by the user.
.. versionadded:: 21.5
paid_media_payload (:obj:`str`): Optional. Optional. Bot-specified paid media payload.
.. versionadded:: 21.6
"""
__slots__ = (
"invoice_payload",
"paid_media",
"user",
)
__slots__ = ("invoice_payload", "paid_media", "paid_media_payload", "user")
def __init__(
self,
user: "User",
invoice_payload: Optional[str] = None,
paid_media: Optional[Sequence[PaidMedia]] = None,
paid_media_payload: Optional[str] = None,
*,
api_kwargs: Optional[JSONDict] = None,
) -> None:
@@ -360,6 +364,7 @@ class TransactionPartnerUser(TransactionPartner):
self.user: User = user
self.invoice_payload: Optional[str] = invoice_payload
self.paid_media: Optional[Tuple[PaidMedia, ...]] = parse_sequence_arg(paid_media)
self.paid_media_payload: Optional[str] = paid_media_payload
self._id_attrs = (
self.type,
self.user,
+32 -1
View File
@@ -30,6 +30,7 @@ from telegram._choseninlineresult import ChosenInlineResult
from telegram._inline.inlinequery import InlineQuery
from telegram._message import Message
from telegram._messagereactionupdated import MessageReactionCountUpdated, MessageReactionUpdated
from telegram._paidmedia import PaidMediaPurchased
from telegram._payment.precheckoutquery import PreCheckoutQuery
from telegram._payment.shippingquery import ShippingQuery
from telegram._poll import Poll, PollAnswer
@@ -156,6 +157,11 @@ class Update(TelegramObject):
.. versionadded:: 21.1
purchased_paid_media (:class:`telegram.PaidMediaPurchased`, optional): A user purchased
paid media with a non-empty payload sent by the bot in a non-channel chat.
.. versionadded:: 21.6
Attributes:
update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a
@@ -263,6 +269,11 @@ class Update(TelegramObject):
were deleted from a connected business account.
.. versionadded:: 21.1
purchased_paid_media (:class:`telegram.PaidMediaPurchased`): Optional. A user purchased
paid media with a non-empty payload sent by the bot in a non-channel chat.
.. versionadded:: 21.6
"""
__slots__ = (
@@ -290,6 +301,7 @@ class Update(TelegramObject):
"poll",
"poll_answer",
"pre_checkout_query",
"purchased_paid_media",
"removed_chat_boost",
"shipping_query",
"update_id",
@@ -383,6 +395,13 @@ class Update(TelegramObject):
""":const:`telegram.constants.UpdateType.DELETED_BUSINESS_MESSAGES`
.. versionadded:: 21.1"""
PURCHASED_PAID_MEDIA: Final[str] = constants.UpdateType.PURCHASED_PAID_MEDIA
""":const:`telegram.constants.UpdateType.PURCHASED_PAID_MEDIA`
.. versionadded:: 21.6
"""
ALL_TYPES: Final[List[str]] = list(constants.UpdateType)
"""List[:obj:`str`]: A list of all available update types.
@@ -413,6 +432,7 @@ class Update(TelegramObject):
business_message: Optional[Message] = None,
edited_business_message: Optional[Message] = None,
deleted_business_messages: Optional[BusinessMessagesDeleted] = None,
purchased_paid_media: Optional[PaidMediaPurchased] = None,
*,
api_kwargs: Optional[JSONDict] = None,
):
@@ -444,6 +464,7 @@ class Update(TelegramObject):
self.deleted_business_messages: Optional[BusinessMessagesDeleted] = (
deleted_business_messages
)
self.purchased_paid_media: Optional[PaidMediaPurchased] = purchased_paid_media
self._effective_user: Optional[User] = None
self._effective_sender: Optional[Union[User, Chat]] = None
@@ -475,6 +496,9 @@ class Update(TelegramObject):
This property now also considers :attr:`business_connection`, :attr:`business_message`
and :attr:`edited_business_message`.
.. versionchanged:: 21.6
This property now also considers :attr:`purchased_paid_media`.
Example:
* If :attr:`message` is present, this will give
:attr:`telegram.Message.from_user`.
@@ -531,6 +555,9 @@ class Update(TelegramObject):
elif self.business_connection:
user = self.business_connection.user
elif self.purchased_paid_media:
user = self.purchased_paid_media.from_user
self._effective_user = user
return user
@@ -601,7 +628,8 @@ class Update(TelegramObject):
This is the case, if :attr:`inline_query`,
:attr:`chosen_inline_result`, :attr:`callback_query` from inline messages,
:attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll`,
:attr:`poll_answer`, or :attr:`business_connection` is present.
:attr:`poll_answer`, :attr:`business_connection`, or :attr:`purchased_paid_media`
is present.
.. versionchanged:: 21.1
This property now also considers :attr:`business_message`,
@@ -768,5 +796,8 @@ class Update(TelegramObject):
data["deleted_business_messages"] = BusinessMessagesDeleted.de_json(
data.get("deleted_business_messages"), bot
)
data["purchased_paid_media"] = PaidMediaPurchased.de_json(
data.get("purchased_paid_media"), bot
)
return super().de_json(data=data, bot=bot)
+1 -1
View File
@@ -51,6 +51,6 @@ class Version(NamedTuple):
__version_info__: Final[Version] = Version(
major=21, minor=5, micro=0, releaselevel="final", serial=0
major=21, minor=6, micro=0, releaselevel="final", serial=0
)
__version__: Final[str] = str(__version_info__)
+77 -56
View File
@@ -152,7 +152,7 @@ class _AccentColor(NamedTuple):
#: :data:`telegram.__bot_api_version_info__`.
#:
#: .. versionadded:: 20.0
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=9)
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=10)
#: :obj:`str`: Telegram Bot API
#: version supported by this version of `python-telegram-bot`. Also available as
#: :data:`telegram.__bot_api_version__`.
@@ -552,6 +552,42 @@ class AccentColor(Enum):
"""
class BackgroundTypeType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundType`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
FILL = "fill"
""":obj:`str`: A :class:`telegram.BackgroundType` with fill background."""
WALLPAPER = "wallpaper"
""":obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background."""
PATTERN = "pattern"
""":obj:`str`: A :class:`telegram.BackgroundType` with pattern background."""
CHAT_THEME = "chat_theme"
""":obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background."""
class BackgroundFillType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundFill`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
SOLID = "solid"
""":obj:`str`: A :class:`telegram.BackgroundFill` with solid fill."""
GRADIENT = "gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
FREEFORM_GRADIENT = "freeform_gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
class BotCommandLimit(IntEnum):
"""This enum contains limitations for :class:`telegram.BotCommand` and
:meth:`telegram.Bot.set_my_commands`.
@@ -833,6 +869,25 @@ class ChatLimit(IntEnum):
"""
class ChatSubscriptionLimit(IntEnum):
"""This enum contains limitations for
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_period` and
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_price`.
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
.. versionadded:: 21.5
"""
__slots__ = ()
SUBSCRIPTION_PERIOD = 2592000
""":obj:`int`: The number of seconds the subscription will be active."""
MIN_PRICE = 1
""":obj:`int`: Amount of stars a user pays, minimum amount the subscription can be set to."""
MAX_PRICE = 2500
""":obj:`int`: Amount of stars a user pays, maximum amount the subscription can be set to."""
class BackgroundTypeLimit(IntEnum):
"""This enum contains limitations for :class:`telegram.BackgroundTypeFill`,
:class:`telegram.BackgroundTypeWallpaper` and :class:`telegram.BackgroundTypePattern`.
@@ -2724,6 +2779,11 @@ class UpdateType(StringEnum):
.. versionadded:: 21.1
"""
PURCHASED_PAID_MEDIA = "purchased_paid_media"
""":obj:`str`: Updates with :attr:`telegram.Update.purchased_paid_media`.
.. versionadded:: 21.6
"""
class InvoiceLimit(IntEnum):
@@ -2795,6 +2855,8 @@ class InvoiceLimit(IntEnum):
:meth:`telegram.Bot.send_invoice`.
* :paramref:`~telegram.Bot.create_invoice_link.payload` parameter of
:meth:`telegram.Bot.create_invoice_link`.
* :paramref:`~telegram.Bot.send_paid_media.payload` parameter of
:meth:`telegram.Bot.send_paid_media`.
"""
MAX_TIP_AMOUNTS = 4
""":obj:`int`: Maximum length of a :obj:`Sequence` passed as:
@@ -2804,6 +2866,20 @@ class InvoiceLimit(IntEnum):
* :paramref:`~telegram.Bot.create_invoice_link.suggested_tip_amounts` parameter of
:meth:`telegram.Bot.create_invoice_link`.
"""
MIN_STAR_COUNT = 1
""":obj:`int`: Minimum amount of starts that must be paid to buy access to a paid media
passed as :paramref:`~telegram.Bot.send_paid_media.star_count` parameter of
:meth:`telegram.Bot.send_paid_media`.
.. versionadded:: 21.6
"""
MAX_STAR_COUNT = 2500
""":obj:`int`: Maximum amount of starts that must be paid to buy access to a paid media
passed as :paramref:`~telegram.Bot.send_paid_media.star_count` parameter of
:meth:`telegram.Bot.send_paid_media`.
.. versionadded:: 21.6
"""
class UserProfilePhotosLimit(IntEnum):
@@ -3066,58 +3142,3 @@ class ReactionEmoji(StringEnum):
""":obj:`str`: Woman Shrugging"""
POUTING_FACE = "😡"
""":obj:`str`: Pouting face"""
class BackgroundTypeType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundType`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
FILL = "fill"
""":obj:`str`: A :class:`telegram.BackgroundType` with fill background."""
WALLPAPER = "wallpaper"
""":obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background."""
PATTERN = "pattern"
""":obj:`str`: A :class:`telegram.BackgroundType` with pattern background."""
CHAT_THEME = "chat_theme"
""":obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background."""
class BackgroundFillType(StringEnum):
"""This enum contains the available types of :class:`telegram.BackgroundFill`. The enum
members of this enumeration are instances of :class:`str` and can be treated as such.
.. versionadded:: 21.2
"""
__slots__ = ()
SOLID = "solid"
""":obj:`str`: A :class:`telegram.BackgroundFill` with solid fill."""
GRADIENT = "gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
FREEFORM_GRADIENT = "freeform_gradient"
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
class ChatSubscriptionLimit(IntEnum):
"""This enum contains limitations for
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_period` and
:paramref:`telegram.Bot.create_chat_subscription_invite_link.subscription_price`.
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
.. versionadded:: 21.5
"""
__slots__ = ()
SUBSCRIPTION_PERIOD = 2592000
""":obj:`int`: The number of seconds the subscription will be active."""
MIN_PRICE = 1
""":obj:`int`: Amount of stars a user pays, minimum amount the subscription can be set to."""
MAX_PRICE = 2500
""":obj:`int`: Amount of stars a user pays, maximum amount the subscription can be set to."""
+2
View File
@@ -48,6 +48,7 @@ __all__ = (
"JobQueue",
"MessageHandler",
"MessageReactionHandler",
"PaidMediaPurchasedHandler",
"PersistenceInput",
"PicklePersistence",
"PollAnswerHandler",
@@ -89,6 +90,7 @@ from ._handlers.conversationhandler import ConversationHandler
from ._handlers.inlinequeryhandler import InlineQueryHandler
from ._handlers.messagehandler import MessageHandler
from ._handlers.messagereactionhandler import MessageReactionHandler
from ._handlers.paidmediapurchasedhandler import PaidMediaPurchasedHandler
from ._handlers.pollanswerhandler import PollAnswerHandler
from ._handlers.pollhandler import PollHandler
from ._handlers.precheckoutqueryhandler import PreCheckoutQueryHandler
+7 -5
View File
@@ -323,7 +323,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
self.update_queue: asyncio.Queue[object] = update_queue
self.context_types: ContextTypes[CCT, UD, CD, BD] = context_types
self.updater: Optional[Updater] = updater
self.handlers: Dict[int, List[BaseHandler[Any, CCT]]] = {}
self.handlers: Dict[int, List[BaseHandler[Any, CCT, Any]]] = {}
self.error_handlers: Dict[
HandlerCallback[object, CCT, None], Union[bool, DefaultValue[bool]]
] = {}
@@ -1352,7 +1352,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
# (in __create_task_callback)
self._mark_for_persistence_update(update=update)
def add_handler(self, handler: BaseHandler[Any, CCT], group: int = DEFAULT_GROUP) -> None:
def add_handler(self, handler: BaseHandler[Any, CCT, Any], group: int = DEFAULT_GROUP) -> None:
"""Register a handler.
TL;DR: Order and priority counts. 0 or 1 handlers per group will be used. End handling of
@@ -1420,8 +1420,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
def add_handlers(
self,
handlers: Union[
Union[List[BaseHandler[Any, CCT]], Tuple[BaseHandler[Any, CCT]]],
Dict[int, Union[List[BaseHandler[Any, CCT]], Tuple[BaseHandler[Any, CCT]]]],
Union[List[BaseHandler[Any, CCT, Any]], Tuple[BaseHandler[Any, CCT, Any]]],
Dict[int, Union[List[BaseHandler[Any, CCT, Any]], Tuple[BaseHandler[Any, CCT, Any]]]],
],
group: Union[int, DefaultValue[int]] = _DEFAULT_0,
) -> None:
@@ -1469,7 +1469,9 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica
"dictionary where the keys are groups and values are sequences of handlers."
)
def remove_handler(self, handler: BaseHandler[Any, CCT], group: int = DEFAULT_GROUP) -> None:
def remove_handler(
self, handler: BaseHandler[Any, CCT, Any], group: int = DEFAULT_GROUP
) -> None:
"""Remove a handler from the specified group.
Args:
+10 -6
View File
@@ -29,6 +29,7 @@ from typing import (
NoReturn,
Optional,
Type,
TypeVar,
Union,
)
@@ -49,6 +50,9 @@ _STORING_DATA_WIKI = (
"/wiki/Storing-bot%2C-user-and-chat-related-data"
)
# something like poor mans "typing.Self" for py<3.11
ST = TypeVar("ST", bound="CallbackContext[Any, Any, Any, Any]")
class CallbackContext(Generic[BT, UD, CD, BD]):
"""
@@ -133,24 +137,24 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
)
def __init__(
self: "CCT",
application: "Application[BT, CCT, UD, CD, BD, Any]",
self: ST,
application: "Application[BT, ST, UD, CD, BD, Any]",
chat_id: Optional[int] = None,
user_id: Optional[int] = None,
):
self._application: Application[BT, CCT, UD, CD, BD, Any] = application
self._application: Application[BT, ST, UD, CD, BD, Any] = application
self._chat_id: Optional[int] = chat_id
self._user_id: Optional[int] = user_id
self.args: Optional[List[str]] = None
self.matches: Optional[List[Match[str]]] = None
self.error: Optional[Exception] = None
self.job: Optional[Job[CCT]] = None
self.job: Optional[Job[Any]] = None
self.coroutine: Optional[
Union[Generator[Optional[Future[object]], None, Any], Awaitable[Any]]
] = None
@property
def application(self) -> "Application[BT, CCT, UD, CD, BD, Any]":
def application(self) -> "Application[BT, ST, UD, CD, BD, Any]":
""":class:`telegram.ext.Application`: The application associated with this context."""
return self._application
@@ -398,7 +402,7 @@ class CallbackContext(Generic[BT, UD, CD, BD]):
return self._application.bot
@property
def job_queue(self) -> Optional["JobQueue[CCT]"]:
def job_queue(self) -> Optional["JobQueue[ST]"]:
"""
:class:`telegram.ext.JobQueue`: The :class:`JobQueue` used by the
:class:`telegram.ext.Application`.
+2
View File
@@ -4235,6 +4235,7 @@ class ExtBot(Bot, Generic[RLARGS]):
reply_parameters: Optional["ReplyParameters"] = None,
reply_markup: Optional[ReplyMarkup] = None,
business_connection_id: Optional[str] = None,
payload: Optional[str] = None,
*,
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
reply_to_message_id: Optional[int] = None,
@@ -4265,6 +4266,7 @@ class ExtBot(Bot, Generic[RLARGS]):
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
business_connection_id=business_connection_id,
payload=payload,
)
async def create_chat_subscription_invite_link(
+4 -3
View File
@@ -32,14 +32,14 @@ RT = TypeVar("RT")
UT = TypeVar("UT")
class BaseHandler(Generic[UT, CCT], ABC):
class BaseHandler(Generic[UT, CCT, RT], ABC):
"""The base class for all update handlers. Create custom handlers by inheriting from it.
Warning:
When setting :paramref:`block` to :obj:`False`, you cannot rely on adding custom
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
This class is a :class:`~typing.Generic` class and accepts two type variables:
This class is a :class:`~typing.Generic` class and accepts three type variables:
1. The type of the updates that this handler will handle. Must coincide with the type of the
first argument of :paramref:`callback`. :meth:`check_update` must only accept
@@ -54,6 +54,7 @@ class BaseHandler(Generic[UT, CCT], ABC):
For this type variable, one should usually provide a :class:`~typing.TypeVar` that is
also used for the mentioned method arguments. That way, a type checker can check whether
this handler fits the definition of the :class:`~Application`.
3. The return type of the :paramref:`callback` function accepted by this handler.
.. seealso:: :wiki:`Types of Handlers <Types-of-Handlers>`
@@ -89,7 +90,7 @@ class BaseHandler(Generic[UT, CCT], ABC):
)
def __init__(
self,
self: "BaseHandler[UT, CCT, RT]",
callback: HandlerCallback[UT, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
):
@@ -29,7 +29,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class BusinessConnectionHandler(BaseHandler[Update, CCT]):
class BusinessConnectionHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram
:attr:`Business Connections <telegram.Update.business_connection>`.
@@ -65,7 +65,7 @@ class BusinessConnectionHandler(BaseHandler[Update, CCT]):
)
def __init__(
self,
self: "BusinessConnectionHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
user_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
@@ -29,7 +29,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class BusinessMessagesDeletedHandler(BaseHandler[Update, CCT]):
class BusinessMessagesDeletedHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle
:attr:`deleted Telegram Business messages <telegram.Update.deleted_business_messages>`.
@@ -65,7 +65,7 @@ class BusinessMessagesDeletedHandler(BaseHandler[Update, CCT]):
)
def __init__(
self,
self: "BusinessMessagesDeletedHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class CallbackQueryHandler(BaseHandler[Update, CCT]):
class CallbackQueryHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram
:attr:`callback queries <telegram.Update.callback_query>`. Optionally based on a regex.
@@ -125,7 +125,7 @@ class CallbackQueryHandler(BaseHandler[Update, CCT]):
__slots__ = ("game_pattern", "pattern")
def __init__(
self,
self: "CallbackQueryHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
pattern: Optional[
Union[str, Pattern[str], type, Callable[[object], Optional[bool]]]
+4 -4
View File
@@ -23,10 +23,10 @@ from typing import Final, Optional
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, HandlerCallback
from telegram.ext._utils.types import CCT, RT, HandlerCallback
class ChatBoostHandler(BaseHandler[Update, CCT]):
class ChatBoostHandler(BaseHandler[Update, CCT, RT]):
"""
Handler class to handle Telegram updates that contain a chat boost.
@@ -84,8 +84,8 @@ class ChatBoostHandler(BaseHandler[Update, CCT]):
and :attr:`telegram.Update.removed_chat_boost`."""
def __init__(
self,
callback: HandlerCallback[Update, CCT, None],
self: "ChatBoostHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_boost_types: int = CHAT_BOOST,
chat_id: Optional[int] = None,
chat_username: Optional[str] = None,
@@ -28,7 +28,7 @@ from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, HandlerCallback
class ChatJoinRequestHandler(BaseHandler[Update, CCT]):
class ChatJoinRequestHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain
:attr:`telegram.Update.chat_join_request`.
@@ -81,7 +81,7 @@ class ChatJoinRequestHandler(BaseHandler[Update, CCT]):
)
def __init__(
self,
self: "ChatJoinRequestHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
+2 -2
View File
@@ -29,7 +29,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class ChatMemberHandler(BaseHandler[Update, CCT]):
class ChatMemberHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a chat member update.
Warning:
@@ -87,7 +87,7 @@ class ChatMemberHandler(BaseHandler[Update, CCT]):
and :attr:`telegram.Update.chat_member`."""
def __init__(
self,
self: "ChatMemberHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_member_types: int = MY_CHAT_MEMBER,
block: DVType[bool] = DEFAULT_TRUE,
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
from telegram.ext import Application
class ChosenInlineResultHandler(BaseHandler[Update, CCT]):
class ChosenInlineResultHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain
:attr:`telegram.Update.chosen_inline_result`.
@@ -76,7 +76,7 @@ class ChosenInlineResultHandler(BaseHandler[Update, CCT]):
__slots__ = ("pattern",)
def __init__(
self,
self: "ChosenInlineResultHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
pattern: Optional[Union[str, Pattern[str]]] = None,
+2 -2
View File
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class CommandHandler(BaseHandler[Update, CCT]):
class CommandHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram commands.
Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the
@@ -118,7 +118,7 @@ class CommandHandler(BaseHandler[Update, CCT]):
__slots__ = ("commands", "filters", "has_args")
def __init__(
self,
self: "CommandHandler[CCT, RT]",
command: SCT[str],
callback: HandlerCallback[Update, CCT, RT],
filters: Optional[filters_module.BaseFilter] = None,
+13 -13
View File
@@ -55,7 +55,7 @@ from telegram.ext._utils.types import CCT, ConversationDict, ConversationKey
if TYPE_CHECKING:
from telegram.ext import Application, Job, JobQueue
_CheckUpdateType = Tuple[object, ConversationKey, BaseHandler[Update, CCT], object]
_CheckUpdateType = Tuple[object, ConversationKey, BaseHandler[Update, CCT, object], object]
_LOGGER = get_logger(__name__, class_name="ConversationHandler")
@@ -119,7 +119,7 @@ class PendingState:
return res
class ConversationHandler(BaseHandler[Update, CCT]):
class ConversationHandler(BaseHandler[Update, CCT, object]):
"""
A handler to hold a conversation with a single or multiple users through Telegram updates by
managing three collections of other handlers.
@@ -296,10 +296,10 @@ class ConversationHandler(BaseHandler[Update, CCT]):
# pylint: disable=super-init-not-called
def __init__(
self,
entry_points: List[BaseHandler[Update, CCT]],
states: Dict[object, List[BaseHandler[Update, CCT]]],
fallbacks: List[BaseHandler[Update, CCT]],
self: "ConversationHandler[CCT]",
entry_points: List[BaseHandler[Update, CCT, object]],
states: Dict[object, List[BaseHandler[Update, CCT, object]]],
fallbacks: List[BaseHandler[Update, CCT, object]],
allow_reentry: bool = False,
per_chat: bool = True,
per_user: bool = True,
@@ -324,9 +324,9 @@ class ConversationHandler(BaseHandler[Update, CCT]):
# Store the actual setting in a protected variable instead
self._block: DVType[bool] = block
self._entry_points: List[BaseHandler[Update, CCT]] = entry_points
self._states: Dict[object, List[BaseHandler[Update, CCT]]] = states
self._fallbacks: List[BaseHandler[Update, CCT]] = fallbacks
self._entry_points: List[BaseHandler[Update, CCT, object]] = entry_points
self._states: Dict[object, List[BaseHandler[Update, CCT, object]]] = states
self._fallbacks: List[BaseHandler[Update, CCT, object]] = fallbacks
self._allow_reentry: bool = allow_reentry
self._per_user: bool = per_user
@@ -359,7 +359,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
stacklevel=2,
)
all_handlers: List[BaseHandler[Update, CCT]] = []
all_handlers: List[BaseHandler[Update, CCT, object]] = []
all_handlers.extend(entry_points)
all_handlers.extend(fallbacks)
@@ -466,7 +466,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
)
@property
def entry_points(self) -> List[BaseHandler[Update, CCT]]:
def entry_points(self) -> List[BaseHandler[Update, CCT, object]]:
"""List[:class:`telegram.ext.BaseHandler`]: A list of :obj:`BaseHandler` objects that can
trigger the start of the conversation.
"""
@@ -479,7 +479,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
)
@property
def states(self) -> Dict[object, List[BaseHandler[Update, CCT]]]:
def states(self) -> Dict[object, List[BaseHandler[Update, CCT, object]]]:
"""Dict[:obj:`object`, List[:class:`telegram.ext.BaseHandler`]]: A :obj:`dict` that
defines the different states of conversation a user can be in and one or more
associated :obj:`BaseHandler` objects that should be used in that state.
@@ -491,7 +491,7 @@ class ConversationHandler(BaseHandler[Update, CCT]):
raise AttributeError("You can not assign a new value to states after initialization.")
@property
def fallbacks(self) -> List[BaseHandler[Update, CCT]]:
def fallbacks(self) -> List[BaseHandler[Update, CCT, object]]:
"""List[:class:`telegram.ext.BaseHandler`]: A list of handlers that might be used if
the user is in a conversation, but every handler for their current state returned
:obj:`False` on :meth:`check_update`.
+2 -2
View File
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class InlineQueryHandler(BaseHandler[Update, CCT]):
class InlineQueryHandler(BaseHandler[Update, CCT, RT]):
"""
BaseHandler class to handle Telegram updates that contain a
:attr:`telegram.Update.inline_query`.
@@ -87,7 +87,7 @@ class InlineQueryHandler(BaseHandler[Update, CCT]):
__slots__ = ("chat_types", "pattern")
def __init__(
self,
self: "InlineQueryHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
pattern: Optional[Union[str, Pattern[str]]] = None,
block: DVType[bool] = DEFAULT_TRUE,
+2 -2
View File
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class MessageHandler(BaseHandler[Update, CCT]):
class MessageHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram messages. They might contain text, media or status
updates.
@@ -75,7 +75,7 @@ class MessageHandler(BaseHandler[Update, CCT]):
__slots__ = ("filters",)
def __init__(
self,
self: "MessageHandler[CCT, RT]",
filters: Optional[filters_module.BaseFilter],
callback: HandlerCallback[Update, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
@@ -28,7 +28,7 @@ from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, HandlerCallback
class MessageReactionHandler(BaseHandler[Update, CCT]):
class MessageReactionHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a message reaction.
Note:
@@ -110,7 +110,7 @@ class MessageReactionHandler(BaseHandler[Update, CCT]):
and :attr:`telegram.Update.message_reaction_count`."""
def __init__(
self,
self: "MessageReactionHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
chat_id: Optional[SCT[int]] = None,
chat_username: Optional[SCT[str]] = None,
@@ -0,0 +1,95 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2024
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains the PaidMediaPurchased class."""
from typing import Optional
from telegram import Update
from telegram._utils.defaultvalue import DEFAULT_TRUE
from telegram._utils.types import SCT, DVType
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils._update_parsing import parse_chat_id, parse_username
from telegram.ext._utils.types import CCT, RT, HandlerCallback
class PaidMediaPurchasedHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram
:attr:`purchased paid media <telegram.Update.purchased_paid_media>`.
.. versionadded:: 21.6
Args:
callback (:term:`coroutine function`): The callback function for this handler. Will be
called when :meth:`check_update` has determined that an update should be processed by
this handler. Callback signature::
async def callback(update: Update, context: CallbackContext)
user_id (:obj:`int` | Collection[:obj:`int`], optional): Filters requests to allow only
those which are from the specified user ID(s).
username (:obj:`str` | Collection[:obj:`str`], optional): Filters requests to allow only
those which are from the specified username(s).
block (:obj:`bool`, optional): Determines whether the return value of the callback should
be awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`.
.. seealso:: :wiki:`Concurrency`
Attributes:
callback (:term:`coroutine function`): The callback function for this handler.
block (:obj:`bool`): Determines whether the return value of the callback should be
awaited before processing the next handler in
:meth:`telegram.ext.Application.process_update`.
"""
__slots__ = (
"_user_ids",
"_usernames",
)
def __init__(
self: "PaidMediaPurchasedHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
user_id: Optional[SCT[int]] = None,
username: Optional[SCT[str]] = None,
block: DVType[bool] = DEFAULT_TRUE,
):
super().__init__(callback, block=block)
self._user_ids = parse_chat_id(user_id)
self._usernames = parse_username(username)
def check_update(self, update: object) -> bool:
"""Determines whether an update should be passed to this handler's :attr:`callback`.
Args:
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
Returns:
:obj:`bool`
"""
if not isinstance(update, Update) or not update.purchased_paid_media:
return False
if not self._user_ids and not self._usernames:
return True
if update.purchased_paid_media.from_user.id in self._user_ids:
return True
return update.purchased_paid_media.from_user.username in self._usernames
+2 -2
View File
@@ -21,10 +21,10 @@
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils.types import CCT
from telegram.ext._utils.types import CCT, RT
class PollAnswerHandler(BaseHandler[Update, CCT]):
class PollAnswerHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a
:attr:`poll answer <telegram.Update.poll_answer>`.
+2 -2
View File
@@ -21,10 +21,10 @@
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils.types import CCT
from telegram.ext._utils.types import CCT, RT
class PollHandler(BaseHandler[Update, CCT]):
class PollHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram updates that contain a
:attr:`poll <telegram.Update.poll>`.
@@ -31,7 +31,7 @@ from telegram.ext._utils.types import CCT, HandlerCallback
RT = TypeVar("RT")
class PreCheckoutQueryHandler(BaseHandler[Update, CCT]):
class PreCheckoutQueryHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram :attr:`telegram.Update.pre_checkout_query`.
Warning:
@@ -73,7 +73,7 @@ class PreCheckoutQueryHandler(BaseHandler[Update, CCT]):
__slots__ = ("pattern",)
def __init__(
self,
self: "PreCheckoutQueryHandler[CCT, RT]",
callback: HandlerCallback[Update, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
pattern: Optional[Union[str, Pattern[str]]] = None,
+2 -2
View File
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class PrefixHandler(BaseHandler[Update, CCT]):
class PrefixHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle custom prefix commands.
This is an intermediate handler between :class:`MessageHandler` and :class:`CommandHandler`.
@@ -123,7 +123,7 @@ class PrefixHandler(BaseHandler[Update, CCT]):
__slots__ = ("commands", "filters")
def __init__(
self,
self: "PrefixHandler[CCT, RT]",
prefix: SCT[str],
command: SCT[str],
callback: HandlerCallback[Update, CCT, RT],
@@ -21,10 +21,10 @@
from telegram import Update
from telegram.ext._handlers.basehandler import BaseHandler
from telegram.ext._utils.types import CCT
from telegram.ext._utils.types import CCT, RT
class ShippingQueryHandler(BaseHandler[Update, CCT]):
class ShippingQueryHandler(BaseHandler[Update, CCT, RT]):
"""Handler class to handle Telegram :attr:`telegram.Update.shipping_query`.
Warning:
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
from telegram.ext import Application
class StringCommandHandler(BaseHandler[str, CCT]):
class StringCommandHandler(BaseHandler[str, CCT, RT]):
"""Handler class to handle string commands. Commands are string updates that start with
``/``. The handler will add a :obj:`list` to the
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
@@ -71,7 +71,7 @@ class StringCommandHandler(BaseHandler[str, CCT]):
__slots__ = ("command",)
def __init__(
self,
self: "StringCommandHandler[CCT, RT]",
command: str,
callback: HandlerCallback[str, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
+2 -2
View File
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
RT = TypeVar("RT")
class StringRegexHandler(BaseHandler[str, CCT]):
class StringRegexHandler(BaseHandler[str, CCT, RT]):
"""Handler class to handle string updates based on a regex which checks the update content.
Read the documentation of the :mod:`re` module for more information. The :func:`re.match`
@@ -74,7 +74,7 @@ class StringRegexHandler(BaseHandler[str, CCT]):
__slots__ = ("pattern",)
def __init__(
self,
self: "StringRegexHandler[CCT, RT]",
pattern: Union[str, Pattern[str]],
callback: HandlerCallback[str, CCT, RT],
block: DVType[bool] = DEFAULT_TRUE,
+2 -2
View File
@@ -29,7 +29,7 @@ RT = TypeVar("RT")
UT = TypeVar("UT")
class TypeHandler(BaseHandler[UT, CCT]):
class TypeHandler(BaseHandler[UT, CCT, RT]):
"""Handler class to handle updates of custom types.
Warning:
@@ -70,7 +70,7 @@ class TypeHandler(BaseHandler[UT, CCT]):
__slots__ = ("strict", "type")
def __init__(
self,
self: "TypeHandler[UT, CCT, RT]",
type: Type[UT], # pylint: disable=redefined-builtin
callback: HandlerCallback[UT, CCT, RT],
strict: bool = False,
+18 -2
View File
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains methods to make POST and GET requests using the httpx library."""
from typing import Collection, Optional, Tuple, Union
from typing import Any, Collection, Dict, Optional, Tuple, Union
import httpx
@@ -122,6 +122,20 @@ class HTTPXRequest(BaseRequest):
:meth:`do_request`. Defaults to ``20`` seconds.
.. versionadded:: 21.0
httpx_kwargs (Dict[:obj:`str`, Any], optional): Additional keyword arguments to be passed
to the `httpx.AsyncClient <https://www.python-httpx.org/api/#asyncclient>`_
constructor.
Warning:
This parameter is intended for advanced users that want to fine-tune the behavior
of the underlying ``httpx`` client. The values passed here will override all the
defaults set by ``python-telegram-bot`` and all other parameters passed to
:class:`HTTPXRequest`. The only exception is the :paramref:`media_write_timeout`
parameter, which is not passed to the client constructor.
No runtime warnings will be issued about parameters that are overridden in this
way.
.. versionadded:: 21.6
"""
@@ -139,6 +153,7 @@ class HTTPXRequest(BaseRequest):
socket_options: Optional[Collection[SocketOpt]] = None,
proxy: Optional[Union[str, httpx.Proxy, httpx.URL]] = None,
media_write_timeout: Optional[float] = 20.0,
httpx_kwargs: Optional[Dict[str, Any]] = None,
):
if proxy_url is not None and proxy is not None:
raise ValueError("The parameters `proxy_url` and `proxy` are mutually exclusive.")
@@ -183,6 +198,7 @@ class HTTPXRequest(BaseRequest):
"limits": limits,
"transport": transport,
**http_kwargs,
**(httpx_kwargs or {}),
}
try:
@@ -221,7 +237,7 @@ class HTTPXRequest(BaseRequest):
return self._client.timeout.read
def _build_client(self) -> httpx.AsyncClient:
return httpx.AsyncClient(**self._client_kwargs) # type: ignore[arg-type]
return httpx.AsyncClient(**self._client_kwargs)
async def initialize(self) -> None:
"""See :meth:`BaseRequest.initialize`."""
+1 -1
View File
@@ -72,7 +72,7 @@ complete and correct. To run it, export an environment variable first:
$ export TEST_OFFICIAL=true
and then run ``pytest tests/test_official.py``. Note: You need py 3.10+ to run this test.
and then run ``pytest tests/test_official/test_official.py``. Note: You need py 3.10+ to run this test.
We also have another marker, ``@pytest.mark.dev``, which you can add to tests that you want to run selectively.
Use as follows:
+21 -17
View File
@@ -51,7 +51,7 @@ async def animation(bot, chat_id):
).animation
class TestAnimationBase:
class AnimationTestBase:
animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI"
animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
width = 320
@@ -66,7 +66,7 @@ class TestAnimationBase:
caption = "Test *animation*"
class TestAnimationWithoutRequest(TestAnimationBase):
class TestAnimationWithoutRequest(AnimationTestBase):
def test_slot_behaviour(self, animation):
for attr in animation.__slots__:
assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -84,7 +84,7 @@ class TestAnimationWithoutRequest(TestAnimationBase):
assert animation.file_name.startswith("game.gif") == self.file_name.startswith("game.gif")
assert isinstance(animation.thumbnail, PhotoSize)
def test_de_json(self, bot, animation):
def test_de_json(self, offline_bot, animation):
json_dict = {
"file_id": self.animation_file_id,
"file_unique_id": self.animation_file_unique_id,
@@ -96,7 +96,7 @@ class TestAnimationWithoutRequest(TestAnimationBase):
"mime_type": self.mime_type,
"file_size": self.file_size,
}
animation = Animation.de_json(json_dict, bot)
animation = Animation.de_json(json_dict, offline_bot)
assert animation.api_kwargs == {}
assert animation.file_id == self.animation_file_id
assert animation.file_unique_id == self.animation_file_unique_id
@@ -140,18 +140,22 @@ class TestAnimationWithoutRequest(TestAnimationBase):
assert a != e
assert hash(a) != hash(e)
async def test_send_animation_custom_filename(self, bot, chat_id, animation_file, monkeypatch):
async def test_send_animation_custom_filename(
self, offline_bot, chat_id, animation_file, monkeypatch
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return next(iter(request_data.multipart_data.values()))[0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_animation(chat_id, animation_file, filename="custom_filename")
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_animation(
chat_id, animation_file, filename="custom_filename"
)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_animation_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_animation_local_files(self, monkeypatch, offline_bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -167,18 +171,18 @@ class TestAnimationWithoutRequest(TestAnimationBase):
data.get("thumbnail"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_animation(chat_id, file, thumbnail=file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_animation(chat_id, file, thumbnail=file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_send_with_animation(self, monkeypatch, bot, chat_id, animation):
async def test_send_with_animation(self, monkeypatch, offline_bot, chat_id, animation):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["animation"] == animation.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_animation(animation=animation, chat_id=chat_id)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_animation(animation=animation, chat_id=chat_id)
async def test_get_file_instance_method(self, monkeypatch, animation):
async def make_assertion(*_, **kwargs):
@@ -219,7 +223,7 @@ class TestAnimationWithoutRequest(TestAnimationBase):
)
class TestAnimationWithRequest(TestAnimationBase):
class TestAnimationWithRequest(AnimationTestBase):
async def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file):
message = await bot.send_animation(
chat_id,
+17 -17
View File
@@ -49,7 +49,7 @@ async def audio(bot, chat_id):
return (await bot.send_audio(chat_id, audio=f, read_timeout=50, thumbnail=thumb)).audio
class TestAudioBase:
class AudioTestBase:
caption = "Test *audio*"
performer = "Leandro Toledo"
title = "Teste"
@@ -67,7 +67,7 @@ class TestAudioBase:
audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestAudioWithoutRequest(TestAudioBase):
class TestAudioWithoutRequest(AudioTestBase):
def test_slot_behaviour(self, audio):
for attr in audio.__slots__:
assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -91,7 +91,7 @@ class TestAudioWithoutRequest(TestAudioBase):
assert audio.thumbnail.width == self.thumb_width
assert audio.thumbnail.height == self.thumb_height
def test_de_json(self, bot, audio):
def test_de_json(self, offline_bot, audio):
json_dict = {
"file_id": self.audio_file_id,
"file_unique_id": self.audio_file_unique_id,
@@ -103,7 +103,7 @@ class TestAudioWithoutRequest(TestAudioBase):
"file_size": self.file_size,
"thumbnail": audio.thumbnail.to_dict(),
}
json_audio = Audio.de_json(json_dict, bot)
json_audio = Audio.de_json(json_dict, offline_bot)
assert json_audio.api_kwargs == {}
assert json_audio.file_id == self.audio_file_id
@@ -147,25 +147,25 @@ class TestAudioWithoutRequest(TestAudioBase):
assert a != e
assert hash(a) != hash(e)
async def test_send_with_audio(self, monkeypatch, bot, chat_id, audio):
async def test_send_with_audio(self, monkeypatch, offline_bot, chat_id, audio):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["audio"] == audio.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_audio(audio=audio, chat_id=chat_id)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_audio(audio=audio, chat_id=chat_id)
async def test_send_audio_custom_filename(self, bot, chat_id, audio_file, monkeypatch):
async def test_send_audio_custom_filename(self, offline_bot, chat_id, audio_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return next(iter(request_data.multipart_data.values()))[0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_audio(chat_id, audio_file, filename="custom_filename")
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_audio(chat_id, audio_file, filename="custom_filename")
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_audio_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_audio_local_files(self, monkeypatch, offline_bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -179,11 +179,11 @@ class TestAudioWithoutRequest(TestAudioBase):
data.get("thumbnail"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_audio(chat_id, file, thumbnail=file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_audio(chat_id, file, thumbnail=file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, audio):
async def make_assertion(*_, **kwargs):
@@ -222,7 +222,7 @@ class TestAudioWithoutRequest(TestAudioBase):
await default_bot.send_audio(chat_id, audio, reply_parameters=ReplyParameters(**kwargs))
class TestAudioWithRequest(TestAudioBase):
class TestAudioWithRequest(AudioTestBase):
async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file):
message = await bot.send_audio(
chat_id,
+9 -7
View File
@@ -52,7 +52,7 @@ async def chat_photo(bot, super_group_id):
)
class TestChatPhotoBase:
class ChatPhotoTestBase:
chatphoto_small_file_id = "smallCgADAQADngIAAuyVeEez0xRovKi9VAI"
chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI"
chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e"
@@ -60,20 +60,20 @@ class TestChatPhotoBase:
chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg"
class TestChatPhotoWithoutRequest(TestChatPhotoBase):
class TestChatPhotoWithoutRequest(ChatPhotoTestBase):
def test_slot_behaviour(self, chat_photo):
for attr in chat_photo.__slots__:
assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(chat_photo)) == len(set(mro_slots(chat_photo))), "duplicate slot"
def test_de_json(self, bot, chat_photo):
def test_de_json(self, offline_bot, chat_photo):
json_dict = {
"small_file_id": self.chatphoto_small_file_id,
"big_file_id": self.chatphoto_big_file_id,
"small_file_unique_id": self.chatphoto_small_file_unique_id,
"big_file_unique_id": self.chatphoto_big_file_unique_id,
}
chat_photo = ChatPhoto.de_json(json_dict, bot)
chat_photo = ChatPhoto.de_json(json_dict, offline_bot)
assert chat_photo.api_kwargs == {}
assert chat_photo.small_file_id == self.chatphoto_small_file_id
assert chat_photo.big_file_id == self.chatphoto_big_file_id
@@ -121,12 +121,14 @@ class TestChatPhotoWithoutRequest(TestChatPhotoBase):
assert a != e
assert hash(a) != hash(e)
async def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo):
async def test_send_with_chat_photo(
self, monkeypatch, offline_bot, super_group_id, chat_photo
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.parameters["photo"] == chat_photo.to_dict()
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
message = await offline_bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
assert message
async def test_get_small_file_instance_method(self, monkeypatch, chat_photo):
+18 -18
View File
@@ -32,42 +32,42 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def contact():
return Contact(
TestContactBase.phone_number,
TestContactBase.first_name,
TestContactBase.last_name,
TestContactBase.user_id,
ContactTestBase.phone_number,
ContactTestBase.first_name,
ContactTestBase.last_name,
ContactTestBase.user_id,
)
class TestContactBase:
class ContactTestBase:
phone_number = "+11234567890"
first_name = "Leandro"
last_name = "Toledo"
user_id = 23
class TestContactWithoutRequest(TestContactBase):
class TestContactWithoutRequest(ContactTestBase):
def test_slot_behaviour(self, contact):
for attr in contact.__slots__:
assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(contact)) == len(set(mro_slots(contact))), "duplicate slot"
def test_de_json_required(self, bot):
def test_de_json_required(self, offline_bot):
json_dict = {"phone_number": self.phone_number, "first_name": self.first_name}
contact = Contact.de_json(json_dict, bot)
contact = Contact.de_json(json_dict, offline_bot)
assert contact.api_kwargs == {}
assert contact.phone_number == self.phone_number
assert contact.first_name == self.first_name
def test_de_json_all(self, bot):
def test_de_json_all(self, offline_bot):
json_dict = {
"phone_number": self.phone_number,
"first_name": self.first_name,
"last_name": self.last_name,
"user_id": self.user_id,
}
contact = Contact.de_json(json_dict, bot)
contact = Contact.de_json(json_dict, offline_bot)
assert contact.api_kwargs == {}
assert contact.phone_number == self.phone_number
@@ -104,20 +104,20 @@ class TestContactWithoutRequest(TestContactBase):
assert a != e
assert hash(a) != hash(e)
async def test_send_contact_without_required(self, bot, chat_id):
async def test_send_contact_without_required(self, offline_bot, chat_id):
with pytest.raises(ValueError, match="Either contact or phone_number and first_name"):
await bot.send_contact(chat_id=chat_id)
await offline_bot.send_contact(chat_id=chat_id)
async def test_send_mutually_exclusive(self, bot, chat_id, contact):
async def test_send_mutually_exclusive(self, offline_bot, chat_id, contact):
with pytest.raises(ValueError, match="Not both"):
await bot.send_contact(
await offline_bot.send_contact(
chat_id=chat_id,
contact=contact,
phone_number=contact.phone_number,
first_name=contact.first_name,
)
async def test_send_with_contact(self, monkeypatch, bot, chat_id, contact):
async def test_send_with_contact(self, monkeypatch, offline_bot, chat_id, contact):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
phone = data["phone_number"] == contact.phone_number
@@ -125,8 +125,8 @@ class TestContactWithoutRequest(TestContactBase):
last = data["last_name"] == contact.last_name
return phone and first and last
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_contact(contact=contact, chat_id=chat_id)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_contact(contact=contact, chat_id=chat_id)
@pytest.mark.parametrize(
("default_bot", "custom"),
@@ -156,7 +156,7 @@ class TestContactWithoutRequest(TestContactBase):
)
class TestContactWithRequest(TestContactBase):
class TestContactWithRequest(ContactTestBase):
@pytest.mark.parametrize(
("default_bot", "custom"),
[
+16 -16
View File
@@ -49,7 +49,7 @@ async def document(bot, chat_id):
return (await bot.send_document(chat_id, document=f, read_timeout=50)).document
class TestDocumentBase:
class DocumentTestBase:
caption = "DocumentTest - *Caption*"
document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif"
file_size = 12948
@@ -62,7 +62,7 @@ class TestDocumentBase:
document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestDocumentWithoutRequest(TestDocumentBase):
class TestDocumentWithoutRequest(DocumentTestBase):
def test_slot_behaviour(self, document):
for attr in document.__slots__:
assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -83,7 +83,7 @@ class TestDocumentWithoutRequest(TestDocumentBase):
assert document.thumbnail.width == self.thumb_width
assert document.thumbnail.height == self.thumb_height
def test_de_json(self, bot, document):
def test_de_json(self, offline_bot, document):
json_dict = {
"file_id": self.document_file_id,
"file_unique_id": self.document_file_unique_id,
@@ -92,7 +92,7 @@ class TestDocumentWithoutRequest(TestDocumentBase):
"mime_type": self.mime_type,
"file_size": self.file_size,
}
test_document = Document.de_json(json_dict, bot)
test_document = Document.de_json(json_dict, offline_bot)
assert test_document.api_kwargs == {}
assert test_document.file_id == self.document_file_id
@@ -128,13 +128,13 @@ class TestDocumentWithoutRequest(TestDocumentBase):
assert a != e
assert hash(a) != hash(e)
async def test_error_send_without_required_args(self, bot, chat_id):
async def test_error_send_without_required_args(self, offline_bot, chat_id):
with pytest.raises(TypeError):
await bot.send_document(chat_id=chat_id)
await offline_bot.send_document(chat_id=chat_id)
@pytest.mark.parametrize("disable_content_type_detection", [True, False, None])
async def test_send_with_document(
self, monkeypatch, bot, chat_id, document, disable_content_type_detection
self, monkeypatch, offline_bot, chat_id, document, disable_content_type_detection
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.parameters
@@ -143,9 +143,9 @@ class TestDocumentWithoutRequest(TestDocumentBase):
)
return data["document"] == document.file_id and type_detection
monkeypatch.setattr(bot.request, "post", make_assertion)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
message = await bot.send_document(
message = await offline_bot.send_document(
document=document,
chat_id=chat_id,
disable_content_type_detection=disable_content_type_detection,
@@ -181,10 +181,10 @@ class TestDocumentWithoutRequest(TestDocumentBase):
)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_document_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_document_local_files(self, monkeypatch, offline_bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -200,11 +200,11 @@ class TestDocumentWithoutRequest(TestDocumentBase):
data.get("thumbnail"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_document(chat_id, file, thumbnail=file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_document(chat_id, file, thumbnail=file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, document):
async def make_assertion(*_, **kwargs):
@@ -218,7 +218,7 @@ class TestDocumentWithoutRequest(TestDocumentBase):
assert await document.get_file()
class TestDocumentWithRequest(TestDocumentBase):
class TestDocumentWithRequest(DocumentTestBase):
async def test_error_send_empty_file(self, bot, chat_id):
with Path(os.devnull).open("rb") as f, pytest.raises(TelegramError):
await bot.send_document(chat_id=chat_id, document=f)
+24 -24
View File
@@ -31,10 +31,10 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def file(bot):
file = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
file_path=TestFileBase.file_path,
file_size=TestFileBase.file_size,
FileTestBase.file_id,
FileTestBase.file_unique_id,
file_path=FileTestBase.file_path,
file_size=FileTestBase.file_size,
)
file.set_bot(bot)
file._unfreeze()
@@ -51,10 +51,10 @@ def encrypted_file(bot):
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
)
ef = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
TestFileBase.file_size,
TestFileBase.file_path,
FileTestBase.file_id,
FileTestBase.file_unique_id,
FileTestBase.file_size,
FileTestBase.file_path,
)
ef.set_bot(bot)
ef.set_credentials(fc)
@@ -69,9 +69,9 @@ def encrypted_local_file(bot):
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
)
ef = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
TestFileBase.file_size,
FileTestBase.file_id,
FileTestBase.file_unique_id,
FileTestBase.file_size,
file_path=str(data_file("image_encrypted.jpg")),
)
ef.set_bot(bot)
@@ -82,16 +82,16 @@ def encrypted_local_file(bot):
@pytest.fixture(scope="module")
def local_file(bot):
file = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
FileTestBase.file_id,
FileTestBase.file_unique_id,
file_path=str(data_file("local_file.txt")),
file_size=TestFileBase.file_size,
file_size=FileTestBase.file_size,
)
file.set_bot(bot)
return file
class TestFileBase:
class FileTestBase:
file_id = "NOTVALIDDOESNOTMATTER"
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
file_path = (
@@ -101,20 +101,20 @@ class TestFileBase:
file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars.
class TestFileWithoutRequest(TestFileBase):
class TestFileWithoutRequest(FileTestBase):
def test_slot_behaviour(self, file):
for attr in file.__slots__:
assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(file)) == len(set(mro_slots(file))), "duplicate slot"
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"file_id": self.file_id,
"file_unique_id": self.file_unique_id,
"file_path": self.file_path,
"file_size": self.file_size,
}
new_file = File.de_json(json_dict, bot)
new_file = File.de_json(json_dict, offline_bot)
assert new_file.api_kwargs == {}
assert new_file.file_id == self.file_id
@@ -131,11 +131,11 @@ class TestFileWithoutRequest(TestFileBase):
assert file_dict["file_path"] == file.file_path
assert file_dict["file_size"] == file.file_size
def test_equality(self, bot):
a = File(self.file_id, self.file_unique_id, bot)
b = File("", self.file_unique_id, bot)
def test_equality(self, offline_bot):
a = File(self.file_id, self.file_unique_id, offline_bot)
b = File("", self.file_unique_id, offline_bot)
c = File(self.file_id, self.file_unique_id, None)
d = File("", "", bot)
d = File("", "", offline_bot)
e = Voice(self.file_id, self.file_unique_id, 0)
assert a == b
@@ -223,7 +223,7 @@ class TestFileWithoutRequest(TestFileBase):
assert buf2[len(buf) :] == buf
assert buf2[: len(buf)] == buf
async def test_download_encrypted(self, monkeypatch, bot, encrypted_file):
async def test_download_encrypted(self, monkeypatch, offline_bot, encrypted_file):
async def test(*args, **kwargs):
return data_file("image_encrypted.jpg").read_bytes()
@@ -273,7 +273,7 @@ class TestFileWithoutRequest(TestFileBase):
assert buf2[: len(buf)] == buf
class TestFileWithRequest(TestFileBase):
class TestFileWithRequest(FileTestBase):
async def test_error_get_empty_file_id(self, bot):
with pytest.raises(TelegramError):
await bot.get_file(file_id="")
+68 -66
View File
@@ -68,94 +68,94 @@ from .test_video import video, video_file # noqa: F401
@pytest.fixture(scope="module")
def input_media_video(class_thumb_file):
return InputMediaVideo(
media=TestInputMediaVideoBase.media,
caption=TestInputMediaVideoBase.caption,
width=TestInputMediaVideoBase.width,
height=TestInputMediaVideoBase.height,
duration=TestInputMediaVideoBase.duration,
parse_mode=TestInputMediaVideoBase.parse_mode,
caption_entities=TestInputMediaVideoBase.caption_entities,
media=InputMediaVideoTestBase.media,
caption=InputMediaVideoTestBase.caption,
width=InputMediaVideoTestBase.width,
height=InputMediaVideoTestBase.height,
duration=InputMediaVideoTestBase.duration,
parse_mode=InputMediaVideoTestBase.parse_mode,
caption_entities=InputMediaVideoTestBase.caption_entities,
thumbnail=class_thumb_file,
supports_streaming=TestInputMediaVideoBase.supports_streaming,
has_spoiler=TestInputMediaVideoBase.has_spoiler,
show_caption_above_media=TestInputMediaVideoBase.show_caption_above_media,
supports_streaming=InputMediaVideoTestBase.supports_streaming,
has_spoiler=InputMediaVideoTestBase.has_spoiler,
show_caption_above_media=InputMediaVideoTestBase.show_caption_above_media,
)
@pytest.fixture(scope="module")
def input_media_photo():
return InputMediaPhoto(
media=TestInputMediaPhotoBase.media,
caption=TestInputMediaPhotoBase.caption,
parse_mode=TestInputMediaPhotoBase.parse_mode,
caption_entities=TestInputMediaPhotoBase.caption_entities,
has_spoiler=TestInputMediaPhotoBase.has_spoiler,
show_caption_above_media=TestInputMediaPhotoBase.show_caption_above_media,
media=InputMediaPhotoTestBase.media,
caption=InputMediaPhotoTestBase.caption,
parse_mode=InputMediaPhotoTestBase.parse_mode,
caption_entities=InputMediaPhotoTestBase.caption_entities,
has_spoiler=InputMediaPhotoTestBase.has_spoiler,
show_caption_above_media=InputMediaPhotoTestBase.show_caption_above_media,
)
@pytest.fixture(scope="module")
def input_media_animation(class_thumb_file):
return InputMediaAnimation(
media=TestInputMediaAnimationBase.media,
caption=TestInputMediaAnimationBase.caption,
parse_mode=TestInputMediaAnimationBase.parse_mode,
caption_entities=TestInputMediaAnimationBase.caption_entities,
width=TestInputMediaAnimationBase.width,
height=TestInputMediaAnimationBase.height,
media=InputMediaAnimationTestBase.media,
caption=InputMediaAnimationTestBase.caption,
parse_mode=InputMediaAnimationTestBase.parse_mode,
caption_entities=InputMediaAnimationTestBase.caption_entities,
width=InputMediaAnimationTestBase.width,
height=InputMediaAnimationTestBase.height,
thumbnail=class_thumb_file,
duration=TestInputMediaAnimationBase.duration,
has_spoiler=TestInputMediaAnimationBase.has_spoiler,
show_caption_above_media=TestInputMediaAnimationBase.show_caption_above_media,
duration=InputMediaAnimationTestBase.duration,
has_spoiler=InputMediaAnimationTestBase.has_spoiler,
show_caption_above_media=InputMediaAnimationTestBase.show_caption_above_media,
)
@pytest.fixture(scope="module")
def input_media_audio(class_thumb_file):
return InputMediaAudio(
media=TestInputMediaAudioBase.media,
caption=TestInputMediaAudioBase.caption,
duration=TestInputMediaAudioBase.duration,
performer=TestInputMediaAudioBase.performer,
title=TestInputMediaAudioBase.title,
media=InputMediaAudioTestBase.media,
caption=InputMediaAudioTestBase.caption,
duration=InputMediaAudioTestBase.duration,
performer=InputMediaAudioTestBase.performer,
title=InputMediaAudioTestBase.title,
thumbnail=class_thumb_file,
parse_mode=TestInputMediaAudioBase.parse_mode,
caption_entities=TestInputMediaAudioBase.caption_entities,
parse_mode=InputMediaAudioTestBase.parse_mode,
caption_entities=InputMediaAudioTestBase.caption_entities,
)
@pytest.fixture(scope="module")
def input_media_document(class_thumb_file):
return InputMediaDocument(
media=TestInputMediaDocumentBase.media,
caption=TestInputMediaDocumentBase.caption,
media=InputMediaDocumentTestBase.media,
caption=InputMediaDocumentTestBase.caption,
thumbnail=class_thumb_file,
parse_mode=TestInputMediaDocumentBase.parse_mode,
caption_entities=TestInputMediaDocumentBase.caption_entities,
disable_content_type_detection=TestInputMediaDocumentBase.disable_content_type_detection,
parse_mode=InputMediaDocumentTestBase.parse_mode,
caption_entities=InputMediaDocumentTestBase.caption_entities,
disable_content_type_detection=InputMediaDocumentTestBase.disable_content_type_detection,
)
@pytest.fixture(scope="module")
def input_paid_media_photo():
return InputPaidMediaPhoto(
media=TestInputMediaPhotoBase.media,
media=InputMediaPhotoTestBase.media,
)
@pytest.fixture(scope="module")
def input_paid_media_video(class_thumb_file):
return InputPaidMediaVideo(
media=TestInputMediaVideoBase.media,
media=InputMediaVideoTestBase.media,
thumbnail=class_thumb_file,
width=TestInputMediaVideoBase.width,
height=TestInputMediaVideoBase.height,
duration=TestInputMediaVideoBase.duration,
supports_streaming=TestInputMediaVideoBase.supports_streaming,
width=InputMediaVideoTestBase.width,
height=InputMediaVideoTestBase.height,
duration=InputMediaVideoTestBase.duration,
supports_streaming=InputMediaVideoTestBase.supports_streaming,
)
class TestInputMediaVideoBase:
class InputMediaVideoTestBase:
type_ = "video"
media = "NOTAREALFILEID"
caption = "My Caption"
@@ -169,7 +169,7 @@ class TestInputMediaVideoBase:
show_caption_above_media = True
class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
class TestInputMediaVideoWithoutRequest(InputMediaVideoTestBase):
def test_slot_behaviour(self, input_media_video):
inst = input_media_video
for attr in inst.__slots__:
@@ -258,7 +258,7 @@ class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
)
class TestInputMediaPhotoBase:
class InputMediaPhotoTestBase:
type_ = "photo"
media = "NOTAREALFILEID"
caption = "My Caption"
@@ -268,7 +268,7 @@ class TestInputMediaPhotoBase:
show_caption_above_media = True
class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
class TestInputMediaPhotoWithoutRequest(InputMediaPhotoTestBase):
def test_slot_behaviour(self, input_media_photo):
inst = input_media_photo
for attr in inst.__slots__:
@@ -322,7 +322,7 @@ class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
assert input_media_photo.media == data_file("telegram.mp4").as_uri()
class TestInputMediaAnimationBase:
class InputMediaAnimationTestBase:
type_ = "animation"
media = "NOTAREALFILEID"
caption = "My Caption"
@@ -335,7 +335,7 @@ class TestInputMediaAnimationBase:
show_caption_above_media = True
class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
class TestInputMediaAnimationWithoutRequest(InputMediaAnimationTestBase):
def test_slot_behaviour(self, input_media_animation):
inst = input_media_animation
for attr in inst.__slots__:
@@ -396,7 +396,7 @@ class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
assert input_media_animation.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputMediaAudioBase:
class InputMediaAudioTestBase:
type_ = "audio"
media = "NOTAREALFILEID"
caption = "My Caption"
@@ -407,7 +407,7 @@ class TestInputMediaAudioBase:
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
class TestInputMediaAudioWithoutRequest(InputMediaAudioTestBase):
def test_slot_behaviour(self, input_media_audio):
inst = input_media_audio
for attr in inst.__slots__:
@@ -467,7 +467,7 @@ class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
assert input_media_audio.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputMediaDocumentBase:
class InputMediaDocumentTestBase:
type_ = "document"
media = "NOTAREALFILEID"
caption = "My Caption"
@@ -476,7 +476,7 @@ class TestInputMediaDocumentBase:
disable_content_type_detection = True
class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
class TestInputMediaDocumentWithoutRequest(InputMediaDocumentTestBase):
def test_slot_behaviour(self, input_media_document):
inst = input_media_document
for attr in inst.__slots__:
@@ -535,7 +535,7 @@ class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
assert input_media_document.thumbnail == data_file("telegram.jpg").as_uri()
class TestInputPaidMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
class TestInputPaidMediaPhotoWithoutRequest(InputMediaPhotoTestBase):
def test_slot_behaviour(self, input_paid_media_photo):
inst = input_paid_media_photo
for attr in inst.__slots__:
@@ -568,7 +568,7 @@ class TestInputPaidMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
assert input_paid_media_photo.media == data_file("telegram.jpg").as_uri()
class TestInputPaidMediaVideoWithoutRequest(TestInputMediaVideoBase):
class TestInputPaidMediaVideoWithoutRequest(InputMediaVideoTestBase):
def test_slot_behaviour(self, input_paid_media_video):
inst = input_paid_media_video
for attr in inst.__slots__:
@@ -655,7 +655,7 @@ def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811
class TestSendMediaGroupWithoutRequest:
async def test_send_media_group_throws_error_with_group_caption_and_individual_captions(
self,
bot,
offline_bot,
chat_id,
media_group,
media_group_no_caption_only_caption_entities,
@@ -670,11 +670,11 @@ class TestSendMediaGroupWithoutRequest:
ValueError,
match="You can only supply either group caption or media with captions.",
):
await bot.send_media_group(chat_id, group, caption="foo")
await offline_bot.send_media_group(chat_id, group, caption="foo")
async def test_send_media_group_custom_filename(
self,
bot,
offline_bot,
chat_id,
photo_file, # noqa: F811
animation_file, # noqa: F811
@@ -690,7 +690,7 @@ class TestSendMediaGroupWithoutRequest:
if result is True:
raise Exception("Test was successful")
monkeypatch.setattr(bot.request, "post", make_assertion)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
media = [
InputMediaAnimation(animation_file, filename="custom_filename"),
@@ -700,10 +700,10 @@ class TestSendMediaGroupWithoutRequest:
]
with pytest.raises(Exception, match="Test was successful"):
await bot.send_media_group(chat_id, media)
await offline_bot.send_media_group(chat_id, media)
async def test_send_media_group_with_thumbs(
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
self, offline_bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
):
async def make_assertion(method, url, request_data: RequestData, *args, **kwargs):
nonlocal input_video
@@ -715,13 +715,13 @@ class TestSendMediaGroupWithoutRequest:
result = video_check and thumb_check
raise Exception(f"Test was {'successful' if result else 'failing'}")
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
monkeypatch.setattr(offline_bot.request, "_request_wrapper", make_assertion)
input_video = InputMediaVideo(video_file, thumbnail=photo_file)
with pytest.raises(Exception, match="Test was successful"):
await bot.send_media_group(chat_id, [input_video, input_video])
await offline_bot.send_media_group(chat_id, [input_video, input_video])
async def test_edit_message_media_with_thumb(
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
self, offline_bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
):
async def make_assertion(
method: str, url: str, request_data: Optional[RequestData] = None, *args, **kwargs
@@ -734,10 +734,12 @@ class TestSendMediaGroupWithoutRequest:
result = video_check and thumb_check
raise Exception(f"Test was {'successful' if result else 'failing'}")
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
monkeypatch.setattr(offline_bot.request, "_request_wrapper", make_assertion)
input_video = InputMediaVideo(video_file, thumbnail=photo_file)
with pytest.raises(Exception, match="Test was successful"):
await bot.edit_message_media(chat_id=chat_id, message_id=123, media=input_video)
await offline_bot.edit_message_media(
chat_id=chat_id, message_id=123, media=input_video
)
@pytest.mark.parametrize(
("default_bot", "custom"),
+7 -7
View File
@@ -29,15 +29,15 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def input_sticker():
return InputSticker(
sticker=TestInputStickerBase.sticker,
emoji_list=TestInputStickerBase.emoji_list,
mask_position=TestInputStickerBase.mask_position,
keywords=TestInputStickerBase.keywords,
format=TestInputStickerBase.format,
sticker=InputStickerTestBase.sticker,
emoji_list=InputStickerTestBase.emoji_list,
mask_position=InputStickerTestBase.mask_position,
keywords=InputStickerTestBase.keywords,
format=InputStickerTestBase.format,
)
class TestInputStickerBase:
class InputStickerTestBase:
sticker = "fake_file_id"
emoji_list = ("👍", "👎")
mask_position = MaskPosition("forehead", 0.5, 0.5, 0.5)
@@ -45,7 +45,7 @@ class TestInputStickerBase:
format = "static"
class TestInputStickerWithoutRequest(TestInputStickerBase):
class TestInputStickerWithoutRequest(InputStickerTestBase):
def test_slot_behaviour(self, input_sticker):
inst = input_sticker
for attr in inst.__slots__:
+32 -30
View File
@@ -31,16 +31,16 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def location():
return Location(
latitude=TestLocationBase.latitude,
longitude=TestLocationBase.longitude,
horizontal_accuracy=TestLocationBase.horizontal_accuracy,
live_period=TestLocationBase.live_period,
heading=TestLocationBase.live_period,
proximity_alert_radius=TestLocationBase.proximity_alert_radius,
latitude=LocationTestBase.latitude,
longitude=LocationTestBase.longitude,
horizontal_accuracy=LocationTestBase.horizontal_accuracy,
live_period=LocationTestBase.live_period,
heading=LocationTestBase.live_period,
proximity_alert_radius=LocationTestBase.proximity_alert_radius,
)
class TestLocationBase:
class LocationTestBase:
latitude = -23.691288
longitude = -46.788279
horizontal_accuracy = 999
@@ -49,13 +49,13 @@ class TestLocationBase:
proximity_alert_radius = 50
class TestLocationWithoutRequest(TestLocationBase):
class TestLocationWithoutRequest(LocationTestBase):
def test_slot_behaviour(self, location):
for attr in location.__slots__:
assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(location)) == len(set(mro_slots(location))), "duplicate slot"
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"latitude": self.latitude,
"longitude": self.longitude,
@@ -64,7 +64,7 @@ class TestLocationWithoutRequest(TestLocationBase):
"heading": self.heading,
"proximity_alert_radius": self.proximity_alert_radius,
}
location = Location.de_json(json_dict, bot)
location = Location.de_json(json_dict, offline_bot)
assert location.api_kwargs == {}
assert location.latitude == self.latitude
@@ -96,26 +96,28 @@ class TestLocationWithoutRequest(TestLocationBase):
assert a != d
assert hash(a) != hash(d)
async def test_send_location_without_required(self, bot, chat_id):
async def test_send_location_without_required(self, offline_bot, chat_id):
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
await bot.send_location(chat_id=chat_id)
await offline_bot.send_location(chat_id=chat_id)
async def test_edit_location_without_required(self, bot):
async def test_edit_location_without_required(self, offline_bot):
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
await bot.edit_message_live_location(chat_id=2, message_id=3)
await offline_bot.edit_message_live_location(chat_id=2, message_id=3)
async def test_send_location_with_all_args(self, bot, location):
async def test_send_location_with_all_args(self, offline_bot, location):
with pytest.raises(ValueError, match="Not both"):
await bot.send_location(chat_id=1, latitude=2.5, longitude=4.6, location=location)
await offline_bot.send_location(
chat_id=1, latitude=2.5, longitude=4.6, location=location
)
async def test_edit_location_with_all_args(self, bot, location):
async def test_edit_location_with_all_args(self, offline_bot, location):
with pytest.raises(ValueError, match="Not both"):
await bot.edit_message_live_location(
await offline_bot.edit_message_live_location(
chat_id=1, message_id=7, latitude=2.5, longitude=4.6, location=location
)
# TODO: Needs improvement with in inline sent live location.
async def test_edit_live_inline_message(self, monkeypatch, bot, location):
async def test_edit_live_inline_message(self, monkeypatch, offline_bot, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
lat = data["latitude"] == str(location.latitude)
@@ -127,8 +129,8 @@ class TestLocationWithoutRequest(TestLocationBase):
live = data["live_period"] == "900"
return lat and lon and id_ and ha and heading and prox_alert and live
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.edit_message_live_location(
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.edit_message_live_location(
inline_message_id=1234,
location=location,
horizontal_accuracy=50,
@@ -138,30 +140,30 @@ class TestLocationWithoutRequest(TestLocationBase):
)
# TODO: Needs improvement with in inline sent live location.
async def test_stop_live_inline_message(self, monkeypatch, bot):
async def test_stop_live_inline_message(self, monkeypatch, offline_bot):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["inline_message_id"] == "1234"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.stop_message_live_location(inline_message_id=1234)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.stop_message_live_location(inline_message_id=1234)
async def test_send_with_location(self, monkeypatch, bot, chat_id, location):
async def test_send_with_location(self, monkeypatch, offline_bot, chat_id, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
lat = request_data.json_parameters["latitude"] == str(location.latitude)
lon = request_data.json_parameters["longitude"] == str(location.longitude)
return lat and lon
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_location(location=location, chat_id=chat_id)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_location(location=location, chat_id=chat_id)
async def test_edit_live_location_with_location(self, monkeypatch, bot, location):
async def test_edit_live_location_with_location(self, monkeypatch, offline_bot, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
lat = request_data.json_parameters["latitude"] == str(location.latitude)
lon = request_data.json_parameters["longitude"] == str(location.longitude)
return lat and lon
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.edit_message_live_location(None, None, location=location)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.edit_message_live_location(None, None, location=location)
@pytest.mark.parametrize(
("default_bot", "custom"),
+19 -19
View File
@@ -65,7 +65,7 @@ def photo(photolist):
return photolist[-1]
class TestPhotoBase:
class PhotoTestBase:
width = 800
height = 800
caption = "<b>PhotoTest</b> - *Caption*"
@@ -75,7 +75,7 @@ class TestPhotoBase:
file_size = [29176, 27662]
class TestPhotoWithoutRequest(TestPhotoBase):
class TestPhotoWithoutRequest(PhotoTestBase):
def test_slot_behaviour(self, photo):
for attr in photo.__slots__:
assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -105,7 +105,7 @@ class TestPhotoWithoutRequest(TestPhotoBase):
# so far
assert thumb.file_size in [1475, 1477]
def test_de_json(self, bot, photo):
def test_de_json(self, offline_bot, photo):
json_dict = {
"file_id": photo.file_id,
"file_unique_id": photo.file_unique_id,
@@ -113,7 +113,7 @@ class TestPhotoWithoutRequest(TestPhotoBase):
"height": self.height,
"file_size": self.file_size,
}
json_photo = PhotoSize.de_json(json_dict, bot)
json_photo = PhotoSize.de_json(json_dict, offline_bot)
assert json_photo.api_kwargs == {}
assert json_photo.file_id == photo.file_id
@@ -160,22 +160,22 @@ class TestPhotoWithoutRequest(TestPhotoBase):
assert a != e
assert hash(a) != hash(e)
async def test_error_without_required_args(self, bot, chat_id):
async def test_error_without_required_args(self, offline_bot, chat_id):
with pytest.raises(TypeError):
await bot.send_photo(chat_id=chat_id)
await offline_bot.send_photo(chat_id=chat_id)
async def test_send_photo_custom_filename(self, bot, chat_id, photo_file, monkeypatch):
async def test_send_photo_custom_filename(self, offline_bot, chat_id, photo_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return next(iter(request_data.multipart_data.values()))[0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_photo(chat_id, photo_file, filename="custom_filename")
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_photo(chat_id, photo_file, filename="custom_filename")
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_photo_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_photo_local_files(self, monkeypatch, offline_bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -187,18 +187,18 @@ class TestPhotoWithoutRequest(TestPhotoBase):
else:
test_flag = isinstance(data.get("photo"), InputFile)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_photo(chat_id, file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_photo(chat_id, file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_send_with_photosize(self, monkeypatch, bot, chat_id, photo):
async def test_send_with_photosize(self, monkeypatch, offline_bot, chat_id, photo):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["photo"] == photo.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_photo(photo=photo, chat_id=chat_id)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_photo(photo=photo, chat_id=chat_id)
async def test_get_file_instance_method(self, monkeypatch, photo):
async def make_assertion(*_, **kwargs):
@@ -237,7 +237,7 @@ class TestPhotoWithoutRequest(TestPhotoBase):
await default_bot.send_photo(chat_id, photo, reply_parameters=ReplyParameters(**kwargs))
class TestPhotoWithRequest(TestPhotoBase):
class TestPhotoWithRequest(PhotoTestBase):
async def test_send_photo_all_args(self, bot, chat_id, photo_file):
message = await bot.send_photo(
chat_id,
+62 -56
View File
@@ -61,7 +61,7 @@ async def sticker(bot, chat_id):
sticker = (await bot.send_sticker(chat_id, sticker=f, read_timeout=50)).sticker
# necessary to properly test needs_repainting
with sticker._unfrozen():
sticker.needs_repainting = TestStickerBase.needs_repainting
sticker.needs_repainting = StickerTestBase.needs_repainting
return sticker
@@ -89,7 +89,7 @@ def video_sticker(bot, chat_id):
return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker
class TestStickerBase:
class StickerTestBase:
# sticker_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.webp'
# Serving sticker from gh since our server sends wrong content_type
sticker_file_url = (
@@ -116,7 +116,7 @@ class TestStickerBase:
premium_animation = File("this_is_an_id", "this_is_an_unique_id")
class TestStickerWithoutRequest(TestStickerBase):
class TestStickerWithoutRequest(StickerTestBase):
def test_slot_behaviour(self, sticker):
for attr in sticker.__slots__:
assert getattr(sticker, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -165,7 +165,7 @@ class TestStickerWithoutRequest(TestStickerBase):
assert sticker_dict["type"] == sticker.type
assert sticker_dict["needs_repainting"] == sticker.needs_repainting
def test_de_json(self, bot, sticker):
def test_de_json(self, offline_bot, sticker):
json_dict = {
"file_id": self.sticker_file_id,
"file_unique_id": self.sticker_file_unique_id,
@@ -181,7 +181,7 @@ class TestStickerWithoutRequest(TestStickerBase):
"custom_emoji_id": self.custom_emoji_id,
"needs_repainting": self.needs_repainting,
}
json_sticker = Sticker.de_json(json_dict, bot)
json_sticker = Sticker.de_json(json_dict, offline_bot)
assert json_sticker.api_kwargs == {}
assert json_sticker.file_id == self.sticker_file_id
@@ -284,22 +284,22 @@ class TestStickerWithoutRequest(TestStickerBase):
assert a != e
assert hash(a) != hash(e)
async def test_error_without_required_args(self, bot, chat_id):
async def test_error_without_required_args(self, offline_bot, chat_id):
with pytest.raises(TypeError):
await bot.send_sticker(chat_id)
await offline_bot.send_sticker(chat_id)
async def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker):
async def test_send_with_sticker(self, monkeypatch, offline_bot, chat_id, sticker):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["sticker"] == sticker.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_sticker(sticker=sticker, chat_id=chat_id)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_sticker(sticker=sticker, chat_id=chat_id)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_sticker_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_sticker_local_files(self, monkeypatch, offline_bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -311,11 +311,11 @@ class TestStickerWithoutRequest(TestStickerBase):
else:
test_flag = isinstance(data.get("sticker"), InputFile)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_sticker(chat_id, file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_sticker(chat_id, file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
@pytest.mark.parametrize(
("default_bot", "custom"),
@@ -345,7 +345,7 @@ class TestStickerWithoutRequest(TestStickerBase):
)
class TestStickerWithRequest(TestStickerBase):
class TestStickerWithRequest(StickerTestBase):
async def test_send_all_args(self, bot, chat_id, sticker_file, sticker):
message = await bot.send_sticker(
chat_id, sticker=sticker_file, disable_notification=False, protect_content=True
@@ -572,7 +572,7 @@ def sticker_set_thumb_file():
yield file
class TestStickerSetBase:
class StickerSetTestBase:
title = "Test stickers"
stickers = [Sticker("file_id", "file_un_id", 512, 512, True, True, Sticker.REGULAR)]
name = "NOTAREALNAME"
@@ -580,15 +580,15 @@ class TestStickerSetBase:
contains_masks = True
class TestStickerSetWithoutRequest(TestStickerSetBase):
class TestStickerSetWithoutRequest(StickerSetTestBase):
def test_slot_behaviour(self):
inst = StickerSet("this", "is", self.stickers, "not")
for attr in inst.__slots__:
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
def test_de_json(self, bot, sticker):
name = f"test_by_{bot.username}"
def test_de_json(self, offline_bot, sticker):
name = f"test_by_{offline_bot.username}"
json_dict = {
"name": name,
"title": self.title,
@@ -597,7 +597,7 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
"sticker_type": self.sticker_type,
"contains_masks": self.contains_masks,
}
sticker_set = StickerSet.de_json(json_dict, bot)
sticker_set = StickerSet.de_json(json_dict, offline_bot)
assert sticker_set.name == name
assert sticker_set.title == self.title
@@ -653,11 +653,11 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
@pytest.mark.parametrize("local_mode", [True, False])
async def test_upload_sticker_file_local_files(
self, monkeypatch, bot, chat_id, local_mode, recwarn
self, monkeypatch, offline_bot, chat_id, local_mode, recwarn
):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -670,24 +670,24 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
else isinstance(data.get("sticker"), InputFile)
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.upload_sticker_file(
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.upload_sticker_file(
chat_id, sticker=file, sticker_format=StickerFormat.STATIC
)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
@pytest.mark.parametrize("local_mode", [True, False])
async def test_create_new_sticker_set_local_files(
self,
monkeypatch,
bot,
offline_bot,
chat_id,
local_mode,
):
monkeypatch.setattr(bot, "_local_mode", local_mode)
# For just test that the correct paths are passed as we have no local bot API set up
monkeypatch.setattr(offline_bot, "_local_mode", local_mode)
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
# always assumed to be local mode because we don't have access to local_mode setting
@@ -698,8 +698,8 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
nonlocal test_flag
test_flag = data.get("stickers")[0].sticker == expected
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.create_new_sticker_set(
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.create_new_sticker_set(
chat_id,
"name",
"title",
@@ -707,7 +707,9 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
)
assert test_flag
async def test_create_new_sticker_all_params(self, monkeypatch, bot, chat_id, mask_position):
async def test_create_new_sticker_all_params(
self, monkeypatch, offline_bot, chat_id, mask_position
):
async def make_assertion(_, data, *args, **kwargs):
assert data["user_id"] == chat_id
assert data["name"] == "name"
@@ -715,8 +717,8 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
assert data["stickers"] == ["wow.png", "wow.tgs", "wow.webp"]
assert data["needs_repainting"] is True
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.create_new_sticker_set(
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.create_new_sticker_set(
chat_id,
"name",
"title",
@@ -725,9 +727,11 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_add_sticker_to_set_local_files(self, monkeypatch, bot, chat_id, local_mode):
monkeypatch.setattr(bot, "_local_mode", local_mode)
# For just test that the correct paths are passed as we have no local bot API set up
async def test_add_sticker_to_set_local_files(
self, monkeypatch, offline_bot, chat_id, local_mode
):
monkeypatch.setattr(offline_bot, "_local_mode", local_mode)
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
# always assumed to be local mode because we don't have access to local_mode setting
@@ -738,8 +742,8 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
nonlocal test_flag
test_flag = data.get("sticker").sticker == expected
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.add_sticker_to_set(
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.add_sticker_to_set(
chat_id,
"name",
sticker=InputSticker(sticker=file, emoji_list=["this"], format="static"),
@@ -748,11 +752,11 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
@pytest.mark.parametrize("local_mode", [True, False])
async def test_set_sticker_set_thumbnail_local_files(
self, monkeypatch, bot, chat_id, local_mode
self, monkeypatch, offline_bot, chat_id, local_mode
):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -764,11 +768,13 @@ class TestStickerSetWithoutRequest(TestStickerSetBase):
else:
test_flag = isinstance(data.get("thumbnail"), InputFile)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.set_sticker_set_thumbnail("name", chat_id, thumbnail=file, format="static")
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.set_sticker_set_thumbnail(
"name", chat_id, thumbnail=file, format="static"
)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, sticker):
async def make_assertion(*_, **kwargs):
@@ -1064,35 +1070,35 @@ class TestStickerSetWithRequest:
@pytest.fixture(scope="module")
def mask_position():
return MaskPosition(
TestMaskPositionBase.point,
TestMaskPositionBase.x_shift,
TestMaskPositionBase.y_shift,
TestMaskPositionBase.scale,
MaskPositionTestBase.point,
MaskPositionTestBase.x_shift,
MaskPositionTestBase.y_shift,
MaskPositionTestBase.scale,
)
class TestMaskPositionBase:
class MaskPositionTestBase:
point = MaskPosition.EYES
x_shift = -1
y_shift = 1
scale = 2
class TestMaskPositionWithoutRequest(TestMaskPositionBase):
class TestMaskPositionWithoutRequest(MaskPositionTestBase):
def test_slot_behaviour(self, mask_position):
inst = mask_position
for attr in inst.__slots__:
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
def test_mask_position_de_json(self, bot):
def test_mask_position_de_json(self, offline_bot):
json_dict = {
"point": self.point,
"x_shift": self.x_shift,
"y_shift": self.y_shift,
"scale": self.scale,
}
mask_position = MaskPosition.de_json(json_dict, bot)
mask_position = MaskPosition.de_json(json_dict, offline_bot)
assert mask_position.api_kwargs == {}
assert mask_position.point == self.point
@@ -1130,7 +1136,7 @@ class TestMaskPositionWithoutRequest(TestMaskPositionBase):
assert hash(a) != hash(e)
class TestMaskPositionWithRequest(TestMaskPositionBase):
class TestMaskPositionWithRequest(MaskPositionTestBase):
async def test_create_new_mask_sticker_set(self, bot, chat_id, sticker_file, mask_position):
name = f"masks_by_{bot.username}"
try:
+19 -19
View File
@@ -31,17 +31,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def venue():
return Venue(
TestVenueBase.location,
TestVenueBase.title,
TestVenueBase.address,
foursquare_id=TestVenueBase.foursquare_id,
foursquare_type=TestVenueBase.foursquare_type,
google_place_id=TestVenueBase.google_place_id,
google_place_type=TestVenueBase.google_place_type,
VenueTestBase.location,
VenueTestBase.title,
VenueTestBase.address,
foursquare_id=VenueTestBase.foursquare_id,
foursquare_type=VenueTestBase.foursquare_type,
google_place_id=VenueTestBase.google_place_id,
google_place_type=VenueTestBase.google_place_type,
)
class TestVenueBase:
class VenueTestBase:
location = Location(longitude=-46.788279, latitude=-23.691288)
title = "title"
address = "address"
@@ -51,13 +51,13 @@ class TestVenueBase:
google_place_type = "google place type"
class TestVenueWithoutRequest(TestVenueBase):
class TestVenueWithoutRequest(VenueTestBase):
def test_slot_behaviour(self, venue):
for attr in venue.__slots__:
assert getattr(venue, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(venue)) == len(set(mro_slots(venue))), "duplicate slot"
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"location": self.location.to_dict(),
"title": self.title,
@@ -67,7 +67,7 @@ class TestVenueWithoutRequest(TestVenueBase):
"google_place_id": self.google_place_id,
"google_place_type": self.google_place_type,
}
venue = Venue.de_json(json_dict, bot)
venue = Venue.de_json(json_dict, offline_bot)
assert venue.api_kwargs == {}
assert venue.location == self.location
@@ -110,13 +110,13 @@ class TestVenueWithoutRequest(TestVenueBase):
assert a != d2
assert hash(a) != hash(d2)
async def test_send_venue_without_required(self, bot, chat_id):
async def test_send_venue_without_required(self, offline_bot, chat_id):
with pytest.raises(ValueError, match="Either venue or latitude, longitude, address and"):
await bot.send_venue(chat_id=chat_id)
await offline_bot.send_venue(chat_id=chat_id)
async def test_send_venue_mutually_exclusive(self, bot, chat_id, venue):
async def test_send_venue_mutually_exclusive(self, offline_bot, chat_id, venue):
with pytest.raises(ValueError, match="Not both"):
await bot.send_venue(
await offline_bot.send_venue(
chat_id=chat_id,
latitude=1,
longitude=1,
@@ -125,7 +125,7 @@ class TestVenueWithoutRequest(TestVenueBase):
venue=venue,
)
async def test_send_with_venue(self, monkeypatch, bot, chat_id, venue):
async def test_send_with_venue(self, monkeypatch, offline_bot, chat_id, venue):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
return (
@@ -139,8 +139,8 @@ class TestVenueWithoutRequest(TestVenueBase):
and data["google_place_type"] == self.google_place_type
)
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.send_venue(chat_id, venue=venue)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
message = await offline_bot.send_venue(chat_id, venue=venue)
assert message
@pytest.mark.parametrize(
@@ -171,7 +171,7 @@ class TestVenueWithoutRequest(TestVenueBase):
)
class TestVenueWithRequest(TestVenueBase):
class TestVenueWithRequest(VenueTestBase):
@pytest.mark.parametrize(
("default_bot", "custom"),
[
+19 -19
View File
@@ -49,7 +49,7 @@ async def video(bot, chat_id):
return (await bot.send_video(chat_id, video=f, read_timeout=50)).video
class TestVideoBase:
class VideoTestBase:
width = 360
height = 640
duration = 5
@@ -66,7 +66,7 @@ class TestVideoBase:
video_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVideoWithoutRequest(TestVideoBase):
class TestVideoWithoutRequest(VideoTestBase):
def test_slot_behaviour(self, video):
for attr in video.__slots__:
assert getattr(video, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -93,7 +93,7 @@ class TestVideoWithoutRequest(TestVideoBase):
assert video.file_size == self.file_size
assert video.mime_type == self.mime_type
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"file_id": self.video_file_id,
"file_unique_id": self.video_file_unique_id,
@@ -104,7 +104,7 @@ class TestVideoWithoutRequest(TestVideoBase):
"file_size": self.file_size,
"file_name": self.file_name,
}
json_video = Video.de_json(json_dict, bot)
json_video = Video.de_json(json_dict, offline_bot)
assert json_video.api_kwargs == {}
assert json_video.file_id == self.video_file_id
@@ -149,30 +149,30 @@ class TestVideoWithoutRequest(TestVideoBase):
assert a != e
assert hash(a) != hash(e)
async def test_error_without_required_args(self, bot, chat_id):
async def test_error_without_required_args(self, offline_bot, chat_id):
with pytest.raises(TypeError):
await bot.send_video(chat_id=chat_id)
await offline_bot.send_video(chat_id=chat_id)
async def test_send_with_video(self, monkeypatch, bot, chat_id, video):
async def test_send_with_video(self, monkeypatch, offline_bot, chat_id, video):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["video"] == video.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_video(chat_id, video=video)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_video(chat_id, video=video)
async def test_send_video_custom_filename(self, bot, chat_id, video_file, monkeypatch):
async def test_send_video_custom_filename(self, offline_bot, chat_id, video_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return next(iter(request_data.multipart_data.values()))[0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await bot.send_video(chat_id, video_file, filename="custom_filename")
assert await offline_bot.send_video(chat_id, video_file, filename="custom_filename")
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_video_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_video_local_files(self, monkeypatch, offline_bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -186,11 +186,11 @@ class TestVideoWithoutRequest(TestVideoBase):
data.get("thumbnail"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_video(chat_id, file, thumbnail=file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_video(chat_id, file, thumbnail=file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, video):
async def make_assertion(*_, **kwargs):
@@ -229,7 +229,7 @@ class TestVideoWithoutRequest(TestVideoBase):
await default_bot.send_video(chat_id, video, reply_parameters=ReplyParameters(**kwargs))
class TestVideoWithRequest(TestVideoBase):
class TestVideoWithRequest(VideoTestBase):
async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file):
message = await bot.send_video(
chat_id,
+23 -19
View File
@@ -48,7 +48,7 @@ async def video_note(bot, chat_id):
return (await bot.send_video_note(chat_id, video_note=f, read_timeout=50)).video_note
class TestVideoNoteBase:
class VideoNoteTestBase:
length = 240
duration = 3
file_size = 132084
@@ -60,7 +60,7 @@ class TestVideoNoteBase:
videonote_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVideoNoteWithoutRequest(TestVideoNoteBase):
class TestVideoNoteWithoutRequest(VideoNoteTestBase):
def test_slot_behaviour(self, video_note):
for attr in video_note.__slots__:
assert getattr(video_note, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -85,7 +85,7 @@ class TestVideoNoteWithoutRequest(TestVideoNoteBase):
assert video_note.duration == self.duration
assert video_note.file_size == self.file_size
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"file_id": self.videonote_file_id,
"file_unique_id": self.videonote_file_unique_id,
@@ -93,7 +93,7 @@ class TestVideoNoteWithoutRequest(TestVideoNoteBase):
"duration": self.duration,
"file_size": self.file_size,
}
json_video_note = VideoNote.de_json(json_dict, bot)
json_video_note = VideoNote.de_json(json_dict, offline_bot)
assert json_video_note.api_kwargs == {}
assert json_video_note.file_id == self.videonote_file_id
@@ -132,32 +132,36 @@ class TestVideoNoteWithoutRequest(TestVideoNoteBase):
assert a != e
assert hash(a) != hash(e)
async def test_error_without_required_args(self, bot, chat_id):
async def test_error_without_required_args(self, offline_bot, chat_id):
with pytest.raises(TypeError):
await bot.send_video_note(chat_id=chat_id)
await offline_bot.send_video_note(chat_id=chat_id)
async def test_send_with_video_note(self, monkeypatch, bot, chat_id, video_note):
async def test_send_with_video_note(self, monkeypatch, offline_bot, chat_id, video_note):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["video_note"] == video_note.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_video_note(chat_id, video_note=video_note)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_video_note(chat_id, video_note=video_note)
async def test_send_video_note_custom_filename(
self, bot, chat_id, video_note_file, monkeypatch
self, offline_bot, chat_id, video_note_file, monkeypatch
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return next(iter(request_data.multipart_data.values()))[0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await bot.send_video_note(chat_id, video_note_file, filename="custom_filename")
assert await offline_bot.send_video_note(
chat_id, video_note_file, filename="custom_filename"
)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_video_note_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_video_note_local_files(
self, monkeypatch, offline_bot, chat_id, local_mode
):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -173,11 +177,11 @@ class TestVideoNoteWithoutRequest(TestVideoNoteBase):
data.get("thumbnail"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_video_note(chat_id, file, thumbnail=file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_video_note(chat_id, file, thumbnail=file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, video_note):
async def make_assertion(*_, **kwargs):
@@ -218,7 +222,7 @@ class TestVideoNoteWithoutRequest(TestVideoNoteBase):
)
class TestVideoNoteWithRequest(TestVideoNoteBase):
class TestVideoNoteWithRequest(VideoNoteTestBase):
async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_file):
message = await bot.send_video_note(
chat_id,
+19 -19
View File
@@ -49,7 +49,7 @@ async def voice(bot, chat_id):
return (await bot.send_voice(chat_id, voice=f, read_timeout=50)).voice
class TestVoiceBase:
class VoiceTestBase:
duration = 3
mime_type = "audio/ogg"
file_size = 9199
@@ -59,7 +59,7 @@ class TestVoiceBase:
voice_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestVoiceWithoutRequest(TestVoiceBase):
class TestVoiceWithoutRequest(VoiceTestBase):
def test_slot_behaviour(self, voice):
for attr in voice.__slots__:
assert getattr(voice, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -78,7 +78,7 @@ class TestVoiceWithoutRequest(TestVoiceBase):
assert voice.mime_type == self.mime_type
assert voice.file_size == self.file_size
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"file_id": self.voice_file_id,
"file_unique_id": self.voice_file_unique_id,
@@ -86,7 +86,7 @@ class TestVoiceWithoutRequest(TestVoiceBase):
"mime_type": self.mime_type,
"file_size": self.file_size,
}
json_voice = Voice.de_json(json_dict, bot)
json_voice = Voice.de_json(json_dict, offline_bot)
assert json_voice.api_kwargs == {}
assert json_voice.file_id == self.voice_file_id
@@ -125,30 +125,30 @@ class TestVoiceWithoutRequest(TestVoiceBase):
assert a != e
assert hash(a) != hash(e)
async def test_error_without_required_args(self, bot, chat_id):
async def test_error_without_required_args(self, offline_bot, chat_id):
with pytest.raises(TypeError):
await bot.sendVoice(chat_id)
await offline_bot.sendVoice(chat_id)
async def test_send_voice_custom_filename(self, bot, chat_id, voice_file, monkeypatch):
async def test_send_voice_custom_filename(self, offline_bot, chat_id, voice_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return next(iter(request_data.multipart_data.values()))[0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await bot.send_voice(chat_id, voice_file, filename="custom_filename")
assert await offline_bot.send_voice(chat_id, voice_file, filename="custom_filename")
async def test_send_with_voice(self, monkeypatch, bot, chat_id, voice):
async def test_send_with_voice(self, monkeypatch, offline_bot, chat_id, voice):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["voice"] == voice.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_voice(chat_id, voice=voice)
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.send_voice(chat_id, voice=voice)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_voice_local_files(self, monkeypatch, bot, chat_id, local_mode):
async def test_send_voice_local_files(self, monkeypatch, offline_bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
offline_bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local Bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
@@ -160,11 +160,11 @@ class TestVoiceWithoutRequest(TestVoiceBase):
else:
test_flag = isinstance(data.get("voice"), InputFile)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_voice(chat_id, file)
monkeypatch.setattr(offline_bot, "_post", make_assertion)
await offline_bot.send_voice(chat_id, file)
assert test_flag
finally:
bot._local_mode = False
offline_bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, voice):
async def make_assertion(*_, **kwargs):
@@ -203,7 +203,7 @@ class TestVoiceWithoutRequest(TestVoiceBase):
await default_bot.send_voice(chat_id, voice, reply_parameters=ReplyParameters(**kwargs))
class TestVoiceWithRequest(TestVoiceBase):
class TestVoiceWithRequest(VoiceTestBase):
async def test_send_all_args(self, bot, chat_id, voice_file, voice):
message = await bot.send_voice(
chat_id,
+12 -12
View File
@@ -26,18 +26,18 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def game():
game = Game(
TestGameBase.title,
TestGameBase.description,
TestGameBase.photo,
text=TestGameBase.text,
text_entities=TestGameBase.text_entities,
animation=TestGameBase.animation,
GameTestBase.title,
GameTestBase.description,
GameTestBase.photo,
text=GameTestBase.text,
text_entities=GameTestBase.text_entities,
animation=GameTestBase.animation,
)
game._unfreeze()
return game
class TestGameBase:
class GameTestBase:
title = "Python-telegram-bot Test Game"
description = "description"
photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)]
@@ -49,26 +49,26 @@ class TestGameBase:
animation = Animation("blah", "unique_id", 320, 180, 1)
class TestGameWithoutRequest(TestGameBase):
class TestGameWithoutRequest(GameTestBase):
def test_slot_behaviour(self, game):
for attr in game.__slots__:
assert getattr(game, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(game)) == len(set(mro_slots(game))), "duplicate slot"
def test_de_json_required(self, bot):
def test_de_json_required(self, offline_bot):
json_dict = {
"title": self.title,
"description": self.description,
"photo": [self.photo[0].to_dict()],
}
game = Game.de_json(json_dict, bot)
game = Game.de_json(json_dict, offline_bot)
assert game.api_kwargs == {}
assert game.title == self.title
assert game.description == self.description
assert game.photo == tuple(self.photo)
def test_de_json_all(self, bot):
def test_de_json_all(self, offline_bot):
json_dict = {
"title": self.title,
"description": self.description,
@@ -77,7 +77,7 @@ class TestGameWithoutRequest(TestGameBase):
"text_entities": [self.text_entities[0].to_dict()],
"animation": self.animation.to_dict(),
}
game = Game.de_json(json_dict, bot)
game = Game.de_json(json_dict, offline_bot)
assert game.api_kwargs == {}
assert game.title == self.title
+6 -6
View File
@@ -26,36 +26,36 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def game_highscore():
return GameHighScore(
TestGameHighScoreBase.position, TestGameHighScoreBase.user, TestGameHighScoreBase.score
GameHighScoreTestBase.position, GameHighScoreTestBase.user, GameHighScoreTestBase.score
)
class TestGameHighScoreBase:
class GameHighScoreTestBase:
position = 12
user = User(2, "test user", False)
score = 42
class TestGameHighScoreWithoutRequest(TestGameHighScoreBase):
class TestGameHighScoreWithoutRequest(GameHighScoreTestBase):
def test_slot_behaviour(self, game_highscore):
for attr in game_highscore.__slots__:
assert getattr(game_highscore, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(game_highscore)) == len(set(mro_slots(game_highscore))), "same slot"
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"position": self.position,
"user": self.user.to_dict(),
"score": self.score,
}
highscore = GameHighScore.de_json(json_dict, bot)
highscore = GameHighScore.de_json(json_dict, offline_bot)
assert highscore.api_kwargs == {}
assert highscore.position == self.position
assert highscore.user == self.user
assert highscore.score == self.score
assert GameHighScore.de_json(None, bot) is None
assert GameHighScore.de_json(None, offline_bot) is None
def test_to_dict(self, game_highscore):
game_highscore_dict = game_highscore.to_dict()
+14 -14
View File
@@ -32,24 +32,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_keyboard_button():
return InlineKeyboardButton(
TestInlineKeyboardButtonBase.text,
url=TestInlineKeyboardButtonBase.url,
callback_data=TestInlineKeyboardButtonBase.callback_data,
switch_inline_query=TestInlineKeyboardButtonBase.switch_inline_query,
InlineKeyboardButtonTestBase.text,
url=InlineKeyboardButtonTestBase.url,
callback_data=InlineKeyboardButtonTestBase.callback_data,
switch_inline_query=InlineKeyboardButtonTestBase.switch_inline_query,
switch_inline_query_current_chat=(
TestInlineKeyboardButtonBase.switch_inline_query_current_chat
InlineKeyboardButtonTestBase.switch_inline_query_current_chat
),
callback_game=TestInlineKeyboardButtonBase.callback_game,
pay=TestInlineKeyboardButtonBase.pay,
login_url=TestInlineKeyboardButtonBase.login_url,
web_app=TestInlineKeyboardButtonBase.web_app,
callback_game=InlineKeyboardButtonTestBase.callback_game,
pay=InlineKeyboardButtonTestBase.pay,
login_url=InlineKeyboardButtonTestBase.login_url,
web_app=InlineKeyboardButtonTestBase.web_app,
switch_inline_query_chosen_chat=(
TestInlineKeyboardButtonBase.switch_inline_query_chosen_chat
InlineKeyboardButtonTestBase.switch_inline_query_chosen_chat
),
)
class TestInlineKeyboardButtonBase:
class InlineKeyboardButtonTestBase:
text = "text"
url = "url"
callback_data = "callback data"
@@ -62,7 +62,7 @@ class TestInlineKeyboardButtonBase:
switch_inline_query_chosen_chat = SwitchInlineQueryChosenChat("a_bot", True, False, True, True)
class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase):
class TestInlineKeyboardButtonWithoutRequest(InlineKeyboardButtonTestBase):
def test_slot_behaviour(self, inline_keyboard_button):
inst = inline_keyboard_button
for attr in inst.__slots__:
@@ -116,7 +116,7 @@ class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase):
== inline_keyboard_button.switch_inline_query_chosen_chat.to_dict()
)
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"text": self.text,
"url": self.url,
@@ -150,7 +150,7 @@ class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase):
== self.switch_inline_query_chosen_chat
)
none = InlineKeyboardButton.de_json({}, bot)
none = InlineKeyboardButton.de_json({}, offline_bot)
assert none is None
def test_equality(self):
+9 -7
View File
@@ -31,10 +31,10 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_keyboard_markup():
return InlineKeyboardMarkup(TestInlineKeyboardMarkupBase.inline_keyboard)
return InlineKeyboardMarkup(InlineKeyboardMarkupTestBase.inline_keyboard)
class TestInlineKeyboardMarkupBase:
class InlineKeyboardMarkupTestBase:
inline_keyboard = [
[
InlineKeyboardButton(text="button1", callback_data="data1"),
@@ -43,7 +43,7 @@ class TestInlineKeyboardMarkupBase:
]
class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase):
class TestInlineKeyboardMarkupWithoutRequest(InlineKeyboardMarkupTestBase):
def test_slot_behaviour(self, inline_keyboard_markup):
inst = inline_keyboard_markup
for attr in inst.__slots__:
@@ -192,7 +192,9 @@ class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase):
with pytest.raises(ValueError, match="should be a sequence of sequences"):
InlineKeyboardMarkup([[[InlineKeyboardButton("only_2d_array_is_allowed", "1")]]])
async def test_expected_values_empty_switch(self, inline_keyboard_markup, bot, monkeypatch):
async def test_expected_values_empty_switch(
self, inline_keyboard_markup, offline_bot, monkeypatch
):
async def make_assertion(
url,
data,
@@ -224,11 +226,11 @@ class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase):
inline_keyboard_markup.inline_keyboard[0][1].callback_data = None
inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = ""
monkeypatch.setattr(bot, "_send_message", make_assertion)
await bot.send_message(123, "test", reply_markup=inline_keyboard_markup)
monkeypatch.setattr(offline_bot, "_send_message", make_assertion)
await offline_bot.send_message(123, "test", reply_markup=inline_keyboard_markup)
class TestInlineKeyborardMarkupWithRequest(TestInlineKeyboardMarkupBase):
class TestInlineKeyborardMarkupWithRequest(InlineKeyboardMarkupTestBase):
async def test_send_message_with_inline_keyboard_markup(
self, bot, chat_id, inline_keyboard_markup
):
+9 -9
View File
@@ -31,17 +31,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query(bot):
ilq = InlineQuery(
TestInlineQueryBase.id_,
TestInlineQueryBase.from_user,
TestInlineQueryBase.query,
TestInlineQueryBase.offset,
location=TestInlineQueryBase.location,
InlineQueryTestBase.id_,
InlineQueryTestBase.from_user,
InlineQueryTestBase.query,
InlineQueryTestBase.offset,
location=InlineQueryTestBase.location,
)
ilq.set_bot(bot)
return ilq
class TestInlineQueryBase:
class InlineQueryTestBase:
id_ = 1234
from_user = User(1, "First name", False)
query = "query text"
@@ -49,13 +49,13 @@ class TestInlineQueryBase:
location = Location(8.8, 53.1)
class TestInlineQueryWithoutRequest(TestInlineQueryBase):
class TestInlineQueryWithoutRequest(InlineQueryTestBase):
def test_slot_behaviour(self, inline_query):
for attr in inline_query.__slots__:
assert getattr(inline_query, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(inline_query)) == len(set(mro_slots(inline_query))), "duplicate slot"
def test_de_json(self, bot):
def test_de_json(self, offline_bot):
json_dict = {
"id": self.id_,
"from": self.from_user.to_dict(),
@@ -63,7 +63,7 @@ class TestInlineQueryWithoutRequest(TestInlineQueryBase):
"offset": self.offset,
"location": self.location.to_dict(),
}
inline_query_json = InlineQuery.de_json(json_dict, bot)
inline_query_json = InlineQuery.de_json(json_dict, offline_bot)
assert inline_query_json.api_kwargs == {}
assert inline_query_json.id == self.id_
+12 -12
View File
@@ -34,20 +34,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_article():
return InlineQueryResultArticle(
TestInlineQueryResultArticleBase.id_,
TestInlineQueryResultArticleBase.title,
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
url=TestInlineQueryResultArticleBase.url,
hide_url=TestInlineQueryResultArticleBase.hide_url,
description=TestInlineQueryResultArticleBase.description,
thumbnail_url=TestInlineQueryResultArticleBase.thumbnail_url,
thumbnail_height=TestInlineQueryResultArticleBase.thumbnail_height,
thumbnail_width=TestInlineQueryResultArticleBase.thumbnail_width,
InlineQueryResultArticleTestBase.id_,
InlineQueryResultArticleTestBase.title,
input_message_content=InlineQueryResultArticleTestBase.input_message_content,
reply_markup=InlineQueryResultArticleTestBase.reply_markup,
url=InlineQueryResultArticleTestBase.url,
hide_url=InlineQueryResultArticleTestBase.hide_url,
description=InlineQueryResultArticleTestBase.description,
thumbnail_url=InlineQueryResultArticleTestBase.thumbnail_url,
thumbnail_height=InlineQueryResultArticleTestBase.thumbnail_height,
thumbnail_width=InlineQueryResultArticleTestBase.thumbnail_width,
)
class TestInlineQueryResultArticleBase:
class InlineQueryResultArticleTestBase:
id_ = "id"
type_ = "article"
title = "title"
@@ -61,7 +61,7 @@ class TestInlineQueryResultArticleBase:
thumbnail_width = 15
class TestInlineQueryResultArticleWithoutRequest(TestInlineQueryResultArticleBase):
class TestInlineQueryResultArticleWithoutRequest(InlineQueryResultArticleTestBase):
def test_slot_behaviour(self, inline_query_result_article):
inst = inline_query_result_article
for attr in inst.__slots__:
+12 -12
View File
@@ -33,20 +33,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_audio():
return InlineQueryResultAudio(
TestInlineQueryResultAudioBase.id_,
TestInlineQueryResultAudioBase.audio_url,
TestInlineQueryResultAudioBase.title,
performer=TestInlineQueryResultAudioBase.performer,
audio_duration=TestInlineQueryResultAudioBase.audio_duration,
caption=TestInlineQueryResultAudioBase.caption,
parse_mode=TestInlineQueryResultAudioBase.parse_mode,
caption_entities=TestInlineQueryResultAudioBase.caption_entities,
input_message_content=TestInlineQueryResultAudioBase.input_message_content,
reply_markup=TestInlineQueryResultAudioBase.reply_markup,
InlineQueryResultAudioTestBase.id_,
InlineQueryResultAudioTestBase.audio_url,
InlineQueryResultAudioTestBase.title,
performer=InlineQueryResultAudioTestBase.performer,
audio_duration=InlineQueryResultAudioTestBase.audio_duration,
caption=InlineQueryResultAudioTestBase.caption,
parse_mode=InlineQueryResultAudioTestBase.parse_mode,
caption_entities=InlineQueryResultAudioTestBase.caption_entities,
input_message_content=InlineQueryResultAudioTestBase.input_message_content,
reply_markup=InlineQueryResultAudioTestBase.reply_markup,
)
class TestInlineQueryResultAudioBase:
class InlineQueryResultAudioTestBase:
id_ = "id"
type_ = "audio"
audio_url = "audio url"
@@ -60,7 +60,7 @@ class TestInlineQueryResultAudioBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultAudioWithoutRequest(TestInlineQueryResultAudioBase):
class TestInlineQueryResultAudioWithoutRequest(InlineQueryResultAudioTestBase):
def test_slot_behaviour(self, inline_query_result_audio):
inst = inline_query_result_audio
for attr in inst.__slots__:
@@ -33,17 +33,17 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_audio():
return InlineQueryResultCachedAudio(
TestInlineQueryResultCachedAudioBase.id_,
TestInlineQueryResultCachedAudioBase.audio_file_id,
caption=TestInlineQueryResultCachedAudioBase.caption,
parse_mode=TestInlineQueryResultCachedAudioBase.parse_mode,
caption_entities=TestInlineQueryResultCachedAudioBase.caption_entities,
input_message_content=TestInlineQueryResultCachedAudioBase.input_message_content,
reply_markup=TestInlineQueryResultCachedAudioBase.reply_markup,
InlineQueryResultCachedAudioTestBase.id_,
InlineQueryResultCachedAudioTestBase.audio_file_id,
caption=InlineQueryResultCachedAudioTestBase.caption,
parse_mode=InlineQueryResultCachedAudioTestBase.parse_mode,
caption_entities=InlineQueryResultCachedAudioTestBase.caption_entities,
input_message_content=InlineQueryResultCachedAudioTestBase.input_message_content,
reply_markup=InlineQueryResultCachedAudioTestBase.reply_markup,
)
class TestInlineQueryResultCachedAudioBase:
class InlineQueryResultCachedAudioTestBase:
id_ = "id"
type_ = "audio"
audio_file_id = "audio file id"
@@ -54,7 +54,7 @@ class TestInlineQueryResultCachedAudioBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedAudioWithoutRequest(TestInlineQueryResultCachedAudioBase):
class TestInlineQueryResultCachedAudioWithoutRequest(InlineQueryResultCachedAudioTestBase):
def test_slot_behaviour(self, inline_query_result_cached_audio):
inst = inline_query_result_cached_audio
for attr in inst.__slots__:
@@ -33,19 +33,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_document():
return InlineQueryResultCachedDocument(
TestInlineQueryResultCachedDocumentBase.id_,
TestInlineQueryResultCachedDocumentBase.title,
TestInlineQueryResultCachedDocumentBase.document_file_id,
caption=TestInlineQueryResultCachedDocumentBase.caption,
parse_mode=TestInlineQueryResultCachedDocumentBase.parse_mode,
caption_entities=TestInlineQueryResultCachedDocumentBase.caption_entities,
description=TestInlineQueryResultCachedDocumentBase.description,
input_message_content=TestInlineQueryResultCachedDocumentBase.input_message_content,
reply_markup=TestInlineQueryResultCachedDocumentBase.reply_markup,
InlineQueryResultCachedDocumentTestBase.id_,
InlineQueryResultCachedDocumentTestBase.title,
InlineQueryResultCachedDocumentTestBase.document_file_id,
caption=InlineQueryResultCachedDocumentTestBase.caption,
parse_mode=InlineQueryResultCachedDocumentTestBase.parse_mode,
caption_entities=InlineQueryResultCachedDocumentTestBase.caption_entities,
description=InlineQueryResultCachedDocumentTestBase.description,
input_message_content=InlineQueryResultCachedDocumentTestBase.input_message_content,
reply_markup=InlineQueryResultCachedDocumentTestBase.reply_markup,
)
class TestInlineQueryResultCachedDocumentBase:
class InlineQueryResultCachedDocumentTestBase:
id_ = "id"
type_ = "document"
document_file_id = "document file id"
@@ -58,7 +58,7 @@ class TestInlineQueryResultCachedDocumentBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedDocumentWithoutRequest(TestInlineQueryResultCachedDocumentBase):
class TestInlineQueryResultCachedDocumentWithoutRequest(InlineQueryResultCachedDocumentTestBase):
def test_slot_behaviour(self, inline_query_result_cached_document):
inst = inline_query_result_cached_document
for attr in inst.__slots__:
@@ -32,19 +32,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_gif():
return InlineQueryResultCachedGif(
TestInlineQueryResultCachedGifBase.id_,
TestInlineQueryResultCachedGifBase.gif_file_id,
title=TestInlineQueryResultCachedGifBase.title,
caption=TestInlineQueryResultCachedGifBase.caption,
parse_mode=TestInlineQueryResultCachedGifBase.parse_mode,
caption_entities=TestInlineQueryResultCachedGifBase.caption_entities,
input_message_content=TestInlineQueryResultCachedGifBase.input_message_content,
reply_markup=TestInlineQueryResultCachedGifBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedGifBase.show_caption_above_media,
InlineQueryResultCachedGifTestBase.id_,
InlineQueryResultCachedGifTestBase.gif_file_id,
title=InlineQueryResultCachedGifTestBase.title,
caption=InlineQueryResultCachedGifTestBase.caption,
parse_mode=InlineQueryResultCachedGifTestBase.parse_mode,
caption_entities=InlineQueryResultCachedGifTestBase.caption_entities,
input_message_content=InlineQueryResultCachedGifTestBase.input_message_content,
reply_markup=InlineQueryResultCachedGifTestBase.reply_markup,
show_caption_above_media=InlineQueryResultCachedGifTestBase.show_caption_above_media,
)
class TestInlineQueryResultCachedGifBase:
class InlineQueryResultCachedGifTestBase:
id_ = "id"
type_ = "gif"
gif_file_id = "gif file id"
@@ -57,7 +57,7 @@ class TestInlineQueryResultCachedGifBase:
show_caption_above_media = True
class TestInlineQueryResultCachedGifWithoutRequest(TestInlineQueryResultCachedGifBase):
class TestInlineQueryResultCachedGifWithoutRequest(InlineQueryResultCachedGifTestBase):
def test_slot_behaviour(self, inline_query_result_cached_gif):
inst = inline_query_result_cached_gif
for attr in inst.__slots__:
@@ -32,19 +32,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_mpeg4_gif():
return InlineQueryResultCachedMpeg4Gif(
TestInlineQueryResultCachedMpeg4GifBase.id_,
TestInlineQueryResultCachedMpeg4GifBase.mpeg4_file_id,
title=TestInlineQueryResultCachedMpeg4GifBase.title,
caption=TestInlineQueryResultCachedMpeg4GifBase.caption,
parse_mode=TestInlineQueryResultCachedMpeg4GifBase.parse_mode,
caption_entities=TestInlineQueryResultCachedMpeg4GifBase.caption_entities,
input_message_content=TestInlineQueryResultCachedMpeg4GifBase.input_message_content,
reply_markup=TestInlineQueryResultCachedMpeg4GifBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedMpeg4GifBase.show_caption_above_media,
InlineQueryResultCachedMpeg4GifTestBase.id_,
InlineQueryResultCachedMpeg4GifTestBase.mpeg4_file_id,
title=InlineQueryResultCachedMpeg4GifTestBase.title,
caption=InlineQueryResultCachedMpeg4GifTestBase.caption,
parse_mode=InlineQueryResultCachedMpeg4GifTestBase.parse_mode,
caption_entities=InlineQueryResultCachedMpeg4GifTestBase.caption_entities,
input_message_content=InlineQueryResultCachedMpeg4GifTestBase.input_message_content,
reply_markup=InlineQueryResultCachedMpeg4GifTestBase.reply_markup,
show_caption_above_media=InlineQueryResultCachedMpeg4GifTestBase.show_caption_above_media,
)
class TestInlineQueryResultCachedMpeg4GifBase:
class InlineQueryResultCachedMpeg4GifTestBase:
id_ = "id"
type_ = "mpeg4_gif"
mpeg4_file_id = "mpeg4 file id"
@@ -57,7 +57,7 @@ class TestInlineQueryResultCachedMpeg4GifBase:
show_caption_above_media = True
class TestInlineQueryResultCachedMpeg4GifWithoutRequest(TestInlineQueryResultCachedMpeg4GifBase):
class TestInlineQueryResultCachedMpeg4GifWithoutRequest(InlineQueryResultCachedMpeg4GifTestBase):
def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif):
inst = inline_query_result_cached_mpeg4_gif
for attr in inst.__slots__:
@@ -32,20 +32,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_photo():
return InlineQueryResultCachedPhoto(
TestInlineQueryResultCachedPhotoBase.id_,
TestInlineQueryResultCachedPhotoBase.photo_file_id,
title=TestInlineQueryResultCachedPhotoBase.title,
description=TestInlineQueryResultCachedPhotoBase.description,
caption=TestInlineQueryResultCachedPhotoBase.caption,
parse_mode=TestInlineQueryResultCachedPhotoBase.parse_mode,
caption_entities=TestInlineQueryResultCachedPhotoBase.caption_entities,
input_message_content=TestInlineQueryResultCachedPhotoBase.input_message_content,
reply_markup=TestInlineQueryResultCachedPhotoBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedPhotoBase.show_caption_above_media,
InlineQueryResultCachedPhotoTestBase.id_,
InlineQueryResultCachedPhotoTestBase.photo_file_id,
title=InlineQueryResultCachedPhotoTestBase.title,
description=InlineQueryResultCachedPhotoTestBase.description,
caption=InlineQueryResultCachedPhotoTestBase.caption,
parse_mode=InlineQueryResultCachedPhotoTestBase.parse_mode,
caption_entities=InlineQueryResultCachedPhotoTestBase.caption_entities,
input_message_content=InlineQueryResultCachedPhotoTestBase.input_message_content,
reply_markup=InlineQueryResultCachedPhotoTestBase.reply_markup,
show_caption_above_media=InlineQueryResultCachedPhotoTestBase.show_caption_above_media,
)
class TestInlineQueryResultCachedPhotoBase:
class InlineQueryResultCachedPhotoTestBase:
id_ = "id"
type_ = "photo"
photo_file_id = "photo file id"
@@ -59,7 +59,7 @@ class TestInlineQueryResultCachedPhotoBase:
show_caption_above_media = True
class TestInlineQueryResultCachedPhotoWithoutRequest(TestInlineQueryResultCachedPhotoBase):
class TestInlineQueryResultCachedPhotoWithoutRequest(InlineQueryResultCachedPhotoTestBase):
def test_slot_behaviour(self, inline_query_result_cached_photo):
inst = inline_query_result_cached_photo
for attr in inst.__slots__:
@@ -31,14 +31,14 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_sticker():
return InlineQueryResultCachedSticker(
TestInlineQueryResultCachedStickerBase.id_,
TestInlineQueryResultCachedStickerBase.sticker_file_id,
input_message_content=TestInlineQueryResultCachedStickerBase.input_message_content,
reply_markup=TestInlineQueryResultCachedStickerBase.reply_markup,
InlineQueryResultCachedStickerTestBase.id_,
InlineQueryResultCachedStickerTestBase.sticker_file_id,
input_message_content=InlineQueryResultCachedStickerTestBase.input_message_content,
reply_markup=InlineQueryResultCachedStickerTestBase.reply_markup,
)
class TestInlineQueryResultCachedStickerBase:
class InlineQueryResultCachedStickerTestBase:
id_ = "id"
type_ = "sticker"
sticker_file_id = "sticker file id"
@@ -46,7 +46,7 @@ class TestInlineQueryResultCachedStickerBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedStickerWithoutRequest(TestInlineQueryResultCachedStickerBase):
class TestInlineQueryResultCachedStickerWithoutRequest(InlineQueryResultCachedStickerTestBase):
def test_slot_behaviour(self, inline_query_result_cached_sticker):
inst = inline_query_result_cached_sticker
for attr in inst.__slots__:
@@ -32,20 +32,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_video():
return InlineQueryResultCachedVideo(
TestInlineQueryResultCachedVideoBase.id_,
TestInlineQueryResultCachedVideoBase.video_file_id,
TestInlineQueryResultCachedVideoBase.title,
caption=TestInlineQueryResultCachedVideoBase.caption,
parse_mode=TestInlineQueryResultCachedVideoBase.parse_mode,
caption_entities=TestInlineQueryResultCachedVideoBase.caption_entities,
description=TestInlineQueryResultCachedVideoBase.description,
input_message_content=TestInlineQueryResultCachedVideoBase.input_message_content,
reply_markup=TestInlineQueryResultCachedVideoBase.reply_markup,
show_caption_above_media=TestInlineQueryResultCachedVideoBase.show_caption_above_media,
InlineQueryResultCachedVideoTestBase.id_,
InlineQueryResultCachedVideoTestBase.video_file_id,
InlineQueryResultCachedVideoTestBase.title,
caption=InlineQueryResultCachedVideoTestBase.caption,
parse_mode=InlineQueryResultCachedVideoTestBase.parse_mode,
caption_entities=InlineQueryResultCachedVideoTestBase.caption_entities,
description=InlineQueryResultCachedVideoTestBase.description,
input_message_content=InlineQueryResultCachedVideoTestBase.input_message_content,
reply_markup=InlineQueryResultCachedVideoTestBase.reply_markup,
show_caption_above_media=InlineQueryResultCachedVideoTestBase.show_caption_above_media,
)
class TestInlineQueryResultCachedVideoBase:
class InlineQueryResultCachedVideoTestBase:
id_ = "id"
type_ = "video"
video_file_id = "video file id"
@@ -59,7 +59,7 @@ class TestInlineQueryResultCachedVideoBase:
show_caption_above_media = True
class TestInlineQueryResultCachedVideoWithoutRequest(TestInlineQueryResultCachedVideoBase):
class TestInlineQueryResultCachedVideoWithoutRequest(InlineQueryResultCachedVideoTestBase):
def test_slot_behaviour(self, inline_query_result_cached_video):
inst = inline_query_result_cached_video
for attr in inst.__slots__:
@@ -32,18 +32,18 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_cached_voice():
return InlineQueryResultCachedVoice(
TestInlineQueryResultCachedVoiceBase.id_,
TestInlineQueryResultCachedVoiceBase.voice_file_id,
TestInlineQueryResultCachedVoiceBase.title,
caption=TestInlineQueryResultCachedVoiceBase.caption,
parse_mode=TestInlineQueryResultCachedVoiceBase.parse_mode,
caption_entities=TestInlineQueryResultCachedVoiceBase.caption_entities,
input_message_content=TestInlineQueryResultCachedVoiceBase.input_message_content,
reply_markup=TestInlineQueryResultCachedVoiceBase.reply_markup,
InlineQueryResultCachedVoiceTestBase.id_,
InlineQueryResultCachedVoiceTestBase.voice_file_id,
InlineQueryResultCachedVoiceTestBase.title,
caption=InlineQueryResultCachedVoiceTestBase.caption,
parse_mode=InlineQueryResultCachedVoiceTestBase.parse_mode,
caption_entities=InlineQueryResultCachedVoiceTestBase.caption_entities,
input_message_content=InlineQueryResultCachedVoiceTestBase.input_message_content,
reply_markup=InlineQueryResultCachedVoiceTestBase.reply_markup,
)
class TestInlineQueryResultCachedVoiceBase:
class InlineQueryResultCachedVoiceTestBase:
id_ = "id"
type_ = "voice"
voice_file_id = "voice file id"
@@ -55,7 +55,7 @@ class TestInlineQueryResultCachedVoiceBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedVoiceWithoutRequest(TestInlineQueryResultCachedVoiceBase):
class TestInlineQueryResultCachedVoiceWithoutRequest(InlineQueryResultCachedVoiceTestBase):
def test_slot_behaviour(self, inline_query_result_cached_voice):
inst = inline_query_result_cached_voice
for attr in inst.__slots__:
+11 -11
View File
@@ -31,19 +31,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_contact():
return InlineQueryResultContact(
TestInlineQueryResultContactBase.id_,
TestInlineQueryResultContactBase.phone_number,
TestInlineQueryResultContactBase.first_name,
last_name=TestInlineQueryResultContactBase.last_name,
thumbnail_url=TestInlineQueryResultContactBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultContactBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultContactBase.thumbnail_height,
input_message_content=TestInlineQueryResultContactBase.input_message_content,
reply_markup=TestInlineQueryResultContactBase.reply_markup,
InlineQueryResultContactTestBase.id_,
InlineQueryResultContactTestBase.phone_number,
InlineQueryResultContactTestBase.first_name,
last_name=InlineQueryResultContactTestBase.last_name,
thumbnail_url=InlineQueryResultContactTestBase.thumbnail_url,
thumbnail_width=InlineQueryResultContactTestBase.thumbnail_width,
thumbnail_height=InlineQueryResultContactTestBase.thumbnail_height,
input_message_content=InlineQueryResultContactTestBase.input_message_content,
reply_markup=InlineQueryResultContactTestBase.reply_markup,
)
class TestInlineQueryResultContactBase:
class InlineQueryResultContactTestBase:
id_ = "id"
type_ = "contact"
phone_number = "phone_number"
@@ -56,7 +56,7 @@ class TestInlineQueryResultContactBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultContactWithoutRequest(TestInlineQueryResultContactBase):
class TestInlineQueryResultContactWithoutRequest(InlineQueryResultContactTestBase):
def test_slot_behaviour(self, inline_query_result_contact):
inst = inline_query_result_contact
for attr in inst.__slots__:
+15 -15
View File
@@ -32,23 +32,23 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_document():
return InlineQueryResultDocument(
TestInlineQueryResultDocumentBase.id_,
TestInlineQueryResultDocumentBase.document_url,
TestInlineQueryResultDocumentBase.title,
TestInlineQueryResultDocumentBase.mime_type,
caption=TestInlineQueryResultDocumentBase.caption,
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
description=TestInlineQueryResultDocumentBase.description,
thumbnail_url=TestInlineQueryResultDocumentBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultDocumentBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultDocumentBase.thumbnail_height,
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
InlineQueryResultDocumentTestBase.id_,
InlineQueryResultDocumentTestBase.document_url,
InlineQueryResultDocumentTestBase.title,
InlineQueryResultDocumentTestBase.mime_type,
caption=InlineQueryResultDocumentTestBase.caption,
parse_mode=InlineQueryResultDocumentTestBase.parse_mode,
caption_entities=InlineQueryResultDocumentTestBase.caption_entities,
description=InlineQueryResultDocumentTestBase.description,
thumbnail_url=InlineQueryResultDocumentTestBase.thumbnail_url,
thumbnail_width=InlineQueryResultDocumentTestBase.thumbnail_width,
thumbnail_height=InlineQueryResultDocumentTestBase.thumbnail_height,
input_message_content=InlineQueryResultDocumentTestBase.input_message_content,
reply_markup=InlineQueryResultDocumentTestBase.reply_markup,
)
class TestInlineQueryResultDocumentBase:
class InlineQueryResultDocumentTestBase:
id_ = "id"
type_ = "document"
document_url = "document url"
@@ -65,7 +65,7 @@ class TestInlineQueryResultDocumentBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultDocumentWithoutRequest(TestInlineQueryResultDocumentBase):
class TestInlineQueryResultDocumentWithoutRequest(InlineQueryResultDocumentTestBase):
def test_slot_behaviour(self, inline_query_result_document):
inst = inline_query_result_document
for attr in inst.__slots__:
+5 -5
View File
@@ -30,20 +30,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_game():
return InlineQueryResultGame(
TestInlineQueryResultGameBase.id_,
TestInlineQueryResultGameBase.game_short_name,
reply_markup=TestInlineQueryResultGameBase.reply_markup,
InlineQueryResultGameTestBase.id_,
InlineQueryResultGameTestBase.game_short_name,
reply_markup=InlineQueryResultGameTestBase.reply_markup,
)
class TestInlineQueryResultGameBase:
class InlineQueryResultGameTestBase:
id_ = "id"
type_ = "game"
game_short_name = "game short name"
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultGameWithoutRequest(TestInlineQueryResultGameBase):
class TestInlineQueryResultGameWithoutRequest(InlineQueryResultGameTestBase):
def test_slot_behaviour(self, inline_query_result_game):
inst = inline_query_result_game
for attr in inst.__slots__:
+16 -16
View File
@@ -32,24 +32,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_gif():
return InlineQueryResultGif(
TestInlineQueryResultGifBase.id_,
TestInlineQueryResultGifBase.gif_url,
TestInlineQueryResultGifBase.thumbnail_url,
gif_width=TestInlineQueryResultGifBase.gif_width,
gif_height=TestInlineQueryResultGifBase.gif_height,
gif_duration=TestInlineQueryResultGifBase.gif_duration,
title=TestInlineQueryResultGifBase.title,
caption=TestInlineQueryResultGifBase.caption,
parse_mode=TestInlineQueryResultGifBase.parse_mode,
caption_entities=TestInlineQueryResultGifBase.caption_entities,
input_message_content=TestInlineQueryResultGifBase.input_message_content,
reply_markup=TestInlineQueryResultGifBase.reply_markup,
thumbnail_mime_type=TestInlineQueryResultGifBase.thumbnail_mime_type,
show_caption_above_media=TestInlineQueryResultGifBase.show_caption_above_media,
InlineQueryResultGifTestBase.id_,
InlineQueryResultGifTestBase.gif_url,
InlineQueryResultGifTestBase.thumbnail_url,
gif_width=InlineQueryResultGifTestBase.gif_width,
gif_height=InlineQueryResultGifTestBase.gif_height,
gif_duration=InlineQueryResultGifTestBase.gif_duration,
title=InlineQueryResultGifTestBase.title,
caption=InlineQueryResultGifTestBase.caption,
parse_mode=InlineQueryResultGifTestBase.parse_mode,
caption_entities=InlineQueryResultGifTestBase.caption_entities,
input_message_content=InlineQueryResultGifTestBase.input_message_content,
reply_markup=InlineQueryResultGifTestBase.reply_markup,
thumbnail_mime_type=InlineQueryResultGifTestBase.thumbnail_mime_type,
show_caption_above_media=InlineQueryResultGifTestBase.show_caption_above_media,
)
class TestInlineQueryResultGifBase:
class InlineQueryResultGifTestBase:
id_ = "id"
type_ = "gif"
gif_url = "gif url"
@@ -67,7 +67,7 @@ class TestInlineQueryResultGifBase:
show_caption_above_media = True
class TestInlineQueryResultGifWithoutRequest(TestInlineQueryResultGifBase):
class TestInlineQueryResultGifWithoutRequest(InlineQueryResultGifTestBase):
def test_slot_behaviour(self, inline_query_result_gif):
inst = inline_query_result_gif
for attr in inst.__slots__:
+15 -15
View File
@@ -31,23 +31,23 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_location():
return InlineQueryResultLocation(
TestInlineQueryResultLocationBase.id_,
TestInlineQueryResultLocationBase.latitude,
TestInlineQueryResultLocationBase.longitude,
TestInlineQueryResultLocationBase.title,
live_period=TestInlineQueryResultLocationBase.live_period,
thumbnail_url=TestInlineQueryResultLocationBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultLocationBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultLocationBase.thumbnail_height,
input_message_content=TestInlineQueryResultLocationBase.input_message_content,
reply_markup=TestInlineQueryResultLocationBase.reply_markup,
horizontal_accuracy=TestInlineQueryResultLocationBase.horizontal_accuracy,
heading=TestInlineQueryResultLocationBase.heading,
proximity_alert_radius=TestInlineQueryResultLocationBase.proximity_alert_radius,
InlineQueryResultLocationTestBase.id_,
InlineQueryResultLocationTestBase.latitude,
InlineQueryResultLocationTestBase.longitude,
InlineQueryResultLocationTestBase.title,
live_period=InlineQueryResultLocationTestBase.live_period,
thumbnail_url=InlineQueryResultLocationTestBase.thumbnail_url,
thumbnail_width=InlineQueryResultLocationTestBase.thumbnail_width,
thumbnail_height=InlineQueryResultLocationTestBase.thumbnail_height,
input_message_content=InlineQueryResultLocationTestBase.input_message_content,
reply_markup=InlineQueryResultLocationTestBase.reply_markup,
horizontal_accuracy=InlineQueryResultLocationTestBase.horizontal_accuracy,
heading=InlineQueryResultLocationTestBase.heading,
proximity_alert_radius=InlineQueryResultLocationTestBase.proximity_alert_radius,
)
class TestInlineQueryResultLocationBase:
class InlineQueryResultLocationTestBase:
id_ = "id"
type_ = "location"
latitude = 0.0
@@ -64,7 +64,7 @@ class TestInlineQueryResultLocationBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultLocationWithoutRequest(TestInlineQueryResultLocationBase):
class TestInlineQueryResultLocationWithoutRequest(InlineQueryResultLocationTestBase):
def test_slot_behaviour(self, inline_query_result_location):
inst = inline_query_result_location
for attr in inst.__slots__:
+16 -16
View File
@@ -32,24 +32,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_mpeg4_gif():
return InlineQueryResultMpeg4Gif(
TestInlineQueryResultMpeg4GifBase.id_,
TestInlineQueryResultMpeg4GifBase.mpeg4_url,
TestInlineQueryResultMpeg4GifBase.thumbnail_url,
mpeg4_width=TestInlineQueryResultMpeg4GifBase.mpeg4_width,
mpeg4_height=TestInlineQueryResultMpeg4GifBase.mpeg4_height,
mpeg4_duration=TestInlineQueryResultMpeg4GifBase.mpeg4_duration,
title=TestInlineQueryResultMpeg4GifBase.title,
caption=TestInlineQueryResultMpeg4GifBase.caption,
parse_mode=TestInlineQueryResultMpeg4GifBase.parse_mode,
caption_entities=TestInlineQueryResultMpeg4GifBase.caption_entities,
input_message_content=TestInlineQueryResultMpeg4GifBase.input_message_content,
reply_markup=TestInlineQueryResultMpeg4GifBase.reply_markup,
thumbnail_mime_type=TestInlineQueryResultMpeg4GifBase.thumbnail_mime_type,
show_caption_above_media=TestInlineQueryResultMpeg4GifBase.show_caption_above_media,
InlineQueryResultMpeg4GifTestBase.id_,
InlineQueryResultMpeg4GifTestBase.mpeg4_url,
InlineQueryResultMpeg4GifTestBase.thumbnail_url,
mpeg4_width=InlineQueryResultMpeg4GifTestBase.mpeg4_width,
mpeg4_height=InlineQueryResultMpeg4GifTestBase.mpeg4_height,
mpeg4_duration=InlineQueryResultMpeg4GifTestBase.mpeg4_duration,
title=InlineQueryResultMpeg4GifTestBase.title,
caption=InlineQueryResultMpeg4GifTestBase.caption,
parse_mode=InlineQueryResultMpeg4GifTestBase.parse_mode,
caption_entities=InlineQueryResultMpeg4GifTestBase.caption_entities,
input_message_content=InlineQueryResultMpeg4GifTestBase.input_message_content,
reply_markup=InlineQueryResultMpeg4GifTestBase.reply_markup,
thumbnail_mime_type=InlineQueryResultMpeg4GifTestBase.thumbnail_mime_type,
show_caption_above_media=InlineQueryResultMpeg4GifTestBase.show_caption_above_media,
)
class TestInlineQueryResultMpeg4GifBase:
class InlineQueryResultMpeg4GifTestBase:
id_ = "id"
type_ = "mpeg4_gif"
mpeg4_url = "mpeg4 url"
@@ -67,7 +67,7 @@ class TestInlineQueryResultMpeg4GifBase:
show_caption_above_media = True
class TestInlineQueryResultMpeg4GifWithoutRequest(TestInlineQueryResultMpeg4GifBase):
class TestInlineQueryResultMpeg4GifWithoutRequest(InlineQueryResultMpeg4GifTestBase):
def test_slot_behaviour(self, inline_query_result_mpeg4_gif):
inst = inline_query_result_mpeg4_gif
for attr in inst.__slots__:
+15 -15
View File
@@ -32,23 +32,23 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_photo():
return InlineQueryResultPhoto(
TestInlineQueryResultPhotoBase.id_,
TestInlineQueryResultPhotoBase.photo_url,
TestInlineQueryResultPhotoBase.thumbnail_url,
photo_width=TestInlineQueryResultPhotoBase.photo_width,
photo_height=TestInlineQueryResultPhotoBase.photo_height,
title=TestInlineQueryResultPhotoBase.title,
description=TestInlineQueryResultPhotoBase.description,
caption=TestInlineQueryResultPhotoBase.caption,
parse_mode=TestInlineQueryResultPhotoBase.parse_mode,
caption_entities=TestInlineQueryResultPhotoBase.caption_entities,
input_message_content=TestInlineQueryResultPhotoBase.input_message_content,
reply_markup=TestInlineQueryResultPhotoBase.reply_markup,
show_caption_above_media=TestInlineQueryResultPhotoBase.show_caption_above_media,
InlineQueryResultPhotoTestBase.id_,
InlineQueryResultPhotoTestBase.photo_url,
InlineQueryResultPhotoTestBase.thumbnail_url,
photo_width=InlineQueryResultPhotoTestBase.photo_width,
photo_height=InlineQueryResultPhotoTestBase.photo_height,
title=InlineQueryResultPhotoTestBase.title,
description=InlineQueryResultPhotoTestBase.description,
caption=InlineQueryResultPhotoTestBase.caption,
parse_mode=InlineQueryResultPhotoTestBase.parse_mode,
caption_entities=InlineQueryResultPhotoTestBase.caption_entities,
input_message_content=InlineQueryResultPhotoTestBase.input_message_content,
reply_markup=InlineQueryResultPhotoTestBase.reply_markup,
show_caption_above_media=InlineQueryResultPhotoTestBase.show_caption_above_media,
)
class TestInlineQueryResultPhotoBase:
class InlineQueryResultPhotoTestBase:
id_ = "id"
type_ = "photo"
photo_url = "photo url"
@@ -66,7 +66,7 @@ class TestInlineQueryResultPhotoBase:
show_caption_above_media = True
class TestInlineQueryResultPhotoWithoutRequest(TestInlineQueryResultPhotoBase):
class TestInlineQueryResultPhotoWithoutRequest(InlineQueryResultPhotoTestBase):
def test_slot_behaviour(self, inline_query_result_photo):
inst = inline_query_result_photo
for attr in inst.__slots__:
+16 -16
View File
@@ -31,24 +31,24 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_venue():
return InlineQueryResultVenue(
TestInlineQueryResultVenueBase.id_,
TestInlineQueryResultVenueBase.latitude,
TestInlineQueryResultVenueBase.longitude,
TestInlineQueryResultVenueBase.title,
TestInlineQueryResultVenueBase.address,
foursquare_id=TestInlineQueryResultVenueBase.foursquare_id,
foursquare_type=TestInlineQueryResultVenueBase.foursquare_type,
thumbnail_url=TestInlineQueryResultVenueBase.thumbnail_url,
thumbnail_width=TestInlineQueryResultVenueBase.thumbnail_width,
thumbnail_height=TestInlineQueryResultVenueBase.thumbnail_height,
input_message_content=TestInlineQueryResultVenueBase.input_message_content,
reply_markup=TestInlineQueryResultVenueBase.reply_markup,
google_place_id=TestInlineQueryResultVenueBase.google_place_id,
google_place_type=TestInlineQueryResultVenueBase.google_place_type,
InlineQueryResultVenueTestBase.id_,
InlineQueryResultVenueTestBase.latitude,
InlineQueryResultVenueTestBase.longitude,
InlineQueryResultVenueTestBase.title,
InlineQueryResultVenueTestBase.address,
foursquare_id=InlineQueryResultVenueTestBase.foursquare_id,
foursquare_type=InlineQueryResultVenueTestBase.foursquare_type,
thumbnail_url=InlineQueryResultVenueTestBase.thumbnail_url,
thumbnail_width=InlineQueryResultVenueTestBase.thumbnail_width,
thumbnail_height=InlineQueryResultVenueTestBase.thumbnail_height,
input_message_content=InlineQueryResultVenueTestBase.input_message_content,
reply_markup=InlineQueryResultVenueTestBase.reply_markup,
google_place_id=InlineQueryResultVenueTestBase.google_place_id,
google_place_type=InlineQueryResultVenueTestBase.google_place_type,
)
class TestInlineQueryResultVenueBase:
class InlineQueryResultVenueTestBase:
id_ = "id"
type_ = "venue"
latitude = "latitude"
@@ -66,7 +66,7 @@ class TestInlineQueryResultVenueBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultVenueWithoutRequest(TestInlineQueryResultVenueBase):
class TestInlineQueryResultVenueWithoutRequest(InlineQueryResultVenueTestBase):
def test_slot_behaviour(self, inline_query_result_venue):
inst = inline_query_result_venue
for attr in inst.__slots__:
+17 -17
View File
@@ -32,25 +32,25 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_video():
return InlineQueryResultVideo(
TestInlineQueryResultVideoBase.id_,
TestInlineQueryResultVideoBase.video_url,
TestInlineQueryResultVideoBase.mime_type,
TestInlineQueryResultVideoBase.thumbnail_url,
TestInlineQueryResultVideoBase.title,
video_width=TestInlineQueryResultVideoBase.video_width,
video_height=TestInlineQueryResultVideoBase.video_height,
video_duration=TestInlineQueryResultVideoBase.video_duration,
caption=TestInlineQueryResultVideoBase.caption,
parse_mode=TestInlineQueryResultVideoBase.parse_mode,
caption_entities=TestInlineQueryResultVideoBase.caption_entities,
description=TestInlineQueryResultVideoBase.description,
input_message_content=TestInlineQueryResultVideoBase.input_message_content,
reply_markup=TestInlineQueryResultVideoBase.reply_markup,
show_caption_above_media=TestInlineQueryResultVideoBase.show_caption_above_media,
InlineQueryResultVideoTestBase.id_,
InlineQueryResultVideoTestBase.video_url,
InlineQueryResultVideoTestBase.mime_type,
InlineQueryResultVideoTestBase.thumbnail_url,
InlineQueryResultVideoTestBase.title,
video_width=InlineQueryResultVideoTestBase.video_width,
video_height=InlineQueryResultVideoTestBase.video_height,
video_duration=InlineQueryResultVideoTestBase.video_duration,
caption=InlineQueryResultVideoTestBase.caption,
parse_mode=InlineQueryResultVideoTestBase.parse_mode,
caption_entities=InlineQueryResultVideoTestBase.caption_entities,
description=InlineQueryResultVideoTestBase.description,
input_message_content=InlineQueryResultVideoTestBase.input_message_content,
reply_markup=InlineQueryResultVideoTestBase.reply_markup,
show_caption_above_media=InlineQueryResultVideoTestBase.show_caption_above_media,
)
class TestInlineQueryResultVideoBase:
class InlineQueryResultVideoTestBase:
id_ = "id"
type_ = "video"
video_url = "video url"
@@ -69,7 +69,7 @@ class TestInlineQueryResultVideoBase:
show_caption_above_media = True
class TestInlineQueryResultVideoWithoutRequest(TestInlineQueryResultVideoBase):
class TestInlineQueryResultVideoWithoutRequest(InlineQueryResultVideoTestBase):
def test_slot_behaviour(self, inline_query_result_video):
inst = inline_query_result_video
for attr in inst.__slots__:
+11 -11
View File
@@ -32,19 +32,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def inline_query_result_voice():
return InlineQueryResultVoice(
id=TestInlineQueryResultVoiceBase.id_,
voice_url=TestInlineQueryResultVoiceBase.voice_url,
title=TestInlineQueryResultVoiceBase.title,
voice_duration=TestInlineQueryResultVoiceBase.voice_duration,
caption=TestInlineQueryResultVoiceBase.caption,
parse_mode=TestInlineQueryResultVoiceBase.parse_mode,
caption_entities=TestInlineQueryResultVoiceBase.caption_entities,
input_message_content=TestInlineQueryResultVoiceBase.input_message_content,
reply_markup=TestInlineQueryResultVoiceBase.reply_markup,
id=InlineQueryResultVoiceTestBase.id_,
voice_url=InlineQueryResultVoiceTestBase.voice_url,
title=InlineQueryResultVoiceTestBase.title,
voice_duration=InlineQueryResultVoiceTestBase.voice_duration,
caption=InlineQueryResultVoiceTestBase.caption,
parse_mode=InlineQueryResultVoiceTestBase.parse_mode,
caption_entities=InlineQueryResultVoiceTestBase.caption_entities,
input_message_content=InlineQueryResultVoiceTestBase.input_message_content,
reply_markup=InlineQueryResultVoiceTestBase.reply_markup,
)
class TestInlineQueryResultVoiceBase:
class InlineQueryResultVoiceTestBase:
id_ = "id"
type_ = "voice"
voice_url = "voice url"
@@ -57,7 +57,7 @@ class TestInlineQueryResultVoiceBase:
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultVoiceWithoutRequest(TestInlineQueryResultVoiceBase):
class TestInlineQueryResultVoiceWithoutRequest(InlineQueryResultVoiceTestBase):
def test_slot_behaviour(self, inline_query_result_voice):
inst = inline_query_result_voice
for attr in inst.__slots__:
@@ -25,19 +25,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def input_contact_message_content():
return InputContactMessageContent(
TestInputContactMessageContentBase.phone_number,
TestInputContactMessageContentBase.first_name,
TestInputContactMessageContentBase.last_name,
InputContactMessageContentTestBase.phone_number,
InputContactMessageContentTestBase.first_name,
InputContactMessageContentTestBase.last_name,
)
class TestInputContactMessageContentBase:
class InputContactMessageContentTestBase:
phone_number = "phone number"
first_name = "first name"
last_name = "last name"
class TestInputContactMessageContentWithoutRequest(TestInputContactMessageContentBase):
class TestInputContactMessageContentWithoutRequest(InputContactMessageContentTestBase):
def test_slot_behaviour(self, input_contact_message_content):
inst = input_contact_message_content
for attr in inst.__slots__:
@@ -26,32 +26,32 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def input_invoice_message_content():
return InputInvoiceMessageContent(
title=TestInputInvoiceMessageContentBase.title,
description=TestInputInvoiceMessageContentBase.description,
payload=TestInputInvoiceMessageContentBase.payload,
provider_token=TestInputInvoiceMessageContentBase.provider_token,
currency=TestInputInvoiceMessageContentBase.currency,
prices=TestInputInvoiceMessageContentBase.prices,
max_tip_amount=TestInputInvoiceMessageContentBase.max_tip_amount,
suggested_tip_amounts=TestInputInvoiceMessageContentBase.suggested_tip_amounts,
provider_data=TestInputInvoiceMessageContentBase.provider_data,
photo_url=TestInputInvoiceMessageContentBase.photo_url,
photo_size=TestInputInvoiceMessageContentBase.photo_size,
photo_width=TestInputInvoiceMessageContentBase.photo_width,
photo_height=TestInputInvoiceMessageContentBase.photo_height,
need_name=TestInputInvoiceMessageContentBase.need_name,
need_phone_number=TestInputInvoiceMessageContentBase.need_phone_number,
need_email=TestInputInvoiceMessageContentBase.need_email,
need_shipping_address=TestInputInvoiceMessageContentBase.need_shipping_address,
title=InputInvoiceMessageContentTestBase.title,
description=InputInvoiceMessageContentTestBase.description,
payload=InputInvoiceMessageContentTestBase.payload,
provider_token=InputInvoiceMessageContentTestBase.provider_token,
currency=InputInvoiceMessageContentTestBase.currency,
prices=InputInvoiceMessageContentTestBase.prices,
max_tip_amount=InputInvoiceMessageContentTestBase.max_tip_amount,
suggested_tip_amounts=InputInvoiceMessageContentTestBase.suggested_tip_amounts,
provider_data=InputInvoiceMessageContentTestBase.provider_data,
photo_url=InputInvoiceMessageContentTestBase.photo_url,
photo_size=InputInvoiceMessageContentTestBase.photo_size,
photo_width=InputInvoiceMessageContentTestBase.photo_width,
photo_height=InputInvoiceMessageContentTestBase.photo_height,
need_name=InputInvoiceMessageContentTestBase.need_name,
need_phone_number=InputInvoiceMessageContentTestBase.need_phone_number,
need_email=InputInvoiceMessageContentTestBase.need_email,
need_shipping_address=InputInvoiceMessageContentTestBase.need_shipping_address,
send_phone_number_to_provider=(
TestInputInvoiceMessageContentBase.send_phone_number_to_provider
InputInvoiceMessageContentTestBase.send_phone_number_to_provider
),
send_email_to_provider=TestInputInvoiceMessageContentBase.send_email_to_provider,
is_flexible=TestInputInvoiceMessageContentBase.is_flexible,
send_email_to_provider=InputInvoiceMessageContentTestBase.send_email_to_provider,
is_flexible=InputInvoiceMessageContentTestBase.is_flexible,
)
class TestInputInvoiceMessageContentBase:
class InputInvoiceMessageContentTestBase:
title = "invoice title"
description = "invoice description"
payload = "invoice payload"
@@ -74,7 +74,7 @@ class TestInputInvoiceMessageContentBase:
is_flexible = True
class TestInputInvoiceMessageContentWithoutRequest(TestInputInvoiceMessageContentBase):
class TestInputInvoiceMessageContentWithoutRequest(InputInvoiceMessageContentTestBase):
def test_slot_behaviour(self, input_invoice_message_content):
inst = input_invoice_message_content
for attr in inst.__slots__:
@@ -203,8 +203,8 @@ class TestInputInvoiceMessageContentWithoutRequest(TestInputInvoiceMessageConten
== input_invoice_message_content.is_flexible
)
def test_de_json(self, bot):
assert InputInvoiceMessageContent.de_json({}, bot=bot) is None
def test_de_json(self, offline_bot):
assert InputInvoiceMessageContent.de_json({}, bot=offline_bot) is None
json_dict = {
"title": self.title,
@@ -229,7 +229,9 @@ class TestInputInvoiceMessageContentWithoutRequest(TestInputInvoiceMessageConten
"is_flexible": self.is_flexible,
}
input_invoice_message_content = InputInvoiceMessageContent.de_json(json_dict, bot=bot)
input_invoice_message_content = InputInvoiceMessageContent.de_json(
json_dict, bot=offline_bot
)
assert input_invoice_message_content.api_kwargs == {}
assert input_invoice_message_content.title == self.title
@@ -25,16 +25,16 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def input_location_message_content():
return InputLocationMessageContent(
TestInputLocationMessageContentBase.latitude,
TestInputLocationMessageContentBase.longitude,
live_period=TestInputLocationMessageContentBase.live_period,
horizontal_accuracy=TestInputLocationMessageContentBase.horizontal_accuracy,
heading=TestInputLocationMessageContentBase.heading,
proximity_alert_radius=TestInputLocationMessageContentBase.proximity_alert_radius,
InputLocationMessageContentTestBase.latitude,
InputLocationMessageContentTestBase.longitude,
live_period=InputLocationMessageContentTestBase.live_period,
horizontal_accuracy=InputLocationMessageContentTestBase.horizontal_accuracy,
heading=InputLocationMessageContentTestBase.heading,
proximity_alert_radius=InputLocationMessageContentTestBase.proximity_alert_radius,
)
class TestInputLocationMessageContentBase:
class InputLocationMessageContentTestBase:
latitude = -23.691288
longitude = -46.788279
live_period = 80
@@ -43,7 +43,7 @@ class TestInputLocationMessageContentBase:
proximity_alert_radius = 999
class TestInputLocationMessageContentWithoutRequest(TestInputLocationMessageContentBase):
class TestInputLocationMessageContentWithoutRequest(InputLocationMessageContentTestBase):
def test_slot_behaviour(self, input_location_message_content):
inst = input_location_message_content
for attr in inst.__slots__:
@@ -26,14 +26,14 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def input_text_message_content():
return InputTextMessageContent(
TestInputTextMessageContentBase.message_text,
parse_mode=TestInputTextMessageContentBase.parse_mode,
entities=TestInputTextMessageContentBase.entities,
link_preview_options=TestInputTextMessageContentBase.link_preview_options,
InputTextMessageContentTestBase.message_text,
parse_mode=InputTextMessageContentTestBase.parse_mode,
entities=InputTextMessageContentTestBase.entities,
link_preview_options=InputTextMessageContentTestBase.link_preview_options,
)
class TestInputTextMessageContentBase:
class InputTextMessageContentTestBase:
message_text = "*message text*"
parse_mode = ParseMode.MARKDOWN
entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)]
@@ -41,7 +41,7 @@ class TestInputTextMessageContentBase:
link_preview_options = LinkPreviewOptions(False, url="https://python-telegram-bot.org")
class TestInputTextMessageContentWithoutRequest(TestInputTextMessageContentBase):
class TestInputTextMessageContentWithoutRequest(InputTextMessageContentTestBase):
def test_slot_behaviour(self, input_text_message_content):
inst = input_text_message_content
for attr in inst.__slots__:
+10 -10
View File
@@ -25,18 +25,18 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def input_venue_message_content():
return InputVenueMessageContent(
TestInputVenueMessageContentBase.latitude,
TestInputVenueMessageContentBase.longitude,
TestInputVenueMessageContentBase.title,
TestInputVenueMessageContentBase.address,
foursquare_id=TestInputVenueMessageContentBase.foursquare_id,
foursquare_type=TestInputVenueMessageContentBase.foursquare_type,
google_place_id=TestInputVenueMessageContentBase.google_place_id,
google_place_type=TestInputVenueMessageContentBase.google_place_type,
InputVenueMessageContentTestBase.latitude,
InputVenueMessageContentTestBase.longitude,
InputVenueMessageContentTestBase.title,
InputVenueMessageContentTestBase.address,
foursquare_id=InputVenueMessageContentTestBase.foursquare_id,
foursquare_type=InputVenueMessageContentTestBase.foursquare_type,
google_place_id=InputVenueMessageContentTestBase.google_place_id,
google_place_type=InputVenueMessageContentTestBase.google_place_type,
)
class TestInputVenueMessageContentBase:
class InputVenueMessageContentTestBase:
latitude = 1.0
longitude = 2.0
title = "title"
@@ -47,7 +47,7 @@ class TestInputVenueMessageContentBase:
google_place_type = "google place type"
class TestInputVenueMessageContentWithoutRequest(TestInputVenueMessageContentBase):
class TestInputVenueMessageContentWithoutRequest(InputVenueMessageContentTestBase):
def test_slot_behaviour(self, input_venue_message_content):
inst = input_venue_message_content
for attr in inst.__slots__:
+5 -5
View File
@@ -26,19 +26,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def encrypted_credentials():
return EncryptedCredentials(
TestEncryptedCredentialsBase.data,
TestEncryptedCredentialsBase.hash,
TestEncryptedCredentialsBase.secret,
EncryptedCredentialsTestBase.data,
EncryptedCredentialsTestBase.hash,
EncryptedCredentialsTestBase.secret,
)
class TestEncryptedCredentialsBase:
class EncryptedCredentialsTestBase:
data = "data"
hash = "hash"
secret = "secret"
class TestEncryptedCredentialsWithoutRequest(TestEncryptedCredentialsBase):
class TestEncryptedCredentialsWithoutRequest(EncryptedCredentialsTestBase):
def test_slot_behaviour(self, encrypted_credentials):
inst = encrypted_credentials
for attr in inst.__slots__:
@@ -26,19 +26,19 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def encrypted_passport_element():
return EncryptedPassportElement(
TestEncryptedPassportElementBase.type_,
EncryptedPassportElementTestBase.type_,
"this is a hash",
data=TestEncryptedPassportElementBase.data,
phone_number=TestEncryptedPassportElementBase.phone_number,
email=TestEncryptedPassportElementBase.email,
files=TestEncryptedPassportElementBase.files,
front_side=TestEncryptedPassportElementBase.front_side,
reverse_side=TestEncryptedPassportElementBase.reverse_side,
selfie=TestEncryptedPassportElementBase.selfie,
data=EncryptedPassportElementTestBase.data,
phone_number=EncryptedPassportElementTestBase.phone_number,
email=EncryptedPassportElementTestBase.email,
files=EncryptedPassportElementTestBase.files,
front_side=EncryptedPassportElementTestBase.front_side,
reverse_side=EncryptedPassportElementTestBase.reverse_side,
selfie=EncryptedPassportElementTestBase.selfie,
)
class TestEncryptedPassportElementBase:
class EncryptedPassportElementTestBase:
type_ = "type"
hash = "this is a hash"
data = "data"
@@ -50,7 +50,7 @@ class TestEncryptedPassportElementBase:
selfie = PassportFile("file_id", 50, 0, 25)
class TestEncryptedPassportElementWithoutRequest(TestEncryptedPassportElementBase):
class TestEncryptedPassportElementWithoutRequest(EncryptedPassportElementTestBase):
def test_slot_behaviour(self, encrypted_passport_element):
inst = encrypted_passport_element
for attr in inst.__slots__:
+2 -2
View File
@@ -28,7 +28,7 @@ with the TEST_WITH_OPT_DEPS environment variable set to False in addition to the
"""
import pytest
from telegram import _bot as bot
import telegram
from telegram._passport import credentials
from tests.auxil.envvars import TEST_WITH_OPT_DEPS
@@ -39,7 +39,7 @@ from tests.auxil.envvars import TEST_WITH_OPT_DEPS
class TestNoPassportWithoutRequest:
def test_bot_init(self, bot_info):
with pytest.raises(RuntimeError, match="passport"):
bot.Bot(bot_info["token"], private_key=1, private_key_password=2)
telegram.Bot(bot_info["token"], private_key=1, private_key_password=2)
def test_credentials_decrypt(self):
with pytest.raises(RuntimeError, match="passport"):
+24 -22
View File
@@ -220,7 +220,7 @@ def passport_data(bot):
return PassportData.de_json(RAW_PASSPORT_DATA, bot=bot)
class TestPassportBase:
class PassportTestBase:
driver_license_selfie_file_id = "DgADBAADEQQAAkopgFNr6oi-wISRtAI"
driver_license_selfie_file_unique_id = "d4e390cca57b4da5a65322b304762a12"
driver_license_front_side_file_id = "DgADBAADxwMAApnQgVPK2-ckL2eXVAI"
@@ -243,7 +243,7 @@ class TestPassportBase:
driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E="
class TestPassportWithoutRequest(TestPassportBase):
class TestPassportWithoutRequest(PassportTestBase):
def test_slot_behaviour(self, passport_data):
inst = passport_data
for attr in inst.__slots__:
@@ -390,8 +390,8 @@ class TestPassportWithoutRequest(TestPassportBase):
assert email.type == "email"
assert email.email == "fb3e3i47zt@dispostable.com"
def test_de_json_and_to_dict(self, bot):
passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot)
def test_de_json_and_to_dict(self, offline_bot):
passport_data = PassportData.de_json(RAW_PASSPORT_DATA, offline_bot)
assert passport_data.api_kwargs == {}
assert passport_data.to_dict() == RAW_PASSPORT_DATA
@@ -414,14 +414,14 @@ class TestPassportWithoutRequest(TestPassportBase):
assert a != c
assert hash(a) != hash(c)
def test_bot_init_invalid_key(self, bot):
def test_bot_init_invalid_key(self, offline_bot):
with pytest.raises(TypeError):
Bot(bot.token, private_key="Invalid key!")
Bot(offline_bot.token, private_key="Invalid key!")
with pytest.raises(ValueError, match="Could not deserialize key data"):
Bot(bot.token, private_key=b"Invalid key!")
Bot(offline_bot.token, private_key=b"Invalid key!")
def test_all_types(self, passport_data, bot, all_passport_data):
def test_all_types(self, passport_data, offline_bot, all_passport_data):
credentials = passport_data.decrypted_credentials.to_dict()
# Copy credentials from other types to all types so we can decrypt everything
@@ -446,46 +446,46 @@ class TestPassportWithoutRequest(TestPassportBase):
# Replaced below
"credentials": {"data": "data", "hash": "hash", "secret": "secret"},
},
bot=bot,
bot=offline_bot,
)
assert new.api_kwargs == {}
new.credentials._decrypted_data = Credentials.de_json(credentials, bot)
new.credentials._decrypted_data = Credentials.de_json(credentials, offline_bot)
assert new.credentials.api_kwargs == {}
assert isinstance(new, PassportData)
assert new.decrypted_data
async def test_passport_data_okay_with_non_crypto_bot(self, bot):
async with make_bot(token=bot.token) as b:
async def test_passport_data_okay_with_non_crypto_bot(self, offline_bot):
async with make_bot(token=offline_bot.token) as b:
assert PassportData.de_json(RAW_PASSPORT_DATA, bot=b)
def test_wrong_hash(self, bot):
def test_wrong_hash(self, offline_bot):
data = deepcopy(RAW_PASSPORT_DATA)
data["credentials"]["hash"] = "bm90Y29ycmVjdGhhc2g=" # Not correct hash
passport_data = PassportData.de_json(data, bot=bot)
passport_data = PassportData.de_json(data, bot=offline_bot)
with pytest.raises(PassportDecryptionError):
assert passport_data.decrypted_data
async def test_wrong_key(self, bot):
async def test_wrong_key(self, offline_bot):
short_key = (
b"-----BEGIN RSA PRIVATE"
b" KEY-----\r\nMIIBOQIBAAJBAKU+OZ2jJm7sCA/ec4gngNZhXYPu+DZ/TAwSMl0W7vAPXAsLplBk\r\nO8l6IBHx8N0ZC4Bc65mO3b2G8YAzqndyqH8CAwEAAQJAWOx3jQFzeVXDsOaBPdAk\r\nYTncXVeIc6tlfUl9mOLyinSbRNCy1XicOiOZFgH1rRKOGIC1235QmqxFvdecySoY\r\nwQIhAOFeGgeX9CrEPuSsd9+kqUcA2avCwqdQgSdy2qggRFyJAiEAu7QHT8JQSkHU\r\nDELfzrzc24AhjyG0z1DpGZArM8COascCIDK42SboXj3Z2UXiQ0CEcMzYNiVgOisq\r\nBUd5pBi+2mPxAiAM5Z7G/Sv1HjbKrOGh29o0/sXPhtpckEuj5QMC6E0gywIgFY6S\r\nNjwrAA+cMmsgY0O2fAzEKkDc5YiFsiXaGaSS4eA=\r\n-----END"
b" RSA PRIVATE KEY-----"
)
async with make_bot(token=bot.token, private_key=short_key) as b:
async with make_bot(token=offline_bot.token, private_key=short_key) as b:
passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot=b)
with pytest.raises(PassportDecryptionError):
assert passport_data.decrypted_data
async with make_bot(token=bot.token, private_key=short_key) as b:
async with make_bot(token=offline_bot.token, private_key=short_key) as b:
passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot=b)
with pytest.raises(PassportDecryptionError):
assert passport_data.decrypted_data
async def test_mocked_download_passport_file(self, passport_data, monkeypatch):
# The files are not coming from our test bot, therefore the file id is invalid/wrong
# when coming from this bot, so we monkeypatch the call, to make sure that Bot.get_file
# The files are not coming from our test offline_bot, therefore the file id is invalid/wrong
# when coming from this offline_bot, so we monkeypatch the call, to make sure that Bot.get_file
# at least gets called
# TODO: Actually download a passport file in a test
selfie = passport_data.decrypted_data[1].selfie
@@ -501,7 +501,9 @@ class TestPassportWithoutRequest(TestPassportBase):
assert file._credentials.file_hash == self.driver_license_selfie_credentials_file_hash
assert file._credentials.secret == self.driver_license_selfie_credentials_secret
async def test_mocked_set_passport_data_errors(self, monkeypatch, bot, chat_id, passport_data):
async def test_mocked_set_passport_data_errors(
self, monkeypatch, offline_bot, chat_id, passport_data
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.parameters
return (
@@ -514,8 +516,8 @@ class TestPassportWithoutRequest(TestPassportBase):
== passport_data.decrypted_credentials.secure_data.driver_license.data.data_hash
)
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.set_passport_data_errors(
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
message = await offline_bot.set_passport_data_errors(
chat_id,
[
PassportElementErrorSelfie(
@@ -25,14 +25,14 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def passport_element_error_data_field():
return PassportElementErrorDataField(
TestPassportElementErrorDataFieldBase.type_,
TestPassportElementErrorDataFieldBase.field_name,
TestPassportElementErrorDataFieldBase.data_hash,
TestPassportElementErrorDataFieldBase.message,
PassportElementErrorDataFieldTestBase.type_,
PassportElementErrorDataFieldTestBase.field_name,
PassportElementErrorDataFieldTestBase.data_hash,
PassportElementErrorDataFieldTestBase.message,
)
class TestPassportElementErrorDataFieldBase:
class PassportElementErrorDataFieldTestBase:
source = "data"
type_ = "test_type"
field_name = "test_field"
@@ -40,7 +40,7 @@ class TestPassportElementErrorDataFieldBase:
message = "Error message"
class TestPassportElementErrorDataFieldWithoutRequest(TestPassportElementErrorDataFieldBase):
class TestPassportElementErrorDataFieldWithoutRequest(PassportElementErrorDataFieldTestBase):
def test_slot_behaviour(self, passport_element_error_data_field):
inst = passport_element_error_data_field
for attr in inst.__slots__:
@@ -25,20 +25,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def passport_element_error_file():
return PassportElementErrorFile(
TestPassportElementErrorFileBase.type_,
TestPassportElementErrorFileBase.file_hash,
TestPassportElementErrorFileBase.message,
PassportElementErrorFileTestBase.type_,
PassportElementErrorFileTestBase.file_hash,
PassportElementErrorFileTestBase.message,
)
class TestPassportElementErrorFileBase:
class PassportElementErrorFileTestBase:
source = "file"
type_ = "test_type"
file_hash = "file_hash"
message = "Error message"
class TestPassportElementErrorFileWithoutRequest(TestPassportElementErrorFileBase):
class TestPassportElementErrorFileWithoutRequest(PassportElementErrorFileTestBase):
def test_slot_behaviour(self, passport_element_error_file):
inst = passport_element_error_file
for attr in inst.__slots__:
@@ -26,20 +26,20 @@ from tests.auxil.slots import mro_slots
@pytest.fixture(scope="module")
def passport_element_error_files():
return PassportElementErrorFiles(
TestPassportElementErrorFilesBase.type_,
TestPassportElementErrorFilesBase.file_hashes,
TestPassportElementErrorFilesBase.message,
PassportElementErrorFilesTestBase.type_,
PassportElementErrorFilesTestBase.file_hashes,
PassportElementErrorFilesTestBase.message,
)
class TestPassportElementErrorFilesBase:
class PassportElementErrorFilesTestBase:
source = "files"
type_ = "test_type"
file_hashes = ["hash1", "hash2"]
message = "Error message"
class TestPassportElementErrorFilesWithoutRequest(TestPassportElementErrorFilesBase):
class TestPassportElementErrorFilesWithoutRequest(PassportElementErrorFilesTestBase):
def test_slot_behaviour(self, passport_element_error_files):
inst = passport_element_error_files
for attr in inst.__slots__:

Some files were not shown because too many files have changed in this diff Show More