diff options
| -rw-r--r-- | CHANGES.txt | 16 | ||||
| -rw-r--r-- | docs/api.rst | 1 | ||||
| -rw-r--r-- | docs/api/static.rst | 11 | ||||
| -rw-r--r-- | docs/narr/assets.rst | 29 | ||||
| -rw-r--r-- | docs/narr/hybrid.rst | 17 | ||||
| -rw-r--r-- | pyramid/static.py | 18 | ||||
| -rw-r--r-- | pyramid/tests/test_integration.py | 4 | ||||
| -rw-r--r-- | pyramid/tests/test_static.py | 56 | ||||
| -rw-r--r-- | pyramid/tests/test_view.py | 54 | ||||
| -rw-r--r-- | pyramid/view.py | 25 |
10 files changed, 184 insertions, 47 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index cd9f42dd9..d898c5ca6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,22 @@ Features rendererinfo to clear out old registry on a rescan. See https://github.com/Pylons/pyramid/pull/234. +- New API class: ``pyramid.static.static_view``. This supersedes the + deprecated ``pyramid.view.static`` class. ``pyramid.satic.static_view`` by + default serves up documents as the result of the request's ``path_info``, + attribute rather than it's ``subpath`` attribute (the inverse was true of + ``pyramid.view.static``, and still is). ``pyramid.static.static_view`` + exposes a ``use_subpath`` flag for use when you don't want the static view + to behave like the older deprecated version. + +Deprecations +------------ + +- The ``pyramid.view.static`` class has been deprecated in favor of the newer + ``pyramid.static.static_view`` class. A deprecation warning is raised when + it is used. You should replace it with a reference to + ``pyramid.static.static_view`` with the ``use_subpath=True`` argument. + 1.1b2 (2011-07-13) ================== diff --git a/docs/api.rst b/docs/api.rst index be7942502..a7e1566d3 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -28,6 +28,7 @@ documentation is organized alphabetically by module name. api/security api/session api/settings + api/static api/testing api/threadlocal api/traversal diff --git a/docs/api/static.rst b/docs/api/static.rst new file mode 100644 index 000000000..c28473584 --- /dev/null +++ b/docs/api/static.rst @@ -0,0 +1,11 @@ +.. _static_module: + +:mod:`pyramid.static` +--------------------- + +.. automodule:: pyramid.static + + .. autoclass:: static_view + :members: + :inherited-members: + diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst index 0d50b0106..d57687477 100644 --- a/docs/narr/assets.rst +++ b/docs/narr/assets.rst @@ -299,7 +299,7 @@ URLs against assets made accessible by registering a custom static view. Root-Relative Custom Static View (URL Dispatch Only) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :class:`pyramid.view.static` helper class generates a Pyramid view +The :class:`pyramid.static.static_view` helper class generates a Pyramid view callable. This view callable can serve static assets from a directory. An instance of this class is actually used by the :meth:`~pyramid.config.Configurator.add_static_view` configuration method, so @@ -310,26 +310,27 @@ its behavior is almost exactly the same once it's configured. exclusively. The root-relative route we'll be registering will always be matched before traversal takes place, subverting any views registered via ``add_view`` (at least those without a ``route_name``). A - :class:`~pyramid.view.static` static view cannot be made root-relative when - you use traversal. + :class:`~pyramid.static.static_view` static view cannot be made + root-relative when you use traversal unless it's registered as a + :term:`NotFound view`. To serve files within a directory located on your filesystem at ``/path/to/static/dir`` as the result of a "catchall" route hanging from the root that exists at the end of your routing table, create an instance of the -:class:`~pyramid.view.static` class inside a ``static.py`` file in your -application root as below. +:class:`~pyramid.static.static_view` class inside a ``static.py`` file in +your application root as below. .. ignore-next-block .. code-block:: python :linenos: - from pyramid.view import static - static_view = static('/path/to/static/dir') + from pyramid.static import static + static_view = static_view('/path/to/static/dir', use_subpath=True) .. note:: For better cross-system flexibility, use an :term:`asset - specification` as the argument to :class:`~pyramid.view.static` instead of - a physical absolute filesystem path, e.g. ``mypackage:static`` instead of - ``/path/to/mypackage/static``. + specification` as the argument to :class:`~pyramid.static.static_view` + instead of a physical absolute filesystem path, e.g. ``mypackage:static`` + instead of ``/path/to/mypackage/static``. Subsequently, you may wire the files that are served by this view up to be accessible as ``/<filename>`` using a configuration method in your @@ -345,8 +346,8 @@ application's startup code. config.add_view('myapp.static.static_view', route_name='catchall_static') The special name ``*subpath`` above is used by the -:class:`~pyramid.view.static` view callable to signify the path of the file -relative to the directory you're serving. +:class:`~pyramid.static.static_view` view callable to signify the path of the +file relative to the directory you're serving. Registering A View Callable to Serve a "Static" Asset ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -425,10 +426,10 @@ feature, a :term:`Configurator` API exists named - A directory containing multiple Chameleon templates. - Individual static files served up by an instance of the - ``pyramid.view.static`` helper class. + ``pyramid.static.static_view`` helper class. - A directory of static files served up by an instance of the - ``pyramid.view.static`` helper class. + ``pyramid.static.static_view`` helper class. - Any other asset (or set of assets) addressed by code that uses the setuptools :term:`pkg_resources` API. diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index 97adaeafd..a0a6a108c 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -431,8 +431,9 @@ Using ``*subpath`` in a Route Pattern There are certain extremely rare cases when you'd like to influence the traversal :term:`subpath` when a route matches without actually performing traversal. For instance, the :func:`pyramid.wsgi.wsgiapp2` decorator and the -:class:`pyramid.view.static` helper attempt to compute ``PATH_INFO`` from the -request's subpath, so it's useful to be able to influence this value. +:class:`pyramid.static.static_view` helper attempt to compute ``PATH_INFO`` +from the request's subpath when its ``use_subpath`` argument is ``True``, so +it's useful to be able to influence this value. When ``*subpath`` exists in a pattern, no path is actually traversed, but the traversal algorithm will return a :term:`subpath` list implied @@ -442,12 +443,16 @@ commonly in route declarations that look like this: .. code-block:: python :linenos: + from pryamid.static import static_view + + www = static_view('mypackage:static', use_subpath=True) + config.add_route('static', '/static/*subpath') - config.add_view('mypackage.views.static_view', route_name='static') + config.add_view(www, route_name='static') -Where ``mypackage.views.static_view`` is an instance of -:class:`pyramid.view.static`. This effectively tells the static helper to -traverse everything in the subpath as a filename. +``mypackage.views.www`` is an instance of +:class:`pyramid.static.static_view`. This effectively tells the static +helper to traverse everything in the subpath as a filename. Corner Cases ------------ diff --git a/pyramid/static.py b/pyramid/static.py index ec7b4cb00..9d8afc09b 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -136,7 +136,8 @@ class StaticURLInfo(object): # it's a view name cache_max_age = extra.pop('cache_max_age', None) # create a view - view = static_view(spec, cache_max_age=cache_max_age) + view = static_view(spec, cache_max_age=cache_max_age, + use_subpath=True) # Mutate extra to allow factory, etc to be passed through here. # Treat permission specially because we'd like to default to @@ -199,6 +200,13 @@ class static_view(object): response headers returned by the view (default is 3600 seconds or five minutes). + ``use_subpath`` influences whether ``request.subpath`` will be used as + ``PATH_INFO`` when calling the underlying WSGI application which actually + serves the static files. If it is ``True``, the static application will + consider ``request.subpath`` as ``PATH_INFO`` input. If it is ``False``, + the static application will consider request.path_info as ``PATH_INFO`` + input. By default, this is ``False``. + .. note:: If the ``root_dir`` is relative to a :term:`package`, or is a :term:`asset specification` the :app:`Pyramid` :class:`pyramid.config.Configurator` method can be @@ -207,7 +215,8 @@ class static_view(object): absolute, configuration will not be able to override the assets it contains. """ - def __init__(self, root_dir, cache_max_age=3600, package_name=None): + def __init__(self, root_dir, cache_max_age=3600, package_name=None, + use_subpath=False): # package_name is for bw compat; it is preferred to pass in a # package-relative path as root_dir # (e.g. ``anotherpackage:foo/static``). @@ -220,6 +229,9 @@ class static_view(object): app = PackageURLParser( package_name, root_dir, cache_max_age=cache_max_age) self.app = app + self.use_subpath = use_subpath def __call__(self, context, request): - return call_app_with_subpath_as_path_info(request, self.app) + if self.use_subpath: + return call_app_with_subpath_as_path_info(request, self.app) + return request.get_response(self.app) diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 0ef1e1631..1ebf83062 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -3,7 +3,7 @@ import unittest from pyramid.wsgi import wsgiapp from pyramid.view import view_config -from pyramid.view import static +from pyramid.static import static_view from zope.interface import Interface @@ -42,7 +42,7 @@ class WGSIAppPlusViewConfigTests(unittest.TestCase): self.assertEqual(view.__original_view__, wsgiapptest) here = os.path.dirname(__file__) -staticapp = static(os.path.join(here, 'fixtures')) +staticapp = static_view(os.path.join(here, 'fixtures'), use_subpath=True) class TestStaticApp(unittest.TestCase): def test_basic(self): diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index e7506628a..a15459da2 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -190,11 +190,12 @@ class Test_static_view(unittest.TestCase): cleanUp() def _getTargetClass(self): - from pyramid.view import static - return static + from pyramid.static import static_view + return static_view - def _makeOne(self, path, package_name=None): - return self._getTargetClass()(path, package_name=package_name) + def _makeOne(self, path, package_name=None, use_subpath=False): + return self._getTargetClass()(path, package_name=package_name, + use_subpath=use_subpath) def _makeEnviron(self, **extras): environ = { @@ -207,10 +208,10 @@ class Test_static_view(unittest.TestCase): environ.update(extras) return environ - def test_abspath(self): + def test_abspath_subpath(self): import os.path path = os.path.dirname(__file__) - view = self._makeOne(path) + view = self._makeOne(path, use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = ['__init__.py'] @@ -219,9 +220,9 @@ class Test_static_view(unittest.TestCase): self.assertEqual(request.copied, True) self.assertEqual(response.directory, os.path.normcase(path)) - def test_relpath(self): + def test_relpath_subpath(self): path = 'fixtures' - view = self._makeOne(path) + view = self._makeOne(path, use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = ['__init__.py'] @@ -233,8 +234,22 @@ class Test_static_view(unittest.TestCase): self.assertEqual(response.package_name, 'pyramid.tests') self.assertEqual(response.cache_max_age, 3600) - def test_relpath_withpackage(self): - view = self._makeOne('another:fixtures') + def test_relpath_notsubpath(self): + path = 'fixtures' + view = self._makeOne(path) + context = DummyContext() + request = DummyRequest() + request.subpath = ['__init__.py'] + request.environ = self._makeEnviron() + response = view(context, request) + self.assertTrue(not hasattr(request, 'copied')) + self.assertEqual(response.root_resource, 'fixtures') + self.assertEqual(response.resource_name, 'fixtures') + self.assertEqual(response.package_name, 'pyramid.tests') + self.assertEqual(response.cache_max_age, 3600) + + def test_relpath_withpackage_subpath(self): + view = self._makeOne('another:fixtures', use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = ['__init__.py'] @@ -246,8 +261,9 @@ class Test_static_view(unittest.TestCase): self.assertEqual(response.package_name, 'another') self.assertEqual(response.cache_max_age, 3600) - def test_relpath_withpackage_name(self): - view = self._makeOne('fixtures', package_name='another') + def test_relpath_withpackage_name_subpath(self): + view = self._makeOne('fixtures', package_name='another', + use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = ['__init__.py'] @@ -259,8 +275,9 @@ class Test_static_view(unittest.TestCase): self.assertEqual(response.package_name, 'another') self.assertEqual(response.cache_max_age, 3600) - def test_no_subpath_preserves_path_info_and_script_name(self): - view = self._makeOne('fixtures', package_name='another') + def test_no_subpath_preserves_path_info_and_script_name_subpath(self): + view = self._makeOne('fixtures', package_name='another', + use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = () @@ -272,8 +289,9 @@ class Test_static_view(unittest.TestCase): 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') + def test_with_subpath_path_info_ends_with_slash_subpath(self): + view = self._makeOne('fixtures', package_name='another', + use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = ('subpath',) @@ -284,7 +302,8 @@ class Test_static_view(unittest.TestCase): self.assertEqual(request.environ['SCRIPT_NAME'], '/path_info') def test_with_subpath_original_script_name_preserved(self): - view = self._makeOne('fixtures', package_name='another') + view = self._makeOne('fixtures', package_name='another', + use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = ('subpath',) @@ -297,7 +316,8 @@ class Test_static_view(unittest.TestCase): '/scriptname/path_info') def test_with_subpath_new_script_name_fixes_trailing_slashes(self): - view = self._makeOne('fixtures', package_name='another') + view = self._makeOne('fixtures', package_name='another', + use_subpath=True) context = DummyContext() request = DummyRequest() request.subpath = ('sub', 'path') diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py index d46cfb3f5..8e5861e7b 100644 --- a/pyramid/tests/test_view.py +++ b/pyramid/tests/test_view.py @@ -565,6 +565,48 @@ class Test_patch_mimetypes(unittest.TestCase): result = self._callFUT(module) self.assertEqual(result, False) +class Test_static(unittest.TestCase): + def setUp(self): + from zope.deprecation import __show__ + __show__.off() + + def tearDown(self): + from zope.deprecation import __show__ + __show__.on() + + def _getTargetClass(self): + from pyramid.view import static + return static + + def _makeOne(self, path, package_name=None): + return self._getTargetClass()(path, package_name=package_name) + + def _makeEnviron(self, **extras): + environ = { + 'wsgi.url_scheme':'http', + 'wsgi.version':(1,0), + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'8080', + 'REQUEST_METHOD':'GET', + } + environ.update(extras) + return environ + + + def test_relpath_subpath(self): + path = 'fixtures' + view = self._makeOne(path) + context = DummyContext() + request = DummyRequest() + request.subpath = ['__init__.py'] + request.environ = self._makeEnviron() + response = view(context, request) + self.assertEqual(request.copied, True) + self.assertEqual(response.root_resource, 'fixtures') + self.assertEqual(response.resource_name, 'fixtures') + self.assertEqual(response.package_name, 'pyramid.tests') + self.assertEqual(response.cache_max_age, 3600) + class ExceptionResponse(Exception): status = '404 Not Found' app_iter = ['Not Found'] @@ -581,6 +623,18 @@ def make_view(response): class DummyRequest: exception = None + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + + def get_response(self, application): + return application + + def copy(self): + self.copied = True + return self + from pyramid.interfaces import IResponse from zope.interface import implements diff --git a/pyramid/view.py b/pyramid/view.py index 6b28601e2..1b59a2ed9 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -1,5 +1,6 @@ import mimetypes import venusian +import warnings from zope.interface import providedBy from zope.deprecation import deprecated @@ -11,6 +12,7 @@ from pyramid.interfaces import IRendererInfo from pyramid.httpexceptions import HTTPFound from pyramid.httpexceptions import default_exceptionresponse_view +from pyramid.path import caller_package from pyramid.renderers import RendererHelper from pyramid.static import static_view from pyramid.threadlocal import get_current_registry @@ -30,12 +32,27 @@ def init_mimetypes(mimetypes): # fallout. init_mimetypes(mimetypes) -# Nasty BW compat hack: dont yet deprecate this (ever?) -class static(static_view): # only subclass for purposes of autodoc - __doc__ = static_view.__doc__ - _marker = object() +class static(static_view): + """ Backwards compatibility alias for + :class:`pyramid.static.static_view`; it overrides that class' constructor + to pass ``use_subpath=True`` by default. This class is deprecated as of + :app:`Pyramid` 1.1. Use :class:`pyramid.static.static_view` instead + (probably with a ``use_subpath=True`` argument). + """ + def __init__(self, root_dir, cache_max_age=3600, package_name=None): + if package_name is None: + package_name = caller_package().__name__ + static_view.__init__(self, root_dir, cache_max_age=cache_max_age, + package_name=package_name, use_subpath=True) + +deprecated( + 'static', + 'The "pyramid.view.static" class is deprecated as of Pyramid 1.1; ' + 'use the "pyramid.static.static_view" class instead with the ' + '"use_subpath" argument set to True.') + def render_view_to_response(context, request, name='', secure=True): """ Call the :term:`view callable` configured with a :term:`view configuration` that matches the :term:`view name` ``name`` |
