diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst index 0edf03353a..e4cee98828 100644 --- a/docs/narr/vhosting.rst +++ b/docs/narr/vhosting.rst @@ -26,20 +26,20 @@ Hosting an Application Under a URL Prefix ``http://example.com/``). If you use a "pure Python" environment, this functionality can be provided by -Paste's `urlmap `_ "composite" WSGI -application. Alternatively, you can use :term:`mod_wsgi` to serve your +`rutter `_, forming a "composite" +WSGI application. Alternatively, you can use :term:`mod_wsgi` to serve your application, which handles this virtual hosting translation for you "under the hood". -If you use the ``urlmap`` composite application "in front" of a :app:`Pyramid` +If you use the ``rutter`` composite application "in front" of a :app:`Pyramid` application or if you use :term:`mod_wsgi` to serve up a :app:`Pyramid` application, nothing special needs to be done within the application for URLs -to be generated that contain a prefix. :mod:`paste.urlmap` and :term:`mod_wsgi` +to be generated that contain a prefix. Rutter and :term:`mod_wsgi` manipulate the :term:`WSGI` environment in such a way that the ``PATH_INFO`` and ``SCRIPT_NAME`` variables are correct for some given prefix. Here's an example of a PasteDeploy configuration snippet that includes a -``urlmap`` composite. +``rutter`` composite. .. code-block:: ini :linenos: @@ -48,13 +48,13 @@ Here's an example of a PasteDeploy configuration snippet that includes a use = egg:mypyramidapp [composite:main] - use = egg:Paste#urlmap + use = egg:rutter#urlmap /pyramidapp = mypyramidapp This "roots" the :app:`Pyramid` application at the prefix ``/pyramidapp`` and serves up the composite as the "main" application in the file. -.. note:: If you're using an Apache server to proxy to a Paste ``urlmap`` +.. note:: If you're using an Apache server to proxy to a ``urlmap`` composite, you may have to use the `ProxyPreserveHost `_ directive to pass the original ``HTTP_HOST`` header along to the diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index c1ddea63fd..450cd9c24f 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -799,58 +799,6 @@ class IResourceURL(Interface): 'The physical url path of the resource as a tuple. (New in 1.5)' ) -class IContextURL(IResourceURL): - """ - .. deprecated:: 1.3 - An adapter which deals with URLs related to a context. Use - :class:`pyramid.interfaces.IResourceURL` instead. - """ - # this class subclasses IResourceURL because request.resource_url looks - # for IResourceURL via queryAdapter. queryAdapter will find a deprecated - # IContextURL registration if no registration for IResourceURL exists. - # In reality, however, IContextURL objects were never required to have - # the virtual_path or physical_path attributes spelled in IResourceURL. - # The inheritance relationship is purely to benefit adapter lookup, - # not to imply an inheritance relationship of interface attributes - # and methods. - # - # Mechanics: - # - # class Fudge(object): - # def __init__(self, one, two): - # print(one, two) - # class Another(object): - # def __init__(self, one, two): - # print(one, two) - # ob = object() - # r.registerAdapter(Fudge, (Interface, Interface), IContextURL) - # print(r.queryMultiAdapter((ob, ob), IResourceURL)) - # r.registerAdapter(Another, (Interface, Interface), IResourceURL) - # print(r.queryMultiAdapter((ob, ob), IResourceURL)) - # - # prints - # - # - # <__main__.Fudge object at 0x1cda890> - # - # <__main__.Another object at 0x1cda850> - - def virtual_root(): - """ Return the virtual root related to a request and the - current context""" - - def __call__(): - """ Return a URL that points to the context. """ - -deprecated( - 'IContextURL', - 'As of Pyramid 1.3 the, "pyramid.interfaces.IContextURL" interface is ' - 'scheduled to be removed. Use the ' - '"pyramid.config.Configurator.add_resource_url_adapter" method to register ' - 'a class that implements "pyramid.interfaces.IResourceURL" instead. ' - 'See the "What\'s new In Pyramid 1.3" document for more details.' - ) - class IPEP302Loader(Interface): """ See http://www.python.org/dev/peps/pep-0302/#id30. """ diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py index 0decd04d6c..27350972e7 100644 --- a/pyramid/tests/test_traversal.py +++ b/pyramid/tests/test_traversal.py @@ -1,5 +1,4 @@ import unittest -import warnings from pyramid.testing import cleanUp @@ -11,11 +10,6 @@ PY2, ) -with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always') - from pyramid.interfaces import IContextURL - assert(len(w) == 1) - class TraversalPathTests(unittest.TestCase): def _callFUT(self, path): from pyramid.traversal import traversal_path @@ -870,182 +864,12 @@ def _getTargetClass(self): from pyramid.traversal import ResourceURL return ResourceURL - def _registerTraverser(self, traverser): - from pyramid.threadlocal import get_current_registry - reg = get_current_registry() - from pyramid.interfaces import ITraverser - from zope.interface import Interface - reg.registerAdapter(traverser, (Interface,), ITraverser) - - def test_class_conforms_to_IContextURL(self): - # bw compat - from zope.interface.verify import verifyClass - verifyClass(IContextURL, self._getTargetClass()) - - def test_instance_conforms_to_IContextURL(self): - from zope.interface.verify import verifyObject - context = DummyContext() - request = DummyRequest() - verifyObject(IContextURL, self._makeOne(context, request)) - def test_instance_conforms_to_IResourceURL(self): from pyramid.interfaces import IResourceURL from zope.interface.verify import verifyObject context = DummyContext() request = DummyRequest() verifyObject(IResourceURL, self._makeOne(context, request)) - - def test_call_withlineage(self): - baz = DummyContext() - bar = DummyContext(baz) - foo = DummyContext(bar) - root = DummyContext(foo) - root.__parent__ = None - root.__name__ = None - foo.__parent__ = root - foo.__name__ = 'foo ' - bar.__parent__ = foo - bar.__name__ = 'bar' - baz.__parent__ = bar - baz.__name__ = 'baz' - request = DummyRequest() - context_url = self._makeOne(baz, request) - result = context_url() - self.assertEqual(result, 'http://example.com:5432/foo%20/bar/baz/') - - def test_call_nolineage(self): - context = DummyContext() - context.__name__ = '' - context.__parent__ = None - request = DummyRequest() - context_url = self._makeOne(context, request) - result = context_url() - self.assertEqual(result, 'http://example.com:5432/') - - def test_call_unicode_mixed_with_bytes_in_resource_names(self): - root = DummyContext() - root.__parent__ = None - root.__name__ = None - one = DummyContext() - one.__parent__ = root - one.__name__ = text_(b'La Pe\xc3\xb1a', 'utf-8') - two = DummyContext() - two.__parent__ = one - two.__name__ = b'La Pe\xc3\xb1a' - request = DummyRequest() - context_url = self._makeOne(two, request) - result = context_url() - self.assertEqual( - result, - 'http://example.com:5432/La%20Pe%C3%B1a/La%20Pe%C3%B1a/') - - def test_call_with_virtual_root_path(self): - from pyramid.interfaces import VH_ROOT_KEY - root = DummyContext() - root.__parent__ = None - root.__name__ = None - one = DummyContext() - one.__parent__ = root - one.__name__ = 'one' - two = DummyContext() - two.__parent__ = one - two.__name__ = 'two' - request = DummyRequest({VH_ROOT_KEY:'/one'}) - context_url = self._makeOne(two, request) - result = context_url() - self.assertEqual(result, 'http://example.com:5432/two/') - - request = DummyRequest({VH_ROOT_KEY:'/one/two'}) - context_url = self._makeOne(two, request) - result = context_url() - self.assertEqual(result, 'http://example.com:5432/') - - def test_call_with_virtual_root_path_physical_not_startwith_vroot(self): - from pyramid.interfaces import VH_ROOT_KEY - root = DummyContext() - root.__parent__ = None - root.__name__ = None - one = DummyContext() - one.__parent__ = root - one.__name__ = 'one' - two = DummyContext() - two.__parent__ = one - two.__name__ = 'two' - request = DummyRequest({VH_ROOT_KEY:'/wrong'}) - context_url = self._makeOne(two, request) - result = context_url() - self.assertEqual(result, 'http://example.com:5432/one/two/') - - def test_call_empty_names_not_ignored(self): - bar = DummyContext() - empty = DummyContext(bar) - root = DummyContext(empty) - root.__parent__ = None - root.__name__ = None - empty.__parent__ = root - empty.__name__ = '' - bar.__parent__ = empty - bar.__name__ = 'bar' - request = DummyRequest() - context_url = self._makeOne(bar, request) - result = context_url() - self.assertEqual(result, 'http://example.com:5432//bar/') - - def test_call_local_url_returns_None(self): - resource = DummyContext() - def resource_url(request, info): - self.assertEqual(info['virtual_path'], '/') - self.assertEqual(info['physical_path'], '/') - return None - resource.__resource_url__ = resource_url - request = DummyRequest() - context_url = self._makeOne(resource, request) - result = context_url() - self.assertEqual(result, 'http://example.com:5432/') - - def test_call_local_url_returns_url(self): - resource = DummyContext() - def resource_url(request, info): - self.assertEqual(info['virtual_path'], '/') - self.assertEqual(info['physical_path'], '/') - return 'abc' - resource.__resource_url__ = resource_url - request = DummyRequest() - context_url = self._makeOne(resource, request) - result = context_url() - self.assertEqual(result, 'abc') - - def test_virtual_root_no_virtual_root_path(self): - root = DummyContext() - root.__name__ = None - root.__parent__ = None - one = DummyContext() - one.__name__ = 'one' - one.__parent__ = root - request = DummyRequest() - context_url = self._makeOne(one, request) - self.assertEqual(context_url.virtual_root(), root) - - def test_virtual_root_no_virtual_root_path_with_root_on_request(self): - context = DummyContext() - context.__parent__ = None - request = DummyRequest() - request.root = DummyContext() - context_url = self._makeOne(context, request) - self.assertEqual(context_url.virtual_root(), request.root) - - def test_virtual_root_with_virtual_root_path(self): - from pyramid.interfaces import VH_ROOT_KEY - context = DummyContext() - context.__parent__ = None - traversed_to = DummyContext() - environ = {VH_ROOT_KEY:'/one'} - request = DummyRequest(environ) - traverser = make_traverser({'context':traversed_to, 'view_name':''}) - self._registerTraverser(traverser) - context_url = self._makeOne(context, request) - self.assertEqual(context_url.virtual_root(), traversed_to) - self.assertEqual(context.request.environ['PATH_INFO'], '/one') def test_IResourceURL_attributes_with_vroot(self): from pyramid.interfaces import VH_ROOT_KEY @@ -1114,14 +938,44 @@ def _callFUT(self, resource, request): from pyramid.traversal import virtual_root return virtual_root(resource, request) - def test_registered(self): + def _registerTraverser(self, traverser): + from pyramid.threadlocal import get_current_registry + reg = get_current_registry() + from pyramid.interfaces import ITraverser from zope.interface import Interface - request = _makeRequest() - request.registry.registerAdapter(DummyContextURL, (Interface,Interface), - IContextURL) + reg.registerAdapter(traverser, (Interface,), ITraverser) + + def test_virtual_root_no_virtual_root_path(self): + root = DummyContext() + root.__name__ = None + root.__parent__ = None + one = DummyContext() + one.__name__ = 'one' + one.__parent__ = root + request = DummyRequest() + result = self._callFUT(one, request) + self.assertEqual(result, root) + + def test_virtual_root_no_virtual_root_path_with_root_on_request(self): context = DummyContext() + context.__parent__ = None + request = DummyRequest() + request.root = DummyContext() result = self._callFUT(context, request) - self.assertEqual(result, '123') + self.assertEqual(result, request.root) + + def test_virtual_root_with_virtual_root_path(self): + from pyramid.interfaces import VH_ROOT_KEY + context = DummyContext() + context.__parent__ = None + traversed_to = DummyContext() + environ = {VH_ROOT_KEY:'/one'} + request = DummyRequest(environ) + traverser = make_traverser({'context':traversed_to, 'view_name':''}) + self._registerTraverser(traverser) + result = self._callFUT(context, request) + self.assertEqual(result, traversed_to) + self.assertEqual(context.request.environ['PATH_INFO'], '/one') def test_default(self): context = DummyContext() @@ -1347,13 +1201,6 @@ def _set_path_info(self, v): path_info = property(_get_path_info, _set_path_info) -class DummyContextURL: - def __init__(self, context, request): - pass - - def virtual_root(self): - return '123' - def _makeRequest(environ=None): from pyramid.registry import Registry request = DummyRequest() diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index 0a788ba97f..ddf28e0b04 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -29,18 +29,6 @@ def __init__(self, environ): request.registry = self.config.registry return request - def _registerContextURL(self, reg): - with warnings.catch_warnings(record=True): - from pyramid.interfaces import IContextURL - from zope.interface import Interface - class DummyContextURL(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'http://example.com/context/' - reg.registerAdapter(DummyContextURL, (Interface, Interface), - IContextURL) - def _registerResourceURL(self, reg): from pyramid.interfaces import IResourceURL from zope.interface import Interface @@ -186,16 +174,6 @@ def test_resource_url_no_registry_on_request(self): root = DummyContext() result = request.resource_url(root) self.assertEqual(result, 'http://example.com:5432/context/') - - def test_resource_url_finds_IContextURL(self): - request = self._makeOne() - self._registerContextURL(request.registry) - root = DummyContext() - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - result = request.resource_url(root) - self.assertEqual(len(w), 1) - self.assertEqual(result, 'http://example.com/context/') def test_resource_url_with_app_url(self): request = self._makeOne() diff --git a/pyramid/traversal.py b/pyramid/traversal.py index 963a76bb5f..a8bd2551be 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -1,7 +1,3 @@ -import warnings - -from zope.deprecation import deprecated - from zope.interface import implementer from zope.interface.interfaces import IInterface @@ -31,10 +27,6 @@ from pyramid.location import lineage from pyramid.threadlocal import get_current_registry -with warnings.catch_warnings(): - warnings.filterwarnings('ignore') - from pyramid.interfaces import IContextURL - empty = text_('') def find_root(resource): @@ -404,8 +396,8 @@ def virtual_root(resource, request): the resource object representing the :term:`virtual root` of the current :term:`request`. Using a virtual root in a :term:`traversal` -based :app:`Pyramid` application permits - rooting, for example, the resource at the traversal path ``/cms`` at - ``http://example.com/`` instead of rooting it at + rooting. For example, the resource at the traversal path ``/cms`` will + be found at ``http://example.com/`` instead of rooting it at ``http://example.com/cms/``. If the ``resource`` passed in is a context obtained via @@ -424,14 +416,14 @@ def virtual_root(resource, request): is called with a ``resource`` argument which is a context obtained via URL dispatch, the resource passed in will be returned unconditionally.""" + if VH_ROOT_KEY in request.environ: + return find_resource(resource, request.environ[VH_ROOT_KEY]) + # shortcut instead of using find_root; we probably already + # have it on the request try: - reg = request.registry + return request.root except AttributeError: - reg = get_current_registry() # b/c - urlgenerator = reg.queryMultiAdapter((resource, request), IContextURL) - if urlgenerator is None: - urlgenerator = TraversalContextURL(resource, request) - return urlgenerator.virtual_root() + return find_root(resource) def traversal_path(path): """ Variant of :func:`pyramid.traversal.traversal_path_info` suitable for @@ -728,10 +720,8 @@ def __call__(self, request): ModelGraphTraverser = ResourceTreeTraverser # b/w compat, not API, used in wild -@implementer(IResourceURL, IContextURL) +@implementer(IResourceURL) class ResourceURL(object): - vroot_varname = VH_ROOT_KEY - def __init__(self, resource, request): physical_path_tuple = resource_path_tuple(resource) physical_path = _join_path_tuple(physical_path_tuple) @@ -744,7 +734,7 @@ def __init__(self, resource, request): virtual_path_tuple = physical_path_tuple environ = request.environ - vroot_path = environ.get(self.vroot_varname) + vroot_path = environ.get(VH_ROOT_KEY) # if the physical path starts with the virtual root path, trim it out # of the virtual path @@ -761,59 +751,6 @@ def __init__(self, resource, request): self.virtual_path_tuple = virtual_path_tuple # IResourceURL attr (1.5) self.physical_path_tuple = physical_path_tuple # IResourceURL attr (1.5) - # bw compat for IContextURL methods - self.resource = resource - self.context = resource - self.request = request - - # IContextURL method (deprecated in 1.3) - def virtual_root(self): - environ = self.request.environ - vroot_varname = self.vroot_varname - if vroot_varname in environ: - return find_resource(self.context, environ[vroot_varname]) - # shortcut instead of using find_root; we probably already - # have it on the request - try: - return self.request.root - except AttributeError: - return find_root(self.context) - - # IContextURL method (deprecated in 1.3) - def __call__(self): - """ Generate a URL based on the :term:`lineage` of a :term:`resource` - object that is ``self.context``. If any resource in the context - lineage has a Unicode name, it will be converted to a UTF-8 string - before being attached to the URL. If a ``HTTP_X_VHM_ROOT`` key is - present in the WSGI environment, its value will be treated as a - 'virtual root path': the path of the URL generated by this will be - left-stripped of this virtual root path value. - """ - local_url = getattr(self.context, '__resource_url__', None) - if local_url is not None: - result = local_url( - self.request, - {'virtual_path':self.virtual_path, - 'physical_path':self.physical_path}, - ) - if result is not None: - # allow it to punt by returning ``None`` - return result - - app_url = self.request.application_url # never ends in a slash - return app_url + self.virtual_path - -TraversalContextURL = ResourceURL # deprecated as of 1.3 - -deprecated( - 'TraversalContextURL', - 'As of Pyramid 1.3 the, "pyramid.traversal.TraversalContextURL" class is ' - 'scheduled to be removed. Use the ' - '"pyramid.config.Configurator.add_resource_url_adapter" method to register ' - 'a class that implements "pyramid.interfaces.IResourceURL" instead. ' - 'See the "What\'s new In Pyramid 1.3" document for a further description.' - ) - @lru_cache(1000) def _join_path_tuple(tuple): return tuple and '/'.join([quote_path_segment(x) for x in tuple]) or '/' diff --git a/pyramid/url.py b/pyramid/url.py index 646cc857d7..c09e79ae01 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -1,7 +1,6 @@ """ Utility functions for dealing with URLs in pyramid """ import os -import warnings from repoze.lru import lru_cache @@ -533,89 +532,72 @@ def resource_url(self, resource, *elements, **kw): virtual_path = getattr(url_adapter, 'virtual_path', None) - if virtual_path is None: - # old-style IContextURL adapter (Pyramid 1.2 and previous) - warnings.warn( - 'Pyramid is using an IContextURL adapter to generate a ' - 'resource URL; any "app_url", "host", "port", or "scheme" ' - 'arguments passed to resource_url are being ignored. To ' - 'avoid this behavior, as of Pyramid 1.3, register an ' - 'IResourceURL adapter instead of an IContextURL ' - 'adapter for the resource type(s). IContextURL adapters ' - 'will be ignored in a later major release of Pyramid.', - DeprecationWarning, - 2) - - resource_url = url_adapter() + app_url = None + scheme = None + host = None + port = None + + if 'route_name' in kw: + newkw = {} + route_name = kw['route_name'] + remainder = getattr(url_adapter, 'virtual_path_tuple', None) + if remainder is None: + # older user-supplied IResourceURL adapter without 1.5 + # virtual_path_tuple + remainder = tuple(url_adapter.virtual_path.split('/')) + remainder_name = kw.get('route_remainder_name', 'traverse') + newkw[remainder_name] = remainder + + for name in ( + 'app_url', 'scheme', 'host', 'port', 'query', 'anchor' + ): + val = kw.get(name, None) + if val is not None: + newkw['_' + name] = val + + if 'route_kw' in kw: + route_kw = kw.get('route_kw') + if route_kw is not None: + newkw.update(route_kw) + + return self.route_url(route_name, *elements, **newkw) + + if 'app_url' in kw: + app_url = kw['app_url'] + + if 'scheme' in kw: + scheme = kw['scheme'] + + if 'host' in kw: + host = kw['host'] + + if 'port' in kw: + port = kw['port'] - else: - # IResourceURL adapter (Pyramid 1.3 and after) - app_url = None - scheme = None - host = None - port = None - - if 'route_name' in kw: - newkw = {} - route_name = kw['route_name'] - remainder = getattr(url_adapter, 'virtual_path_tuple', None) - if remainder is None: - # older user-supplied IResourceURL adapter without 1.5 - # virtual_path_tuple - remainder = tuple(url_adapter.virtual_path.split('/')) - remainder_name = kw.get('route_remainder_name', 'traverse') - newkw[remainder_name] = remainder - - for name in ( - 'app_url', 'scheme', 'host', 'port', 'query', 'anchor' - ): - val = kw.get(name, None) - if val is not None: - newkw['_' + name] = val - - if 'route_kw' in kw: - route_kw = kw.get('route_kw') - if route_kw is not None: - newkw.update(route_kw) - - return self.route_url(route_name, *elements, **newkw) - - if 'app_url' in kw: - app_url = kw['app_url'] - - if 'scheme' in kw: - scheme = kw['scheme'] - - if 'host' in kw: - host = kw['host'] - - if 'port' in kw: - port = kw['port'] - - if app_url is None: - if scheme or host or port: - app_url = self._partial_application_url(scheme, host, port) - else: - app_url = self.application_url - - resource_url = None - local_url = getattr(resource, '__resource_url__', None) - - if local_url is not None: - # the resource handles its own url generation - d = dict( - virtual_path=virtual_path, - physical_path=url_adapter.physical_path, - app_url=app_url, - ) - - # allow __resource_url__ to punt by returning None - resource_url = local_url(self, d) - - if resource_url is None: - # the resource did not handle its own url generation or the - # __resource_url__ function returned None - resource_url = app_url + virtual_path + if app_url is None: + if scheme or host or port: + app_url = self._partial_application_url(scheme, host, port) + else: + app_url = self.application_url + + resource_url = None + local_url = getattr(resource, '__resource_url__', None) + + if local_url is not None: + # the resource handles its own url generation + d = dict( + virtual_path=virtual_path, + physical_path=url_adapter.physical_path, + app_url=app_url, + ) + + # allow __resource_url__ to punt by returning None + resource_url = local_url(self, d) + + if resource_url is None: + # the resource did not handle its own url generation or the + # __resource_url__ function returned None + resource_url = app_url + virtual_path qs = '' anchor = ''