mypy: Migrate queue_processors.py to python3 syntax.

Forward-declarations of QueueProcessingWorker allow code order to remain
unchanged.

Note added re use of Dict vs Mapping.
This commit is contained in:
neiljp (Neil Pilgrim) 2018-03-10 07:29:46 +00:00 committed by Tim Abbott
parent 6e048c5d3f
commit fed51666d6
1 changed files with 39 additions and 69 deletions

View File

@ -65,10 +65,10 @@ class WorkerDeclarationException(Exception):
ConcreteQueueWorker = TypeVar('ConcreteQueueWorker', bound='QueueProcessingWorker')
def assign_queue(queue_name, enabled=True, queue_type="consumer"):
# type: (str, bool, str) -> Callable[[Type[ConcreteQueueWorker]], Type[ConcreteQueueWorker]]
def decorate(clazz):
# type: (Type[ConcreteQueueWorker]) -> Type[ConcreteQueueWorker]
def assign_queue(
queue_name: str, enabled: bool=True, queue_type: str="consumer"
) -> Callable[[Type[ConcreteQueueWorker]], Type[ConcreteQueueWorker]]:
def decorate(clazz: Type[ConcreteQueueWorker]) -> Type[ConcreteQueueWorker]:
clazz.queue_name = queue_name
if enabled:
register_worker(queue_name, clazz, queue_type)
@ -77,26 +77,22 @@ def assign_queue(queue_name, enabled=True, queue_type="consumer"):
worker_classes = {} # type: Dict[str, Any] # Any here should be QueueProcessingWorker type
queues = {} # type: Dict[str, Dict[str, Type[QueueProcessingWorker]]]
def register_worker(queue_name, clazz, queue_type):
# type: (str, Type[QueueProcessingWorker], str) -> None
def register_worker(queue_name: str, clazz: Type['QueueProcessingWorker'], queue_type: str) -> None:
if queue_type not in queues:
queues[queue_type] = {}
queues[queue_type][queue_name] = clazz
worker_classes[queue_name] = clazz
def get_worker(queue_name):
# type: (str) -> QueueProcessingWorker
def get_worker(queue_name: str) -> 'QueueProcessingWorker':
return worker_classes[queue_name]()
def get_active_worker_queues(queue_type=None):
# type: (Optional[str]) -> List[str]
def get_active_worker_queues(queue_type: Optional[str]=None) -> List[str]:
"""Returns all the non-test worker queues."""
if queue_type is None:
return list(worker_classes.keys())
return list(queues[queue_type].keys())
def check_and_send_restart_signal():
# type: () -> None
def check_and_send_restart_signal() -> None:
try:
if not connection.is_usable():
logging.warning("*** Sending self SIGUSR1 to trigger a restart.")
@ -104,20 +100,19 @@ def check_and_send_restart_signal():
except Exception:
pass
def retry_send_email_failures(func):
# type: (Callable[[Any, Dict[str, Any]], None]) -> Callable[[QueueProcessingWorker, Dict[str, Any]], None]
def retry_send_email_failures(
func: Callable[[Any, Dict[str, Any]], None]
) -> Callable[['QueueProcessingWorker', Dict[str, Any]], None]:
# If we don't use cast() and use QueueProcessingWorker instead of Any in
# function type annotation then mypy complains.
func = cast(Callable[[QueueProcessingWorker, Dict[str, Any]], None], func)
@wraps(func)
def wrapper(worker, data):
# type: (QueueProcessingWorker, Dict[str, Any]) -> None
def wrapper(worker: 'QueueProcessingWorker', data: Dict[str, Any]) -> None:
try:
func(worker, data)
except (smtplib.SMTPServerDisconnected, socket.gaierror, EmailNotDeliveredException):
def on_failure(event):
# type: (Dict[str, Any]) -> None
def on_failure(event: Dict[str, Any]) -> None:
logging.exception("Event {} failed".format(event))
retry_event(worker.queue_name, data, on_failure)
@ -127,18 +122,15 @@ def retry_send_email_failures(func):
class QueueProcessingWorker:
queue_name = None # type: str
def __init__(self):
# type: () -> None
def __init__(self) -> None:
self.q = None # type: SimpleQueueClient
if self.queue_name is None:
raise WorkerDeclarationException("Queue worker declared without queue_name")
def consume(self, data):
# type: (Dict[str, Any]) -> None
def consume(self, data: Dict[str, Any]) -> None:
raise WorkerDeclarationException("No consumer defined!")
def consume_wrapper(self, data):
# type: (Dict[str, Any]) -> None
def consume_wrapper(self, data: Dict[str, Any]) -> None:
try:
self.consume(data)
except Exception:
@ -156,21 +148,17 @@ class QueueProcessingWorker:
finally:
reset_queries()
def _log_problem(self):
# type: () -> None
def _log_problem(self) -> None:
logging.exception("Problem handling data on queue %s" % (self.queue_name,))
def setup(self):
# type: () -> None
def setup(self) -> None:
self.q = SimpleQueueClient()
def start(self):
# type: () -> None
def start(self) -> None:
self.q.register_json_consumer(self.queue_name, self.consume_wrapper)
self.q.start_consuming()
def stop(self): # nocoverage
# type: () -> None
def stop(self) -> None: # nocoverage
self.q.stop_consuming()
class LoopQueueProcessingWorker(QueueProcessingWorker):
@ -195,8 +183,8 @@ class LoopQueueProcessingWorker(QueueProcessingWorker):
@assign_queue('signups')
class SignupWorker(QueueProcessingWorker):
def consume(self, data):
# type: (Dict[str, Any]) -> None
def consume(self, data: Dict[str, Any]) -> None:
# TODO: This is the only implementation with Dict cf Mapping; should we simplify?
user_profile = get_user_profile_by_id(data['user_id'])
logging.info("Processing signup for user %s in realm %s" % (
user_profile.email, user_profile.realm.string_id))
@ -218,9 +206,7 @@ class SignupWorker(QueueProcessingWorker):
@assign_queue('invites')
class ConfirmationEmailWorker(QueueProcessingWorker):
def consume(self, data):
# type: (Mapping[str, Any]) -> None
def consume(self, data: Mapping[str, Any]) -> None:
if "email" in data:
# When upgrading from a version up through 1.7.1, there may be
# existing items in the queue with `email` instead of `prereg_id`.
@ -255,8 +241,7 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
@assign_queue('user_activity')
class UserActivityWorker(QueueProcessingWorker):
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
user_profile = get_user_profile_by_id(event["user_profile_id"])
client = get_client(event["client"])
log_time = timestamp_to_datetime(event["time"])
@ -265,16 +250,14 @@ class UserActivityWorker(QueueProcessingWorker):
@assign_queue('user_activity_interval')
class UserActivityIntervalWorker(QueueProcessingWorker):
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
user_profile = get_user_profile_by_id(event["user_profile_id"])
log_time = timestamp_to_datetime(event["time"])
do_update_user_activity_interval(user_profile, log_time)
@assign_queue('user_presence')
class UserPresenceWorker(QueueProcessingWorker):
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
logging.debug("Received presence event: %s" % (event),)
user_profile = get_user_profile_by_id(event["user_profile_id"])
client = get_client(event["client"])
@ -325,22 +308,19 @@ class MissedMessageSendingWorker(EmailSendingWorker): # nocoverage
@assign_queue('missedmessage_mobile_notifications')
class PushNotificationsWorker(QueueProcessingWorker): # nocoverage
def consume(self, data):
# type: (Mapping[str, Any]) -> None
def consume(self, data: Mapping[str, Any]) -> None:
handle_push_notification(data['user_profile_id'], data)
# We probably could stop running this queue worker at all if ENABLE_FEEDBACK is False
@assign_queue('feedback_messages')
class FeedbackBot(QueueProcessingWorker):
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
logging.info("Received feedback from %s" % (event["sender_email"],))
handle_feedback(event)
@assign_queue('error_reports')
class ErrorReporter(QueueProcessingWorker):
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
logging.info("Processing traceback with type %s for %s" % (event['type'], event.get('user_email')))
if settings.ERROR_REPORTING:
do_report_error(event['report']['host'], event['type'], event['report'])
@ -350,8 +330,7 @@ class SlowQueryWorker(LoopQueueProcessingWorker):
# Sleep 1 minute between checking the queue
sleep_delay = 60 * 1
def consume_batch(self, slow_queries):
# type: (List[Dict[str, Any]]) -> None
def consume_batch(self, slow_queries: List[Dict[str, Any]]) -> None:
for query in slow_queries:
logging.info("Slow query: %s" % (query))
@ -371,15 +350,13 @@ class SlowQueryWorker(LoopQueueProcessingWorker):
@assign_queue("message_sender")
class MessageSenderWorker(QueueProcessingWorker):
def __init__(self):
# type: () -> None
def __init__(self) -> None:
super().__init__()
self.redis_client = get_redis_client()
self.handler = BaseHandler()
self.handler.load_middleware()
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
server_meta = event['server_meta']
environ = {
@ -435,8 +412,7 @@ class MessageSenderWorker(QueueProcessingWorker):
class DigestWorker(QueueProcessingWorker): # nocoverage
# Who gets a digest is entirely determined by the enqueue_digest_emails
# management command, not here.
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
logging.info("Received digest event: %s" % (event,))
handle_digest_email(event["user_profile_id"], event["cutoff"])
@ -444,8 +420,7 @@ class DigestWorker(QueueProcessingWorker): # nocoverage
class MirrorWorker(QueueProcessingWorker):
# who gets a digest is entirely determined by the enqueue_digest_emails
# management command, not here.
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
message = force_str(event["message"])
mirror_email(email.message_from_string(message),
rcpt_to=event["rcpt_to"], pre_checked=True)
@ -456,8 +431,7 @@ class TestWorker(QueueProcessingWorker):
# creating significant side effects. It can be useful in development or
# for troubleshooting prod/staging. It pulls a message off the test queue
# and appends it to a file in /tmp.
def consume(self, event): # nocoverage
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None: # nocoverage
fn = settings.ZULIP_WORKER_TEST_FILE
message = ujson.dumps(event)
logging.info("TestWorker should append this message to %s: %s" % (fn, message))
@ -466,8 +440,7 @@ class TestWorker(QueueProcessingWorker):
@assign_queue('embed_links')
class FetchLinksEmbedData(QueueProcessingWorker):
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
for url in event['urls']:
url_preview.get_link_embed_data(url)
@ -496,8 +469,7 @@ class FetchLinksEmbedData(QueueProcessingWorker):
@assign_queue('outgoing_webhooks')
class OutgoingWebhookWorker(QueueProcessingWorker):
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
message = event['message']
dup_event = cast(Dict[str, Any], event)
dup_event['command'] = message['content']
@ -512,12 +484,10 @@ class OutgoingWebhookWorker(QueueProcessingWorker):
@assign_queue('embedded_bots')
class EmbeddedBotWorker(QueueProcessingWorker):
def get_bot_api_client(self, user_profile):
# type: (UserProfile) -> EmbeddedBotHandler
def get_bot_api_client(self, user_profile: UserProfile) -> EmbeddedBotHandler:
return EmbeddedBotHandler(user_profile)
def consume(self, event):
# type: (Mapping[str, Any]) -> None
def consume(self, event: Mapping[str, Any]) -> None:
user_profile_id = event['user_profile_id']
user_profile = get_user_profile_by_id(user_profile_id)