From 04798cbf5b0976b840430f19c61cd870df28a650 Mon Sep 17 00:00:00 2001 From: Tim Mundt Date: Fri, 16 Dec 2016 16:07:14 +0100 Subject: [PATCH] added methods to load config from a file --- sanic/config.py | 73 +++++++++++++++++++++++++++++++++++++++++++++---- sanic/router.py | 17 ++++-------- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/sanic/config.py b/sanic/config.py index 3dbf06c8..55cebc61 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -1,5 +1,11 @@ -class Config: - LOGO = """ +import os +import types + + +class Config(dict): + def __init__(self, defaults=None): + dict.__init__(self, defaults or {}) + self.LOGO = """ ▄▄▄▄▄ ▀▀▀██████▄▄▄ _______________ ▄▄▄▄▄ █████████▄ / \\ @@ -20,6 +26,63 @@ class Config: ▌ ▐ ▀▀▄▄▄▀ ▀▀▄▄▀ """ - REQUEST_MAX_SIZE = 100000000 # 100 megababies - REQUEST_TIMEOUT = 60 # 60 seconds - ROUTER_CACHE_SIZE = 1024 + self.REQUEST_MAX_SIZE = 100000000 # 100 megababies + self.REQUEST_TIMEOUT = 60 # 60 seconds + self.ROUTER_CACHE_SIZE = 1024 + + def __getattr__(self, attr): + return self[attr] + + def __setattr__(self, attr, value): + self[attr] = value + + def from_envvar(self, variable_name): + """Loads a configuration from an environment variable pointing to + a configuration file. + :param variable_name: name of the environment variable + :return: bool. ``True`` if able to load config, ``False`` otherwise. + """ + config_file = os.environ.get(variable_name) + if not config_file: + raise RuntimeError('The environment variable %r is not set and ' + 'thus configuration could not be loaded.' % + variable_name) + return self.from_pyfile(config_file) + + def from_pyfile(self, filename): + """Updates the values in the config from a Python file. Only the uppercase + varibales in that module are stored in the config. + :param filename: an absolute path to the config file + """ + module = types.ModuleType('config') + module.__file__ = filename + try: + with open(filename) as config_file: + exec(compile(config_file.read(), filename, 'exec'), + module.__dict__) + except IOError as e: + e.strerror = 'Unable to load configuration file (%s)' % e.strerror + raise + self.from_object(module) + return True + + def from_object(self, obj): + """Updates the values from the given object. + Objects are usually either modules or classes. + + Just the uppercase variables in that object are stored in the config. + Example usage:: + + from yourapplication import default_config + app.config.from_object(default_config) + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + :param obj: an object + """ + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) diff --git a/sanic/router.py b/sanic/router.py index 4cc1f073..29bebc3e 100644 --- a/sanic/router.py +++ b/sanic/router.py @@ -1,7 +1,6 @@ import re from collections import defaultdict, namedtuple from functools import lru_cache -from .config import Config from .exceptions import NotFound, InvalidUsage Route = namedtuple('Route', ['handler', 'methods', 'pattern', 'parameters']) @@ -14,6 +13,8 @@ REGEX_TYPES = { 'alpha': (str, r'[A-Za-z]+'), } +ROUTER_CACHE_SIZE = 1024 + def url_hash(url): return url.count('/') @@ -30,17 +31,11 @@ class Router: @sanic.route('/my/url/', methods=['GET', 'POST', ...]) def my_route(request, my_parameter): do stuff... - or - @sanic.route('/my/url/:type', methods['GET', 'POST', ...]) - def my_route_with_type(request, my_parameter): - do stuff... Parameters will be passed as keyword arguments to the request handling - function. Provided parameters can also have a type by appending :type to - the . Given parameter must be able to be type-casted to this. - If no type is provided, a string is expected. A regular expression can - also be passed in as the type. The argument given to the function will - always be a string, independent of the type. + function provided Parameters can also have a type by appending :type to + the . If no type is provided, a string is expected. A regular + expression can also be passed in as the type """ routes_static = None routes_dynamic = None @@ -118,7 +113,7 @@ class Router: """ return self._get(request.url, request.method) - @lru_cache(maxsize=Config.ROUTER_CACHE_SIZE) + @lru_cache(maxsize=ROUTER_CACHE_SIZE) def _get(self, url, method): """ Gets a request handler based on the URL of the request, or raises an