commit
fed7d0fb3d
|
@ -24,7 +24,7 @@ Please give me a docstring!
|
||||||
from milla.controllers import FaviconController
|
from milla.controllers import FaviconController
|
||||||
from milla.util import asbool
|
from milla.util import asbool
|
||||||
from webob.exc import HTTPNotFound, WSGIHTTPException, HTTPMethodNotAllowed
|
from webob.exc import HTTPNotFound, WSGIHTTPException, HTTPMethodNotAllowed
|
||||||
import milla.dispatch
|
import milla.dispatch.traversal
|
||||||
|
|
||||||
__all__ = ['Application']
|
__all__ = ['Application']
|
||||||
|
|
||||||
|
@ -35,9 +35,8 @@ class Application(object):
|
||||||
alternatively, a root object that will be passed to a new
|
alternatively, a root object that will be passed to a new
|
||||||
:py:class:`milla.dispatch.traversal.Traverser`.
|
:py:class:`milla.dispatch.traversal.Traverser`.
|
||||||
|
|
||||||
:param root: A root object, passed to a traverser, which is
|
:param obj: An object implementing the dispatcher protocol, or an
|
||||||
automatically created if a root is given
|
object to be used as the root for a Traverser
|
||||||
:param dispatcher: An object implementing the dispatcher protocol
|
|
||||||
|
|
||||||
``Application`` instances are WSGI applications.
|
``Application`` instances are WSGI applications.
|
||||||
|
|
||||||
|
@ -49,8 +48,11 @@ class Application(object):
|
||||||
|
|
||||||
DEFAULT_ALLOWED_METHODS = ['GET', 'HEAD']
|
DEFAULT_ALLOWED_METHODS = ['GET', 'HEAD']
|
||||||
|
|
||||||
def __init__(self, dispatcher):
|
def __init__(self, obj):
|
||||||
self.dispatcher = dispatcher
|
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}
|
self.config = {'milla.favicon': True}
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
try:
|
||||||
|
import configparser
|
||||||
|
except ImportError:
|
||||||
|
import ConfigParser as configparser
|
||||||
|
|
||||||
|
def read_config(filename):
|
||||||
|
'''Parse an ini file into a nested dictionary
|
||||||
|
|
||||||
|
:param string filename: Path to the ini file to read
|
||||||
|
:returns: A dictionary whose keys correspond to the section and
|
||||||
|
option, joined with a dot character (.)
|
||||||
|
|
||||||
|
For example, consider the following ini file::
|
||||||
|
|
||||||
|
[xmen]
|
||||||
|
storm = Ororo Monroe
|
||||||
|
cyclops = Scott Summers
|
||||||
|
|
||||||
|
[avengers]
|
||||||
|
hulk = Bruce Banner
|
||||||
|
iron_man = Tony Stark
|
||||||
|
|
||||||
|
The resulting dictionary would look like this::
|
||||||
|
|
||||||
|
{
|
||||||
|
'xmen.storm': 'Ororo Monroe',
|
||||||
|
'xmen.cyclops': 'Scott Summers',
|
||||||
|
'avengers.hulk': 'Bruce Banner',
|
||||||
|
'avengers.iron_man': 'Tony Stark',
|
||||||
|
}
|
||||||
|
|
||||||
|
Thus, the option values for any section can be obtained as follows::
|
||||||
|
|
||||||
|
config['xmen.storm']
|
||||||
|
|
||||||
|
This dictionary can be used to configure an :py:class:`~milla.Application`
|
||||||
|
instance by using the ``update`` method::
|
||||||
|
|
||||||
|
app = milla.Application(router)
|
||||||
|
app.config.update(config)
|
||||||
|
'''
|
||||||
|
|
||||||
|
cparser = configparser.SafeConfigParser()
|
||||||
|
cparser.readfp(open(filename))
|
||||||
|
|
||||||
|
config = {}
|
||||||
|
for section in cparser.sections():
|
||||||
|
for option in cparser.options(section):
|
||||||
|
config['.'.join((section, option))] = cparser.get(section, option)
|
||||||
|
return config
|
|
@ -97,7 +97,7 @@ class Router(object):
|
||||||
for attr in functools.WRAPPER_ASSIGNMENTS:
|
for attr in functools.WRAPPER_ASSIGNMENTS:
|
||||||
try:
|
try:
|
||||||
value = getattr(controller, attr)
|
value = getattr(controller, attr)
|
||||||
except AttributeError:
|
except AttributeError: #pragma: no cover
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
setattr(func, attr, value)
|
setattr(func, attr, value)
|
||||||
|
|
|
@ -457,3 +457,31 @@ def test_create_href_full_keywords():
|
||||||
request = milla.Request(environ)
|
request = milla.Request(environ)
|
||||||
url = request.create_href_full('/bar', foo='baz')
|
url = request.create_href_full('/bar', foo='baz')
|
||||||
assert url == 'http://127.0.0.1/bar?foo=baz'
|
assert url == 'http://127.0.0.1/bar?foo=baz'
|
||||||
|
|
||||||
|
def test_static_resource():
|
||||||
|
'''Request.static_resource creates valid URL from config'''
|
||||||
|
|
||||||
|
def controller(request):
|
||||||
|
return request.static_resource('/image.png')
|
||||||
|
|
||||||
|
environ = environ_for_testing()
|
||||||
|
app = milla.Application(StubResolver(controller))
|
||||||
|
app.config['milla.static_root'] = '/static'
|
||||||
|
response = ResponseMaker()
|
||||||
|
app_iter = app(environ, response.start_response)
|
||||||
|
response.finish_response(app_iter)
|
||||||
|
assert response.body == b'/static/image.png', response.body
|
||||||
|
|
||||||
|
def test_static_resource_undefined():
|
||||||
|
'''Request.static_resource returns the path unmodified with no root defined'''
|
||||||
|
|
||||||
|
def controller(request):
|
||||||
|
return request.static_resource('/image.png')
|
||||||
|
|
||||||
|
environ = environ_for_testing()
|
||||||
|
app = milla.Application(StubResolver(controller))
|
||||||
|
response = ResponseMaker()
|
||||||
|
app_iter = app(environ, response.start_response)
|
||||||
|
response.finish_response(app_iter)
|
||||||
|
assert response.body == b'/image.png', response.body
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
:Updater: $Author$
|
:Updater: $Author$
|
||||||
'''
|
'''
|
||||||
import milla.dispatch.routing
|
import milla.dispatch.routing
|
||||||
|
import nose.tools
|
||||||
|
|
||||||
def fake_controller():
|
def fake_controller():
|
||||||
pass
|
pass
|
||||||
|
@ -44,6 +45,7 @@ def test_urlvars():
|
||||||
assert func.keywords['bar'] == 'abc'
|
assert func.keywords['bar'] == 'abc'
|
||||||
assert func.keywords['baz'] == 'def'
|
assert func.keywords['baz'] == 'def'
|
||||||
|
|
||||||
|
@nose.tools.raises(milla.dispatch.UnresolvedPath)
|
||||||
def test_regexp_urlvar():
|
def test_regexp_urlvar():
|
||||||
'''Ensure the dispatcher can resolve alternate regexps in urlvars
|
'''Ensure the dispatcher can resolve alternate regexps in urlvars
|
||||||
|
|
||||||
|
@ -61,13 +63,9 @@ def test_regexp_urlvar():
|
||||||
assert func.func == controller
|
assert func.func == controller
|
||||||
assert func.keywords['arg'] == 'abcde'
|
assert func.keywords['arg'] == 'abcde'
|
||||||
|
|
||||||
try:
|
router.resolve('/test/1234')
|
||||||
func = router.resolve('/test/1234')
|
|
||||||
except milla.dispatch.UnresolvedPath:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise AssertionError
|
|
||||||
|
|
||||||
|
@nose.tools.raises(milla.dispatch.UnresolvedPath)
|
||||||
def test_unresolved():
|
def test_unresolved():
|
||||||
'''Ensure the resolver raises an exception for unresolved paths
|
'''Ensure the resolver raises an exception for unresolved paths
|
||||||
|
|
||||||
|
@ -80,12 +78,7 @@ def test_unresolved():
|
||||||
|
|
||||||
router = milla.dispatch.routing.Router()
|
router = milla.dispatch.routing.Router()
|
||||||
router.add_route('/test', controller)
|
router.add_route('/test', controller)
|
||||||
try:
|
|
||||||
router.resolve('/tset')
|
router.resolve('/tset')
|
||||||
except milla.dispatch.UnresolvedPath:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise AssertionError
|
|
||||||
|
|
||||||
def test_unrelated():
|
def test_unrelated():
|
||||||
'''Ensure the dispatcher is not confused by unrelated paths
|
'''Ensure the dispatcher is not confused by unrelated paths
|
||||||
|
@ -119,3 +112,41 @@ def test_string_controller():
|
||||||
router.add_route('/test', 'milla.tests.test_routing:fake_controller')
|
router.add_route('/test', 'milla.tests.test_routing:fake_controller')
|
||||||
func = router.resolve('/test')
|
func = router.resolve('/test')
|
||||||
assert func.func == fake_controller
|
assert func.func == fake_controller
|
||||||
|
|
||||||
|
@nose.tools.raises(milla.HTTPMovedPermanently)
|
||||||
|
def test_trailing_slash_redir():
|
||||||
|
'''Paths that match except the trailing slash return a HTTP redirect
|
||||||
|
'''
|
||||||
|
|
||||||
|
def controller():
|
||||||
|
pass
|
||||||
|
|
||||||
|
router = milla.dispatch.routing.Router()
|
||||||
|
router.add_route('/test/', controller)
|
||||||
|
func = router.resolve('/test')
|
||||||
|
assert func is not controller
|
||||||
|
func()
|
||||||
|
|
||||||
|
@nose.tools.raises(milla.dispatch.routing.UnresolvedPath)
|
||||||
|
def test_trailing_slash_none():
|
||||||
|
'''Paths that match except the trailing slash are ignored
|
||||||
|
'''
|
||||||
|
|
||||||
|
def controller():
|
||||||
|
pass
|
||||||
|
|
||||||
|
router = milla.dispatch.routing.Router(None)
|
||||||
|
router.add_route('/test/', controller)
|
||||||
|
router.resolve('/test')
|
||||||
|
|
||||||
|
def test_trailing_slash_silent():
|
||||||
|
'''Paths that match except the trailing slash are treated the same
|
||||||
|
'''
|
||||||
|
|
||||||
|
def controller():
|
||||||
|
pass
|
||||||
|
|
||||||
|
router = milla.dispatch.routing.Router(milla.dispatch.routing.Router.SILENT)
|
||||||
|
router.add_route('/test/', controller)
|
||||||
|
func = router.resolve('/test')
|
||||||
|
assert func.func is controller
|
Loading…
Reference in New Issue