Whitespace cleanup

master
Dustin 2015-04-15 19:45:17 -05:00
parent 6519cfbb9e
commit 86b19bb9e7
11 changed files with 228 additions and 222 deletions

View File

@ -1,11 +1,11 @@
# Copyright 2011 Dustin C. Hatch
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -21,17 +21,18 @@ from webob.exc import *
import webob
try:
import urllib.parse
except ImportError: #pragma: no cover
except ImportError: # pragma: no cover
import urllib
import urlparse
urllib.parse = urlparse
urllib.parse.urlencode = urllib.urlencode #@UndefinedVariable
urllib.parse.urlencode = urllib.urlencode
def allow(*methods):
'''Specify the allowed HTTP verbs for a controller callable
Example::
@milla.allow('GET', 'POST')
def controller(request):
return 'Hello, world!'
@ -56,12 +57,12 @@ class Request(webob.Request):
@classmethod
def blank(cls, path, *args, **kwargs):
'''Create a simple request for the specified path
'''Create a simple request for the specified path
See :py:meth:`webob.Request.blank <webob.request.BaseRequest.blank>`
for information on other arguments and keywords
'''
req = super(Request, cls).blank(path, *args, **kwargs)
req.config = {}
return req
@ -73,7 +74,7 @@ class Request(webob.Request):
Any other keyword arguments will be encoded and appended to the URL
as querystring arguments.
The HREF returned will will be the absolute path on the same host
and protocol as the request. To get the full URL including scheme
and host information, use :py:meth:`create_href_full` instead.
@ -88,22 +89,22 @@ class Request(webob.Request):
def create_href_full(self, path, **keywords):
'''Combine the application's full URL with a path to form a new URL
:param path: relative path to join with the request URL
Any other keyword arguments will be encoded and appended to the
URL as querystring arguments/
The HREF returned will be the full URL, including scheme and host
information. To get the path only, use :py:meth:`create_href`
instead.
'''
url = self._merge_url(self.application_url, path)
if keywords:
url += '?' + urllib.parse.urlencode(keywords)
return url
def static_resource(self, path):

View File

@ -28,6 +28,7 @@ import milla.dispatch.traversal
__all__ = ['Application']
class Application(object):
'''Represents a Milla web application
@ -88,7 +89,7 @@ class Application(object):
return response
func = options_response
else:
func = HTTPMethodNotAllowed(headers=allow_header)
func = HTTPMethodNotAllowed(headers=allow_header)
return func(environ, start_response)
start_response_wrapper = StartResponseWrapper(start_response)
@ -118,7 +119,7 @@ class Application(object):
return ''
else:
return response.app_iter
def _call_after(self, func):
try:
return self._find_attr(func, '__after__')
@ -130,7 +131,7 @@ class Application(object):
return self._find_attr(func, '__before__')
except AttributeError:
return lambda r: None
def _find_attr(self, obj, attr):
try:
# Object has the specified attribute itself

View File

@ -1,11 +1,11 @@
# Copyright 2011 Dustin C. Hatch
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -21,7 +21,7 @@
class NotAuthorized(Exception):
'''Base class for unauthorized exceptions
This class is both an exception and a controller callable. If the
request validator raises an instance of this class, it will be
called and the resulting value will become the HTTP response. The
@ -33,17 +33,17 @@ class NotAuthorized(Exception):
'''Return a response indicating the request is not authorized
:param request: WebOb Request instance for the current request
All other arguments and keywords are ignored.
'''
response = request.ResponseClass(str(self))
response.status_int = 403
return response
class RequestValidator(object):
'''Base class for request validators
A request validator is a class that exposes a ``validate`` method,
which accepts an instance of :py:class:`webob.Request` and an
optional ``requirement``. The ``validate`` method should return
@ -51,7 +51,7 @@ class RequestValidator(object):
:py:exc:`NotAuthorized` on failure. The base implementation will
raise an instance of the exception specified by
:py:attr:`exc_class`, which defaults to :py:class`NotAuthorized`.
To customize the response to unauthorized requests, it is
sufficient to subclass :py:class:`NotAuthorized`, override its
:py:meth:`~NotAuthorized.__call__` method, and specify the class
@ -63,7 +63,7 @@ class RequestValidator(object):
def validate(self, request, requirement=None):
'''Validates a request
:param request: The request to validate. Should be an instance
of :py:class:`webob.Request`.
:param requirement: (Optional) A requirement to check. Should be
@ -71,10 +71,10 @@ class RequestValidator(object):
or :py:class:`~milla.auth.permissions.PermissionRequirement`,
or some other class with a ``check`` method that accepts a
sequence of permissions.
The base implementation will perform authorization in the
following way:
1. Does the ``request`` have a ``user`` attribute? If not,
raise :py:exc:`NotAuthorized`.
2. Is the truth value of ``request.user`` true? If not, raise
@ -83,16 +83,16 @@ class RequestValidator(object):
attribute? If not, raise :py:exc:`NotAuthorized`.
4. Do the user's permissions meet the requirements? If not,
raise :py:exc:`NotAuthorized`.
If none of the above steps raised an exception, the method will
return ``None``, indicating that the validation was successful.
.. note:: WebOb Request instances do not have a ``user``
attribute by default. You will need to supply this yourself,
i.e. in a WSGI middleware or in the ``__before__`` method of
your controller class.
'''
try:
user = request.user
except AttributeError:

View File

@ -1,11 +1,11 @@
# Copyright 2011 Dustin C. Hatch
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,8 +15,6 @@
:Created: Mar 3, 2011
:Author: dustin
:Updated: $Date$
:Updater: $Author$
'''
from functools import wraps
@ -24,10 +22,13 @@ from milla.auth import RequestValidator, NotAuthorized, permissions
import milla
import pkg_resources
__all__ = ['auth_required', 'require_perms']
VALIDATOR_EP_GROUP = 'milla.request_validator'
def _find_request(*args, **kwargs):
try:
return kwargs['request']
@ -36,6 +37,7 @@ def _find_request(*args, **kwargs):
if isinstance(arg, milla.Request):
return arg
def _validate_request(func, requirement, *args, **kwargs):
request = _find_request(*args, **kwargs)
ep_name = request.config.get('request_validator', 'default')
@ -55,20 +57,21 @@ def _validate_request(func, requirement, *args, **kwargs):
return e(request)
return func(*args, **kwargs)
def auth_required(func):
'''Simple decorator to enforce authentication for a controller
Example usage::
class SomeController(object):
def __before__(request):
request.user = find_a_user_somehow(request)
@milla.auth_required
def __call__(request):
return 'Hello, world!'
In this example, the ``SomeController`` controller class implements
an ``__before__`` method that adds the ``user`` attribute to the
``request`` instance. This could be done by extracting user
@ -76,7 +79,7 @@ def auth_required(func):
method is decorated with ``auth_required``, which will ensure that
the user is successfully authenticated. This is handled by a
*request validator*.
If the request is not authorized, the decorated method will never
be called. Instead, the response is generated by calling the
:py:exc:`~milla.auth.NotAuthorized` exception raised inside
@ -88,20 +91,21 @@ def auth_required(func):
return _validate_request(func, None, *args, **kwargs)
return wrapper
class require_perms(object):
'''Decorator that requires the user have certain permissions
'''Decorator that requires the user have certain permissions
Example usage::
class SomeController(object):
def __before__(request):
request.user = find_a_user_somehow(request)
@milla.require_perms('some_permission', 'and_this_permission')
def __call__(request):
return 'Hello, world!'
In this example, the ``SomeController`` controller class implements
an ``__before__`` method that adds the ``user`` attribute to the
``request`` instance. This could be done by extracting user
@ -109,9 +113,9 @@ class require_perms(object):
method is decorated with ``require_perms``, which will ensure that
the user is successfully authenticated and the the user has the
specified permissions. This is handled by a *request validator*.
There are two ways to specify the required permissions:
* By passing the string name of all required permissions as
positional arguments. A complex permission requirement will be
constructed that requires *all* of the given permissions to be

View File

@ -1,11 +1,11 @@
# Copyright 2011 Dustin C. Hatch
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,11 +18,11 @@ Examples::
>>> req = Permission('foo') & Permission('bar')
>>> req.check(PermissionContainer(['foo', 'baz'], ['bar']))
True
>>> req = Permission('login')
>>> req.check(['login'])
True
>>> req = Permission('login') | Permission('admin')
>>> req.check(['none'])
False
@ -30,62 +30,62 @@ Examples::
class PermissionContainer(object):
'''Container object for user and group permissions
:param list user_perms: List of permissions held by the user itself
:param list group_perms: List of permissions held by the groups to
which the user belongs
Iterating over :py:class:`PermissionContainer` objects results in
a flattened representation of all permissions.
'''
def __init__(self, user_perms=[], group_perms=[]):
self._user_perms = user_perms
self._group_perms = group_perms
def __iter__(self):
for perm in self._user_perms:
yield perm
for perm in self._group_perms:
yield perm
def __contains__(self, perm):
return perm in self._user_perms or perm in self._group_perms
class BasePermission(object):
'''Base class for permissions and requirements
Complex permission requirements can be created using the bitwise
``and`` and ``or`` operators::
login_and_view = Permission('login') & Permission('view')
admin_or_root = Permission('admin') | Permission('root')
complex = Permission('login') & Permission('view') | Permission('admin')
'''
def __and__(self, other):
assert isinstance(other, BasePermission)
return PermissionRequirementAll(self, other)
def __or__(self, other):
assert isinstance(other, BasePermission)
return PermissionRequirementAny(self, other)
class Permission(BasePermission):
'''Simple permission implementation
:param str name: Name of the permission
Permissions must implement a ``check`` method that accepts an
iterable and returns ``True`` if the permission is present or
``False`` otherwise.
'''
def __init__(self, name):
self.name = name
def __str__(self):
return str(self.name)
@ -94,27 +94,27 @@ class Permission(BasePermission):
def check(self, perms):
'''Check if the permission is held
This method can be overridden to provide more robust
support, but this implementation is simple::
return self in perms
'''
return self in perms
class PermissionRequirement(BasePermission):
'''Base class for complex permission requirements'''
def __init__(self, *requirements):
self.requirements = requirements
def __str__(self):
return ', '.join(self.requirements)
class PermissionRequirementAll(PermissionRequirement):
'''Complex permission requirement needing all given permissions'''
def check(self, perms):
for req in self.requirements:
if not req.check(perms):
@ -123,7 +123,7 @@ class PermissionRequirementAll(PermissionRequirement):
class PermissionRequirementAny(PermissionRequirement):
'''Complex permission requirement needing any given permissions'''
def check(self, perms):
for req in self.requirements:
if req.check(perms):

View File

@ -1,11 +1,11 @@
# Copyright 2011 Dustin C. Hatch
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

View File

@ -1,11 +1,11 @@
# Copyright 2011, 2012, 2015 Dustin C. Hatch
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -28,13 +28,13 @@ import warnings
class Router(object):
'''A dispatcher that maps arbitrary paths to controller callables
Typical usage::
router = Router()
router.add_route('/foo/{bar}/{baz:\d+}', some_func)
app = milla.Application(dispatcher=router)
In many cases, paths with trailing slashes need special handling.
The ``Router`` has two ways of dealing with requests that should
have a trailing slash but do not. The default is to send the client
@ -44,19 +44,19 @@ class Router(object):
return HTTP 404 Not Found for requests with missing trailing
slashes. To change the behavior, pass a different value to the
constructor's ``trailing_slash`` keyword.
Redirect the client to the proper path (the default)::
router = Router(trailing_slash=Router.REDIRECT)
router.add_route('/my_collection/', some_func)
Pretend the request had a trailing slash, even if it didn't::
router = Router(trailing_slash=Router.SILENT)
router.add_route('/my_collection/', some_func)
Do nothing, let the client get a 404 error::
router = Router(trailing_slash=None)
router.add_route('/my_collection/', some_func)
'''
@ -74,12 +74,12 @@ class Router(object):
def resolve(self, path_info):
'''Find a controller for a given path
:param path_info: Path for which to locate a controller
:returns: A :py:class:`functools.partial` instance that sets
the values collected from variable segments as keyword
arguments to the callable
arguments to the callable
This method walks through the routing table created with calls
to :py:meth:`add_route` and finds the first whose template
matches the given path. Variable segments are added as keywords
@ -132,9 +132,9 @@ class Router(object):
def _compile_template(self, template):
'''Compiles a template into a real regular expression
:param template: A route template string
Converts the ``{name}`` or ``{name:regexp}`` syntax into a full
regular expression for later parsing.
'''
@ -163,40 +163,40 @@ class Router(object):
def add_route(self, template, controller, **vars):
'''Add a route to the routing table
:param template: Route template string
:param controller: Controller callable or string Python path
Route template strings are path segments, beginning with ``/``.
Paths can also contain variable segments, delimited with curly
braces.
Example::
/some/other/{variable}/{path}
By default, variable segments will match any character except a
``/``. Alternate expressions can be passed by specifying them
alongside the name, separated by a ``:``.
Example::
/some/other/{alternate:[a-zA-Z]}
Variable path segments will be passed as keywords to the
controller. In the first example above, assuming ``controller``
is the name of the callable passed, and the request path was
``/some/other/great/place``::
controller(request, variable='great', path='place')
The ``controller`` argument itself can be any callable that
accepts a *WebOb* request as its first argument, and any
keywords that may be passed from variable segments. It can
also be a string Python path to such a callable. For example::
`some.module:function`
This string will resolve to the function ``function`` in the
module ``some.module``.
'''
@ -208,17 +208,17 @@ class Router(object):
class Generator(object):
'''URL generator
Creates URL references based on a *WebOb* request.
Typical usage:
>>> generator = Generator(request)
>>> generator.generate('foo', 'bar')
'/foo/bar'
A common pattern is to wrap this in a stub function::
url = Generator(request).generate
.. deprecated:: 0.2

View File

@ -1,11 +1,11 @@
# Copyright 2011 Dustin C. Hatch
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -23,35 +23,35 @@ from milla.dispatch import UnresolvedPath
class Traverser(object):
'''Default URL dispatcher
:param root: The root object at which lookup will begin
The default URL dispatcher uses object attribute traversal to
locate a handler for a given path. For example, consider the
following class::
class Root(object):
def foo(self):
return 'Hello, world!'
The path ``/foo`` would resolve to the ``foo`` method of the
``Root`` class.
If a path cannot be resolved, :py:exc:`UnresolvedPath` will be
raised.
'''
def __init__(self, root):
self.root = root
def resolve(self, path_info):
'''Find a handler given a path
:param path_info: Path for which to find a handler
:returns: A handler callable
'''
def walk_path(handler, parts):
if not parts or not parts[0]:
# No more parts, or the last part is blank, we're done
@ -66,9 +66,9 @@ class Traverser(object):
except AttributeError:
# No default either, can't resolve
raise UnresolvedPath
# Strip the leading slash and split the path
split_path = path_info.lstrip('/').split('/')
handler = walk_path(self.root, split_path)
return handler
return handler

View File

@ -10,21 +10,21 @@ import nose.tools
def test_permission_check():
'''Ensure Permission.check returns True for lists of strings
'''
perm = milla.auth.permissions.Permission('foo')
assert perm.check(['foo'])
def test_permission_check_false():
'''Ensure Permission.check returns False for lists of strings
'''
perm = milla.auth.permissions.Permission('foo')
assert not perm.check(['bar'])
def test_permission_check_perm():
'''Ensure Permission.check returns True for lists of Permissions
'''
req = milla.auth.permissions.Permission('foo')
perm = milla.auth.permissions.Permission('foo')
assert req.check([perm])
@ -32,7 +32,7 @@ def test_permission_check_perm():
def test_permission_check_perm_false():
'''Ensure Permission.check returns True for lists of Permissions
'''
req = milla.auth.permissions.Permission('foo')
perm = milla.auth.permissions.Permission('bar')
assert not req.check([perm])
@ -40,7 +40,7 @@ def test_permission_check_perm_false():
def test_permission_check_container():
'''Ensure Permission.check returns True for PermissionContainers of strings
'''
perm = milla.auth.permissions.Permission('foo')
container = milla.auth.permissions.PermissionContainer(['foo'])
assert perm.check(container)
@ -48,7 +48,7 @@ def test_permission_check_container():
def test_permission_check_container_false():
'''Ensure Permission.check returns True for PermissionContainers of strings
'''
perm = milla.auth.permissions.Permission('foo')
container = milla.auth.permissions.PermissionContainer(['bar'])
assert not perm.check(container)
@ -56,7 +56,7 @@ def test_permission_check_container_false():
def test_permission_check_container_perm():
'''Ensure Permission.check returns True for PermissionContainers of Permissions
'''
perm = milla.auth.permissions.Permission('foo')
req = milla.auth.permissions.Permission('foo')
container = milla.auth.permissions.PermissionContainer([perm])
@ -65,7 +65,7 @@ def test_permission_check_container_perm():
def test_permission_check_container_perm_false():
'''Ensure Permission.check returns False for PermissionContainers of Permissions
'''
perm = milla.auth.permissions.Permission('foo')
req = milla.auth.permissions.Permission('bar')
container = milla.auth.permissions.PermissionContainer([perm])
@ -74,14 +74,14 @@ def test_permission_check_container_perm_false():
def test_permission_container_iter():
'''Ensure iterating a PermissionContainer yields all permissions
'''
container = milla.auth.permissions.PermissionContainer(['foo'], ['bar'])
assert list(container) == ['foo', 'bar']
def test_permission_and():
'''Ensure AND-ing Permissions returns a PermissionRequirementAll
'''
perm1 = milla.auth.permissions.Permission('foo')
perm2 = milla.auth.permissions.Permission('bar')
req = perm1 & perm2
@ -91,7 +91,7 @@ def test_permission_and():
def test_permission_or():
'''Ensure OR-ing Permissions returns a PermissionRequirementAny
'''
perm1 = milla.auth.permissions.Permission('foo')
perm2 = milla.auth.permissions.Permission('bar')
req = perm1 | perm2
@ -101,7 +101,7 @@ def test_permission_or():
def test_permission_str():
'''Ensure calling str on a Permission returns its name
'''
perm_name = 'foo'
perm = milla.auth.permissions.Permission(perm_name)
assert str(perm) == perm_name
@ -109,7 +109,7 @@ def test_permission_str():
def test_permission_eq():
'''Ensure two Permissions with the same name are equal but not identical
'''
perm_name = 'foo'
perm1 = milla.auth.permissions.Permission(perm_name)
perm2 = milla.auth.permissions.Permission(perm_name)
@ -119,7 +119,7 @@ def test_permission_eq():
def test_permission_check_container_group():
'''Test group permissions in PermissionContainer objects
'''
perm = milla.auth.permissions.Permission('foo')
req = milla.auth.permissions.Permission('foo')
container = milla.auth.permissions.PermissionContainer([], [perm])
@ -128,7 +128,7 @@ def test_permission_check_container_group():
def test_permissionrequirement_all():
'''Ensure PermissionRequirementAll requires all listed permissions
'''
perm1 = milla.auth.permissions.Permission('foo')
perm2 = milla.auth.permissions.Permission('bar')
req = milla.auth.permissions.PermissionRequirementAll(perm1, perm2)
@ -141,7 +141,7 @@ def test_permissionrequirement_all():
def test_permissionrequirement_any():
'''Ensure PermissionRequirementAll requires only one permission
'''
perm1 = milla.auth.permissions.Permission('foo')
perm2 = milla.auth.permissions.Permission('bar')
req = milla.auth.permissions.PermissionRequirementAny(perm1, perm2)
@ -154,7 +154,7 @@ def test_permissionrequirement_any():
def test_exception_callable():
'''Ensure that NotAuthorizedException is a valid controller callable
'''
exc = milla.auth.NotAuthorized()
request = milla.Request.blank('/')
response = exc(request)
@ -165,7 +165,7 @@ def test_exception_callable():
def test_request_validator_nouser():
'''Ensure ensure requests without a user attribute raise NotAuthorized
'''
validator = milla.auth.RequestValidator()
request = milla.Request.blank('/')
validator.validate(request)
@ -174,7 +174,7 @@ def test_request_validator_nouser():
def test_request_validator_emptyuser():
'''Ensure requests with an empty user raise NotAuthorized
'''
validator = milla.auth.RequestValidator()
request = milla.Request.blank('/')
request.user = None
@ -182,13 +182,13 @@ def test_request_validator_emptyuser():
def test_request_validator_user_noperms():
'''Ensure user permissions are not checked if no requirement is given
If no ``requirement`` is given to
:py:meth:`milla.auth.RequestValidator.validate`, then the fact that the
request's ``user`` attribute doesn't have a ``permissions`` attribute
shouldn't matter.
shouldn't matter.
'''
class User(object):
pass
@ -201,10 +201,10 @@ def test_request_validator_user_noperms():
def test_request_validator_missingperms():
'''Ensure requests whose user has no permissions attribute are invalid
'''
class User(object):
pass
validator = milla.auth.RequestValidator()
request = milla.Request.blank('/')
request.user = User()
@ -215,10 +215,10 @@ def test_request_validator_missingperms():
def test_request_validator_emptyperms():
'''Ensure requests whose user has an empty set of permissions are invalid
'''
class User(object):
pass
validator = milla.auth.RequestValidator()
request = milla.Request.blank('/')
request.user = User()
@ -230,10 +230,10 @@ def test_request_validator_emptyperms():
def test_request_validator_incorrectperms():
'''Ensure requests whose user has incorrect permissions raise NotAuthorized
'''
class User(object):
pass
validator = milla.auth.RequestValidator()
request = milla.Request.blank('/')
request.user = User()
@ -244,10 +244,10 @@ def test_request_validator_incorrectperms():
def test_request_validator_correctperms():
'''Ensure requests from users with appropriate permissions are valid
'''
class User(object):
pass
validator = milla.auth.RequestValidator()
request = milla.Request.blank('/')
request.user = User()
@ -258,7 +258,7 @@ def test_request_validator_correctperms():
def test_find_request_kwarg():
'''Ensure _find_request finds a request in keyword arguments
'''
request = milla.Request.blank('/')
found = milla.auth.decorators._find_request('foo', request=request)
assert found is request
@ -266,7 +266,7 @@ def test_find_request_kwarg():
def test_find_request_arg1():
'''Ensure _find_request finds a request in position 1
'''
request = milla.Request.blank('/')
found = milla.auth.decorators._find_request(request)
assert found is request
@ -274,7 +274,7 @@ def test_find_request_arg1():
def test_find_request_arg2():
'''Ensure _find_request finds a request in another position
'''
request = milla.Request.blank('/')
found = milla.auth.decorators._find_request('foo', request)
assert found is request
@ -282,14 +282,14 @@ def test_find_request_arg2():
def test_auth_required_true():
'''Test the auth_required decorator with a valid user
'''
class User(object):
pass
@milla.auth.decorators.auth_required
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
response = controller(request)
@ -298,7 +298,7 @@ def test_auth_required_true():
def test_auth_required_false():
'''Test the auth_required decorator with no user
'''
@milla.auth.decorators.auth_required
def controller(request):
return 'success'
@ -312,10 +312,10 @@ def test_auth_required_false():
def test_require_perms_none():
'''Test the require_perms decorator with no requirement
'''
class User(object):
pass
@milla.auth.decorators.require_perms()
def controller(request):
return 'success'
@ -328,14 +328,14 @@ def test_require_perms_none():
def test_require_perms_valid_str():
'''Test the require_perms decorator with valid permissions as strings
'''
class User(object):
pass
@milla.auth.decorators.require_perms('foo')
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
request.user.permissions = ['foo']
@ -345,15 +345,15 @@ def test_require_perms_valid_str():
def test_require_perms_valid_permission():
'''Test the require_perms decorator with valid permissions as Permissions
'''
class User(object):
pass
req = milla.auth.permissions.Permission('foo')
@milla.auth.decorators.require_perms(req)
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
request.user.permissions = ['foo']
@ -363,10 +363,10 @@ def test_require_perms_valid_permission():
def test_require_perms_multi_valid_string():
'''Test the require_perms decorator with multiple requirements as strings
'''
class User(object):
pass
@milla.auth.decorators.require_perms('foo', 'bar')
def controller(request):
return 'success'
@ -380,10 +380,10 @@ def test_require_perms_multi_valid_string():
def test_require_perms_multi_valid_permission():
'''Test the require_perms decorator with multiple requirements as Permissions
'''
class User(object):
pass
req1 = milla.auth.permissions.Permission('foo')
req2 = milla.auth.permissions.Permission('bar')
@milla.auth.decorators.require_perms(req1, req2)
@ -399,14 +399,14 @@ def test_require_perms_multi_valid_permission():
def test_require_perms_invalid_none():
'''Test the require_perms decorator with no permissions
'''
class User(object):
pass
@milla.auth.decorators.require_perms('foo')
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
response = controller(request)
@ -416,10 +416,10 @@ def test_require_perms_invalid_none():
def test_require_perms_invalid_empty():
'''Test the require_perms decorator with an empty permissions set
'''
class User(object):
pass
@milla.auth.decorators.require_perms('foo')
def controller(request):
return 'success'
@ -434,14 +434,14 @@ def test_require_perms_invalid_empty():
def test_require_perms_invalid_string():
'''Test the require_perms decorator with invalid permissions as strings
'''
class User(object):
pass
@milla.auth.decorators.require_perms('foo')
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
request.user.permissions = ['bar']
@ -452,15 +452,15 @@ def test_require_perms_invalid_string():
def test_require_perms_invalid_permission():
'''Test the require_perms decorator with invalid permissions as Permissions
'''
class User(object):
pass
req = milla.auth.permissions.Permission('foo')
@milla.auth.decorators.require_perms(req)
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
request.user.permissions = ['bar']
@ -471,14 +471,14 @@ def test_require_perms_invalid_permission():
def test_require_perms_multi_invalid_string():
'''Test the require_perms decorator with multiple invalid permissions as strings
'''
class User(object):
pass
@milla.auth.decorators.require_perms('foo', 'bar')
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
request.user.permissions = ['bar']
@ -489,16 +489,16 @@ def test_require_perms_multi_invalid_string():
def test_require_perms_multi_invalid_permission():
'''Test the require_perms decorator with multiple invalid permissions as Permissions
'''
class User(object):
pass
req1 = milla.auth.permissions.Permission('foo')
req2 = milla.auth.permissions.Permission('foo')
@milla.auth.decorators.require_perms(req1, req2)
def controller(request):
return 'success'
request = milla.Request.blank('/')
request.user = User()
request.user.permissions = ['bar']

View File

@ -12,7 +12,7 @@ import nose.tools
def test_static():
'''Ensure the dispatcher can resolve a static path
Given the path ``/foo/bar/baz`` and a route for the exact same
path, the resolver should return the controller mapped to the
route.
@ -28,7 +28,7 @@ def test_static():
def test_urlvars():
'''Ensure the dispatcher can resolve a path with variable segments
Given the path ``/foo/abc/def`` and a route ``/foo/{bar}/{baz}``,
the resolver should return the controller mapped to the route with
preset keywords ``bar='abc', baz='def'``.
@ -47,7 +47,7 @@ def test_urlvars():
@nose.tools.raises(milla.dispatch.UnresolvedPath)
def test_regexp_urlvar():
'''Ensure the dispatcher can resolve alternate regexps in urlvars
Given a route ``/test/{arg:[a-z]+}``, the resolver should return
the mapped controller for the path ``/test/abcde``, but not the
path ``/test/1234``.
@ -67,7 +67,7 @@ def test_regexp_urlvar():
@nose.tools.raises(milla.dispatch.UnresolvedPath)
def test_unresolved():
'''Ensure the resolver raises an exception for unresolved paths
Given a route ``/test``, the resolver should raise
:py:exc:`~milla.dispatch.UnresolvedPath` for the path ``/tset``.
'''
@ -81,7 +81,7 @@ def test_unresolved():
def test_unrelated():
'''Ensure the dispatcher is not confused by unrelated paths
Given routes for ``/testA`` and ``/testB``, the resolver should
return the controller mapped to the former for the path ``/testA``,
without regard for the latter.
@ -101,7 +101,7 @@ def test_unrelated():
def test_string_controller():
'''Ensure the dispatcher can find a controller given a string
Given a string path to a controller function, the callable defined
therein should be returned by the resolver for the corresponding
path.
@ -134,10 +134,10 @@ def test_trailing_slash_redir():
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')
@ -145,11 +145,11 @@ def test_trailing_slash_none():
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
assert func.func is controller

View File

@ -9,7 +9,7 @@ import milla.dispatch.traversal
def test_root():
'''Ensure the root path resolves to the root handler
Given the path ``/``, the resolver should return the root handler,
which was given to it at initialization
'''
@ -24,8 +24,8 @@ def test_root():
def test_unrelated():
'''Ensure unrelated attributes do not confuse the dispatcher
Given the path ``/`` and a root handler with attributes and
Given the path ``/`` and a root handler with attributes and
methods, the resolver should still return the root handler
'''
@ -41,7 +41,7 @@ def test_unrelated():
def test_unresolved():
'''Ensure that the resolver returns remaining parts
Given the path ``/foo/bar/baz`` and a root handler with no
children, the resolver should raise
:py:exc:`~milla.dispatch.UnresolvedPath`
@ -61,7 +61,7 @@ def test_unresolved():
def test_method():
'''Ensure the resolver finds an instance method handler
Given the path ``/test`` and a root handler with an instance
method named ``test``, the resolver should return that method.
'''
@ -77,7 +77,7 @@ def test_method():
def test_nested_class():
'''Ensure the resolver finds a nested class handler
Given the path ``/test`` and a root handler with an inner class
named ``test``, the resolver should return the inner class.
'''
@ -93,7 +93,7 @@ def test_nested_class():
def test_nested_class_method():
'''Ensure the resolver finds an instance method of a nested class
Given the path ``/test/test`` and a root handler with an inner
class named ``test``, which in turn has an instance method named
``test``, the resolver should return the ``test`` method of the
@ -112,7 +112,7 @@ def test_nested_class_method():
def test_attribute():
'''Ensure the resolver finds a handler in an instance attribute
Given the path ``/test`` and a root handler with an attribute named
``test`` containing another class, the resolver should return that
class.
@ -130,10 +130,10 @@ def test_attribute():
def test_default():
'''Ensure the resolver finds the default handler
Given the path ``/test`` and a root handler with a method named
``default``, but no method named ``test``, the resolver should
return the ``default`` method.
return the ``default`` method.
'''
class Root(object):
@ -147,7 +147,7 @@ def test_default():
def test_nested_default():
'''Ensure the resolver finds a nested default handler
Given the path ``/test/bar`` and a root handler with a ``test``
attribute containing a class instance with a ``default`` method but
no ``bar`` method, the resolver should return the ``default``