Merge branch 'dev-2.x' into add_rename_argument_decorator

This commit is contained in:
Egor 2019-08-15 16:48:35 +03:00 committed by GitHub
commit 95f83d7cef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 876 additions and 835 deletions

View file

@ -97,15 +97,13 @@ class I18nMiddleware(BaseMiddleware):
if locale not in self.locales:
if n is 1:
return singular
else:
return plural
return plural
translator = self.locales[locale]
if plural is None:
return translator.gettext(singular)
else:
return translator.ngettext(singular, plural, n)
return translator.ngettext(singular, plural, n)
def lazy_gettext(self, singular, plural=None, n=1, locale=None, enable_cache=True) -> LazyProxy:
"""

View file

@ -85,39 +85,56 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
filters_factory.bind(StateFilter, exclude_event_handlers=[
self.errors_handlers,
self.poll_handlers
self.poll_handlers,
])
filters_factory.bind(ContentTypeFilter, event_handlers=[
self.message_handlers, self.edited_message_handlers,
self.channel_post_handlers, self.edited_channel_post_handlers,
self.message_handlers,
self.edited_message_handlers,
self.channel_post_handlers,
self.edited_channel_post_handlers,
]),
filters_factory.bind(Command, event_handlers=[
self.message_handlers, self.edited_message_handlers
self.message_handlers,
self.edited_message_handlers
])
filters_factory.bind(Text, event_handlers=[
self.message_handlers, self.edited_message_handlers,
self.channel_post_handlers, self.edited_channel_post_handlers,
self.callback_query_handlers, self.poll_handlers, self.inline_query_handlers
self.message_handlers,
self.edited_message_handlers,
self.channel_post_handlers,
self.edited_channel_post_handlers,
self.callback_query_handlers,
self.poll_handlers,
self.inline_query_handlers,
])
filters_factory.bind(HashTag, event_handlers=[
self.message_handlers, self.edited_message_handlers,
self.channel_post_handlers, self.edited_channel_post_handlers
self.message_handlers,
self.edited_message_handlers,
self.channel_post_handlers,
self.edited_channel_post_handlers,
])
filters_factory.bind(Regexp, event_handlers=[
self.message_handlers, self.edited_message_handlers,
self.channel_post_handlers, self.edited_channel_post_handlers,
self.callback_query_handlers, self.poll_handlers, self.inline_query_handlers
self.message_handlers,
self.edited_message_handlers,
self.channel_post_handlers,
self.edited_channel_post_handlers,
self.callback_query_handlers,
self.poll_handlers,
self.inline_query_handlers,
])
filters_factory.bind(RegexpCommandsFilter, event_handlers=[
self.message_handlers, self.edited_message_handlers
self.message_handlers,
self.edited_message_handlers,
])
filters_factory.bind(ExceptionsFilter, event_handlers=[
self.errors_handlers
self.errors_handlers,
])
filters_factory.bind(IdFilter, event_handlers=[
self.message_handlers, self.edited_message_handlers,
self.channel_post_handlers, self.edited_channel_post_handlers,
self.callback_query_handlers, self.inline_query_handlers
self.message_handlers,
self.edited_message_handlers,
self.channel_post_handlers,
self.edited_channel_post_handlers,
self.callback_query_handlers,
self.inline_query_handlers,
])
def __del__(self):

View file

@ -27,5 +27,5 @@ __all__ = [
'get_filter_spec',
'get_filters_spec',
'execute_filter',
'check_filters'
'check_filters',
]

View file

@ -84,9 +84,9 @@ class Command(Filter):
if not ignore_mention and mention and (await message.bot.me).username.lower() != mention.lower():
return False
elif prefix not in prefixes:
if prefix not in prefixes:
return False
elif (command.lower() if ignore_case else command) not in commands:
if (command.lower() if ignore_case else command) not in commands:
return False
return {'command': Command.CommandObj(command=command, prefix=prefix, mention=mention)}
@ -149,7 +149,7 @@ class CommandStart(Command):
:param deep_link: string or compiled regular expression (by ``re.compile(...)``).
"""
super(CommandStart, self).__init__(['start'])
super().__init__(['start'])
self.deep_link = deep_link
async def check(self, message: types.Message):
@ -159,7 +159,7 @@ class CommandStart(Command):
:param message:
:return:
"""
check = await super(CommandStart, self).check(message)
check = await super().check(message)
if check and self.deep_link is not None:
if not isinstance(self.deep_link, re.Pattern):
@ -179,7 +179,7 @@ class CommandHelp(Command):
"""
def __init__(self):
super(CommandHelp, self).__init__(['help'])
super().__init__(['help'])
class CommandSettings(Command):
@ -188,7 +188,7 @@ class CommandSettings(Command):
"""
def __init__(self):
super(CommandSettings, self).__init__(['settings'])
super().__init__(['settings'])
class CommandPrivacy(Command):
@ -197,7 +197,7 @@ class CommandPrivacy(Command):
"""
def __init__(self):
super(CommandPrivacy, self).__init__(['privacy'])
super().__init__(['privacy'])
class Text(Filter):
@ -205,6 +205,13 @@ class Text(Filter):
Simple text filter
"""
_default_params = (
('text', 'equals'),
('text_contains', 'contains'),
('text_startswith', 'startswith'),
('text_endswith', 'endswith'),
)
def __init__(self,
equals: Optional[Union[str, LazyProxy, Iterable[Union[str, LazyProxy]]]] = None,
contains: Optional[Union[str, LazyProxy, Iterable[Union[str, LazyProxy]]]] = None,
@ -244,14 +251,9 @@ class Text(Filter):
@classmethod
def validate(cls, full_config: Dict[str, Any]):
if 'text' in full_config:
return {'equals': full_config.pop('text')}
elif 'text_contains' in full_config:
return {'contains': full_config.pop('text_contains')}
elif 'text_startswith' in full_config:
return {'startswith': full_config.pop('text_startswith')}
elif 'text_endswith' in full_config:
return {'endswith': full_config.pop('text_endswith')}
for param, key in cls._default_params:
if param in full_config:
return {key: full_config.pop(param)}
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery, Poll]):
if isinstance(obj, Message):
@ -269,19 +271,26 @@ class Text(Filter):
if self.ignore_case:
text = text.lower()
_pre_process_func = lambda s: str(s).lower()
else:
_pre_process_func = str
# now check
if self.equals is not None:
self.equals = list(map(lambda s: str(s).lower() if self.ignore_case else str(s), self.equals))
return text in self.equals
elif self.contains is not None:
self.contains = list(map(lambda s: str(s).lower() if self.ignore_case else str(s), self.contains))
return all(map(text.__contains__, self.contains))
elif self.startswith is not None:
self.startswith = list(map(lambda s: str(s).lower() if self.ignore_case else str(s), self.startswith))
return any(map(text.startswith, self.startswith))
elif self.endswith is not None:
self.endswith = list(map(lambda s: str(s).lower() if self.ignore_case else str(s), self.endswith))
return any(map(text.endswith, self.endswith))
equals = list(map(_pre_process_func, self.equals))
return text in equals
if self.contains is not None:
contains = list(map(_pre_process_func, self.contains))
return all(map(text.__contains__, contains))
if self.startswith is not None:
startswith = list(map(_pre_process_func, self.startswith))
return any(map(text.startswith, startswith))
if self.endswith is not None:
endswith = list(map(_pre_process_func, self.endswith))
return any(map(text.endswith, endswith))
return False
@ -556,9 +565,9 @@ class IdFilter(Filter):
if self.user_id and self.chat_id:
return user_id in self.user_id and chat_id in self.chat_id
elif self.user_id:
if self.user_id:
return user_id in self.user_id
elif self.chat_id:
if self.chat_id:
return chat_id in self.chat_id
return False

View file

@ -70,4 +70,4 @@ class FiltersFactory:
yield filter_
if full_config:
raise NameError('Invalid filter name(s): \'' + '\', '.join(full_config.keys()) + '\'')
raise NameError("Invalid filter name(s): '" + "', ".join(full_config.keys()) + "'")

View file

@ -82,7 +82,7 @@ class FilterRecord:
Filters record for factory
"""
def __init__(self, callback: typing.Callable,
def __init__(self, callback: typing.Union[typing.Callable, 'AbstractFilter'],
validator: typing.Optional[typing.Callable] = None,
event_handlers: typing.Optional[typing.Iterable[Handler]] = None,
exclude_event_handlers: typing.Optional[typing.Iterable[Handler]] = None):

View file

@ -25,17 +25,17 @@ class State:
@property
def state(self):
if self._state is None:
return None
elif self._state == '*':
if self._state is None or self._state == '*':
return self._state
elif self._group_name is None and self._group:
if self._group_name is None and self._group:
group = self._group.__full_group_name__
elif self._group_name:
group = self._group_name
else:
group = '@'
return f"{group}:{self._state}"
return f'{group}:{self._state}'
def set_parent(self, group):
if not issubclass(group, StatesGroup):
@ -73,7 +73,6 @@ class StatesGroupMeta(type):
elif inspect.isclass(prop) and issubclass(prop, StatesGroup):
childs.append(prop)
prop._parent = cls
# continue
cls._parent = None
cls._childs = tuple(childs)
@ -83,13 +82,13 @@ class StatesGroupMeta(type):
return cls
@property
def __group_name__(cls):
def __group_name__(cls) -> str:
return cls._group_name
@property
def __full_group_name__(cls):
def __full_group_name__(cls) -> str:
if cls._parent:
return cls._parent.__full_group_name__ + '.' + cls._group_name
return '.'.join((cls._parent.__full_group_name__, cls._group_name))
return cls._group_name
@property
@ -97,7 +96,7 @@ class StatesGroupMeta(type):
return cls._states
@property
def childs(cls):
def childs(cls) -> tuple:
return cls._childs
@property
@ -130,9 +129,9 @@ class StatesGroupMeta(type):
def __contains__(cls, item):
if isinstance(item, str):
return item in cls.all_states_names
elif isinstance(item, State):
if isinstance(item, State):
return item in cls.all_states
elif isinstance(item, StatesGroup):
if isinstance(item, StatesGroup):
return item in cls.all_childs
return False

View file

@ -1,7 +1,7 @@
import inspect
from contextvars import ContextVar
from dataclasses import dataclass
from typing import Optional, Iterable
from typing import Optional, Iterable, List
ctx_data = ContextVar('ctx_handler_data')
current_handler = ContextVar('current_handler')
@ -41,11 +41,10 @@ class Handler:
self.dispatcher = dispatcher
self.once = once
self.handlers = []
self.handlers: List[Handler.HandlerObj] = []
self.middleware_key = middleware_key
def register(self, handler, filters=None, index=None):
from .filters import get_filters_spec
"""
Register callback
@ -55,6 +54,8 @@ class Handler:
:param filters: list of filters
:param index: you can reorder handlers
"""
from .filters import get_filters_spec
spec = _get_spec(handler)
if filters and not isinstance(filters, (list, tuple, set)):

View file

@ -523,7 +523,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
'disable_web_page_preview': self.disable_web_page_preview,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
def write(self, *text, sep=' '):
@ -642,7 +642,7 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'caption': self.caption,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -704,7 +704,7 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'title': self.title,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -817,7 +817,7 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'caption': self.caption,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -871,7 +871,7 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'duration': self.duration,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -924,7 +924,7 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'length': self.length,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1050,7 +1050,7 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'longitude': self.longitude,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1109,7 +1109,7 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'foursquare_id': self.foursquare_id,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1160,7 +1160,7 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'last_name': self.last_name,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1220,7 +1220,7 @@ class KickChatMember(BaseResponse):
return {
'chat_id': self.chat_id,
'user_id': self.user_id,
'until_date': prepare_arg(self.until_date)
'until_date': prepare_arg(self.until_date),
}
@ -1608,7 +1608,7 @@ class EditMessageText(BaseResponse, ParseModeMixin, DisableWebPagePreviewMixin):
'text': self.text,
'parse_mode': self.parse_mode,
'disable_web_page_preview': self.disable_web_page_preview,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1649,7 +1649,7 @@ class EditMessageCaption(BaseResponse):
'message_id': self.message_id,
'inline_message_id': self.inline_message_id,
'caption': self.caption,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1685,7 +1685,7 @@ class EditMessageReplyMarkup(BaseResponse):
'chat_id': self.chat_id,
'message_id': self.message_id,
'inline_message_id': self.inline_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1756,7 +1756,7 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'sticker': self.sticker,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}
@ -1848,7 +1848,7 @@ class AddStickerToSet(BaseResponse):
'name': self.name,
'png_sticker': self.png_sticker,
'emojis': self.emojis,
'mask_position': prepare_arg(self.mask_position)
'mask_position': prepare_arg(self.mask_position),
}
@ -2177,5 +2177,5 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
'game_short_name': self.game_short_name,
'disable_notification': self.disable_notification,
'reply_to_message_id': self.reply_to_message_id,
'reply_markup': prepare_arg(self.reply_markup)
'reply_markup': prepare_arg(self.reply_markup),
}

View file

@ -94,60 +94,60 @@ class Message(base.TelegramObject):
def content_type(self):
if self.text:
return ContentType.TEXT
elif self.audio:
if self.audio:
return ContentType.AUDIO
elif self.animation:
if self.animation:
return ContentType.ANIMATION
elif self.document:
if self.document:
return ContentType.DOCUMENT
elif self.game:
if self.game:
return ContentType.GAME
elif self.photo:
if self.photo:
return ContentType.PHOTO
elif self.sticker:
if self.sticker:
return ContentType.STICKER
elif self.video:
if self.video:
return ContentType.VIDEO
elif self.video_note:
if self.video_note:
return ContentType.VIDEO_NOTE
elif self.voice:
if self.voice:
return ContentType.VOICE
elif self.contact:
if self.contact:
return ContentType.CONTACT
elif self.venue:
if self.venue:
return ContentType.VENUE
elif self.location:
if self.location:
return ContentType.LOCATION
elif self.new_chat_members:
if self.new_chat_members:
return ContentType.NEW_CHAT_MEMBERS
elif self.left_chat_member:
if self.left_chat_member:
return ContentType.LEFT_CHAT_MEMBER
elif self.invoice:
if self.invoice:
return ContentType.INVOICE
elif self.successful_payment:
if self.successful_payment:
return ContentType.SUCCESSFUL_PAYMENT
elif self.connected_website:
if self.connected_website:
return ContentType.CONNECTED_WEBSITE
elif self.migrate_from_chat_id:
if self.migrate_from_chat_id:
return ContentType.MIGRATE_FROM_CHAT_ID
elif self.migrate_to_chat_id:
if self.migrate_to_chat_id:
return ContentType.MIGRATE_TO_CHAT_ID
elif self.pinned_message:
if self.pinned_message:
return ContentType.PINNED_MESSAGE
elif self.new_chat_title:
if self.new_chat_title:
return ContentType.NEW_CHAT_TITLE
elif self.new_chat_photo:
if self.new_chat_photo:
return ContentType.NEW_CHAT_PHOTO
elif self.delete_chat_photo:
if self.delete_chat_photo:
return ContentType.DELETE_CHAT_PHOTO
elif self.group_chat_created:
if self.group_chat_created:
return ContentType.GROUP_CHAT_CREATED
elif self.passport_data:
if self.passport_data:
return ContentType.PASSPORT_DATA
elif self.poll:
if self.poll:
return ContentType.POLL
else:
return ContentType.UNKNOWN
return ContentType.UNKNOWN
def is_command(self):
"""

View file

@ -49,30 +49,24 @@ class MessageEntity(base.TelegramObject):
entity_text = self.get_text(text)
if self.type == MessageEntityType.BOLD:
if as_html:
return markdown.hbold(entity_text)
return markdown.bold(entity_text)
elif self.type == MessageEntityType.ITALIC:
if as_html:
return markdown.hitalic(entity_text)
return markdown.italic(entity_text)
elif self.type == MessageEntityType.PRE:
if as_html:
return markdown.hpre(entity_text)
return markdown.pre(entity_text)
elif self.type == MessageEntityType.CODE:
if as_html:
return markdown.hcode(entity_text)
return markdown.code(entity_text)
elif self.type == MessageEntityType.URL:
if as_html:
return markdown.hlink(entity_text, entity_text)
return markdown.link(entity_text, entity_text)
elif self.type == MessageEntityType.TEXT_LINK:
if as_html:
return markdown.hlink(entity_text, self.url)
return markdown.link(entity_text, self.url)
elif self.type == MessageEntityType.TEXT_MENTION and self.user:
method = markdown.hbold if as_html else markdown.bold
return method(entity_text)
if self.type == MessageEntityType.ITALIC:
method = markdown.hitalic if as_html else markdown.italic
return method(entity_text)
if self.type == MessageEntityType.PRE:
method = markdown.hpre if as_html else markdown.pre
return method(entity_text)
if self.type == MessageEntityType.CODE:
method = markdown.hcode if as_html else markdown.code
return method(entity_text)
if self.type == MessageEntityType.URL:
method = markdown.hlink if as_html else markdown.link
return method(entity_text, entity_text)
if self.type == MessageEntityType.TEXT_LINK:
method = markdown.hlink if as_html else markdown.link
return method(entity_text, self.url)
if self.type == MessageEntityType.TEXT_MENTION and self.user:
return self.user.get_mention(entity_text)
return entity_text

View file

@ -1,5 +1,7 @@
from __future__ import annotations
from typing import Optional
import babel
from . import base
@ -45,7 +47,7 @@ class User(base.TelegramObject):
return self.full_name
@property
def locale(self) -> babel.core.Locale or None:
def locale(self) -> Optional[babel.core.Locale]:
"""
Get user's locale

View file

@ -28,13 +28,13 @@ class CallbackData:
def __init__(self, prefix, *parts, sep=':'):
if not isinstance(prefix, str):
raise TypeError(f"Prefix must be instance of str not {type(prefix).__name__}")
elif not prefix:
raise ValueError('Prefix can\'t be empty')
elif sep in prefix:
raise ValueError(f"Separator '{sep}' can't be used in prefix")
elif not parts:
raise TypeError('Parts is not passed!')
raise TypeError(f'Prefix must be instance of str not {type(prefix).__name__}')
if not prefix:
raise ValueError("Prefix can't be empty")
if sep in prefix:
raise ValueError(f"Separator {sep!r} can't be used in prefix")
if not parts:
raise TypeError('Parts were not passed!')
self.prefix = prefix
self.sep = sep
@ -59,20 +59,20 @@ class CallbackData:
if args:
value = args.pop(0)
else:
raise ValueError(f"Value for '{part}' is not passed!")
raise ValueError(f'Value for {part!r} was not passed!')
if value is not None and not isinstance(value, str):
value = str(value)
if not value:
raise ValueError(f"Value for part {part} can't be empty!'")
elif self.sep in value:
raise ValueError(f"Symbol defined as separator can't be used in values of parts")
raise ValueError(f"Value for part {part!r} can't be empty!'")
if self.sep in value:
raise ValueError(f"Symbol {self.sep!r} is defined as the separator and can't be used in parts' values")
data.append(value)
if args or kwargs:
raise TypeError('Too many arguments is passed!')
raise TypeError('Too many arguments were passed!')
callback_data = self.sep.join(data)
if len(callback_data) > 64:
@ -106,30 +106,31 @@ class CallbackData:
"""
for key in config.keys():
if key not in self._part_names:
raise ValueError(f"Invalid field name '{key}'")
raise ValueError(f'Invalid field name {key!r}')
return CallbackDataFilter(self, config)
class CallbackDataFilter(Filter):
def __init__(self, factory: CallbackData, config: typing.Dict[str, str]):
self.config = config
self.factory = factory
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]):
raise ValueError('That filter can\'t be used in filters factory!')
raise ValueError("That filter can't be used in filters factory!")
async def check(self, query: types.CallbackQuery):
try:
data = self.factory.parse(query.data)
except ValueError:
return False
else:
for key, value in self.config.items():
if isinstance(value, (list, tuple, set)):
if data.get(key) not in value:
return False
else:
if value != data.get(key):
return False
return {'callback_data': data}
for key, value in self.config.items():
if isinstance(value, (list, tuple, set, frozenset)):
if data.get(key) not in value:
return False
else:
if data.get(key) != value:
return False
return {'callback_data': data}

View file

@ -15,7 +15,7 @@ from ..dispatcher.webhook import BOT_DISPATCHER_KEY, DEFAULT_ROUTE_NAME, Webhook
APP_EXECUTOR_KEY = 'APP_EXECUTOR'
def _setup_callbacks(executor, on_startup=None, on_shutdown=None):
def _setup_callbacks(executor: 'Executor', on_startup=None, on_shutdown=None):
if on_startup is not None:
executor.on_startup(on_startup)
if on_shutdown is not None:
@ -23,7 +23,7 @@ def _setup_callbacks(executor, on_startup=None, on_shutdown=None):
def start_polling(dispatcher, *, loop=None, skip_updates=False, reset_webhook=True,
on_startup=None, on_shutdown=None, timeout=20, fast=True):
on_startup=None, on_shutdown=None, timeout=20, relax=0.1, fast=True):
"""
Start bot in long-polling mode
@ -38,7 +38,7 @@ def start_polling(dispatcher, *, loop=None, skip_updates=False, reset_webhook=Tr
executor = Executor(dispatcher, skip_updates=skip_updates, loop=loop)
_setup_callbacks(executor, on_startup, on_shutdown)
executor.start_polling(reset_webhook=reset_webhook, timeout=timeout, fast=fast)
executor.start_polling(reset_webhook=reset_webhook, timeout=timeout, relax=relax, fast=fast)
def set_webhook(dispatcher: Dispatcher, webhook_path: str, *, loop: Optional[asyncio.AbstractEventLoop] = None,
@ -291,7 +291,7 @@ class Executor:
self.set_webhook(webhook_path=webhook_path, request_handler=request_handler, route_name=route_name)
self.run_app(**kwargs)
def start_polling(self, reset_webhook=None, timeout=20, fast=True):
def start_polling(self, reset_webhook=None, timeout=20, relax=0.1, fast=True):
"""
Start bot in long-polling mode
@ -303,7 +303,8 @@ class Executor:
try:
loop.run_until_complete(self._startup_polling())
loop.create_task(self.dispatcher.start_polling(reset_webhook=reset_webhook, timeout=timeout, fast=fast))
loop.create_task(self.dispatcher.start_polling(reset_webhook=reset_webhook, timeout=timeout,
relax=relax, fast=fast))
loop.run_forever()
except (KeyboardInterrupt, SystemExit):
# loop.stop()
@ -339,7 +340,7 @@ class Executor:
async def _skip_updates(self):
await self.dispatcher.reset_webhook(True)
await self.dispatcher.skip_updates()
log.warning(f"Updates are skipped successfully.")
log.warning(f'Updates were skipped successfully.')
async def _welcome(self):
user = await self.dispatcher.bot.me

View file

@ -120,15 +120,15 @@ class HelperMode(Helper):
"""
if mode == cls.SCREAMING_SNAKE_CASE:
return cls._screaming_snake_case(text)
elif mode == cls.snake_case:
if mode == cls.snake_case:
return cls._snake_case(text)
elif mode == cls.lowercase:
if mode == cls.lowercase:
return cls._snake_case(text).replace('_', '')
elif mode == cls.lowerCamelCase:
if mode == cls.lowerCamelCase:
return cls._camel_case(text)
elif mode == cls.CamelCase:
if mode == cls.CamelCase:
return cls._camel_case(text, True)
elif callable(mode):
if callable(mode):
return mode(text)
return text

View file

@ -31,7 +31,7 @@ T = TypeVar('T')
class ContextInstanceMixin:
def __init_subclass__(cls, **kwargs):
cls.__context_instance = contextvars.ContextVar('instance_' + cls.__name__)
cls.__context_instance = contextvars.ContextVar(f'instance_{cls.__name__}')
return cls
@classmethod
@ -43,5 +43,5 @@ class ContextInstanceMixin:
@classmethod
def set_current(cls: Type[T], value: T):
if not isinstance(value, cls):
raise TypeError(f"Value should be instance of '{cls.__name__}' not '{type(value).__name__}'")
raise TypeError(f'Value should be instance of {cls.__name__!r} not {type(value).__name__!r}')
cls.__context_instance.set(value)

View file

@ -52,14 +52,14 @@ def prepare_arg(value):
"""
if value is None:
return value
elif isinstance(value, (list, dict)) or hasattr(value, 'to_python'):
if isinstance(value, (list, dict)) or hasattr(value, 'to_python'):
return json.dumps(_normalize(value))
elif isinstance(value, datetime.timedelta):
if isinstance(value, datetime.timedelta):
now = datetime.datetime.now()
return int((now + value).timestamp())
elif isinstance(value, datetime.datetime):
if isinstance(value, datetime.datetime):
return round(value.timestamp())
elif isinstance(value, LazyProxy):
if isinstance(value, LazyProxy):
return str(value)
return value