Merge default into py3k

--HG--
branch : py3k
master
Dustin C. Hatch 2012-12-19 15:38:36 -06:00
commit fed7d0fb3d
5 changed files with 130 additions and 19 deletions

View File

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

50
src/milla/config.py Normal file
View File

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

View File

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

View File

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

View File

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