From bcb3312f9faf032bd197a3650d82bad1bf633f4c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 20 Apr 2011 15:07:19 -0400 Subject: fix scriptname and path_info computation in wsgiapp2 decorator; share code between static and wsgiapp2 decorator --- pyramid/request.py | 42 ++++++++++++++++++++++++++++++++++++++++++ pyramid/static.py | 46 +++++++--------------------------------------- pyramid/wsgi.py | 29 ++++++----------------------- 3 files changed, 55 insertions(+), 62 deletions(-) diff --git a/pyramid/request.py b/pyramid/request.py index 9d2b9344b..050828d01 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -393,3 +393,45 @@ def add_global_response_headers(request, headerlist): response.headerlist.append((k, v)) request.add_response_callback(add_headers) +def copy_request_with_subpath_as_path_info(request, default_script_name='', + default_path_info='/'): + # Make a copy of the request and use the request's subpath (if it exists) + # as the request copy's PATH_INFO. Set the request copy's SCRIPT_NAME to + # the prefix before the subpath. + # + # Postconditions: + # - SCRIPT_NAME and PATH_INFO are empty or start with / + # - At least one of SCRIPT_NAME or PATH_INFO are set. + # - SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should + # be '/'). + + script_name = request.environ.get('SCRIPT_NAME', '') + path_info = request.environ.get('PATH_INFO', '/') + + subpath = getattr(request, 'subpath', ()) + + if subpath: + # compute new_path_info + default_path_info = '/' + '/'.join(subpath) + if path_info.endswith('/'): + # readd trailing slash stripped by subpath (traversal) + # conversion + default_path_info += '/' + + # compute new_script_name + tmp = [] + workback = (script_name + path_info).split('/') + while workback: + el = workback.pop() + if el: + tmp.insert(0, el) + if tmp == subpath: + default_script_name = '/'.join(workback) + break + + request_copy = request.copy() + + request_copy.environ['SCRIPT_NAME'] = default_script_name + request_copy.environ['PATH_INFO'] = default_path_info + + return request_copy diff --git a/pyramid/static.py b/pyramid/static.py index 223652768..903316deb 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -13,6 +13,7 @@ from zope.interface import implements from pyramid.asset import resolve_asset_spec from pyramid.interfaces import IStaticURLInfo from pyramid.path import caller_package +from pyramid.request import copy_request_with_subpath_as_path_info from pyramid.url import route_url class PackageURLParser(StaticURLParser): @@ -208,44 +209,11 @@ class static_view(object): self.app = app def __call__(self, context, request): - # Point PATH_INFO to the static file/dir path; point SCRIPT_NAME - # to the prefix before it. - - # Postconditions: - # - SCRIPT_NAME and PATH_INFO are empty or start with / - # - At least one of SCRIPT_NAME or PATH_INFO are set. - # - SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should - # be '/'). - - request_copy = request.copy() - script_name = request_copy.environ.get('SCRIPT_NAME', '') - path_info = request_copy.environ.get('PATH_INFO', '/') - - new_script_name = script_name - new_path_info = path_info - - subpath = list(request.subpath) - - if subpath: - # compute new_path_info - new_path_info = '/' + '/'.join(subpath) - if path_info.endswith('/'): - # readd trailing slash stripped by subpath (traversal) - # conversion - new_path_info += '/' - - # compute new_script_name - tmp = [] - workback = (script_name + path_info).split('/') - while workback: - el = workback.pop() - if el: - tmp.insert(0, el) - if tmp == subpath: - new_script_name = '/'.join(workback) - break - - request_copy.environ['SCRIPT_NAME'] = new_script_name - request_copy.environ['PATH_INFO'] = new_path_info + script_name = request.environ.get('SCRIPT_NAME', '') + path_info = request.environ.get('PATH_INFO', '/') + + request_copy = copy_request_with_subpath_as_path_info(request, + script_name, + path_info) return request_copy.get_response(self.app) diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py index e988a000e..959f84bbc 100644 --- a/pyramid/wsgi.py +++ b/pyramid/wsgi.py @@ -1,5 +1,5 @@ from pyramid.compat import wraps -from pyramid.traversal import quote_path_segment +from pyramid.request import copy_request_with_subpath_as_path_info def wsgiapp(wrapped): """ Decorator to turn a WSGI application into a :app:`Pyramid` @@ -62,25 +62,8 @@ def wsgiapp2(wrapped): up before the application is invoked. """ def decorator(context, request): - traversed = request.traversed - vroot_path = request.virtual_root_path - if not vroot_path: - vroot_path = () - view_name = request.view_name - subpath = request.subpath - if not subpath: - subpath = () - script_tuple = traversed[len(vroot_path):] - script_list = [ quote_path_segment(name) for name in script_tuple ] - if view_name: - script_list.append(quote_path_segment(view_name)) - script_name = '/' + '/'.join(script_list) - path_list = [ quote_path_segment(name) for name in subpath ] - path_info = '/' + '/'.join(path_list) - request.environ['PATH_INFO'] = path_info - script_name = request.environ['SCRIPT_NAME'] + script_name - if script_name.endswith('/'): - script_name = script_name[:-1] - request.environ['SCRIPT_NAME'] = script_name - return request.get_response(wrapped) - return wraps(wrapped)(decorator) # grokkability + request_copy = copy_request_with_subpath_as_path_info(request) + return request_copy.get_response(wrapped) + + return wraps(wrapped)(decorator) + -- cgit v1.2.3 From 76071718a63dc86be4bb53713fadbdc618c719ad Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 21 Apr 2011 11:46:31 -0400 Subject: fewer tests fail --- pyramid/request.py | 32 +++++++++++++++++--------------- pyramid/static.py | 8 ++------ pyramid/tests/test_wsgi.py | 5 +++++ pyramid/wsgi.py | 5 ++--- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/pyramid/request.py b/pyramid/request.py index 050828d01..1aceccdba 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -10,6 +10,7 @@ from pyramid.interfaces import IResponseFactory from pyramid.exceptions import ConfigurationError from pyramid.decorator import reify from pyramid.response import Response +from pyramid.traversal import quote_path_segment from pyramid.url import resource_url from pyramid.url import route_url from pyramid.url import static_url @@ -393,11 +394,10 @@ def add_global_response_headers(request, headerlist): response.headerlist.append((k, v)) request.add_response_callback(add_headers) -def copy_request_with_subpath_as_path_info(request, default_script_name='', - default_path_info='/'): - # Make a copy of the request and use the request's subpath (if it exists) - # as the request copy's PATH_INFO. Set the request copy's SCRIPT_NAME to - # the prefix before the subpath. +def subpath_as_path_info(request, default_script_name='',default_path_info='/'): + # Copy the request. Use the request's subpath (if it exists) as the new + # request's PATH_INFO. Set the request copy's SCRIPT_NAME to the prefix + # before the subpath. # # Postconditions: # - SCRIPT_NAME and PATH_INFO are empty or start with / @@ -408,15 +408,18 @@ def copy_request_with_subpath_as_path_info(request, default_script_name='', script_name = request.environ.get('SCRIPT_NAME', '') path_info = request.environ.get('PATH_INFO', '/') - subpath = getattr(request, 'subpath', ()) + new_script_name = default_script_name + new_path_info = default_path_info + + subpath = list(getattr(request, 'subpath', ())) if subpath: # compute new_path_info - default_path_info = '/' + '/'.join(subpath) + new_path_info = '/' + '/'.join([quote_path_segment(x) for x in subpath]) if path_info.endswith('/'): # readd trailing slash stripped by subpath (traversal) # conversion - default_path_info += '/' + new_path_info += '/' # compute new_script_name tmp = [] @@ -424,14 +427,13 @@ def copy_request_with_subpath_as_path_info(request, default_script_name='', while workback: el = workback.pop() if el: - tmp.insert(0, el) + tmp.insert(0, el.decode('utf-8')) if tmp == subpath: - default_script_name = '/'.join(workback) + new_script_name = '/'.join(workback) break - request_copy = request.copy() - - request_copy.environ['SCRIPT_NAME'] = default_script_name - request_copy.environ['PATH_INFO'] = default_path_info + request = request.copy() + request.environ['SCRIPT_NAME'] = new_script_name + request.environ['PATH_INFO'] = new_path_info - return request_copy + return request diff --git a/pyramid/static.py b/pyramid/static.py index 903316deb..c80952129 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -13,7 +13,7 @@ from zope.interface import implements from pyramid.asset import resolve_asset_spec from pyramid.interfaces import IStaticURLInfo from pyramid.path import caller_package -from pyramid.request import copy_request_with_subpath_as_path_info +from pyramid.request import subpath_as_path_info from pyramid.url import route_url class PackageURLParser(StaticURLParser): @@ -211,9 +211,5 @@ class static_view(object): def __call__(self, context, request): script_name = request.environ.get('SCRIPT_NAME', '') path_info = request.environ.get('PATH_INFO', '/') - - request_copy = copy_request_with_subpath_as_path_info(request, - script_name, - path_info) - + request_copy = subpath_as_path_info(request, script_name, path_info) return request_copy.get_response(self.app) diff --git a/pyramid/tests/test_wsgi.py b/pyramid/tests/test_wsgi.py index f63667352..067c86127 100644 --- a/pyramid/tests/test_wsgi.py +++ b/pyramid/tests/test_wsgi.py @@ -110,3 +110,8 @@ class DummyContext: class DummyRequest: def get_response(self, application): return application + + def copy(self): + self.copied = True + return self + diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py index 959f84bbc..ef8863cfa 100644 --- a/pyramid/wsgi.py +++ b/pyramid/wsgi.py @@ -1,5 +1,5 @@ from pyramid.compat import wraps -from pyramid.request import copy_request_with_subpath_as_path_info +from pyramid.request import subpath_as_path_info def wsgiapp(wrapped): """ Decorator to turn a WSGI application into a :app:`Pyramid` @@ -62,8 +62,7 @@ def wsgiapp2(wrapped): up before the application is invoked. """ def decorator(context, request): - request_copy = copy_request_with_subpath_as_path_info(request) + request_copy = subpath_as_path_info(request) return request_copy.get_response(wrapped) - return wraps(wrapped)(decorator) -- cgit v1.2.3 From b64268655a5cb17d31ad2c95b3d6a962d881f77a Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 21 Apr 2011 20:48:46 -0400 Subject: share call_app_with_subpath_as_path_info between static view and wsgiapp2 --- pyramid/request.py | 69 ++++++++++++++++++++++----------------- pyramid/static.py | 7 ++-- pyramid/tests/test_integration.py | 4 +-- pyramid/tests/test_static.py | 5 +-- pyramid/tests/test_wsgi.py | 43 ++++++++---------------- pyramid/wsgi.py | 5 ++- 6 files changed, 61 insertions(+), 72 deletions(-) diff --git a/pyramid/request.py b/pyramid/request.py index 1aceccdba..e53a70023 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -394,10 +394,11 @@ def add_global_response_headers(request, headerlist): response.headerlist.append((k, v)) request.add_response_callback(add_headers) -def subpath_as_path_info(request, default_script_name='',default_path_info='/'): - # Copy the request. Use the request's subpath (if it exists) as the new - # request's PATH_INFO. Set the request copy's SCRIPT_NAME to the prefix - # before the subpath. +def call_app_subpath_as_path_info(request, app): + # Copy the request. Use the source request's subpath (if it exists) as + # the new request's PATH_INFO. Set the request copy's SCRIPT_NAME to the + # prefix before the subpath. Call the application with the new request + # and return a response. # # Postconditions: # - SCRIPT_NAME and PATH_INFO are empty or start with / @@ -408,32 +409,40 @@ def subpath_as_path_info(request, default_script_name='',default_path_info='/'): script_name = request.environ.get('SCRIPT_NAME', '') path_info = request.environ.get('PATH_INFO', '/') - new_script_name = default_script_name - new_path_info = default_path_info + new_script_name = '' subpath = list(getattr(request, 'subpath', ())) - if subpath: - # compute new_path_info - new_path_info = '/' + '/'.join([quote_path_segment(x) for x in subpath]) - if path_info.endswith('/'): - # readd trailing slash stripped by subpath (traversal) - # conversion - new_path_info += '/' - - # compute new_script_name - tmp = [] - workback = (script_name + path_info).split('/') - while workback: - el = workback.pop() - if el: - tmp.insert(0, el.decode('utf-8')) - if tmp == subpath: - new_script_name = '/'.join(workback) - break - - request = request.copy() - request.environ['SCRIPT_NAME'] = new_script_name - request.environ['PATH_INFO'] = new_path_info - - return request + # compute new_path_info + new_path_info = '/' + '/'.join([quote_path_segment(x) for x in subpath]) + + if new_path_info != '/': + if path_info != '/': + if path_info.endswith('/'): + # readd trailing slash stripped by subpath (traversal) + # conversion + new_path_info += '/' + + # compute new_script_name + tmp = [] + workback = (script_name + path_info).split('/') + + # strip trailing slash from workback to avoid appending undue slash + # to script_name + if workback and (workback[-1] == ''): + workback = workback[:-1] + + while workback: + if tmp == subpath: + break + el = workback.pop() + if el: + tmp.insert(0, el.decode('utf-8')) + + new_script_name = '/'.join(workback) + + request_copy = request.copy() + request_copy.environ['SCRIPT_NAME'] = new_script_name + request_copy.environ['PATH_INFO'] = new_path_info + + return request_copy.get_response(app) diff --git a/pyramid/static.py b/pyramid/static.py index c80952129..22e6e9256 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -13,7 +13,7 @@ from zope.interface import implements from pyramid.asset import resolve_asset_spec from pyramid.interfaces import IStaticURLInfo from pyramid.path import caller_package -from pyramid.request import subpath_as_path_info +from pyramid.request import call_app_subpath_as_path_info from pyramid.url import route_url class PackageURLParser(StaticURLParser): @@ -209,7 +209,4 @@ class static_view(object): self.app = app def __call__(self, context, request): - script_name = request.environ.get('SCRIPT_NAME', '') - path_info = request.environ.get('PATH_INFO', '/') - request_copy = subpath_as_path_info(request, script_name, path_info) - return request_copy.get_response(self.app) + return call_app_subpath_as_path_info(request, self.app) diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 1f9484279..e3a1a8003 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -104,7 +104,7 @@ class TestStaticApp(unittest.TestCase): from webob import Request context = DummyContext() from StringIO import StringIO - request = Request({'PATH_INFO':'', + request = Request({'PATH_INFO':'/static', 'SCRIPT_NAME':'/script_name', 'SERVER_NAME':'localhost', 'SERVER_PORT':'80', @@ -112,7 +112,7 @@ class TestStaticApp(unittest.TestCase): 'wsgi.version':(1,0), 'wsgi.url_scheme':'http', 'wsgi.input':StringIO()}) - request.subpath = ['static'] + request.subpath = ('static',) result = staticapp(context, request) self.assertEqual(result.status, '301 Moved Permanently') self.assertEqual(result.location, diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index b9d464bf1..0095b29d3 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -268,8 +268,9 @@ class Test_static_view(unittest.TestCase): SCRIPT_NAME='/script_name') view(context, request) self.assertEqual(request.copied, True) - self.assertEqual(request.environ['PATH_INFO'], '/path_info') - self.assertEqual(request.environ['SCRIPT_NAME'], '/script_name') + self.assertEqual(request.environ['PATH_INFO'], '/') + self.assertEqual(request.environ['SCRIPT_NAME'], + '/script_name/path_info') def test_with_subpath_path_info_ends_with_slash(self): view = self._makeOne('fixtures', package_name='another') diff --git a/pyramid/tests/test_wsgi.py b/pyramid/tests/test_wsgi.py index 067c86127..06bcf1cb2 100644 --- a/pyramid/tests/test_wsgi.py +++ b/pyramid/tests/test_wsgi.py @@ -20,11 +20,9 @@ class WSGIApp2Tests(unittest.TestCase): def test_decorator_with_subpath_and_view_name(self): context = DummyContext() request = DummyRequest() - request.traversed = ['a', 'b'] - request.virtual_root_path = ['a'] - request.subpath = ['subpath'] - request.view_name = 'view_name' - request.environ = {'SCRIPT_NAME':'/foo'} + request.subpath = ('subpath',) + request.environ = {'SCRIPT_NAME':'/foo', + 'PATH_INFO':'/b/view_name/subpath'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) @@ -34,11 +32,8 @@ class WSGIApp2Tests(unittest.TestCase): def test_decorator_with_subpath_no_view_name(self): context = DummyContext() request = DummyRequest() - request.traversed = ['a', 'b'] - request.virtual_root_path = ['a'] - request.subpath = ['subpath'] - request.view_name = '' - request.environ = {'SCRIPT_NAME':'/foo'} + request.subpath = ('subpath',) + request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/b/subpath'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) @@ -48,11 +43,8 @@ class WSGIApp2Tests(unittest.TestCase): def test_decorator_no_subpath_with_view_name(self): context = DummyContext() request = DummyRequest() - request.traversed = ['a', 'b'] - request.virtual_root_path = ['a'] - request.subpath = [] - request.view_name = 'view_name' - request.environ = {'SCRIPT_NAME':'/foo'} + request.subpath = () + request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/b/view_name'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) @@ -62,11 +54,8 @@ class WSGIApp2Tests(unittest.TestCase): def test_decorator_traversed_empty_with_view_name(self): context = DummyContext() request = DummyRequest() - request.traversed = [] - request.virtual_root_path = [] - request.subpath = [] - request.view_name = 'view_name' - request.environ = {'SCRIPT_NAME':'/foo'} + request.subpath = () + request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/view_name'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) @@ -76,11 +65,8 @@ class WSGIApp2Tests(unittest.TestCase): def test_decorator_traversed_empty_no_view_name(self): context = DummyContext() request = DummyRequest() - request.traversed = [] - request.virtual_root_path = [] - request.subpath = [] - request.view_name = '' - request.environ = {'SCRIPT_NAME':'/foo'} + request.subpath = () + request.environ = {'SCRIPT_NAME':'/foo', 'PATH_INFO':'/'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) @@ -90,11 +76,8 @@ class WSGIApp2Tests(unittest.TestCase): def test_decorator_traversed_empty_no_view_name_no_script_name(self): context = DummyContext() request = DummyRequest() - request.traversed = [] - request.virtual_root_path = [] - request.subpath = [] - request.view_name = '' - request.environ = {'SCRIPT_NAME':''} + request.subpath = () + request.environ = {'SCRIPT_NAME':'', 'PATH_INFO':'/'} decorator = self._callFUT(dummyapp) response = decorator(context, request) self.assertEqual(response, dummyapp) diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py index ef8863cfa..71045bb54 100644 --- a/pyramid/wsgi.py +++ b/pyramid/wsgi.py @@ -1,5 +1,5 @@ from pyramid.compat import wraps -from pyramid.request import subpath_as_path_info +from pyramid.request import call_app_subpath_as_path_info def wsgiapp(wrapped): """ Decorator to turn a WSGI application into a :app:`Pyramid` @@ -62,7 +62,6 @@ def wsgiapp2(wrapped): up before the application is invoked. """ def decorator(context, request): - request_copy = subpath_as_path_info(request) - return request_copy.get_response(wrapped) + return call_app_subpath_as_path_info(request, wrapped) return wraps(wrapped)(decorator) -- cgit v1.2.3 From 896fb1c8ee3a6059d5c7a111bf8271d80474435b Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 21 Apr 2011 20:59:58 -0400 Subject: spellings --- pyramid/request.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/pyramid/request.py b/pyramid/request.py index e53a70023..68eb36863 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -406,18 +406,18 @@ def call_app_subpath_as_path_info(request, app): # - SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should # be '/'). - script_name = request.environ.get('SCRIPT_NAME', '') - path_info = request.environ.get('PATH_INFO', '/') + environ = request.environ + script_name = environ.get('SCRIPT_NAME', '') + path_info = environ.get('PATH_INFO', '/') + subpath = list(getattr(request, 'subpath', ())) new_script_name = '' - subpath = list(getattr(request, 'subpath', ())) - # compute new_path_info new_path_info = '/' + '/'.join([quote_path_segment(x) for x in subpath]) - if new_path_info != '/': - if path_info != '/': + if new_path_info != '/': # don't want a sole double-slash + if path_info != '/': # if orig path_info is '/', we're already done if path_info.endswith('/'): # readd trailing slash stripped by subpath (traversal) # conversion @@ -428,7 +428,7 @@ def call_app_subpath_as_path_info(request, app): workback = (script_name + path_info).split('/') # strip trailing slash from workback to avoid appending undue slash - # to script_name + # to end of script_name if workback and (workback[-1] == ''): workback = workback[:-1] @@ -441,8 +441,7 @@ def call_app_subpath_as_path_info(request, app): new_script_name = '/'.join(workback) - request_copy = request.copy() - request_copy.environ['SCRIPT_NAME'] = new_script_name - request_copy.environ['PATH_INFO'] = new_path_info - - return request_copy.get_response(app) + new_request = request.copy() + new_request.environ['SCRIPT_NAME'] = new_script_name + new_request.environ['PATH_INFO'] = new_path_info + return new_request.get_response(app) -- cgit v1.2.3 From 8cf6dd15d512a8c0a4bab9a6b87aa519b8f6d5b3 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 21 Apr 2011 21:06:24 -0400 Subject: name change --- pyramid/request.py | 4 ++-- pyramid/static.py | 4 ++-- pyramid/wsgi.py | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pyramid/request.py b/pyramid/request.py index 68eb36863..7bf7c320d 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -394,7 +394,7 @@ def add_global_response_headers(request, headerlist): response.headerlist.append((k, v)) request.add_response_callback(add_headers) -def call_app_subpath_as_path_info(request, app): +def call_app_with_subpath_as_path_info(request, app): # Copy the request. Use the source request's subpath (if it exists) as # the new request's PATH_INFO. Set the request copy's SCRIPT_NAME to the # prefix before the subpath. Call the application with the new request @@ -424,7 +424,6 @@ def call_app_subpath_as_path_info(request, app): new_path_info += '/' # compute new_script_name - tmp = [] workback = (script_name + path_info).split('/') # strip trailing slash from workback to avoid appending undue slash @@ -432,6 +431,7 @@ def call_app_subpath_as_path_info(request, app): if workback and (workback[-1] == ''): workback = workback[:-1] + tmp = [] while workback: if tmp == subpath: break diff --git a/pyramid/static.py b/pyramid/static.py index 22e6e9256..170b027f1 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -13,7 +13,7 @@ from zope.interface import implements from pyramid.asset import resolve_asset_spec from pyramid.interfaces import IStaticURLInfo from pyramid.path import caller_package -from pyramid.request import call_app_subpath_as_path_info +from pyramid.request import call_app_with_subpath_as_path_info from pyramid.url import route_url class PackageURLParser(StaticURLParser): @@ -209,4 +209,4 @@ class static_view(object): self.app = app def __call__(self, context, request): - return call_app_subpath_as_path_info(request, self.app) + return call_app_with_subpath_as_path_info(request, self.app) diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py index 71045bb54..47a89c0eb 100644 --- a/pyramid/wsgi.py +++ b/pyramid/wsgi.py @@ -1,5 +1,5 @@ from pyramid.compat import wraps -from pyramid.request import call_app_subpath_as_path_info +from pyramid.request import call_app_with_subpath_as_path_info def wsgiapp(wrapped): """ Decorator to turn a WSGI application into a :app:`Pyramid` @@ -62,6 +62,5 @@ def wsgiapp2(wrapped): up before the application is invoked. """ def decorator(context, request): - return call_app_subpath_as_path_info(request, wrapped) + return call_app_with_subpath_as_path_info(request, wrapped) return wraps(wrapped)(decorator) - -- cgit v1.2.3 From 44494c2d9d7266aede41c2ce72cb11b62c809b0a Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 21 Apr 2011 22:13:32 -0400 Subject: add an integration test for wsgiapp2 --- pyramid/tests/test_integration.py | 16 ++++++++++++++++ pyramid/tests/wsgiapp2app/__init__.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 pyramid/tests/wsgiapp2app/__init__.py diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index e3a1a8003..dd77d3aec 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -390,6 +390,22 @@ class SelfScanAppTest(unittest.TestCase): res = self.testapp.get('/two', status=200) self.assertTrue('two' in res.body) +class WSGIApp2AppTest(unittest.TestCase): + def setUp(self): + from pyramid.tests.wsgiapp2app import main + config = main() + app = config.make_wsgi_app() + from webtest import TestApp + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + def test_hello(self): + res = self.testapp.get('/hello', status=200) + self.assertTrue('Hello' in res.body) + class DummyContext(object): pass diff --git a/pyramid/tests/wsgiapp2app/__init__.py b/pyramid/tests/wsgiapp2app/__init__.py new file mode 100644 index 000000000..0880556ef --- /dev/null +++ b/pyramid/tests/wsgiapp2app/__init__.py @@ -0,0 +1,17 @@ +from pyramid.view import view_config +from pyramid.wsgi import wsgiapp2 + +@view_config(name='hello', renderer='string') +@wsgiapp2 +def hello(environ, start_response): + assert environ['PATH_INFO'] == '/' + assert environ['SCRIPT_NAME'] == '/hello' + response_headers = [('Content-Type', 'text/plain')] + start_response('200 OK', response_headers) + return ['Hello!'] + +def main(): + from pyramid.config import Configurator + c = Configurator() + c.scan() + return c -- cgit v1.2.3 From 65a5a94e13e49b29b34287ede881d323505a281b Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 21 Apr 2011 23:25:36 -0400 Subject: add unit tests for call_app_with_subpath_as_path_info --- pyramid/request.py | 12 ++++---- pyramid/tests/test_request.py | 67 +++++++++++++++++++++++++++++++++++++++++++ pyramid/tests/test_static.py | 4 +-- 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/pyramid/request.py b/pyramid/request.py index 7bf7c320d..0fe8b9379 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -414,7 +414,7 @@ def call_app_with_subpath_as_path_info(request, app): new_script_name = '' # compute new_path_info - new_path_info = '/' + '/'.join([quote_path_segment(x) for x in subpath]) + new_path_info = '/' + '/'.join([x.encode('utf-8') for x in subpath]) if new_path_info != '/': # don't want a sole double-slash if path_info != '/': # if orig path_info is '/', we're already done @@ -426,11 +426,6 @@ def call_app_with_subpath_as_path_info(request, app): # compute new_script_name workback = (script_name + path_info).split('/') - # strip trailing slash from workback to avoid appending undue slash - # to end of script_name - if workback and (workback[-1] == ''): - workback = workback[:-1] - tmp = [] while workback: if tmp == subpath: @@ -438,6 +433,11 @@ def call_app_with_subpath_as_path_info(request, app): el = workback.pop() if el: tmp.insert(0, el.decode('utf-8')) + + # strip all trailing slashes from workback to avoid appending undue slashes + # to end of script_name + while workback and (workback[-1] == ''): + workback = workback[:-1] new_script_name = '/'.join(workback) diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index 66a451220..60d59ece6 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -325,6 +325,66 @@ class Test_add_global_response_headers(unittest.TestCase): request.response_callbacks[0](None, response) self.assertEqual(response.headerlist, [('c', 1)] ) +class Test_call_app_with_subpath_as_path_info(unittest.TestCase): + def _callFUT(self, request, app): + from pyramid.request import call_app_with_subpath_as_path_info + return call_app_with_subpath_as_path_info(request, app) + + def test_it_all_request_and_environment_data_missing(self): + request = DummyRequest({}) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '') + self.assertEqual(request.environ['PATH_INFO'], '/') + + def test_it_with_subpath_and_path_info(self): + request = DummyRequest({'PATH_INFO':'/hello'}) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '') + self.assertEqual(request.environ['PATH_INFO'], '/hello') + + def test_it_with_subpath_and_path_info_path_info_endswith_slash(self): + request = DummyRequest({'PATH_INFO':'/hello/'}) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '') + self.assertEqual(request.environ['PATH_INFO'], '/hello/') + + def test_it_with_subpath_and_path_info_extra_script_name(self): + request = DummyRequest({'PATH_INFO':'/hello', 'SCRIPT_NAME':'/script'}) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '/script') + self.assertEqual(request.environ['PATH_INFO'], '/hello') + + def test_it_with_extra_slashes_in_path_info(self): + request = DummyRequest({'PATH_INFO':'//hello/', + 'SCRIPT_NAME':'/script'}) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '/script') + self.assertEqual(request.environ['PATH_INFO'], '/hello/') + + def test_subpath_path_info_and_script_name_have_utf8(self): + la = 'La Pe\xc3\xb1a' + request = DummyRequest({'PATH_INFO':'/'+la, 'SCRIPT_NAME':'/'+la}) + request.subpath = (unicode(la, 'utf-8'), ) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '/' + la) + self.assertEqual(request.environ['PATH_INFO'], '/' + la) + class DummyRequest: def __init__(self, environ=None): if environ is None: @@ -334,6 +394,13 @@ class DummyRequest: def add_response_callback(self, callback): self.response_callbacks = [callback] + def get_response(self, app): + return app + + def copy(self): + self.copied = True + return self + class DummyResponse: def __init__(self): self.headerlist = [] diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index 0095b29d3..588dc32e2 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -296,7 +296,7 @@ class Test_static_view(unittest.TestCase): self.assertEqual(request.environ['SCRIPT_NAME'], '/scriptname/path_info') - def test_with_subpath_new_script_name_fixes_trailing_double_slashes(self): + def test_with_subpath_new_script_name_fixes_trailing_slashes(self): view = self._makeOne('fixtures', package_name='another') context = DummyContext() request = DummyRequest() @@ -305,7 +305,7 @@ class Test_static_view(unittest.TestCase): view(context, request) self.assertEqual(request.copied, True) self.assertEqual(request.environ['PATH_INFO'], '/sub/path/') - self.assertEqual(request.environ['SCRIPT_NAME'], '/path_info/') + self.assertEqual(request.environ['SCRIPT_NAME'], '/path_info') class TestStaticURLInfo(unittest.TestCase): def _getTargetClass(self): -- cgit v1.2.3 From ba0a5f88d916d97fe52540a535b8b5520815201a Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 21 Apr 2011 23:42:51 -0400 Subject: add changelog entries, fix docs for wsgiapp2 --- CHANGES.txt | 13 +++++++++++++ pyramid/wsgi.py | 14 +++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5f08606be..c8b264587 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -136,6 +136,13 @@ Bug Fixes - Redirects issued by a static view did not take into account any existing ``SCRIPT_NAME`` (such as one set by a url mapping composite). Now they do. +- The ``pyramid.wsgi.wsgiapp2`` decorator did not take into account the + ``SCRIPT_NAME`` in the origin request. + +- The ``pyramid.wsgi.wsgiapp2`` decorator effectively only worked when it + decorated a view found via traversal; it ignored the ``PATH_INFO`` that was + part of a url-dispatch-matched view. + Deprecations ------------ @@ -169,6 +176,12 @@ Behavior Changes renderer changes the content type (to ``application/json`` or ``text/plain`` for JSON and string renderers respectively). +- The ``pyramid.wsgi.wsgiapp2`` now uses a slightly different method of + figuring out how to "fix" ``SCRIPT_NAME`` and ``PATH_INFO`` for the + downstream application. As a result, those values may differ slightly from + the perspective of the downstream application (for example, ``SCRIPT_NAME`` + will now never possess a trailing slash). + Dependencies ------------ diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py index 47a89c0eb..e4c61ff63 100644 --- a/pyramid/wsgi.py +++ b/pyramid/wsgi.py @@ -31,7 +31,7 @@ def wsgiapp(wrapped): """ def decorator(context, request): return request.get_response(wrapped) - return wraps(wrapped)(decorator) # grokkability + return wraps(wrapped)(decorator) def wsgiapp2(wrapped): """ Decorator to turn a WSGI application into a :app:`Pyramid` @@ -56,10 +56,14 @@ def wsgiapp2(wrapped): config.add_view(hello_world, name='hello_world.txt') The ``wsgiapp2`` decorator will convert the result of the WSGI - application to a Response and return it to :app:`Pyramid` as if - the WSGI app were a :app:`Pyramid` view. The ``SCRIPT_NAME`` - and ``PATH_INFO`` values present in the WSGI environment are fixed - up before the application is invoked. """ + application to a Response and return it to :app:`Pyramid` as if the WSGI + app were a :app:`Pyramid` view. The ``SCRIPT_NAME`` and ``PATH_INFO`` + values present in the WSGI environment are fixed up before the + application is invoked. In particular, a new WSGI environment is + generated, and the :term:`subpath` of the request passed to ``wsgiapp2`` + is used as the new request's ``PATH_INFO`` and everything preceding the + subpath is used as the ``SCRIPT_NAME``. The new environment is passed to + the downstream WSGI application.""" def decorator(context, request): return call_app_with_subpath_as_path_info(request, wrapped) -- cgit v1.2.3