Merged branch py3k (closes #2)

master
Dustin C. Hatch 2013-01-20 14:53:38 -06:00
commit 3408919faa
10 changed files with 71 additions and 66 deletions

View File

@ -2,8 +2,8 @@
<?eclipse-pydev version="1.0"?>
<pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">python3</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/Milla/src</path>
</pydev_pathproperty>

View File

@ -15,7 +15,7 @@ if sys.version_info < (2, 7):
setup(
name='Milla',
version='tip',
version='py3k',
description='Lightweight WSGI framework for web applications',
long_description='''\
Milla is a simple WSGI framework for Python web applications. It is mostly

View File

@ -19,8 +19,13 @@ from milla.app import *
from milla.auth.decorators import *
from webob.exc import *
import webob
import urllib
import urlparse
try:
import urllib.parse
except ImportError: #pragma: no cover
import urllib
import urlparse
urllib.parse = urlparse
urllib.parse.urlencode = urllib.urlencode #@UndefinedVariable
def allow(*methods):
'''Specify the allowed HTTP verbs for a controller callable
@ -77,7 +82,7 @@ class Request(webob.Request):
url = self._merge_url(self.script_name, path)
if keywords:
url += '?' + urllib.urlencode(keywords)
url += '?' + urllib.parse.urlencode(keywords)
return url
@ -97,7 +102,7 @@ class Request(webob.Request):
url = self._merge_url(self.application_url, path)
if keywords:
url += '?' + urllib.urlencode(keywords)
url += '?' + urllib.parse.urlencode(keywords)
return url
@ -141,4 +146,4 @@ class Request(webob.Request):
if not root.endswith('/'):
root += '/'
return urlparse.urljoin(root, path)
return urllib.parse.urljoin(root, path)

View File

@ -25,7 +25,6 @@ from milla.controllers import FaviconController
from milla.util import asbool
from webob.exc import HTTPNotFound, WSGIHTTPException, HTTPMethodNotAllowed
import milla.dispatch.traversal
import webob
__all__ = ['Application']
@ -93,51 +92,55 @@ class Application(object):
start_response_wrapper = StartResponseWrapper(start_response)
request.start_response = start_response_wrapper
try:
# If the callable has an __before__ attribute, call it
if hasattr(func, '__before__'):
func.__before__(request)
# If the callable is an instance method and its class has
# a __before__ method, call that
elif hasattr(func, 'im_self') and \
hasattr(func.im_self, '__before__'):
func.im_self.__before__(request)
# The callable might be a partial, so check the inner func
elif hasattr(func, 'func'):
if hasattr(func.func, '__before__'):
func.func.__before__(request)
elif hasattr(func.func, 'im_self') and \
hasattr(func.func.im_self, '__before__'):
func.func.im_self.__before__(request)
self._call_before(func)(request)
response = func(request)
except WSGIHTTPException as e:
return e(environ, start_response)
finally:
# If the callable has an __after__ method, call it
if hasattr(func, '__after__'):
func.__after__(request)
# If the callable is an instance method and its class has
# an __after__ method, call that
elif hasattr(func, 'im_self') and \
hasattr(func.im_self, '__after__'):
func.im_self.__after__(request)
# The callable might be a partial, so check the inner func
elif hasattr(func, 'func'):
if hasattr(func.func, '__after__'):
func.func.__after__(request)
elif hasattr(func.func, 'im_self') and \
hasattr(func.func.im_self, '__after__'):
func.func.im_self.__after__(request)
self._call_after(func)(request)
# The callable might have returned just a string, which is OK,
# but we need to wrap it in a WebOb response
# but we need to wrap it in a Response object
try:
# In Python 2, it could be a str or a unicode object
basestring = basestring #@UndefinedVariable
except NameError:
# Python 3 has no unicode objects and thus no need for
# basestring so we, just make it an alias for str
basestring = str
if isinstance(response, basestring) or not response:
response = webob.Response(response)
response = request.ResponseClass(response)
if not start_response_wrapper.called:
start_response(response.status, response.headerlist)
if not environ['REQUEST_METHOD'] == 'HEAD':
return response.app_iter
def _call_after(self, func):
try:
return self._find_attr(func, '__after__')
except AttributeError:
return lambda r: None
def _call_before(self, func):
try:
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
return getattr(obj, attr)
except AttributeError:
# Object is a bound method; look for the attribute on the instance
if hasattr(obj, '__self__'):
return self._find_attr(obj.__self__, attr)
# Object is a partial; look for the attribute on the inner function
elif hasattr(obj, 'func'):
return self._find_attr(obj.func, attr)
raise
class StartResponseWrapper():
def __init__(self, start_response):

View File

@ -37,7 +37,7 @@ class NotAuthorized(Exception):
All other arguments and keywords are ignored.
'''
response = request.ResponseClass(unicode(self))
response = request.ResponseClass(str(self))
response.status_int = 403
return response

View File

@ -124,7 +124,7 @@ class require_perms(object):
def __init__(self, *requirements):
requirement = None
for req in requirements:
if isinstance(req, basestring):
if not hasattr(req, 'check'):
req = permissions.Permission(req)
if not requirement:
requirement = req

View File

@ -86,17 +86,8 @@ class Permission(BasePermission):
def __init__(self, name):
self.name = name
def __unicode__(self):
if isinstance(self.name, unicode):
return self.name
else:
return self.name.decode('utf-8')
def __str__(self):
if isinstance(self.name, str):
return self.name
else:
return self.name.encode('utf-8')
return str(self.name)
def __eq__(self, other):
return self is other or str(self) == str(other)
@ -119,7 +110,7 @@ class PermissionRequirement(BasePermission):
self.requirements = requirements
def __str__(self):
return unicode(self).encode('utf-8')
return ', '.join(self.requirements)
class PermissionRequirementAll(PermissionRequirement):
'''Complex permission requirement needing all given permissions'''

View File

@ -40,8 +40,14 @@ def read_config(filename):
app.config.update(config)
'''
with open(filename) as f:
# ConfigParser API changed in Python 3.2
if hasattr(configparser.ConfigParser, 'read_file'):
cparser = configparser.ConfigParser()
cparser.read_file(f)
else:
cparser = configparser.SafeConfigParser()
cparser.readfp(open(filename))
cparser.readfp(f)
config = {}
for section in cparser.sections():

View File

@ -200,7 +200,7 @@ class Router(object):
module ``some.module``.
'''
if isinstance(controller, basestring):
if not hasattr(controller, '__call__'):
controller = self._import_controller(controller)
self.routes.append((self._compile_template(template),
controller, vars))

View File

@ -33,7 +33,7 @@ class ResponseMaker(object):
def __init__(self, http_version=1.1):
self.http_version = http_version
self.headers = ''
self.body = ''
self.body = b''
def start_response(self, status, response_headers):
self.headers += 'HTTP/{0} {1}\r\n'.format(self.http_version, status)
@ -78,7 +78,7 @@ def test_favicon():
app_iter = app(environ, response.start_response)
response.finish_response(app_iter)
assert response.headers.startswith('HTTP/1.1 200'), response.headers
assert response.body.startswith('\x00\x00\x01\x00'), response.body
assert response.body.startswith(b'\x00\x00\x01\x00'), response.body
def test_allow_header_disallowed():
'''HTTP 405 is returned for disallowed HTTP request methods
@ -138,7 +138,7 @@ def test_emulated_method():
})
body = environ['wsgi.input']
body.seek(0)
body.write('_method=PUT')
body.write(b'_method=PUT')
body.seek(0)
response = ResponseMaker()
app_iter = app(environ, response.start_response)
@ -358,7 +358,7 @@ def test_httperror_response():
'''
def controller(request):
raise webob.exc.HTTPClientError()
raise webob.exc.HTTPClientError('NotFound')
app = milla.app.Application(StubResolver(controller))
environ = environ_for_testing()
@ -366,7 +366,7 @@ def test_httperror_response():
app_iter = app(environ, response.start_response)
response.finish_response(app_iter)
assert response.headers.startswith('HTTP/1.1 400'), response.headers
assert webob.exc.HTTPClientError.explanation in response.body, response.body
assert b'NotFound' in response.body, response.body
def test_single_start_response():
'''Ensure start_response is only called once'''
@ -393,7 +393,7 @@ def test_single_start_response():
response.finish_response(app_iter)
assert start_response.call_count == 1, start_response.call_count
assert response.headers.startswith('HTTP/1.1 200 OK'), response.headers
assert response.body == 'test', response.body
assert response.body == b'test', response.body
def test_allow_decorator():
'''Ensure allow decorator sets allowed_methods on controllers'''