app: Add BaseApplication class

The `Application` class is now a sub-class of `BaseApplication`.
Applications that require more control of the framework's behavior can
extend `BaseApplication` instead of `Application`. The `BaseApplication`
class also provides some new features:

* `BaseApplication.setup_routes` is always called when an instance is
  created, and can be used to configure the request dispatcher
* `BaseApplication.update_config` updates the application configuration
  from a configuration file

The major difference between `BaseApplication` and `Application` is that
the latter requires a request dispatcher, while the former does not.
This pattern allows simple applications to construct a `Router` or root
object and then initialize the `Application` without needing to define a
sub-class.
master
Dustin 2016-07-10 19:42:39 -05:00
parent 4090df1286
commit 44a28fda68
1 changed files with 73 additions and 24 deletions

View File

@ -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):