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.util import asbool
from webob.exc import HTTPNotFound, WSGIHTTPException, HTTPMethodNotAllowed
import milla.dispatch
import milla.dispatch.traversal
__all__ = ['Application']
@ -35,9 +35,8 @@ class Application(object):
alternatively, a root object that will be passed to a new
:py:class:`milla.dispatch.traversal.Traverser`.
:param root: A root object, passed to a traverser, which is
automatically created if a root is given
:param dispatcher: An object implementing the dispatcher protocol
: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.
@ -49,8 +48,11 @@ class Application(object):
DEFAULT_ALLOWED_METHODS = ['GET', 'HEAD']
def __init__(self, dispatcher):
self.dispatcher = dispatcher
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}
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:
try:
value = getattr(controller, attr)
except AttributeError:
except AttributeError: #pragma: no cover
pass
else:
setattr(func, attr, value)

View File

@ -457,3 +457,31 @@ def test_create_href_full_keywords():
request = milla.Request(environ)
url = request.create_href_full('/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$
'''
import milla.dispatch.routing
import nose.tools
def fake_controller():
pass
@ -44,6 +45,7 @@ def test_urlvars():
assert func.keywords['bar'] == 'abc'
assert func.keywords['baz'] == 'def'
@nose.tools.raises(milla.dispatch.UnresolvedPath)
def test_regexp_urlvar():
'''Ensure the dispatcher can resolve alternate regexps in urlvars
@ -61,13 +63,9 @@ def test_regexp_urlvar():
assert func.func == controller
assert func.keywords['arg'] == 'abcde'
try:
func = router.resolve('/test/1234')
except milla.dispatch.UnresolvedPath:
pass
else:
raise AssertionError
router.resolve('/test/1234')
@nose.tools.raises(milla.dispatch.UnresolvedPath)
def test_unresolved():
'''Ensure the resolver raises an exception for unresolved paths
@ -80,12 +78,7 @@ def test_unresolved():
router = milla.dispatch.routing.Router()
router.add_route('/test', controller)
try:
router.resolve('/tset')
except milla.dispatch.UnresolvedPath:
pass
else:
raise AssertionError
def test_unrelated():
'''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')
func = router.resolve('/test')
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