1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Merge pull request #957 from Badiboy/master

Empty list optimization, Py2 arteacts removed
This commit is contained in:
Badiboy 2020-08-25 16:46:28 +03:00 committed by GitHub
commit 47b9c1d3fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 169 additions and 110 deletions

View File

@ -1,5 +1,4 @@
py==1.4.29
pytest==3.0.2
requests==2.20.0
six==1.9.0
wheel==0.24.0

View File

@ -22,7 +22,7 @@ setup(name='pyTelegramBotAPI',
packages=['telebot'],
license='GPL2',
keywords='telegram bot api tools',
install_requires=['requests', 'six'],
install_requires=['requests'],
extras_require={
'json': 'ujson',
'redis': 'redis>=3.4.1'

View File

@ -7,8 +7,6 @@ import sys
import threading
import time
import six
logger = logging.getLogger('TeleBot')
formatter = logging.Formatter(
'%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"'
@ -42,6 +40,15 @@ class Handler:
return getattr(self, item)
class ExceptionHandler:
"""
Class for handling exceptions while Polling
"""
def handle(self, exception):
return False
class TeleBot:
""" This is TeleBot Class
Methods:
@ -86,7 +93,7 @@ class TeleBot:
def __init__(
self, token, parse_mode=None, threaded=True, skip_pending=False, num_threads=2,
next_step_backend=None, reply_backend=None
next_step_backend=None, reply_backend=None, exception_handler=None
):
"""
:param token: bot API token
@ -111,6 +118,8 @@ class TeleBot:
if not self.reply_backend:
self.reply_backend = MemoryHandlerBackend()
self.exception_handler = exception_handler
self.message_handlers = []
self.edited_message_handlers = []
self.channel_post_handlers = []
@ -123,20 +132,20 @@ class TeleBot:
self.poll_handlers = []
self.poll_answer_handlers = []
self.typed_middleware_handlers = {
'message': [],
'edited_message': [],
'channel_post': [],
'edited_channel_post': [],
'inline_query': [],
'chosen_inline_result': [],
'callback_query': [],
'shipping_query': [],
'pre_checkout_query': [],
'poll': [],
}
self.default_middleware_handlers = []
if apihelper.ENABLE_MIDDLEWARE:
self.typed_middleware_handlers = {
'message': [],
'edited_message': [],
'channel_post': [],
'edited_channel_post': [],
'inline_query': [],
'chosen_inline_result': [],
'callback_query': [],
'shipping_query': [],
'pre_checkout_query': [],
'poll': [],
}
self.default_middleware_handlers = []
self.threaded = threaded
if self.threaded:
@ -286,17 +295,22 @@ class TeleBot:
self.process_new_updates(updates)
def process_new_updates(self, updates):
new_messages = []
new_edited_messages = []
new_channel_posts = []
new_edited_channel_posts = []
new_inline_querys = []
new_chosen_inline_results = []
new_callback_querys = []
new_shipping_querys = []
new_pre_checkout_querys = []
new_polls = []
new_poll_answers = []
upd_count = len(updates)
logger.debug('Received {0} new updates'.format(upd_count))
if (upd_count == 0):
return
new_messages = None
new_edited_messages = None
new_channel_posts = None
new_edited_channel_posts = None
new_inline_queries = None
new_chosen_inline_results = None
new_callback_queries = None
new_shipping_queries = None
new_pre_checkout_queries = None
new_polls = None
new_poll_answers = None
for update in updates:
if apihelper.ENABLE_MIDDLEWARE:
@ -305,50 +319,60 @@ class TeleBot:
if update.update_id > self.last_update_id:
self.last_update_id = update.update_id
if update.message:
if new_messages is None: new_messages = []
new_messages.append(update.message)
if update.edited_message:
if new_edited_messages is None: new_edited_messages = []
new_edited_messages.append(update.edited_message)
if update.channel_post:
if new_channel_posts is None: new_channel_posts = []
new_channel_posts.append(update.channel_post)
if update.edited_channel_post:
if new_edited_channel_posts is None: new_edited_channel_posts = []
new_edited_channel_posts.append(update.edited_channel_post)
if update.inline_query:
new_inline_querys.append(update.inline_query)
if new_inline_queries is None: new_inline_queries = []
new_inline_queries.append(update.inline_query)
if update.chosen_inline_result:
if new_chosen_inline_results is None: new_chosen_inline_results = []
new_chosen_inline_results.append(update.chosen_inline_result)
if update.callback_query:
new_callback_querys.append(update.callback_query)
if new_callback_queries is None: new_callback_queries = []
new_callback_queries.append(update.callback_query)
if update.shipping_query:
new_shipping_querys.append(update.shipping_query)
if new_shipping_queries is None: new_shipping_queries = []
new_shipping_queries.append(update.shipping_query)
if update.pre_checkout_query:
new_pre_checkout_querys.append(update.pre_checkout_query)
if new_pre_checkout_queries is None: new_pre_checkout_queries = []
new_pre_checkout_queries.append(update.pre_checkout_query)
if update.poll:
if new_polls is None: new_polls = []
new_polls.append(update.poll)
if update.poll_answer:
if new_poll_answers is None: new_poll_answers = []
new_poll_answers.append(update.poll_answer)
logger.debug('Received {0} new updates'.format(len(updates)))
if len(new_messages) > 0:
if new_messages:
self.process_new_messages(new_messages)
if len(new_edited_messages) > 0:
if new_edited_messages:
self.process_new_edited_messages(new_edited_messages)
if len(new_channel_posts) > 0:
if new_channel_posts:
self.process_new_channel_posts(new_channel_posts)
if len(new_edited_channel_posts) > 0:
if new_edited_channel_posts:
self.process_new_edited_channel_posts(new_edited_channel_posts)
if len(new_inline_querys) > 0:
self.process_new_inline_query(new_inline_querys)
if len(new_chosen_inline_results) > 0:
if new_inline_queries:
self.process_new_inline_query(new_inline_queries)
if new_chosen_inline_results:
self.process_new_chosen_inline_query(new_chosen_inline_results)
if len(new_callback_querys) > 0:
self.process_new_callback_query(new_callback_querys)
if len(new_shipping_querys) > 0:
self.process_new_shipping_query(new_shipping_querys)
if len(new_pre_checkout_querys) > 0:
self.process_new_pre_checkout_query(new_pre_checkout_querys)
if len(new_polls) > 0:
if new_callback_queries:
self.process_new_callback_query(new_callback_queries)
if new_shipping_queries:
self.process_new_shipping_query(new_shipping_queries)
if new_pre_checkout_queries:
self.process_new_pre_checkout_query(new_pre_checkout_queries)
if new_polls:
self.process_new_poll(new_polls)
if len(new_poll_answers) > 0:
if new_poll_answers:
self.process_new_poll_answer(new_poll_answers)
def process_new_messages(self, new_messages):
@ -398,6 +422,8 @@ class TeleBot:
default_middleware_handler(self, update)
def __notify_update(self, new_messages):
if len(self.update_listener) == 0:
return
for listener in self.update_listener:
self._exec_task(listener, new_messages)
@ -452,20 +478,41 @@ class TeleBot:
error_interval = 0.25
except apihelper.ApiException as e:
logger.error(e)
if not none_stop:
self.__stop_polling.set()
logger.info("Exception occurred. Stopping.")
if self.exception_handler is not None:
handled = self.exception_handler.handle(e)
else:
handled = False
if not handled:
logger.error(e)
if not none_stop:
self.__stop_polling.set()
logger.info("Exception occurred. Stopping.")
else:
polling_thread.clear_exceptions()
self.worker_pool.clear_exceptions()
logger.info("Waiting for {0} seconds until retry".format(error_interval))
time.sleep(error_interval)
error_interval *= 2
else:
polling_thread.clear_exceptions()
self.worker_pool.clear_exceptions()
logger.info("Waiting for {0} seconds until retry".format(error_interval))
time.sleep(error_interval)
error_interval *= 2
except KeyboardInterrupt:
logger.info("KeyboardInterrupt received.")
self.__stop_polling.set()
break
except Exception as e:
if self.exception_handler is not None:
handled = self.exception_handler.handle(e)
else:
handled = False
if not handled:
raise e
else:
polling_thread.clear_exceptions()
self.worker_pool.clear_exceptions()
time.sleep(error_interval)
polling_thread.stop()
logger.info('Stopped polling.')
@ -480,18 +527,35 @@ class TeleBot:
self.__retrieve_updates(timeout)
error_interval = 0.25
except apihelper.ApiException as e:
logger.error(e)
if not none_stop:
self.__stop_polling.set()
logger.info("Exception occurred. Stopping.")
if self.exception_handler is not None:
handled = self.exception_handler.handle(e)
else:
handled = False
if not handled:
logger.error(e)
if not none_stop:
self.__stop_polling.set()
logger.info("Exception occurred. Stopping.")
else:
logger.info("Waiting for {0} seconds until retry".format(error_interval))
time.sleep(error_interval)
error_interval *= 2
else:
logger.info("Waiting for {0} seconds until retry".format(error_interval))
time.sleep(error_interval)
error_interval *= 2
except KeyboardInterrupt:
logger.info("KeyboardInterrupt received.")
self.__stop_polling.set()
break
except Exception as e:
if self.exception_handler is not None:
handled = self.exception_handler.handle(e)
else:
handled = False
if not handled:
raise e
else:
time.sleep(error_interval)
logger.info('Stopped polling.')
@ -1063,7 +1127,6 @@ class TeleBot:
"""
return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title)
def set_chat_permissions(self, chat_id, permissions):
"""
Use this method to set default chat permissions for all members.
@ -1278,7 +1341,7 @@ class TeleBot:
def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices,
start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None,
send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None,
send_phone_number_to_provider=None, send_email_to_provider=None, is_flexible=None,
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, timeout=None):
"""
Sends invoice
@ -1394,7 +1457,7 @@ class TeleBot:
:return:
"""
parse_mode = self.parse_mode if not parse_mode else parse_mode
result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id,
parse_mode, reply_markup)
if type(result) == bool:
@ -1542,8 +1605,9 @@ class TeleBot:
for message in new_messages:
if hasattr(message, "reply_to_message") and message.reply_to_message is not None:
handlers = self.reply_backend.get_handlers(message.reply_to_message.message_id)
for handler in handlers:
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
if handlers:
for handler in handlers:
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
def register_next_step_handler(self, message, callback, *args, **kwargs):
"""
@ -1615,11 +1679,12 @@ class TeleBot:
for i, message in enumerate(new_messages):
need_pop = False
handlers = self.next_step_backend.get_handlers(message.chat.id)
for handler in handlers:
need_pop = True
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
if handlers:
for handler in handlers:
need_pop = True
self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"])
if need_pop:
new_messages.pop(i) # removing message that detects with next_step_handler
new_messages.pop(i) # removing message that was detected with next_step_handler
@staticmethod
def _build_handler_dict(handler, **filters):
@ -1721,9 +1786,7 @@ class TeleBot:
func=func,
content_types=content_types,
**kwargs)
self.add_message_handler(handler_dict)
return handler
return decorator
@ -1845,6 +1908,7 @@ class TeleBot:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_inline_handler(handler_dict)
@ -1867,6 +1931,7 @@ class TeleBot:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_chosen_inline_handler(handler_dict)
@ -1889,6 +1954,7 @@ class TeleBot:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_callback_query_handler(handler_dict)
@ -1911,6 +1977,7 @@ class TeleBot:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_shipping_query_handler(handler_dict)
@ -1933,6 +2000,7 @@ class TeleBot:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_pre_checkout_query_handler(handler_dict)
@ -1955,6 +2023,7 @@ class TeleBot:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_poll_handler(handler_dict)
@ -1977,6 +2046,7 @@ class TeleBot:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_poll_answer_handler(handler_dict)
@ -1999,7 +2069,7 @@ class TeleBot:
:param message:
:return:
"""
for message_filter, filter_value in six.iteritems(message_handler['filters']):
for message_filter, filter_value in message_handler['filters'].items():
if filter_value is None:
continue
@ -2033,6 +2103,8 @@ class TeleBot:
:param new_messages:
:return:
"""
if len(handlers) == 0:
return
for message in new_messages:
for message_handler in handlers:
if self._test_message_handler(message_handler, message):

View File

@ -1005,6 +1005,7 @@ def send_invoice(
:param reply_to_message_id: If the message is a reply, ID of the original message
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
:param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
:param timeout:
:return:
"""
method_url = r'sendInvoice'
@ -1318,7 +1319,7 @@ class ApiTelegramException(ApiException):
"""
def __init__(self, function_name, result, result_json):
super(ApiTelegramException, self).__init__(
"Error code: {0} Description: {1}" \
"Error code: {0}. Description: {1}" \
.format(result_json['error_code'], result_json['description']),
function_name,
result)

View File

@ -32,10 +32,13 @@ class MemoryHandlerBackend(HandlerBackend):
self.handlers[handler_group_id] = [handler]
def clear_handlers(self, handler_group_id):
self.handlers.pop(handler_group_id, [])
self.handlers.pop(handler_group_id, None)
def get_handlers(self, handler_group_id):
return self.handlers.pop(handler_group_id, [])
return self.handlers.pop(handler_group_id, None)
def load_handlers(self, filename, del_file_after_loading):
raise NotImplementedError()
class FileHandlerBackend(HandlerBackend):
@ -50,19 +53,15 @@ class FileHandlerBackend(HandlerBackend):
self.handlers[handler_group_id].append(handler)
else:
self.handlers[handler_group_id] = [handler]
self.start_save_timer()
def clear_handlers(self, handler_group_id):
self.handlers.pop(handler_group_id, [])
self.handlers.pop(handler_group_id, None)
self.start_save_timer()
def get_handlers(self, handler_group_id):
handlers = self.handlers.pop(handler_group_id, [])
handlers = self.handlers.pop(handler_group_id, None)
self.start_save_timer()
return handlers
def start_save_timer(self):
@ -136,10 +135,9 @@ class RedisHandlerBackend(HandlerBackend):
self.redis.delete(self._key(handler_group_id))
def get_handlers(self, handler_group_id):
handlers = []
handlers = None
value = self.redis.get(self._key(handler_group_id))
if value:
handlers = pickle.loads(value)
self.clear_handlers(handler_group_id)
return handlers

View File

@ -7,8 +7,6 @@ try:
except ImportError:
import json
import six
from telebot import util
DISABLE_KEYLEN_ERROR = False
@ -81,13 +79,13 @@ class JsonDeserializable(object):
def __str__(self):
d = {}
for x, y in six.iteritems(self.__dict__):
for x, y in self.__dict__.items():
if hasattr(y, '__dict__'):
d[x] = y.__dict__
else:
d[x] = y
return six.text_type(d)
return str(d)
class Update(JsonDeserializable):

View File

@ -2,21 +2,12 @@
import random
import re
import string
import sys
import threading
import traceback
import warnings
import functools
import six
from six import string_types
# Python3 queue support.
try:
import Queue
except ImportError:
import queue as Queue
import queue as Queue
import logging
try:
@ -51,7 +42,7 @@ class WorkerThread(threading.Thread):
self.continue_event = threading.Event()
self.exception_callback = exception_callback
self.exc_info = None
self.exception_info = None
self._running = True
self.start()
@ -73,11 +64,11 @@ class WorkerThread(threading.Thread):
pass
except Exception as e:
logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc())
self.exc_info = sys.exc_info()
self.exception_info = e
self.exception_event.set()
if self.exception_callback:
self.exception_callback(self, self.exc_info)
self.exception_callback(self, self.exception_info)
self.continue_event.wait()
def put(self, task, *args, **kwargs):
@ -85,7 +76,7 @@ class WorkerThread(threading.Thread):
def raise_exceptions(self):
if self.exception_event.is_set():
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
raise self.exception_info
def clear_exceptions(self):
self.exception_event.clear()
@ -103,19 +94,19 @@ class ThreadPool:
self.num_threads = num_threads
self.exception_event = threading.Event()
self.exc_info = None
self.exception_info = None
def put(self, func, *args, **kwargs):
self.tasks.put((func, args, kwargs))
def on_exception(self, worker_thread, exc_info):
self.exc_info = exc_info
self.exception_info = exc_info
self.exception_event.set()
worker_thread.continue_event.set()
def raise_exceptions(self):
if self.exception_event.is_set():
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
raise self.exception_info
def clear_exceptions(self):
self.exception_event.clear()
@ -140,15 +131,15 @@ class AsyncTask:
def _run(self):
try:
self.result = self.target(*self.args, **self.kwargs)
except:
self.result = sys.exc_info()
except Exception as e:
self.result = e
self.done = True
def wait(self):
if not self.done:
self.thread.join()
if isinstance(self.result, BaseException):
six.reraise(self.result[0], self.result[1], self.result[2])
raise self.result
else:
return self.result
@ -164,7 +155,7 @@ def async_dec():
def is_string(var):
return isinstance(var, string_types)
return isinstance(var, str)
def is_dict(var):
return isinstance(var, dict)