# 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 . """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