def test_promise_thread(): def run(resolve, _): sleep(0.25) resolve(3) run = Mock(wraps=run) p = Promise(run, T.THREAD) assert p._state == S.PENDING assert p._value is None assert p._timeout is None assert p._thread is not None sleep(0.05) run.assert_called_once_with(p._resolve, p._reject) run.reset_mock() assert not p._event.is_set() assert not p.wait(0.05) assert run.call_count == 0 assert p._state == S.PENDING assert p._value is None assert p._timeout is None assert p._thread is not None assert not p._event.is_set() assert p.wait() assert run.call_count == 0 assert p._state == S.RESOLVED assert p._value == 3 assert p._thread is None assert p._event.is_set() assert p.wait()
def test_promise_immediate(): run = Mock(wraps=lambda resolve, reject: resolve(2)) p = Promise(run, ptype=T.IMMEDIATE) run.assert_called_once_with(p._resolve, p._reject) assert p._state == S.RESOLVED assert p._value == 2 assert p._event.is_set() assert p.wait()
def test_promise_timeout(): p = Promise(lambda resolve, _: resolve(0), ptype=T.MANUAL, timeout=0.01) p2 = p.then(lambda value: value + 1, timeout=None) p2.wait(0.1) assert p._state == S.PENDING assert p2._state == S.REJECTED assert p._value is None assert isinstance(p2._value, PromiseTimeout)
def test_promise_on_reject_2(): on_resolve = Mock(wraps=lambda _: int('x')) on_reject = Mock(wraps=lambda _: Promise.reject(2)) p = Promise.resolve(0).then(on_resolve, on_reject) p.wait() on_resolve.assert_called_once_with(0) assert on_reject.call_count == 0 assert isinstance(p._value, Exception) assert p._state == S.REJECTED
def test_promise_on_reject(): on_resolve = Mock(wraps=lambda _: 1) on_reject = Mock(wraps=lambda _: Promise.reject(2)) p = Promise.reject(0).then(on_resolve, on_reject) p.wait() on_reject.assert_called_once_with(0) assert on_resolve.call_count == 0 assert p._value == 2 assert p._state == S.REJECTED
def run(_=None): nonlocal i if i == reject_i: if error: raise error() return Promise.reject(-i) i += 1 if i < length: return Promise.resolve(i).then(run) return i
def test_promise_chain_length(length, reject_i, error): i = 0 def run(_=None): nonlocal i if i == reject_i: if error: raise error() return Promise.reject(-i) i += 1 return i run = Mock(wraps=run) chain = [Promise.resolve(0)] for i_ in range(length): chain.append(chain[i_].then(run)) assert i == 0 assert run.call_count == 0 assert [p._state for p in chain[1:]] == [S.PENDING] * length assert chain[len(chain) - 1].wait(0.2) assert i == reject_i assert run.call_count == i if reject_i >= length else i + 1 assert [p._state for p in chain[1:i + 1]] == [S.RESOLVED] * reject_i assert [p._state for p in chain[i + 1:]] == [S.REJECTED] * (length - reject_i) assert [p._value for p in chain[1:i + 1]] == list(range(1, reject_i + 1)) reject_n = length - reject_i if error is not None: assert [type(p._value) for p in chain[i + 1:]] == [error] * reject_n else: assert [p._value for p in chain[i + 1:]] == [-i] * reject_n
def test_promise_resolve(): p = Promise.resolve(0) assert p._state == S.RESOLVED assert p._value == 0 assert p._timeout is None assert p._thread is None assert p._event.is_set()
def test_promise_reject(): p = Promise.reject(1) assert p._state == S.REJECTED assert p._value == 1 assert p._timeout is None assert p._thread is None assert p._event.is_set()
def run(_=None): nonlocal i if i == reject_i: if error: raise error() return Promise.reject(-i) i += 1 return i
def test_promise_default(): run = Mock(wraps=lambda resolve, reject: resolve(2)) p = Promise(run) assert p._state == S.PENDING assert p._value is None assert p._timeout is None assert p._thread is None assert run.call_count == 0 assert not p._event.is_set() assert p.wait() run.assert_called_once_with(p._resolve, p._reject) assert p._state == S.RESOLVED assert p._value == 2 assert p._event.is_set() run.reset_mock() assert p.wait() assert run.call_count == 0
def check_permission(bot, user, min_value=Permission.USER): try: value = get_permission(user) except sqlite3.ProgrammingError: get_permission_ = Promise.wrap(get_permission, ptype=PT.MANUAL) bot.queue.put(get_permission_) get_permission_.wait() value = get_permission_.value if not isinstance(value, int): bot.logger.error('check_permission: %r', value) return False, True return value >= min_value, value > Permission.IGNORED
def cmd_ocr(self, _, update): msg = update.message args = strip_command(msg.text) if args and not re.match(r'^[a-z]{3}([+_][a-z]{3})*$', args): update.message.reply_text('invalid language %r' % args) return if msg.photo: pass elif msg.reply_to_message and msg.reply_to_message.photo: msg = msg.reply_to_message else: update.message.reply_text('no input image') return deferred = Promise.defer() self.state.bot.download_file(msg, self.state.file_dir, deferred) self.state.run_async(self._run_script, update, 'ocr', [args], deferred.promise, 'no text found')
def test_promise_chain_depth(length, reject_i, error): i = 0 def run(_=None): nonlocal i if i == reject_i: if error: raise error() return Promise.reject(-i) i += 1 if i < length: return Promise.resolve(i).then(run) return i run = Mock(wraps=run) start = Promise.resolve(0) chain = start.then(run) end = chain.then(lambda _: 10) assert i == 0 assert run.call_count == 0 assert start._state == S.RESOLVED assert chain._state == S.PENDING assert end._state == S.PENDING assert end.wait(0.2) if reject_i >= length: assert chain._value == length assert end._value == 10 assert chain._state == S.RESOLVED assert end._state == S.RESOLVED else: if error is not None: assert isinstance(chain._value, error) assert isinstance(chain._value, error) else: assert chain._value == -reject_i assert end._value == -reject_i assert chain._state == S.REJECTED assert end._state == S.REJECTED assert i == reject_i assert run.call_count == i if reject_i >= length else i + 1 assert start._state == S.RESOLVED
def cmd_makesticker(self, _, update): msg = update.message if msg.photo or msg.document: pass elif msg.reply_to_message and ( msg.reply_to_message.photo or msg.reply_to_message.document ): msg = msg.reply_to_message else: update.message.reply_text('no input image') return deferred = Promise.defer() self.state.bot.download_file(msg, self.state.file_dir, deferred) self.state.run_async( self._run_script, update, 'make_sticker', ['{{TMP}}'], deferred.promise, return_file='png', timeout=self.state.query_timeout )
def cmd_getfile(self, _, update): msg = update.message try: ftype, _ = get_file(msg) except ValueError: try: msg = msg.reply_to_message ftype, _ = get_file(msg) except (AttributeError, ValueError): update.message.reply_text('no input file', quote=True) return if ftype == 'sticker': convert = 'unmake_sticker' ext = 'png' #elif ftype == 'video_note': # convert = 'unmake_video' # ext = '' #elif ftype == 'voice': # convert = 'unmake_voice' # ext = '' else: update.message.reply_text( 'file type "%s" is not supported' % ftype, quote=True ) return deferred = Promise.defer() self.state.bot.download_file(msg, self.state.file_dir, deferred) self.state.run_async( self._run_script, update, convert, ['{{TMP}}'], deferred.promise, return_file=ext, timeout=self.state.query_timeout )
def test_promise_state_not_set(): p = Promise(lambda *_: None, ptype=T.IMMEDIATE) assert p._state == S.REJECTED assert isinstance(p._value, PromiseStateNotSet) assert p._timeout is None assert p._thread is None
def ret(self, bot, update): self.logger.info('command %s', method.__name__) if update.message is not None: if (update.message.text and not match_command_user( update.message.text, self.state.username)): self.logger.info('!match_command_user %s' % update.message.text) return chat = update.message.chat if chat.type == chat.PRIVATE: perm = permission_private else: perm = permission perm, reply = check_permission(self, update.message.from_user, perm) if not perm: if reply: self.logger.warning('permission denied: %s', update.message) update.message.reply_text('permission denied', quote=True) return try: res = method(self, bot, update) except (BotError, dice.DiceBaseException) as ex: self.logger.warning(ex) update.message.reply_text(str(ex), quote=True) return except Exception as ex: self.logger.error(ex) update.message.reply_text(repr(ex), quote=True) raise on_resolve = lambda msg: reply_text(update, msg, True) on_reject = on_resolve if type_ == CommandType.NONE: return res elif type_ == CommandType.REPLY_TEXT: pass elif type_ == CommandType.REPLY_STICKER: on_resolve = lambda msg: reply_sticker(update, msg, True) elif type_ == CommandType.GET_OPTIONS: on_resolve = lambda res: reply_keyboard(update, res) elif type_ == CommandType.SET_OPTION: on_resolve = lambda msg: reply_callback_query(update, msg) on_reject = on_resolve elif type_ == CommandType.REPLY_TEXT_PAGINATED: on_resolve = lambda msg: reply_text_paginated( update, msg, True) else: raise ValueError('invalid command type: %s' % type_) if res is None: self.logger.info('%s: no command', method.__name__) return if isinstance(res, Promise): promise = res elif not callable(res): on_resolve(res) return else: promise = Promise.wrap(res, update, ptype=PT.MANUAL) self.queue.put(promise) promise.then(on_resolve, on_reject).wait()
def _search(self, update, query, reset=False): chat = update.effective_chat chat_id = chat.id settings = self.state.get_chat_settings(chat) if update.callback_query: user = update.callback_query.from_user reply_to = None else: user = update.message.from_user reply_to = update.message.message_id primary_bot = self.state.bot.primary.bot if chat.type == chat.PRIVATE: bot = primary_bot else: bot = self.state.bot.updaters[-1].bot try: enabled = settings['search_enabled'] except KeyError: enabled = False if not enabled: if reply_to is not None: primary_bot.send_message(chat_id, '"search_enabled": false', quote=True, reply_to_message_id=reply_to) return learn = Promise.wrap(self.state.learn_search_query, query, user, reset, ptype=PT.MANUAL) self.state.bot.queue.put(learn) learn.wait() offset = learn.value if isinstance(offset, Exception): self.logger.error(offset) return try: bot.send_chat_action(chat_id, ChatAction.UPLOAD_PHOTO) except (Unauthorized, BadRequest) as ex: self.logger.warning('send_chat_action: %r', ex) try: primary_bot.send_message(chat_id, 'add secondary bot to group', quote=True, reply_to_message_id=reply_to) except TelegramError as ex: self.logger.warning('send error message: %r', ex) return except TelegramError as ex: self.logger.error('send_chat_action: %r', ex) try: res, is_last = self.state.search(query, offset) except SearchError as ex: primary_bot.send_message(chat_id, str(ex), quote=True, reply_to_message_id=reply_to) except Exception as ex: primary_bot.send_message(chat_id, repr(ex), quote=True, reply_to_message_id=reply_to) return else: keyboard = [ InlineKeyboardButton('\U0001f517 %d' % (offset + 1), url=res.url) ] if offset >= 1: keyboard.append( InlineKeyboardButton('reset', callback_data='picreset')) if not is_last: keyboard.append( InlineKeyboardButton('next', callback_data='pic')) keyboard = InlineKeyboardMarkup([keyboard]) if self.RE_VIDEO_LINK.match(res.url): bot.send_message(chat_id, '%s\n%s\n\n%s' % (res.title, res.url, query), reply_markup=keyboard) return for url in (res.image, res.thumbnail, None): try: self.logger.info('%r %r', query, url) if url is None: bot.send_message( chat_id, '(bad request)\n%s\n%s\n\n%s' % (res.image, res.url, query)) else: send_image(bot, chat_id, url, caption=query, reply_markup=keyboard) return except TelegramError as ex: self.logger.info('image post failed: %r: %r', res, ex)