diff --git a/src/milla/app.py b/src/milla/app.py index ad86df9..9334de1 100644 --- a/src/milla/app.py +++ b/src/milla/app.py @@ -21,40 +21,33 @@ Please give me a docstring! :Updater: $Author$ ''' +from milla import util from milla.controllers import FaviconController -from milla.util import asbool from webob.exc import HTTPNotFound, WSGIHTTPException, HTTPMethodNotAllowed import milla.dispatch.traversal +import os import sys -__all__ = ['Application'] +__all__ = [ + 'Application', + 'BaseApplication', +] -class Application(object): - '''Represents a Milla web application +class BaseApplication(object): + '''Base class for Milla applications - Constructing an ``Application`` instance needs a dispatcher, or - alternatively, a root object that will be passed to a new - :py:class:`milla.dispatch.traversal.Traverser`. - - :param obj: An object implementing the dispatcher protocol, or an - object to be used as the root for a Traverser - - ``Application`` instances are WSGI applications. - - .. py:attribute:: config - - A mapping of configuration settings. For each request, the - configuration is copied and assigned to ``request.config``. + This class can be used by applications that need to customize the + behavior of the framework. In most cases, :py:class:`Application` + instances can be created directly and a sublcass is not necessary. ''' - def __init__(self, obj): - if not hasattr(obj, 'resolve'): - # Object is not a dispatcher, but the root object for traversal - obj = milla.dispatch.traversal.Traverser(obj) - self.dispatcher = obj - self.config = {'milla.favicon': True} + DEFAULT_CONFIG = {} + + def __init__(self): + self.config = self.DEFAULT_CONFIG.copy() + self.setup_routes() def __call__(self, environ, start_response): start_response = StartResponseWrapper(start_response) @@ -127,6 +120,32 @@ class Application(object): return self._find_attr(obj.func, attr) raise + def update_config(self, filename): + '''Update application configuration from a file + + :param filename: Path to configuration file + + This method will update the application configuration using + the values found in the specified configuration file. If the + specified file does not exist or is not accessible, no + changes will be made. + + The configuration file will be read using + :py:func:`milla.util.read_config`. + ''' + + if filename and os.access(filename, os.R_OK): + self.config.update(util.read_config(filename)) + + def setup_routes(self): + '''Initialize the request dispatcher + + The default implementation of this method does nothing, but it + is called when an instance of :py:class:`BaseApplication` is + created, and can be used by subclasses to set up the request + dispatcher. + ''' + def make_request(self, environ): '''Create a :py:class:`~milla.Request` from a WSGI environment @@ -162,7 +181,7 @@ class Application(object): return self.dispatcher.resolve(path_info) except milla.dispatch.UnresolvedPath: if (path_info == '/favicon.ico' and - asbool(self.config.get('milla.favicon'))): + util.asbool(self.config.get('milla.favicon'))): return FaviconController() else: return path_not_found @@ -182,6 +201,36 @@ class Application(object): raise +class Application(BaseApplication): + '''Represents a Milla web application + + Constructing an ``Application`` instance needs a dispatcher, or + alternatively, a root object that will be passed to a new + :py:class:`milla.dispatch.traversal.Traverser`. + + :param obj: An object implementing the dispatcher protocol, or an + object to be used as the root for a Traverser + + ``Application`` instances are WSGI applications. + + .. py:attribute:: config + + A mapping of configuration settings. For each request, the + configuration is copied and assigned to ``request.config``. + ''' + + DEFAULT_CONFIG = { + 'milla.favicon': True, + } + + def __init__(self, obj): + super(Application, self).__init__() + if not hasattr(obj, 'resolve'): + # Object is not a dispatcher, but the root object for traversal + obj = milla.dispatch.traversal.Traverser(obj) + self.dispatcher = obj + + class StartResponseWrapper(): def __init__(self, start_response):