summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt15
-rw-r--r--TODO.txt4
-rw-r--r--docs/narr/hooks.rst24
-rw-r--r--docs/narr/viewconfig.rst8
-rw-r--r--docs/quick_tour.rst14
-rw-r--r--pyramid/config/views.py8
-rw-r--r--pyramid/response.py25
-rw-r--r--pyramid/session.py6
-rw-r--r--pyramid/static.py17
-rw-r--r--pyramid/tests/test_static.py12
-rw-r--r--pyramid/url.py6
-rw-r--r--setup.cfg48
-rw-r--r--tox.ini8
13 files changed, 133 insertions, 62 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 434557f89..1939ad125 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -16,12 +16,25 @@ Backward Incompatibilities
See https://github.com/Pylons/pyramid/pull/2615
-- ``pcreate`` is now interactive by default. You will be prompted if it
+- ``pcreate`` is now interactive by default. You will be prompted if
a file already exists with different content. Previously if there were
similar files it would silently skip them unless you specified
``--interactive`` or ``--overwrite``.
See https://github.com/Pylons/pyramid/pull/2775
+- Removed undocumented argument ``cachebust_match`` from
+ ``pyramid.static.static_view``. This argument was shipped accidentally
+ in Pyramid 1.6. See https://github.com/Pylons/pyramid/pull/2681
+
+- Change static view to avoid setting the ``Content-Encoding`` response header
+ to an encoding guessed using Python's ``mimetypes`` module. This was causing
+ clients to decode the content of gzipped files when downloading them. The
+ client would end up with a ``foo.txt.gz`` file on disk that was already
+ decoded, thus should really be ``foo.txt``. Also, the ``Content-Encoding``
+ should only have been used if the client itself broadcast support for the
+ encoding via ``Accept-Encoding`` request headers.
+ See https://github.com/Pylons/pyramid/pull/2810
+
Features
--------
diff --git a/TODO.txt b/TODO.txt
index 797f8acef..d62efee5a 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -120,9 +120,7 @@ Future
- 1.6: Remove IContextURL and TraversalContextURL.
-- 1.8: Remove set_request_property.
-- 1.8: Drop Python 3.3 support.
-
+- 1.9: Remove set_request_property.
- 1.9: Remove extra code enabling ``pyramid.security.remember(principal=...)``
and force use of ``userid``.
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index b22b31bf9..d21edc7b4 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -116,14 +116,6 @@ callable:
.. note::
- Both :meth:`pyramid.config.Configurator.add_notfound_view` and
- :class:`pyramid.view.notfound_view_config` are new as of Pyramid 1.3.
- Older Pyramid documentation instructed users to use ``add_view`` instead,
- with a ``context`` of ``HTTPNotFound``. This still works; the convenience
- method and decorator are just wrappers around this functionality.
-
-.. warning::
-
When a Not Found View callable accepts an argument list as described in
:ref:`request_and_context_view_definitions`, the ``context`` passed as the
first argument to the view callable will be the
@@ -131,6 +123,13 @@ callable:
available, the resource context will still be available as
``request.context``.
+.. warning::
+
+ The :term:`Not Found View` callables are only invoked when a
+ :exc:`~pyramid.httpexceptions.HTTPNotFound` exception is raised. If the
+ exception is returned from a view then it will be treated as a regular
+ response object and it will not trigger the custom view.
+
.. index::
single: forbidden view
@@ -210,6 +209,13 @@ Here's some sample code that implements a minimal forbidden view:
whether the ``pyramid.debug_authorization`` environment setting is true or
false.
+.. warning::
+
+ The :term:`forbidden view` callables are only invoked when a
+ :exc:`~pyramid.httpexceptions.HTTPForbidden` exception is raised. If the
+ exception is returned from a view then it will be treated as a regular
+ response object and it will not trigger the custom view.
+
.. index::
single: request factory
@@ -744,7 +750,9 @@ The API that must be implemented by a class that provides
""" Accept the resource and request and set self.physical_path and
self.virtual_path """
self.virtual_path = some_function_of(resource, request)
+ self.virtual_path_tuple = some_function_of(resource, request)
self.physical_path = some_other_function_of(resource, request)
+ self.physical_path_tuple = some_function_of(resource, request)
The default context URL generator is available for perusal as the class
:class:`pyramid.traversal.ResourceURL` in the `traversal module
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index 7cb8e0306..3b683ff79 100644
--- a/docs/narr/viewconfig.rst
+++ b/docs/narr/viewconfig.rst
@@ -252,7 +252,7 @@ Non-Predicate Arguments
def myview(request):
...
- Is similar to doing::
+ Is similar to decorating the view callable directly::
@view_config(...)
@decorator2
@@ -260,8 +260,10 @@ Non-Predicate Arguments
def myview(request):
...
- All view callables in the decorator chain must return a response object
- implementing :class:`pyramid.interfaces.IResponse` or raise an exception:
+ An important distinction is that each decorator will receive a response
+ object implementing :class:`pyramid.interfaces.IResponse` instead of the
+ raw value returned from the view callable. All decorators in the chain must
+ return a response object or raise an exception:
.. code-block:: python
diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst
index 39b4cafb3..45c706b0d 100644
--- a/docs/quick_tour.rst
+++ b/docs/quick_tour.rst
@@ -504,7 +504,7 @@ Pyramid's ``pcreate`` command can list the available scaffolds:
.. code-block:: bash
- $ pcreate --list
+ $ $VENV/bin/pcreate --list
Available scaffolds:
alchemy: Pyramid project using SQLAlchemy, SQLite, URL dispatch, and Jinja2
pyramid_jinja2_starter: Pyramid Jinja2 starter project
@@ -517,7 +517,7 @@ that scaffold to make our project:
.. code-block:: bash
- $ pcreate --scaffold pyramid_jinja2_starter hello_world
+ $ $VENV/bin/pcreate --scaffold pyramid_jinja2_starter hello_world
We next use the normal Python command to set up our package for development:
@@ -678,10 +678,10 @@ egregious, as Pyramid has had a deep commitment to full test coverage since
before its release.
Our ``pyramid_jinja2_starter`` scaffold generated a ``tests.py`` module with
-one unit test in it. To run it, let's install the handy ``pytest`` test runner
-by editing ``setup.py``. While we're at it, we'll throw in the ``pytest-cov``
-tool which yells at us for code that isn't tested. Insert and edit the
-following lines as shown:
+one unit test in it. It also configured ``setup.py`` with test requirements:
+``py.test`` as the test runner, ``WebTest`` for running view tests, and the
+``pytest-cov`` tool which yells at us for code that isn't tested. The
+highlighted lines show this:
.. code-block:: python
:linenos:
@@ -711,7 +711,7 @@ following lines as shown:
'testing': tests_require,
},
-We changed ``setup.py`` which means we need to rerun ``$VENV/bin/pip install -e
+To install the test requirements, run ``$VENV/bin/pip install -e
".[testing]"``. We can now run all our tests:
.. code-block:: bash
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index acdc00704..6082d8b48 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -444,9 +444,11 @@ class ViewsConfiguratorMixin(object):
think about preserving function attributes such as ``__name__`` and
``__module__`` within decorator logic).
- All view callables in the decorator chain must return a response
- object implementing :class:`pyramid.interfaces.IResponse` or raise
- an exception:
+ An important distinction is that each decorator will receive a
+ response object implementing :class:`pyramid.interfaces.IResponse`
+ instead of the raw value returned from the view callable. All
+ decorators in the chain must return a response object or raise an
+ exception:
.. code-block:: python
diff --git a/pyramid/response.py b/pyramid/response.py
index 892e5dfff..1d9daae7d 100644
--- a/pyramid/response.py
+++ b/pyramid/response.py
@@ -54,16 +54,7 @@ class FileResponse(Response):
def __init__(self, path, request=None, cache_max_age=None,
content_type=None, content_encoding=None):
if content_type is None:
- content_type, content_encoding = mimetypes.guess_type(
- path,
- strict=False
- )
- if content_type is None:
- content_type = 'application/octet-stream'
- # str-ifying content_type is a workaround for a bug in Python 2.7.7
- # on Windows where mimetypes.guess_type returns unicode for the
- # content_type.
- content_type = str(content_type)
+ content_type, content_encoding = _guess_type(path)
super(FileResponse, self).__init__(
conditional_response=True,
content_type=content_type,
@@ -180,3 +171,17 @@ def _get_response_factory(registry):
)
return response_factory
+
+
+def _guess_type(path):
+ content_type, content_encoding = mimetypes.guess_type(
+ path,
+ strict=False
+ )
+ if content_type is None:
+ content_type = 'application/octet-stream'
+ # str-ifying content_type is a workaround for a bug in Python 2.7.7
+ # on Windows where mimetypes.guess_type returns unicode for the
+ # content_type.
+ content_type = str(content_type)
+ return content_type, content_encoding
diff --git a/pyramid/session.py b/pyramid/session.py
index a3cbe5172..47b80f617 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -215,9 +215,9 @@ def check_csrf_token(request,
supplied by ``request.session.get_csrf_token()``, and ``raises`` is
``True``, this function will raise an
:exc:`pyramid.exceptions.BadCSRFToken` exception.
- If the check does succeed and ``raises`` is ``False``, this
- function will return ``False``. If the CSRF check is successful, this
- function will return ``True`` unconditionally.
+ If the values differ and ``raises`` is ``False``, this function will
+ return ``False``. If the CSRF check is successful, this function will
+ return ``True`` unconditionally.
Note that using this function requires that a :term:`session factory` is
configured.
diff --git a/pyramid/static.py b/pyramid/static.py
index 0965be95c..a8088129e 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -32,7 +32,12 @@ from pyramid.httpexceptions import (
)
from pyramid.path import caller_package
-from pyramid.response import FileResponse
+
+from pyramid.response import (
+ _guess_type,
+ FileResponse,
+)
+
from pyramid.traversal import traversal_path_info
slash = text_('/')
@@ -83,7 +88,7 @@ class static_view(object):
"""
def __init__(self, root_dir, cache_max_age=3600, package_name=None,
- use_subpath=False, index='index.html', cachebust_match=None):
+ use_subpath=False, index='index.html'):
# package_name is for bw compat; it is preferred to pass in a
# package-relative path as root_dir
# (e.g. ``anotherpackage:foo/static``).
@@ -96,15 +101,12 @@ class static_view(object):
self.docroot = docroot
self.norm_docroot = normcase(normpath(docroot))
self.index = index
- self.cachebust_match = cachebust_match
def __call__(self, context, request):
if self.use_subpath:
path_tuple = request.subpath
else:
path_tuple = traversal_path_info(request.environ['PATH_INFO'])
- if self.cachebust_match:
- path_tuple = self.cachebust_match(path_tuple)
path = _secure_path(path_tuple)
if path is None:
@@ -134,7 +136,10 @@ class static_view(object):
if not exists(filepath):
raise HTTPNotFound(request.url)
- return FileResponse(filepath, request, self.cache_max_age)
+ content_type, content_encoding = _guess_type(filepath)
+ return FileResponse(
+ filepath, request, self.cache_max_age,
+ content_type, content_encoding=None)
def add_slash_redirect(self, request):
url = request.path_url + '/'
diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py
index 2ca86bc44..f76cc5067 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -113,14 +113,6 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
response = inst(context, request)
self.assertTrue(b'<html>static</html>' in response.body)
- def test_cachebust_match(self):
- inst = self._makeOne('pyramid.tests:fixtures/static')
- inst.cachebust_match = lambda subpath: subpath[1:]
- request = self._makeRequest({'PATH_INFO':'/foo/index.html'})
- context = DummyContext()
- response = inst(context, request)
- self.assertTrue(b'<html>static</html>' in response.body)
-
def test_resource_is_file_with_wsgi_file_wrapper(self):
from pyramid.response import _BLOCK_SIZE
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -186,14 +178,14 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
from pyramid.httpexceptions import HTTPNotFound
self.assertRaises(HTTPNotFound, inst, context, request)
- def test_resource_with_content_encoding(self):
+ def test_gz_resource_no_content_encoding(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
request = self._makeRequest({'PATH_INFO':'/arcs.svg.tgz'})
context = DummyContext()
response = inst(context, request)
self.assertEqual(response.status, '200 OK')
self.assertEqual(response.content_type, 'application/x-tar')
- self.assertEqual(response.content_encoding, 'gzip')
+ self.assertEqual(response.content_encoding, None)
response.app_iter.close()
def test_resource_no_content_encoding(self):
diff --git a/pyramid/url.py b/pyramid/url.py
index 0214d35ad..d6587e783 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -365,7 +365,7 @@ class URLMethodsMixin(object):
of ``query`` may be a sequence of two-tuples *or* a data structure with
an ``.items()`` method that returns a sequence of two-tuples
(presumably a dictionary). This data structure will be turned into a
- query string per the documentation of :func:``pyramid.url.urlencode``
+ query string per the documentation of :func:`pyramid.url.urlencode`
function. This will produce a query string in the
``x-www-form-urlencoded`` encoding. A non-``x-www-form-urlencoded``
query string may be used by passing a *string* value as ``query`` in
@@ -455,7 +455,7 @@ class URLMethodsMixin(object):
``resource_url(someresource, 'element1', 'element2', query={'a':1},
route_name='blogentry')`` is roughly equivalent to doing::
- remainder_path = request.resource_path(someobject)
+ traversal_path = request.resource_path(someobject)
url = request.route_url(
'blogentry',
'element1',
@@ -487,7 +487,7 @@ class URLMethodsMixin(object):
'element2', route_name='blogentry', route_kw={'id':'4'},
_query={'a':'1'})`` is roughly equivalent to::
- remainder_path = request.resource_path_tuple(someobject)
+ traversal_path = request.resource_path_tuple(someobject)
kw = {'id':'4', '_query':{'a':'1'}, 'traverse':traversal_path}
url = request.route_url(
'blogentry',
diff --git a/setup.cfg b/setup.cfg
index b52d24bdb..9a241ddf5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -14,6 +14,52 @@ docs = develop easy_install pyramid[docs]
universal = 1
[flake8]
-ignore = E301,E302,E731,E261,E123,E121,E128,E129,E125,W291,E501,W293,E303,W391,E266,E231,E201,E202,E127,E262,E265
+ignore =
+ # E121: continuation line under-indented for hanging indent
+ E121,
+ # E123: closing bracket does not match indentation of opening bracket's line
+ E123,
+ # E125: continuation line with same indent as next logical line
+ E125,
+ # E127: continuation line over-indented for visual indent
+ E127,
+ # E128: continuation line under-indented for visual indent
+ E128,
+ # E129: visually indented line with same indent as next logical line
+ E129,
+ # E201: whitespace after '('
+ E201,
+ # E202: whitespace before ')'
+ E202,
+ # E231: missing whitespace after ',', ';', or ':'
+ E231,
+ # E261: at least two spaces before inline comment
+ E261,
+ # E262: inline comment should start with '# '
+ E262,
+ # E265: block comment should start with '# '
+ E265,
+ # E266: too many leading '#' for block comment
+ E266,
+ # E301: expected 1 blank line, found 0
+ E301,
+ # E302: expected 2 blank lines, found 0
+ E302,
+ # E303: too many blank lines (3)
+ E303,
+ # E305: expected 2 blank lines after class or function definition, found 1
+ E305,
+ # E306: expected 1 blank line before a nested definition, found 0
+ E306,
+ # E501: line too long (82 > 79 characters)
+ E501,
+ # E731: do not assign a lambda expression, use a def
+ E731,
+ # W291: trailing whitespace
+ W291,
+ # W293: blank line contains whitespace
+ W293,
+ # W391: blank line at end of file
+ W391
exclude = pyramid/tests/,pyramid/compat.py,pyramid/resource.py
show-source = True
diff --git a/tox.ini b/tox.ini
index 884b78c68..d1df0f031 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,7 +3,7 @@ envlist =
py27,py34,py35,py36,py37,pypy,
docs,pep8,
{py2,py3}-cover,coverage,
-skip-missing-interpreters = True
+skip_missing_interpreters = True
[testenv]
# Most of these are defaults but if you specify any you can't fall back
@@ -19,7 +19,7 @@ basepython =
py3: python3.5
commands =
- pip install pyramid[testing]
+ pip install -q pyramid[testing]
nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:}
[testenv:py27-scaffolds]
@@ -72,7 +72,7 @@ commands =
# combination of versions of coverage and nosexcover that i can find.
[testenv:py2-cover]
commands =
- pip install pyramid[testing]
+ pip install -q pyramid[testing]
coverage run --source=pyramid {envbindir}/nosetests
coverage xml -o coverage-py2.xml
setenv =
@@ -80,7 +80,7 @@ setenv =
[testenv:py3-cover]
commands =
- pip install pyramid[testing]
+ pip install -q pyramid[testing]
coverage run --source=pyramid {envbindir}/nosetests
coverage xml -o coverage-py3.xml
setenv =