Merged branch py3k (closes #2)
commit
3408919faa
|
@ -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>
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
@ -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)
|
||||
|
|
|
@ -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,50 +92,54 @@ 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():
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'''
|
||||
|
|
|
@ -40,8 +40,14 @@ def read_config(filename):
|
|||
app.config.update(config)
|
||||
'''
|
||||
|
||||
cparser = configparser.SafeConfigParser()
|
||||
cparser.readfp(open(filename))
|
||||
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(f)
|
||||
|
||||
config = {}
|
||||
for section in cparser.sections():
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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'''
|
||||
|
|
Loading…
Reference in New Issue