def __init__(self, import_name, config={}, **kw): """initialize the Application :param import_name: the __name__ of the module we are running under. :param config: a dictionary of configuration values """ self.import_name = import_name # initialize URL mapping variables self.url_map = werkzeug.routing.Map() self.handlers = {} # initialize configuration self.config = AttributeMapper(self.enforced_defaults or {}) # initialize module configuration for module in self.modules: self.config.modules.setdefault(module.name, AttributeMapper()) # update configuration from our self.config.update(self.defaults) # first read module configuration from a file if given if "module_config_file" in config: cfg = ConfigParser() cfg.read(config['module_config_file']) for section in cfg.sections(): for option in cfg.options(section): config["modules.%s.%s" % (section, option)] = cfg.get( section, option) # now override defaults from config eventually self.config.update(fix_types(config, self.config_types)) # lastly override via keyword arguments self.config.update(fix_types(kw, self.config_types)) # TODO: update from environment vars? self.finalize_modules() # let user dynamically add some modules # now bind all the modules to our app and create a mapping # we also make sure he that URLs of modules are registered first so their namespaces work correctly for module in self.modules: module.bind_to_app(self) self.module_map[module.name] = module # initialize the actual routes for route in self.routes: self.add_url_rule(route.path, route.endpoint, route.handler, **route.options) # clean up static url path if self.config.static_folder is not None: sup = self.config.static_url_path if sup.endswith("/"): sup = sup[:-1] # remove any trailing slash if not sup.startswith("/"): sup = "/" + sup # add a leading slash if missing self.add_url_rule(sup + '/<path:filename>', endpoint='static', handler=static.StaticFileHandler) # now call the hook for changing the setup after initialization self.finalize_setup() # for testing purposes. Set app.config.testing = True and this will be populated. self.last_handler = None # we did not have any request yet self._got_first_request = False # check config if self.config.server_name is None and not self.config.testing: print "*" * 80 print "WARNING: you don't have a server_name set in your configuration and because" print "of that we cannot create proper URLs." print "We will now use localhost as server name but this might create broken URLs." print "*" * 80 print self.config.server_name = "localhost" if self.config.session_cookie_domain is None and not self.config.testing: print "*" * 80 print "WARNING: session_cookie_domain is not set in your configuration" print "this means that depending on the browser, the session cookie" print "can not be stored. This also means that no flash messages will appear" print "*" * 80 print if self.config.testing: self.config.server_name = 'example.org' self.config.session_cookie_domain = 'example.org'
def __init__(self, import_name, config={}, **kw): """initialize the Application :param import_name: the __name__ of the module we are running under. :param config: a dictionary of configuration values """ self.import_name = import_name # initialize URL mapping variables self.url_map = werkzeug.routing.Map() self.handlers = {} # initialize configuration self.config = AttributeMapper(self.enforced_defaults or {}) # initialize module configuration for module in self.modules: self.config.modules.setdefault(module.name, AttributeMapper()) # update configuration from our self.config.update(self.defaults) # first read module configuration from a file if given if "module_config_file" in config: cfg = ConfigParser() cfg.read(config['module_config_file']) for section in cfg.sections(): for option in cfg.options(section): config["modules.%s.%s" %(section,option)] = cfg.get(section, option) # now override defaults from config eventually self.config.update(fix_types(config, self.config_types)) # lastly override via keyword arguments self.config.update(fix_types(kw, self.config_types)) # TODO: update from environment vars? self.finalize_modules() # let user dynamically add some modules # now bind all the modules to our app and create a mapping # we also make sure he that URLs of modules are registered first so their namespaces work correctly for module in self.modules: module.bind_to_app(self) self.module_map[module.name] = module # initialize the actual routes for route in self.routes: self.add_url_rule( route.path, route.endpoint, route.handler, **route.options) # clean up static url path if self.config.static_folder is not None: sup = self.config.static_url_path if sup.endswith("/"): sup = sup[:-1] # remove any trailing slash if not sup.startswith("/"): sup = "/"+sup # add a leading slash if missing self.add_url_rule(sup+ '/<path:filename>', endpoint='static', handler=static.StaticFileHandler) # now call the hook for changing the setup after initialization self.finalize_setup() # for testing purposes. Set app.config.testing = True and this will be populated. self.last_handler = None # we did not have any request yet self._got_first_request = False # check config if self.config.server_name is None and not self.config.testing: print "*"*80 print "WARNING: you don't have a server_name set in your configuration and because" print "of that we cannot create proper URLs." print "We will now use localhost as server name but this might create broken URLs." print "*"*80 print self.config.server_name = "localhost" if self.config.session_cookie_domain is None and not self.config.testing: print "*"*80 print "WARNING: session_cookie_domain is not set in your configuration" print "this means that depending on the browser, the session cookie" print "can not be stored. This also means that no flash messages will appear" print "*"*80 print if self.config.testing: self.config.server_name = 'example.org' self.config.session_cookie_domain = 'example.org'
class Application(object): """a base class for dispatching WSGI requests""" defaults = {} routes = [] # list of rules error_handlers = {} # mapping from error code to error handler classes handlers = {} # mapping from endpoint to handler classes # class to be used for URL Routes url_rule_class = werkzeug.routing.Rule # session interface session_interface = sessions.SecureCookieSessionInterface() # class to be used as test client class test_client_class = None # response class to use response_class = wrappers.Response request_class = wrappers.Request # last handler for testing last_handler = None # enforeced defaults (these have to be existent in the config # for starflyer to work (DO NOT CHANGE!) enforced_defaults = { 'session_cookie_name': "s", 'secret_key': None, 'permanent_session_lifetime': datetime.timedelta(days=31), 'session_cookie_domain': None, 'session_cookie_path': None, 'session_cookie_httponly': True, 'session_cookie_secure': False, 'logger_name': None, 'server_name': None, 'application_root': None, 'preferred_url_scheme': "http", 'propagate_exceptions': None, # this is used for testing and debugging and means to re-raise it and not use an error handler for uncaught exceptions 'debug': False, 'testing': False, 'force_exceptions': False, # True = make also http exceptions raise and not return a response (for testing) 'static_cache_timeout': 12 * 60 * 60, 'template_folder': "templates/", 'static_folder': "static/", 'static_url_path': "/static", 'modules': AttributeMapper(), # config placeholder for modules } # here you can define which types the config parameters are supposed to be in config_types = { 'debug': bool, 'testing': bool, 'session_cookie_httponly': bool, 'session_cookie_secure': bool, } jinja_options = ImmutableDict(extensions=[ 'jinja2.ext.autoescape', 'jinja2.ext.with_', 'jinja2.ext.i18n' ]) jinja_filters = ImmutableDict() modules = [] # list of modules module_map = AttributeMapper() # mapped version of modules def __init__(self, import_name, config={}, **kw): """initialize the Application :param import_name: the __name__ of the module we are running under. :param config: a dictionary of configuration values """ self.import_name = import_name # initialize URL mapping variables self.url_map = werkzeug.routing.Map() self.handlers = {} # initialize configuration self.config = AttributeMapper(self.enforced_defaults or {}) # initialize module configuration for module in self.modules: self.config.modules.setdefault(module.name, AttributeMapper()) # update configuration from our self.config.update(self.defaults) # first read module configuration from a file if given if "module_config_file" in config: cfg = ConfigParser() cfg.read(config['module_config_file']) for section in cfg.sections(): for option in cfg.options(section): config["modules.%s.%s" % (section, option)] = cfg.get( section, option) # now override defaults from config eventually self.config.update(fix_types(config, self.config_types)) # lastly override via keyword arguments self.config.update(fix_types(kw, self.config_types)) # TODO: update from environment vars? self.finalize_modules() # let user dynamically add some modules # now bind all the modules to our app and create a mapping # we also make sure he that URLs of modules are registered first so their namespaces work correctly for module in self.modules: module.bind_to_app(self) self.module_map[module.name] = module # initialize the actual routes for route in self.routes: self.add_url_rule(route.path, route.endpoint, route.handler, **route.options) # clean up static url path if self.config.static_folder is not None: sup = self.config.static_url_path if sup.endswith("/"): sup = sup[:-1] # remove any trailing slash if not sup.startswith("/"): sup = "/" + sup # add a leading slash if missing self.add_url_rule(sup + '/<path:filename>', endpoint='static', handler=static.StaticFileHandler) # now call the hook for changing the setup after initialization self.finalize_setup() # for testing purposes. Set app.config.testing = True and this will be populated. self.last_handler = None # we did not have any request yet self._got_first_request = False # check config if self.config.server_name is None and not self.config.testing: print "*" * 80 print "WARNING: you don't have a server_name set in your configuration and because" print "of that we cannot create proper URLs." print "We will now use localhost as server name but this might create broken URLs." print "*" * 80 print self.config.server_name = "localhost" if self.config.session_cookie_domain is None and not self.config.testing: print "*" * 80 print "WARNING: session_cookie_domain is not set in your configuration" print "this means that depending on the browser, the session cookie" print "can not be stored. This also means that no flash messages will appear" print "*" * 80 print if self.config.testing: self.config.server_name = 'example.org' self.config.session_cookie_domain = 'example.org' #### #### hooks for first request, finalizing and error handling #### def before_first_request(self, request): """if you want to run something on the first incoming request then put it here""" pass def before_handler(self, handler): """this is run before handler processing starts but the handler is already initialized. You can check request, session and all that and maybe add some variables to the handler. If you return something else than None, handler processing will not happen and the return value will be taken as response instead. """ def after_handler(self, handler, response): """This hook is run after the handler processing is done but before the response is sent out. You can check the handler or response and maybe return your own response here. In case you do that this response will be used instead. If you return None then the original handler response will be used. """ def finalize_response(self, response): """with this hook you can do something very generic to a response after all processing. Please not that the response can also be an :class:`~werkzeug.exception.HTTPException` instance which does not have a status code. TODO: Shall we only do finalize on non-exception responses? """ return response def finalize_setup(self): """a hook you can use to finalize the setup. You can add new routes, change configuration values etc. """ def finalize_modules(self): """a hook you can use to add modules to the modules list more dynamically and while using the app's config for it. Simply add them to the ``self.modules`` list via ``append()``. """ #### #### handler related hooks you can override #### def get_render_context(self, handler): """create the global app wide render context which is then passed to the template for rendering. Here you can pass in global variables etc. You also get the active handler for inspecting session, request and so on or for choosing based on the handler what to pass. Note though that handler based parameters are probably better located in the handler itself. :param handler: The active handler instance """ return {} #### #### TEMPLATE related #### @property def jinja_loader(self): """create the jinja template loader for this app. Also modules can create those loaders and the ``create_global_jinja_loader`` method will gather all those together. Hence we have these two functions for obviously doing the same thing. In case you want to change the loader for app templates, simply override this property in your subclassed app. """ if self.config.template_folder is not None: return jinja2.PackageLoader(self.import_name, self.config.template_folder) return None @property def global_jinja_loader(self): """create the global jinja loader by collecting all the loaders of the app and the modules together to one big loader """ return DispatchingJinjaLoader(self) @werkzeug.cached_property def jinja_env(self): """create the jinja environment""" options = dict(self.jinja_options) if 'loader' not in options: options['loader'] = self.global_jinja_loader #if 'autoescape' not in options: #options['autoescape'] = self.select_jinja_autoescape rv = jinja2.Environment(**options) for name, flt in self.jinja_filters.items(): rv.filters[name] = flt return rv #### #### SESSION related #### (directly copied from flask) #### def open_session(self, request): """Creates or opens a new session. Default implementation stores all session data in a signed cookie. This requires that the :attr:`secret_key` is set. Instead of overriding this method we recommend replacing the :class:`session_interface`. :param request: an instance of :attr:`request_class`. """ return self.session_interface.open_session(self, request) def save_session(self, session, response): """Saves the session if it needs updates. For the default implementation, check :meth:`open_session`. Instead of overriding this method we recommend replacing the :class:`session_interface`. :param session: the session to be saved (a :class:`~werkzeug.contrib.securecookie.SecureCookie` object) :param response: an instance of :attr:`response_class` """ return self.session_interface.save_session(self, session, response) def make_null_session(self): """Creates a new instance of a missing session. Instead of overriding this method we recommend replacing the :class:`session_interface`. .. versionadded:: 0.7 """ return self.session_interface.make_null_session(self) #### #### CONFIGURATION related #### def add_url_rule(self, url_or_path, endpoint=None, handler=None, **options): """add another url rule to the url map""" if isinstance(url_or_path, URL): path = url_or_path.path endpoint = url_or_path.endpoint handler = url_or_path.handler options = url_or_path.options else: path = url_or_path if endpoint is None: assert handler is not None, "handler and endpoint not provided" endpoint = handler.__name__ options['endpoint'] = endpoint options['defaults'] = options.get('defaults') or None rule = self.url_rule_class(path, **options) self.url_map.add(rule) if handler is not None: self.handlers[endpoint] = handler #### #### request processing #### @property def propagate_exceptions(self): """Returns the value of the `propagate_exceptions` configuration value in case it's set, otherwise a sensible default is returned. """ rv = self.config.propagate_exceptions if rv is not None: return rv return self.config.testing or self.config.debug def check_first_request(self, request): """check if we have already run the hooks for the first request. If not, do so now""" if not self._got_first_request: self.before_first_request(request) self._got_first_request = True def find_handler(self, request): """retrieve the handler for the given URL. If it is not found, a routing exception is raised. :returns: an instance of :class:`~starflyer.Handler` """ # create the url adapter urls = self.create_url_adapter(request) try: url_rule, request.view_args = urls.match(return_rule=True) request.url_rule = url_rule request.url_adapter = urls except werkzeug.exceptions.HTTPException, e: # this basically means 404 but maybe some debugging can occur # this is reraised then though self.raise_routing_exception(request, e) # check if we are called from a module module = None endpoint = url_rule.endpoint parts = endpoint.split(".") if len(parts) == 2: module_name = parts[0] module = self.module_map.get(module_name, None) # try to find the right handler for this url and instantiate it return self.handlers[url_rule.endpoint](self, request, module=module)
class Application(object): """a base class for dispatching WSGI requests""" defaults = {} routes = [] # list of rules error_handlers = {} # mapping from error code to error handler classes handlers = {} # mapping from endpoint to handler classes # class to be used for URL Routes url_rule_class = werkzeug.routing.Rule # session interface session_interface = sessions.SecureCookieSessionInterface() # class to be used as test client class test_client_class = None # response class to use response_class = wrappers.Response request_class = wrappers.Request # last handler for testing last_handler = None # enforeced defaults (these have to be existent in the config # for starflyer to work (DO NOT CHANGE!) enforced_defaults = { 'session_cookie_name' : "s", 'secret_key' : None, 'permanent_session_lifetime' : datetime.timedelta(days=31), 'session_cookie_domain' : None, 'session_cookie_path' : None, 'session_cookie_httponly' : True, 'session_cookie_secure' : False, 'logger_name' : None, 'server_name' : None, 'application_root' : None, 'preferred_url_scheme' : "http", 'propagate_exceptions' : None, # this is used for testing and debugging and means to re-raise it and not use an error handler for uncaught exceptions 'debug' : False, 'testing' : False, 'force_exceptions' : False, # True = make also http exceptions raise and not return a response (for testing) 'static_cache_timeout' : 12 * 60 * 60, 'template_folder' : "templates/", 'static_folder' : "static/", 'static_url_path' : "/static", 'modules' : AttributeMapper(), # config placeholder for modules } # here you can define which types the config parameters are supposed to be in config_types = { 'debug' : bool, 'testing' : bool, 'session_cookie_httponly' : bool, 'session_cookie_secure' : bool, } jinja_options = ImmutableDict( extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_', 'jinja2.ext.i18n'] ) jinja_filters = ImmutableDict() modules = [] # list of modules module_map = AttributeMapper() # mapped version of modules def __init__(self, import_name, config={}, **kw): """initialize the Application :param import_name: the __name__ of the module we are running under. :param config: a dictionary of configuration values """ self.import_name = import_name # initialize URL mapping variables self.url_map = werkzeug.routing.Map() self.handlers = {} # initialize configuration self.config = AttributeMapper(self.enforced_defaults or {}) # initialize module configuration for module in self.modules: self.config.modules.setdefault(module.name, AttributeMapper()) # update configuration from our self.config.update(self.defaults) # first read module configuration from a file if given if "module_config_file" in config: cfg = ConfigParser() cfg.read(config['module_config_file']) for section in cfg.sections(): for option in cfg.options(section): config["modules.%s.%s" %(section,option)] = cfg.get(section, option) # now override defaults from config eventually self.config.update(fix_types(config, self.config_types)) # lastly override via keyword arguments self.config.update(fix_types(kw, self.config_types)) # TODO: update from environment vars? self.finalize_modules() # let user dynamically add some modules # now bind all the modules to our app and create a mapping # we also make sure he that URLs of modules are registered first so their namespaces work correctly for module in self.modules: module.bind_to_app(self) self.module_map[module.name] = module # initialize the actual routes for route in self.routes: self.add_url_rule( route.path, route.endpoint, route.handler, **route.options) # clean up static url path if self.config.static_folder is not None: sup = self.config.static_url_path if sup.endswith("/"): sup = sup[:-1] # remove any trailing slash if not sup.startswith("/"): sup = "/"+sup # add a leading slash if missing self.add_url_rule(sup+ '/<path:filename>', endpoint='static', handler=static.StaticFileHandler) # now call the hook for changing the setup after initialization self.finalize_setup() # for testing purposes. Set app.config.testing = True and this will be populated. self.last_handler = None # we did not have any request yet self._got_first_request = False # check config if self.config.server_name is None and not self.config.testing: print "*"*80 print "WARNING: you don't have a server_name set in your configuration and because" print "of that we cannot create proper URLs." print "We will now use localhost as server name but this might create broken URLs." print "*"*80 print self.config.server_name = "localhost" if self.config.session_cookie_domain is None and not self.config.testing: print "*"*80 print "WARNING: session_cookie_domain is not set in your configuration" print "this means that depending on the browser, the session cookie" print "can not be stored. This also means that no flash messages will appear" print "*"*80 print if self.config.testing: self.config.server_name = 'example.org' self.config.session_cookie_domain = 'example.org' #### #### hooks for first request, finalizing and error handling #### def before_first_request(self, request): """if you want to run something on the first incoming request then put it here""" pass def before_handler(self, handler): """this is run before handler processing starts but the handler is already initialized. You can check request, session and all that and maybe add some variables to the handler. If you return something else than None, handler processing will not happen and the return value will be taken as response instead. """ def after_handler(self, handler, response): """This hook is run after the handler processing is done but before the response is sent out. You can check the handler or response and maybe return your own response here. In case you do that this response will be used instead. If you return None then the original handler response will be used. """ def finalize_response(self, response): """with this hook you can do something very generic to a response after all processing. Please not that the response can also be an :class:`~werkzeug.exception.HTTPException` instance which does not have a status code. TODO: Shall we only do finalize on non-exception responses? """ return response def finalize_setup(self): """a hook you can use to finalize the setup. You can add new routes, change configuration values etc. """ def finalize_modules(self): """a hook you can use to add modules to the modules list more dynamically and while using the app's config for it. Simply add them to the ``self.modules`` list via ``append()``. """ #### #### handler related hooks you can override #### def get_render_context(self, handler): """create the global app wide render context which is then passed to the template for rendering. Here you can pass in global variables etc. You also get the active handler for inspecting session, request and so on or for choosing based on the handler what to pass. Note though that handler based parameters are probably better located in the handler itself. :param handler: The active handler instance """ return {} #### #### TEMPLATE related #### @property def jinja_loader(self): """create the jinja template loader for this app. Also modules can create those loaders and the ``create_global_jinja_loader`` method will gather all those together. Hence we have these two functions for obviously doing the same thing. In case you want to change the loader for app templates, simply override this property in your subclassed app. """ if self.config.template_folder is not None: return jinja2.PackageLoader(self.import_name, self.config.template_folder) return None @property def global_jinja_loader(self): """create the global jinja loader by collecting all the loaders of the app and the modules together to one big loader """ return DispatchingJinjaLoader(self) @werkzeug.cached_property def jinja_env(self): """create the jinja environment""" options = dict(self.jinja_options) if 'loader' not in options: options['loader'] = self.global_jinja_loader #if 'autoescape' not in options: #options['autoescape'] = self.select_jinja_autoescape rv = jinja2.Environment(**options) for name, flt in self.jinja_filters.items(): rv.filters[name] = flt return rv #### #### SESSION related #### (directly copied from flask) #### def open_session(self, request): """Creates or opens a new session. Default implementation stores all session data in a signed cookie. This requires that the :attr:`secret_key` is set. Instead of overriding this method we recommend replacing the :class:`session_interface`. :param request: an instance of :attr:`request_class`. """ return self.session_interface.open_session(self, request) def save_session(self, session, response): """Saves the session if it needs updates. For the default implementation, check :meth:`open_session`. Instead of overriding this method we recommend replacing the :class:`session_interface`. :param session: the session to be saved (a :class:`~werkzeug.contrib.securecookie.SecureCookie` object) :param response: an instance of :attr:`response_class` """ return self.session_interface.save_session(self, session, response) def make_null_session(self): """Creates a new instance of a missing session. Instead of overriding this method we recommend replacing the :class:`session_interface`. .. versionadded:: 0.7 """ return self.session_interface.make_null_session(self) #### #### CONFIGURATION related #### def add_url_rule(self, url_or_path, endpoint = None, handler = None, **options): """add another url rule to the url map""" if isinstance(url_or_path, URL): path = url_or_path.path endpoint = url_or_path.endpoint handler = url_or_path.handler options = url_or_path.options else: path = url_or_path if endpoint is None: assert handler is not None, "handler and endpoint not provided" endpoint = handler.__name__ options['endpoint'] = endpoint options['defaults'] = options.get('defaults') or None rule = self.url_rule_class(path, **options) self.url_map.add(rule) if handler is not None: self.handlers[endpoint] = handler #### #### request processing #### @property def propagate_exceptions(self): """Returns the value of the `propagate_exceptions` configuration value in case it's set, otherwise a sensible default is returned. """ rv = self.config.propagate_exceptions if rv is not None: return rv return self.config.testing or self.config.debug def check_first_request(self, request): """check if we have already run the hooks for the first request. If not, do so now""" if not self._got_first_request: self.before_first_request(request) self._got_first_request = True def find_handler(self, request): """retrieve the handler for the given URL. If it is not found, a routing exception is raised. :returns: an instance of :class:`~starflyer.Handler` """ # create the url adapter urls = self.create_url_adapter(request) try: url_rule, request.view_args = urls.match(return_rule=True) request.url_rule = url_rule request.url_adapter = urls except werkzeug.exceptions.HTTPException, e: # this basically means 404 but maybe some debugging can occur # this is reraised then though self.raise_routing_exception(request, e) # check if we are called from a module module = None endpoint = url_rule.endpoint parts = endpoint.split(".") if len(parts)==2: module_name = parts[0] module = self.module_map.get(module_name, None) # try to find the right handler for this url and instantiate it return self.handlers[url_rule.endpoint](self, request, module = module)