mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-27 03:34:37 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 392d4e1a9c | |||
| 9cb34af65a | |||
| e9cb6675ca | |||
| 982f6707e1 | |||
| d55d981e22 | |||
| f20953f7a9 | |||
| e18220be10 |
+20
@@ -2,6 +2,26 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 12.5.1
|
||||
==============
|
||||
*Released 2020-03-30*
|
||||
|
||||
**Minor changes, doc fixes or bug fixes:**
|
||||
|
||||
- Add missing docs for `PollHandler` and `PollAnswerHandler` (`#1853`_)
|
||||
- Fix wording in `Filters` docs (`#1855`_)
|
||||
- Reorder tests to make them more stable (`#1835`_)
|
||||
- Make `ConversationHandler` attributes immutable (`#1756`_)
|
||||
- Make `PrefixHandler` attributes `command` and `prefix` editable (`#1636`_)
|
||||
- Fix UTC as default `tzinfo` for `Job` (`#1696`_)
|
||||
|
||||
.. _`#1853`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1853
|
||||
.. _`#1855`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1855
|
||||
.. _`#1835`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1835
|
||||
.. _`#1756`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1756
|
||||
.. _`#1636`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1636
|
||||
.. _`#1696`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1696
|
||||
|
||||
Version 12.5
|
||||
============
|
||||
*Released 2020-03-29*
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ author = u'Leandro Toledo'
|
||||
# The short X.Y version.
|
||||
version = '12.5' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '12.5' # telegram.__version__
|
||||
release = '12.5.1' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ext.PollAnswerHandler
|
||||
==============================
|
||||
|
||||
.. autoclass:: telegram.ext.PollAnswerHandler
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ext.PollHandler
|
||||
========================
|
||||
|
||||
.. autoclass:: telegram.ext.PollHandler
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -26,6 +26,8 @@ Handlers
|
||||
telegram.ext.commandhandler
|
||||
telegram.ext.inlinequeryhandler
|
||||
telegram.ext.messagehandler
|
||||
telegram.ext.pollanswerhandler
|
||||
telegram.ext.pollhandler
|
||||
telegram.ext.precheckoutqueryhandler
|
||||
telegram.ext.prefixhandler
|
||||
telegram.ext.regexhandler
|
||||
|
||||
@@ -302,6 +302,10 @@ class PrefixHandler(CommandHandler):
|
||||
pass_user_data=False,
|
||||
pass_chat_data=False):
|
||||
|
||||
self._prefix = list()
|
||||
self._command = list()
|
||||
self._commands = list()
|
||||
|
||||
super(PrefixHandler, self).__init__(
|
||||
'nocommand', callback, filters=filters, allow_edited=None, pass_args=pass_args,
|
||||
pass_update_queue=pass_update_queue,
|
||||
@@ -309,15 +313,36 @@ class PrefixHandler(CommandHandler):
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data)
|
||||
|
||||
self.prefix = prefix
|
||||
self.command = command
|
||||
self._build_commands()
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self._prefix
|
||||
|
||||
@prefix.setter
|
||||
def prefix(self, prefix):
|
||||
if isinstance(prefix, string_types):
|
||||
self.prefix = [prefix.lower()]
|
||||
self._prefix = [prefix.lower()]
|
||||
else:
|
||||
self.prefix = prefix
|
||||
self._prefix = prefix
|
||||
self._build_commands()
|
||||
|
||||
@property
|
||||
def command(self):
|
||||
return self._command
|
||||
|
||||
@command.setter
|
||||
def command(self, command):
|
||||
if isinstance(command, string_types):
|
||||
self.command = [command.lower()]
|
||||
self._command = [command.lower()]
|
||||
else:
|
||||
self.command = command
|
||||
self.command = [x.lower() + y.lower() for x in self.prefix for y in self.command]
|
||||
self._command = command
|
||||
self._build_commands()
|
||||
|
||||
def _build_commands(self):
|
||||
self._commands = [x.lower() + y.lower() for x in self.prefix for y in self.command]
|
||||
|
||||
def check_update(self, update):
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
@@ -334,7 +359,7 @@ class PrefixHandler(CommandHandler):
|
||||
|
||||
if message.text:
|
||||
text_list = message.text.split()
|
||||
if text_list[0].lower() not in self.command:
|
||||
if text_list[0].lower() not in self._commands:
|
||||
return None
|
||||
filter_result = self.filters(update)
|
||||
if filter_result:
|
||||
|
||||
@@ -165,23 +165,23 @@ class ConversationHandler(Handler):
|
||||
persistent=False,
|
||||
map_to_parent=None):
|
||||
|
||||
self.entry_points = entry_points
|
||||
self.states = states
|
||||
self.fallbacks = fallbacks
|
||||
self._entry_points = entry_points
|
||||
self._states = states
|
||||
self._fallbacks = fallbacks
|
||||
|
||||
self.allow_reentry = allow_reentry
|
||||
self.per_user = per_user
|
||||
self.per_chat = per_chat
|
||||
self.per_message = per_message
|
||||
self.conversation_timeout = conversation_timeout
|
||||
self.name = name
|
||||
self._allow_reentry = allow_reentry
|
||||
self._per_user = per_user
|
||||
self._per_chat = per_chat
|
||||
self._per_message = per_message
|
||||
self._conversation_timeout = conversation_timeout
|
||||
self._name = name
|
||||
if persistent and not self.name:
|
||||
raise ValueError("Conversations can't be persistent when handler is unnamed.")
|
||||
self.persistent = persistent
|
||||
self._persistence = None
|
||||
""":obj:`telegram.ext.BasePersistance`: The persistence used to store conversations.
|
||||
Set by dispatcher"""
|
||||
self.map_to_parent = map_to_parent
|
||||
self._map_to_parent = map_to_parent
|
||||
|
||||
self.timeout_jobs = dict()
|
||||
self._timeout_jobs_lock = Lock()
|
||||
@@ -225,6 +225,87 @@ class ConversationHandler(Handler):
|
||||
"since inline queries have no chat context.")
|
||||
break
|
||||
|
||||
@property
|
||||
def entry_points(self):
|
||||
return self._entry_points
|
||||
|
||||
@entry_points.setter
|
||||
def entry_points(self, value):
|
||||
raise ValueError('You can not assign a new value to entry_points after initialization.')
|
||||
|
||||
@property
|
||||
def states(self):
|
||||
return self._states
|
||||
|
||||
@states.setter
|
||||
def states(self, value):
|
||||
raise ValueError('You can not assign a new value to states after initialization.')
|
||||
|
||||
@property
|
||||
def fallbacks(self):
|
||||
return self._fallbacks
|
||||
|
||||
@fallbacks.setter
|
||||
def fallbacks(self, value):
|
||||
raise ValueError('You can not assign a new value to fallbacks after initialization.')
|
||||
|
||||
@property
|
||||
def allow_reentry(self):
|
||||
return self._allow_reentry
|
||||
|
||||
@allow_reentry.setter
|
||||
def allow_reentry(self, value):
|
||||
raise ValueError('You can not assign a new value to allow_reentry after initialization.')
|
||||
|
||||
@property
|
||||
def per_user(self):
|
||||
return self._per_user
|
||||
|
||||
@per_user.setter
|
||||
def per_user(self, value):
|
||||
raise ValueError('You can not assign a new value to per_user after initialization.')
|
||||
|
||||
@property
|
||||
def per_chat(self):
|
||||
return self._per_chat
|
||||
|
||||
@per_chat.setter
|
||||
def per_chat(self, value):
|
||||
raise ValueError('You can not assign a new value to per_chat after initialization.')
|
||||
|
||||
@property
|
||||
def per_message(self):
|
||||
return self._per_message
|
||||
|
||||
@per_message.setter
|
||||
def per_message(self, value):
|
||||
raise ValueError('You can not assign a new value to per_message after initialization.')
|
||||
|
||||
@property
|
||||
def conversation_timeout(self):
|
||||
return self._conversation_timeout
|
||||
|
||||
@conversation_timeout.setter
|
||||
def conversation_timeout(self, value):
|
||||
raise ValueError('You can not assign a new value to conversation_timeout after '
|
||||
'initialization.')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
raise ValueError('You can not assign a new value to name after initialization.')
|
||||
|
||||
@property
|
||||
def map_to_parent(self):
|
||||
return self._map_to_parent
|
||||
|
||||
@map_to_parent.setter
|
||||
def map_to_parent(self, value):
|
||||
raise ValueError('You can not assign a new value to map_to_parent after initialization.')
|
||||
|
||||
@property
|
||||
def persistence(self):
|
||||
return self._persistence
|
||||
|
||||
@@ -50,7 +50,7 @@ class BaseFilter(object):
|
||||
>>> Filters.text & (~ Filters.forwarded)
|
||||
|
||||
Note:
|
||||
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
|
||||
Filters use the same short circuiting logic as python's `and`, `or` and `not`.
|
||||
This means that for example:
|
||||
|
||||
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
|
||||
@@ -368,7 +368,7 @@ class Filters(object):
|
||||
if you need to specify flags on your pattern.
|
||||
|
||||
Note:
|
||||
Filters use the same short circuiting logic that pythons `and`, `or` and `not`.
|
||||
Filters use the same short circuiting logic as python's `and`, `or` and `not`.
|
||||
This means that for example:
|
||||
|
||||
>>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)')
|
||||
|
||||
@@ -285,7 +285,7 @@ class JobQueue(object):
|
||||
if job.enabled:
|
||||
try:
|
||||
current_week_day = datetime.datetime.now(job.tzinfo).date().weekday()
|
||||
if any(day == current_week_day for day in job.days):
|
||||
if current_week_day in job.days:
|
||||
self.logger.debug('Running job %s', job.name)
|
||||
job.run(self._dispatcher)
|
||||
|
||||
@@ -400,7 +400,7 @@ class Job(object):
|
||||
days=Days.EVERY_DAY,
|
||||
name=None,
|
||||
job_queue=None,
|
||||
tzinfo=_UTC):
|
||||
tzinfo=None):
|
||||
|
||||
self.callback = callback
|
||||
self.context = context
|
||||
@@ -413,7 +413,7 @@ class Job(object):
|
||||
|
||||
self._days = None
|
||||
self.days = days
|
||||
self.tzinfo = tzinfo
|
||||
self.tzinfo = tzinfo or _UTC
|
||||
|
||||
self._job_queue = weakref.proxy(job_queue) if job_queue is not None else None
|
||||
|
||||
|
||||
+1
-1
@@ -17,4 +17,4 @@
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
__version__ = '12.5'
|
||||
__version__ = '12.5.1'
|
||||
|
||||
+17
-17
@@ -208,18 +208,6 @@ class TestBot(object):
|
||||
assert message_quiz.poll.type == Poll.QUIZ
|
||||
assert message_quiz.poll.is_closed
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_game(self, bot, chat_id):
|
||||
game_short_name = 'test_game'
|
||||
message = bot.send_game(chat_id, game_short_name)
|
||||
|
||||
assert message.game
|
||||
assert message.game.description == ('A no-op test game, for python-telegram-bot '
|
||||
'bot framework testing.')
|
||||
assert message.game.animation.file_id != ''
|
||||
assert message.game.photo[0].file_size == 851
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_chat_action(self, bot, chat_id):
|
||||
@@ -596,6 +584,18 @@ class TestBot(object):
|
||||
def test_delete_chat_sticker_set(self):
|
||||
pass
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_game(self, bot, chat_id):
|
||||
game_short_name = 'test_game'
|
||||
message = bot.send_game(chat_id, game_short_name)
|
||||
|
||||
assert message.game
|
||||
assert message.game.description == ('A no-op test game, for python-telegram-bot '
|
||||
'bot framework testing.')
|
||||
assert message.game.animation.file_id != ''
|
||||
assert message.game.photo[0].file_size == 851
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_game_score_1(self, bot, chat_id):
|
||||
@@ -795,17 +795,17 @@ class TestBot(object):
|
||||
assert isinstance(invite_link, string_types)
|
||||
assert invite_link != ''
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_delete_chat_photo(self, bot, channel_id):
|
||||
assert bot.delete_chat_photo(channel_id)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_chat_photo(self, bot, channel_id):
|
||||
with open('tests/data/telegram_test_channel.jpg', 'rb') as f:
|
||||
assert bot.set_chat_photo(channel_id, f)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_delete_chat_photo(self, bot, channel_id):
|
||||
assert bot.delete_chat_photo(channel_id)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_chat_title(self, bot, channel_id):
|
||||
|
||||
@@ -379,6 +379,29 @@ class TestPrefixHandler(BaseTest):
|
||||
assert not is_match(handler, make_message_update('/test'))
|
||||
assert not mock_filter.tested
|
||||
|
||||
def test_edit_prefix(self):
|
||||
handler = self.make_default_handler()
|
||||
handler.prefix = ['?', '§']
|
||||
assert handler._commands == list(combinations(['?', '§'], self.COMMANDS))
|
||||
handler.prefix = '+'
|
||||
assert handler._commands == list(combinations(['+'], self.COMMANDS))
|
||||
|
||||
def test_edit_command(self):
|
||||
handler = self.make_default_handler()
|
||||
handler.command = 'foo'
|
||||
assert handler._commands == list(combinations(self.PREFIXES, ['foo']))
|
||||
|
||||
def test_basic_after_editing(self, dp, prefix, command):
|
||||
"""Test the basic expected response from a prefix handler"""
|
||||
handler = self.make_default_handler()
|
||||
dp.add_handler(handler)
|
||||
text = prefix + command
|
||||
|
||||
assert self.response(dp, make_message_update(text))
|
||||
handler.command = 'foo'
|
||||
text = prefix + 'foo'
|
||||
assert self.response(dp, make_message_update(text))
|
||||
|
||||
def test_context(self, cdp, prefix_message_update):
|
||||
handler = self.make_default_handler(self.callback_context)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
@@ -179,6 +179,38 @@ class TestConversationHandler(object):
|
||||
return self._set_state(update, self.STOPPING)
|
||||
|
||||
# Tests
|
||||
@pytest.mark.parametrize('attr', ['entry_points', 'states', 'fallbacks', 'per_chat', 'name',
|
||||
'per_user', 'allow_reentry', 'conversation_timeout', 'map_to_parent'],
|
||||
indirect=False)
|
||||
def test_immutable(self, attr):
|
||||
ch = ConversationHandler('entry_points', {'states': ['states']}, 'fallbacks',
|
||||
per_chat='per_chat',
|
||||
per_user='per_user', per_message=False,
|
||||
allow_reentry='allow_reentry',
|
||||
conversation_timeout='conversation_timeout',
|
||||
name='name', map_to_parent='map_to_parent')
|
||||
|
||||
value = getattr(ch, attr)
|
||||
if isinstance(value, list):
|
||||
assert value[0] == attr
|
||||
elif isinstance(value, dict):
|
||||
assert list(value.keys())[0] == attr
|
||||
else:
|
||||
assert getattr(ch, attr) == attr
|
||||
with pytest.raises(ValueError, match='You can not assign a new value to {}'.format(attr)):
|
||||
setattr(ch, attr, True)
|
||||
|
||||
def test_immutable_per_message(self):
|
||||
ch = ConversationHandler('entry_points', {'states': ['states']}, 'fallbacks',
|
||||
per_chat='per_chat',
|
||||
per_user='per_user', per_message=False,
|
||||
allow_reentry='allow_reentry',
|
||||
conversation_timeout='conversation_timeout',
|
||||
name='name', map_to_parent='map_to_parent')
|
||||
assert ch.per_message is False
|
||||
with pytest.raises(ValueError, match='You can not assign a new value to per_message'):
|
||||
ch.per_message = True
|
||||
|
||||
def test_per_all_false(self):
|
||||
with pytest.raises(ValueError, match="can't all be 'False'"):
|
||||
ConversationHandler(self.entry_points, self.states, self.fallbacks,
|
||||
|
||||
+12
-1
@@ -28,7 +28,7 @@ from flaky import flaky
|
||||
|
||||
from telegram.ext import JobQueue, Updater, Job, CallbackContext
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.utils.helpers import _UtcOffsetTimezone
|
||||
from telegram.utils.helpers import _UtcOffsetTimezone, _UTC
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@@ -330,3 +330,14 @@ class TestJobQueue(object):
|
||||
sleep(0.03)
|
||||
|
||||
assert self.result == 0
|
||||
|
||||
def test_job_default_tzinfo(self, job_queue):
|
||||
"""Test that default tzinfo is always set to UTC"""
|
||||
job_1 = job_queue.run_once(self.job_run_once, 0.01)
|
||||
job_2 = job_queue.run_repeating(self.job_run_once, 10)
|
||||
job_3 = job_queue.run_daily(self.job_run_once, time=dtm.time(hour=15))
|
||||
|
||||
jobs = [job_1, job_2, job_3]
|
||||
|
||||
for job in jobs:
|
||||
assert job.tzinfo == _UTC
|
||||
|
||||
Reference in New Issue
Block a user