calendar-social/calsocial/utils.py

122 lines
3.5 KiB
Python

# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Utility functions for Calendar.social
"""
from contextlib import contextmanager
@contextmanager
def force_locale(locale):
"""Temporarily overrides the currently selected locale.
Sometimes it is useful to switch the current locale to different one, do some tasks and then
revert back to the original one. For example, if the user uses German on the web site, but
you want to send them an email in English, you can use this function as a context manager::
with force_locale('en_US'):
send_email(gettext('Hello!'), ...)
Shamelessly extracted from Flask-Babel.
:param locale: The locale to temporary switch to (ex: 'en_US').
:type locale: str
"""
from flask import current_app, has_request_context, request
def _get_current_context():
if has_request_context():
return request
if current_app:
return current_app
return None
ctx = _get_current_context()
if ctx is None:
yield
return
babel = current_app.extensions['babel']
orig_locale_selector_func = babel.locale_selector_func
orig_attrs = {}
for key in ('babel_translations', 'babel_locale'):
orig_attrs[key] = getattr(ctx, key, None)
try:
babel.locale_selector_func = lambda: locale
for key in orig_attrs:
setattr(ctx, key, None)
yield
finally:
babel.locale_selector_func = orig_locale_selector_func
for key, value in orig_attrs.items():
setattr(ctx, key, value)
class RoutedMixin:
"""Mixin to lazily register class methods as routes
Works both for `Flask` and `Blueprint` objects.
Example::
class MyBlueprint(Blueprint, RoutedMixin):
def __init__(self, *args, **kwargs):
do_whatever_you_like()
RoutedMixin.register_routes(self)
@RoutedMixin.route('/')
def index(self):
return 'Hello, World!'
"""
def register_routes(self):
"""Register all routes that were marked with :meth:`route`
"""
for attr_name in self.__dir__():
attr = getattr(self, attr_name)
if not callable(attr):
continue
args, kwargs = getattr(attr, 'routing', (None, None))
if args is None:
continue
self.route(*args, **kwargs)(attr)
@staticmethod
def route(*args, **kwargs):
"""Mark a function as a future route
Such functions will be iterated over when the application is initialised. ``*args`` and
``**kwargs`` will be passed verbatim to `Flask.route()`.
"""
def decorator(func): # pylint: disable=missing-docstring
setattr(func, 'routing', (args, kwargs))
return func
return decorator