diff options
| -rw-r--r-- | .travis.yml | 4 | ||||
| -rw-r--r-- | CHANGES.txt | 17 | ||||
| -rw-r--r-- | RELEASING.txt | 10 | ||||
| -rw-r--r-- | docs/quick_tour.rst | 6 | ||||
| -rw-r--r-- | docs/quick_tutorial/scaffolds.rst | 6 | ||||
| -rw-r--r-- | pyramid/authentication.py | 16 | ||||
| -rw-r--r-- | pyramid/renderers.py | 17 | ||||
| -rw-r--r-- | pyramid/scaffolds/__init__.py | 7 | ||||
| -rw-r--r-- | pyramid/scripts/proutes.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_authentication.py | 5 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 4 | ||||
| -rw-r--r-- | pyramid/tests/test_renderers.py | 31 | ||||
| -rw-r--r-- | tox.ini | 4 |
13 files changed, 68 insertions, 61 deletions
diff --git a/.travis.yml b/.travis.yml index fbdd88224..7528a948a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,10 @@ matrix: env: TOXENV=docs - python: 3.5 env: TOXENV=pep8 + - python: nightly + env: TOXENV=py36 + allow_failures: + - env: TOXENV=py36 install: - travis_retry pip install tox diff --git a/CHANGES.txt b/CHANGES.txt index a614a4499..b485ae59e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,8 @@ Backward Incompatibilities To run your server as a daemon you should use a process manager instead of pserve. + See https://github.com/Pylons/pyramid/pull/2615 + Features -------- @@ -29,10 +31,17 @@ Bug Fixes and `attr` is involved. See: https://github.com/Pylons/pyramid/pull/2687 -- The JSON renderers now encode their result as UTF-8. The renderer helper - will now warn the user and encode the result as UTF-8 if a renderer returns a - text type and the response does not have a valid character set. See - https://github.com/Pylons/pyramid/pull/2706 +- Fix a ``FutureWarning`` in Python 3.5 when using ``re.split`` on the + ``format`` setting to the ``proutes`` script. + See https://github.com/Pylons/pyramid/pull/2714 + +- Fix a ``RuntimeWarning`` emitted by WebOb when using arbitrary objects + as the ``userid`` in the ``AuthTktAuthenticationPolicy``. This is now caught + by the policy and the object is serialized as a base64 string to avoid + the cryptic warning. Since the userid will be read back as a string on + subsequent requests a more useful warning is emitted encouraging you to + use a primitive type instead. + See https://github.com/Pylons/pyramid/pull/2715 Deprecations ------------ diff --git a/RELEASING.txt b/RELEASING.txt index d8572fa94..326dea993 100644 --- a/RELEASING.txt +++ b/RELEASING.txt @@ -148,12 +148,12 @@ Here are the changes: <<changes>> -A "What's New In Pyramid 1.X" document exists at -http://docs.pylonsproject.org/projects/pyramid/1.X-branch/whatsnew-1.X.html . +What's New In Pyramid 1.X: +http://docs.pylonsproject.org/projects/pyramid/en/1.X-branch/whatsnew-1.X.html -You will be able to see the 1.X release documentation (across all -alphas and betas, as well as when it eventually gets to final release) -at http://docs.pylonsproject.org/projects/pyramid/1.X-branch/ . +1.X release documentation (across all alphas and betas, as well as when it gets +to final release): +http://docs.pylonsproject.org/projects/pyramid/en/1.X-branch/ You can install it via PyPI: diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index fb957be1c..88cd69400 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -504,10 +504,10 @@ Pyramid's ``pcreate`` command can list the available scaffolds: $ pcreate --list Available scaffolds: - alchemy: Pyramid SQLAlchemy project using url dispatch + alchemy: Pyramid project using SQLAlchemy, SQLite, URL dispatch, and Jinja2 pyramid_jinja2_starter: Pyramid Jinja2 starter project - starter: Pyramid starter project - zodb: Pyramid ZODB project using traversal + starter: Pyramid starter project using URL dispatch and Chameleon + zodb: Pyramid project using ZODB, traversal, and Chameleon The ``pyramid_jinja2`` add-on gave us a scaffold that we can use. From the parent directory of where we want our Python package to be generated, let's use diff --git a/docs/quick_tutorial/scaffolds.rst b/docs/quick_tutorial/scaffolds.rst index 7845f2b71..ad002f4fd 100644 --- a/docs/quick_tutorial/scaffolds.rst +++ b/docs/quick_tutorial/scaffolds.rst @@ -38,9 +38,9 @@ Steps $ $VENV/bin/pcreate --list Available scaffolds: - alchemy: Pyramid SQLAlchemy project using url dispatch - starter: Pyramid starter project - zodb: Pyramid ZODB project using traversal + alchemy: Pyramid project using SQLAlchemy, SQLite, URL dispatch, and Jinja2 + starter: Pyramid starter project using URL dispatch and Chameleon + zodb: Pyramid project using ZODB, traversal, and Chameleon #. Tell ``pcreate`` to use the ``starter`` scaffold to make our project: diff --git a/pyramid/authentication.py b/pyramid/authentication.py index 034da9e46..7d766fd06 100644 --- a/pyramid/authentication.py +++ b/pyramid/authentication.py @@ -6,6 +6,7 @@ import hashlib import base64 import re import time as time_mod +import warnings from zope.interface import implementer @@ -948,8 +949,19 @@ class AuthTktCookieHelper(object): if encoding_data: encoding, encoder = encoding_data - userid = encoder(userid) - user_data = 'userid_type:%s' % encoding + else: + warnings.warn( + "userid is of type {}, and is not supported by the " + "AuthTktAuthenticationPolicy. Explicitly converting to string " + "and storing as base64. Subsequent requests will receive a " + "string as the userid, it will not be decoded back to the type " + "provided.".format(type(userid)), RuntimeWarning + ) + encoding, encoder = self.userid_type_encoders.get(text_type) + userid = str(userid) + + userid = encoder(userid) + user_data = 'userid_type:%s' % encoding new_tokens = [] for token in tokens: diff --git a/pyramid/renderers.py b/pyramid/renderers.py index 5b915ffdf..9b3f19510 100644 --- a/pyramid/renderers.py +++ b/pyramid/renderers.py @@ -1,7 +1,6 @@ import json import os import re -import warnings from zope.interface import ( implementer, @@ -273,7 +272,7 @@ class JSON(object): if ct == response.default_content_type: response.content_type = 'application/json' default = self._make_default(request) - return self.serializer(value, default=default, **self.kw).encode('UTF-8') + return self.serializer(value, default=default, **self.kw) return _render @@ -380,7 +379,7 @@ class JSONP(JSON): raise HTTPBadRequest('Invalid JSONP callback function name.') ct = 'application/javascript' - body = '/**/{0}({1});'.format(callback, val).encode('UTF-8') + body = '/**/{0}({1});'.format(callback, val) response = request.response if response.content_type == response.default_content_type: response.content_type = ct @@ -468,17 +467,7 @@ class RendererHelper(object): if result is not None: if isinstance(result, text_type): - if response.charset is None: - warnings.warn( - "Renderer returned a result of type {0}, " - "however the response Content-Type <{1}> does not " - "have a charset. Implicitly encoding the result as " - "UTF-8.".format(type(result), response.content_type), - RuntimeWarning - ) - response.body = result.encode('UTF-8') - else: - response.text = result + response.text = result elif isinstance(result, bytes): response.body = result elif hasattr(result, '__iter__'): diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py index 62c3eeecc..841dc403e 100644 --- a/pyramid/scaffolds/__init__.py +++ b/pyramid/scaffolds/__init__.py @@ -4,7 +4,7 @@ from textwrap import dedent from pyramid.compat import native_ -from pyramid.scaffolds.template import Template # API +from pyramid.scaffolds.template import Template # API class PyramidTemplate(Template): """ @@ -60,5 +60,6 @@ class ZODBProjectTemplate(PyramidTemplate): class AlchemyProjectTemplate(PyramidTemplate): _template_dir = 'alchemy' - summary = 'Pyramid project using SQLAlchemy, SQLite, URL dispatch, and' - ' Chameleon' + summary = ( + 'Pyramid project using SQLAlchemy, SQLite, URL dispatch, and ' + 'Jinja2') diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py index 19d91cc72..f75810c06 100644 --- a/pyramid/scripts/proutes.py +++ b/pyramid/scripts/proutes.py @@ -296,7 +296,7 @@ class PRoutesCommand(object): items = config.items('proutes') for k, v in items: if 'format' == k: - cols = re.split(r'[,|\s|\n]*', v) + cols = re.split(r'[,|\s\n]+', v) self.column_format = [x.strip() for x in cols] except configparser.NoSectionError: diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py index 32923c9ab..b9a4c6be4 100644 --- a/pyramid/tests/test_authentication.py +++ b/pyramid/tests/test_authentication.py @@ -1089,7 +1089,10 @@ class TestAuthTktCookieHelper(unittest.TestCase): helper = self._makeOne('secret') request = self._makeRequest() userid = object() - result = helper.remember(request, userid) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always', RuntimeWarning) + result = helper.remember(request, userid) + self.assertTrue(str(w[-1].message).startswith('userid is of type')) values = self._parseHeaders(result) self.assertEqual(len(result), 3) value = values[0] diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index c57deec7a..878574e88 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -2168,7 +2168,7 @@ class TestViewsConfigurationMixin(unittest.TestCase): ctx_iface=implementedBy(HTTPNotFound), request_iface=IRequest) result = view(None, request) - self._assertBody(result, b'{}') + self._assertBody(result, '{}') def test_add_forbidden_view_with_renderer(self): from zope.interface import implementedBy @@ -2185,7 +2185,7 @@ class TestViewsConfigurationMixin(unittest.TestCase): ctx_iface=implementedBy(HTTPForbidden), request_iface=IRequest) result = view(None, request) - self._assertBody(result, b'{}') + self._assertBody(result, '{}') def test_set_view_mapper(self): from pyramid.interfaces import IViewMapperFactory diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py index ce337cd99..65bfa5582 100644 --- a/pyramid/tests/test_renderers.py +++ b/pyramid/tests/test_renderers.py @@ -18,7 +18,7 @@ class TestJSON(unittest.TestCase): def test_it(self): renderer = self._makeOne()(None) result = renderer({'a':1}, {}) - self.assertEqual(result, b'{"a": 1}') + self.assertEqual(result, '{"a": 1}') def test_with_request_content_type_notset(self): request = testing.DummyRequest() @@ -43,7 +43,7 @@ class TestJSON(unittest.TestCase): renderer = self._makeOne() renderer.add_adapter(datetime, adapter) result = renderer(None)({'a':now}, {'request':request}) - self.assertEqual(result, '{{"a": "{0}"}}'.format(now.isoformat()).encode('UTF-8')) + self.assertEqual(result, '{"a": "%s"}' % now.isoformat()) def test_with_custom_adapter2(self): request = testing.DummyRequest() @@ -54,7 +54,7 @@ class TestJSON(unittest.TestCase): now = datetime.utcnow() renderer = self._makeOne(adapters=((datetime, adapter),)) result = renderer(None)({'a':now}, {'request':request}) - self.assertEqual(result, '{{"a": "{0}"}}'.format(now.isoformat()).encode('UTF-8')) + self.assertEqual(result, '{"a": "%s"}' % now.isoformat()) def test_with_custom_serializer(self): class Serializer(object): @@ -66,7 +66,7 @@ class TestJSON(unittest.TestCase): renderer = self._makeOne(serializer=serializer, baz=5) obj = {'a':'b'} result = renderer(None)(obj, {}) - self.assertEqual(result, b'foo') + self.assertEqual(result, 'foo') self.assertEqual(serializer.obj, obj) self.assertEqual(serializer.kw['baz'], 5) self.assertTrue('default' in serializer.kw) @@ -84,7 +84,7 @@ class TestJSON(unittest.TestCase): objects = [MyObject(1), MyObject(2)] renderer = self._makeOne()(None) result = renderer(objects, {'request':request}) - self.assertEqual(result, b'[{"x": 1}, {"x": 2}]') + self.assertEqual(result, '[{"x": 1}, {"x": 2}]') def test_with_object_adapter_no___json__(self): class MyObject(object): @@ -290,19 +290,6 @@ class TestRendererHelper(unittest.TestCase): response = helper._make_response(la.encode('utf-8'), request) self.assertEqual(response.body, la.encode('utf-8')) - def test__make_response_result_is_str_no_charset(self): - from pyramid.response import Response - request = testing.DummyRequest() - request.response = Response(content_type='application/json', charset=None) - - self.assertIsNone(request.response.charset) - - helper = self._makeOne('loo.foo') - la = text_('/La Pe\xc3\xb1a', 'utf-8') - response = helper._make_response(la, request) - self.assertIsNone(response.charset) - self.assertEqual(response.body, la.encode('utf-8')) - def test__make_response_result_is_iterable(self): from pyramid.response import Response request = testing.DummyRequest() @@ -505,7 +492,7 @@ class Test_render(unittest.TestCase): request.response = response # use a json renderer, which will mutate the response result = self._callFUT('json', dict(a=1), request=request) - self.assertEqual(result, b'{"a": 1}') + self.assertEqual(result, '{"a": 1}') self.assertEqual(request.response, response) def test_no_response_to_preserve(self): @@ -520,7 +507,7 @@ class Test_render(unittest.TestCase): request = DummyRequestWithClassResponse() # use a json renderer, which will mutate the response result = self._callFUT('json', dict(a=1), request=request) - self.assertEqual(result, b'{"a": 1}') + self.assertEqual(result, '{"a": 1}') self.assertFalse('response' in request.__dict__) class Test_render_to_response(unittest.TestCase): @@ -640,7 +627,7 @@ class TestJSONP(unittest.TestCase): request = testing.DummyRequest() request.GET['callback'] = 'callback' result = renderer({'a':'1'}, {'request':request}) - self.assertEqual(result, b'/**/callback({"a": "1"});') + self.assertEqual(result, '/**/callback({"a": "1"});') self.assertEqual(request.response.content_type, 'application/javascript') @@ -650,7 +637,7 @@ class TestJSONP(unittest.TestCase): request = testing.DummyRequest() request.GET['callback'] = 'angular.callbacks._0' result = renderer({'a':'1'}, {'request':request}) - self.assertEqual(result, b'/**/angular.callbacks._0({"a": "1"});') + self.assertEqual(result, '/**/angular.callbacks._0({"a": "1"});') self.assertEqual(request.response.content_type, 'application/javascript') @@ -1,8 +1,9 @@ [tox] envlist = - py27,py33,py34,py35,pypy, + py27,py33,py34,py35,py36,pypy, docs,pep8, {py2,py3}-cover,coverage, +skip-missing-interpreters = True [testenv] # Most of these are defaults but if you specify any you can't fall back @@ -12,6 +13,7 @@ basepython = py33: python3.3 py34: python3.4 py35: python3.5 + py36: python3.6 pypy: pypy py2: python2.7 py3: python3.5 |
