From 0a2ea908f35f245b1c4750d8d97ffe645ce29a57 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 13 Nov 2013 17:36:35 -0800 Subject: tox.ini: Add py34 Useful for testing with the new Python 3.4 alpha versions. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index b50e56544..2bf213ca4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py26,py27,py32,py33,pypy,cover + py26,py27,py32,py33,py34,pypy,cover [testenv] commands = -- cgit v1.2.3 From dd15238fa182aff0ab09dd9d3f469f821f9810ef Mon Sep 17 00:00:00 2001 From: dobesv Date: Thu, 27 Feb 2014 10:34:12 -0800 Subject: Add get_logout_headers to request The documentation for forget() says it is deprecated and to use get_logout_headers() on the request instead. However, no such method has been added to the request. --- pyramid/security.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pyramid/security.py b/pyramid/security.py index 848574233..dafd19611 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -355,6 +355,25 @@ class AuthenticationAPIMixin(object): return [Everyone] return policy.effective_principals(self) + def get_logout_headers(self): + """ + Return a sequence of header tuples (e.g. ``[('Set-Cookie', + 'foo=abc')]``) suitable for 'forgetting' the set of credentials + possessed by the currently authenticated user. A common usage + might look like so within the body of a view function + (``response`` is assumed to be an :term:`WebOb` -style + :term:`response` object computed previously by the view code):: + + request.response.headerlist.extend(request.get_logout_headers()) + + If no :term:`authentication policy` is in use, this function will + always return an empty sequence. + """ + policy = self._get_authentication_policy() + if policy is None: + return [] + return policy.forget(request) + class AuthorizationAPIMixin(object): def has_permission(self, permission, context=None): -- cgit v1.2.3 From 8b434dd3d40ed42639a73d9ba5a750732049a043 Mon Sep 17 00:00:00 2001 From: dobesv Date: Thu, 27 Feb 2014 11:09:08 -0800 Subject: Remove reference to request.get_logout_headers() --- pyramid/security.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/pyramid/security.py b/pyramid/security.py index dafd19611..aa4aece69 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -160,9 +160,6 @@ def forget(request): If no :term:`authentication policy` is in use, this function will always return an empty sequence. - - .. deprecated:: 1.5 - Use :meth:`pyramid.request.Request.get_logout_headers` instead. """ policy = _get_authentication_policy(request) if policy is None: @@ -354,26 +351,7 @@ class AuthenticationAPIMixin(object): if policy is None: return [Everyone] return policy.effective_principals(self) - - def get_logout_headers(self): - """ - Return a sequence of header tuples (e.g. ``[('Set-Cookie', - 'foo=abc')]``) suitable for 'forgetting' the set of credentials - possessed by the currently authenticated user. A common usage - might look like so within the body of a view function - (``response`` is assumed to be an :term:`WebOb` -style - :term:`response` object computed previously by the view code):: - - request.response.headerlist.extend(request.get_logout_headers()) - If no :term:`authentication policy` is in use, this function will - always return an empty sequence. - """ - policy = self._get_authentication_policy() - if policy is None: - return [] - return policy.forget(request) - class AuthorizationAPIMixin(object): def has_permission(self, permission, context=None): -- cgit v1.2.3 From 89dc4659eedfe75bada6d131722fbb00f1bbc49f Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 16:23:56 -0500 Subject: improve timeout docs --- pyramid/session.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pyramid/session.py b/pyramid/session.py index 3a045b91b..9fe2add60 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -192,14 +192,17 @@ def BaseCookieSessionFactory( ``timeout`` A number of seconds of inactivity before a session times out. If - ``None`` then the cookie never expires. Default: 1200. + ``None`` then the cookie never expires. This lifetime only applies + to the *value* within the cookie. Meaning that if the cookie expires + due to a lower ``max_age``, then this setting has no effect. + Default: ``1200``. ``reissue_time`` The number of seconds that must pass before the cookie is automatically reissued as the result of a request which accesses the session. The duration is measured as the number of seconds since the last session cookie was issued and 'now'. If this value is ``0``, a new cookie - will be reissued on every request accesses the session. If ``None`` + will be reissued on every request accessing the session. If ``None`` then the cookie's lifetime will never be extended. A good rule of thumb: if you want auto-expired cookies based on @@ -548,14 +551,17 @@ def SignedCookieSessionFactory( ``timeout`` A number of seconds of inactivity before a session times out. If - ``None`` then the cookie never expires. Default: 1200. + ``None`` then the cookie never expires. This lifetime only applies + to the *value* within the cookie. Meaning that if the cookie expires + due to a lower ``max_age``, then this setting has no effect. + Default: ``1200``. ``reissue_time`` The number of seconds that must pass before the cookie is automatically - reissued as the result of a request which accesses the session. The + reissued as the result of accessing the session. The duration is measured as the number of seconds since the last session cookie was issued and 'now'. If this value is ``0``, a new cookie - will be reissued on every request accesses the session. If ``None`` + will be reissued on every request accessing the session. If ``None`` then the cookie's lifetime will never be extended. A good rule of thumb: if you want auto-expired cookies based on -- cgit v1.2.3 From 1098ac88253ab7bee225bbb620381d3c7c4f99af Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 16:28:08 -0500 Subject: 79-char line widths! --- pyramid/session.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pyramid/session.py b/pyramid/session.py index 9fe2add60..ea7a5628c 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -165,10 +165,10 @@ def BaseCookieSessionFactory( Parameters: ``serializer`` - An object with two methods: ``loads`` and ``dumps``. The ``loads`` method - should accept bytes and return a Python object. The ``dumps`` method - should accept a Python object and return bytes. A ``ValueError`` should - be raised for malformed inputs. + An object with two methods: ``loads`` and ``dumps``. The ``loads`` + method should accept bytes and return a Python object. The ``dumps`` + method should accept a Python object and return bytes. A ``ValueError`` + should be raised for malformed inputs. ``cookie_name`` The name of the cookie used for sessioning. Default: ``'session'``. @@ -473,8 +473,8 @@ def UnencryptedCookieSessionFactoryConfig( deprecated( 'UnencryptedCookieSessionFactoryConfig', 'The UnencryptedCookieSessionFactoryConfig callable is deprecated as of ' - 'Pyramid 1.5. Use ``pyramid.session.SignedCookieSessionFactory`` instead. ' - 'Caveat: Cookies generated using SignedCookieSessionFactory are not ' + 'Pyramid 1.5. Use ``pyramid.session.SignedCookieSessionFactory`` instead.' + ' Caveat: Cookies generated using SignedCookieSessionFactory are not ' 'compatible with cookies generated using UnencryptedCookieSessionFactory, ' 'so existing user session data will be destroyed if you switch to it.' ) @@ -578,11 +578,11 @@ def SignedCookieSessionFactory( while rendering a view. Default: ``True``. ``serializer`` - An object with two methods: ``loads`` and ``dumps``. The ``loads`` method - should accept bytes and return a Python object. The ``dumps`` method - should accept a Python object and return bytes. A ``ValueError`` should - be raised for malformed inputs. If a serializer is not passed, the - :class:`pyramid.session.PickleSerializer` serializer will be used. + An object with two methods: ``loads`` and ``dumps``. The ``loads`` + method should accept bytes and return a Python object. The ``dumps`` + method should accept a Python object and return bytes. A ``ValueError`` + should be raised for malformed inputs. If a serializer is not passed, + the :class:`pyramid.session.PickleSerializer` serializer will be used. .. versionadded: 1.5a3 """ @@ -591,7 +591,7 @@ def SignedCookieSessionFactory( signed_serializer = SignedSerializer( secret, - salt, + salt, hashalg, serializer=serializer, ) -- cgit v1.2.3 From 7075e1742529f7de53dd794e9ded0baaa3584135 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 16:51:10 -0500 Subject: reproduce timeout=None bug --- pyramid/tests/test_session.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py index 1ad0729b3..1abc43e82 100644 --- a/pyramid/tests/test_session.py +++ b/pyramid/tests/test_session.py @@ -52,6 +52,15 @@ class SharedCookieSessionTests(object): session = self._makeOne(request, timeout=1) self.assertEqual(dict(session), {}) + def test_timeout_never(self): + import time + request = testing.DummyRequest() + LONG_TIME = 31536000 + cookieval = self._serialize((time.time() + LONG_TIME, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, timeout=None) + self.assertEqual(dict(session), {'state': 1}) + def test_changed(self): request = testing.DummyRequest() session = self._makeOne(request) -- cgit v1.2.3 From 8f4fbd84220a3256cbeee3f2adcc333c0ef0e6aa Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 16:51:43 -0500 Subject: fix timeout=None bug as well as some other potential unpacking problems --- pyramid/session.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pyramid/session.py b/pyramid/session.py index ea7a5628c..5cc51869c 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -256,17 +256,24 @@ def BaseCookieSessionFactory( if value is not None: try: - renewed, created, state = value + # since the value is not necessarily signed, we have + # to unpack it a little carefully + rval, cval, sval = value + renewed = float(rval) + created = float(cval) + state = sval new = False - if now - renewed > self._timeout: - # expire the session because it was not renewed - # before the timeout threshold - state = {} - except TypeError: + except (TypeError, ValueError): # value failed to unpack properly or renewed was not # a numeric type so we'll fail deserialization here state = {} + if self._timeout is not None: + if now - renewed > self._timeout: + # expire the session because it was not renewed + # before the timeout threshold + state = {} + self.created = created self.accessed = renewed self.renewed = renewed -- cgit v1.2.3 From 9549f66c78f18e4a041e96cbc78e7aa8d75fb4fe Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 16:52:12 -0500 Subject: reproduce reissue_time=None bug --- pyramid/tests/test_session.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py index 1abc43e82..1aaa7a2ba 100644 --- a/pyramid/tests/test_session.py +++ b/pyramid/tests/test_session.py @@ -288,6 +288,14 @@ class TestBaseCookieSession(SharedCookieSessionTests, unittest.TestCase): self.assertEqual(session['state'], 1) self.assertFalse(session._dirty) + def test_reissue_never(self): + request = testing.DummyRequest() + cookieval = self._serialize((0, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time=None, timeout=None) + self.assertEqual(session['state'], 1) + self.assertFalse(session._dirty) + class TestSignedCookieSession(SharedCookieSessionTests, unittest.TestCase): def _makeOne(self, request, **kw): from pyramid.session import SignedCookieSessionFactory @@ -314,6 +322,14 @@ class TestSignedCookieSession(SharedCookieSessionTests, unittest.TestCase): self.assertEqual(session['state'], 1) self.assertFalse(session._dirty) + def test_reissue_never(self): + request = testing.DummyRequest() + cookieval = self._serialize((0, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time=None, timeout=None) + self.assertEqual(session['state'], 1) + self.assertFalse(session._dirty) + def test_custom_salt(self): import time request = testing.DummyRequest() -- cgit v1.2.3 From b2dd47336c200bbbfea31526f85652226c335687 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 16:52:44 -0500 Subject: handle reissue_time=None properly --- pyramid/session.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyramid/session.py b/pyramid/session.py index 5cc51869c..56d99e9de 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -27,8 +27,9 @@ def manage_accessed(wrapped): method is called.""" def accessed(session, *arg, **kw): session.accessed = now = int(time.time()) - if now - session.renewed > session._reissue_time: - session.changed() + if session._reissue_time is not None: + if now - session.renewed > session._reissue_time: + session.changed() return wrapped(session, *arg, **kw) accessed.__doc__ = wrapped.__doc__ return accessed -- cgit v1.2.3 From e175ffca6a3c005b61856d50802a289f0483cfb7 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Feb 2014 16:58:47 -0500 Subject: update changelog --- CHANGES.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 84d0694e3..434eab898 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,12 @@ Unreleased - Avoid crash in ``pserve --reload`` under Py3k, when iterating over posiibly mutated ``sys.modules``. +- Fixed a bug in ``UnencryptedCookieSessionFactoryConfig`` and + ``SignedCookieSessionFactory`` where ``timeout=None`` would cause a new + session to always be created. Also in ``SignedCookieSessionFactory`` a + ``reissue_time=None`` would cause an exception when modifying the session. + See https://github.com/Pylons/pyramid/issues/1247 + 1.5b1 (2014-02-08) ================== -- cgit v1.2.3 From 76144dfae72a0a6d2bd33a414deb296937e90e49 Mon Sep 17 00:00:00 2001 From: Robert Buchholz Date: Mon, 3 Mar 2014 16:24:31 +0100 Subject: Hand RepozeWho1AuthenticationPolicy.remember kwargs to repoze.who #1249 Documentation for pyramid.security.remember supports keyword arguments to hand over to the authentication policy. However, when using RepozeWho1AuthenticationPolicy, all of the kw were dropped in remember. It is my understanding that with repoze.who, additional configuration parameters shall be stored in the identity dictionary. In our case, setting the max_age parameter to the authtkt identifier, would be done using an identity {'repoze.who.userid':principal, 'max_age': 23}. It seems sensible just to hand over kw through the identity dictionary and all users to specify max_age or other parameters such as userdata. --- pyramid/authentication.py | 11 +++++++++-- pyramid/tests/test_authentication.py | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pyramid/authentication.py b/pyramid/authentication.py index ba7b864f9..b84981bbc 100644 --- a/pyramid/authentication.py +++ b/pyramid/authentication.py @@ -336,12 +336,19 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy): return effective_principals def remember(self, request, principal, **kw): - """ Store the ``principal`` as ``repoze.who.userid``.""" + """ Store the ``principal`` as ``repoze.who.userid``. + + The identity to authenticated to :mod:`repoze.who` + will contain the given principal as ``userid``, and + provide all keyword arguments as additional identity + keys. Useful keys could be ``max_age`` or ``userdata``. + """ identifier = self._get_identifier(request) if identifier is None: return [] environ = request.environ - identity = {'repoze.who.userid':principal} + identity = kw + identity['repoze.who.userid'] = principal return identifier.remember(environ, identity) def forget(self, request): diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py index 79d2a5923..e25e9faa1 100644 --- a/pyramid/tests/test_authentication.py +++ b/pyramid/tests/test_authentication.py @@ -350,6 +350,14 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase): self.assertEqual(result[0], request.environ) self.assertEqual(result[1], {'repoze.who.userid':'fred'}) + def test_remember_kwargs(self): + authtkt = DummyWhoPlugin() + request = DummyRequest( + {'repoze.who.plugins':{'auth_tkt':authtkt}}) + policy = self._makeOne() + result = policy.remember(request, 'fred', max_age=23) + self.assertEqual(result[1], {'repoze.who.userid':'fred', 'max_age': 23}) + def test_forget_no_plugins(self): request = DummyRequest({}) policy = self._makeOne() -- cgit v1.2.3 From 82ec99eebc6a6dfe2903bc0383f31a9b146b3d80 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 3 Mar 2014 16:36:04 -0800 Subject: update inconsistent effective_principals docs fixes #1196 --- pyramid/security.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pyramid/security.py b/pyramid/security.py index aa4aece69..c98d4e6cc 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -340,10 +340,9 @@ class AuthenticationAPIMixin(object): @property def effective_principals(self): """ Return the list of 'effective' :term:`principal` identifiers - for the ``request``. This will include the userid of the - currently authenticated user if a user is currently - authenticated. If no :term:`authentication policy` is in effect, - this will return an empty sequence. + for the ``request``. If no :term:`authentication policy` is in effect, + this will return a one-element list containing the + :data:`pyramid.security.Everyone` principal. .. versionadded:: 1.5 """ -- cgit v1.2.3 From b4e59b7168273b774acd975457906dffa176caba Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Mar 2014 18:47:46 -0500 Subject: 'request_method' is *not* limited to the named set. --- pyramid/config/views.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 1ef4efdb3..2f6c758ab 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -879,13 +879,13 @@ class ViewsConfiguratorMixin(object): request_method - This value can be one of the strings ``GET``, ``POST``, ``PUT``, - ``DELETE``, or ``HEAD`` representing an HTTP ``REQUEST_METHOD``, or - a tuple containing one or more of these strings. A view - declaration with this argument ensures that the view will only be - called when the request's ``method`` attribute (aka the - ``REQUEST_METHOD`` of the WSGI environment) string matches a - supplied value. Note that use of ``GET`` also implies that the + This value can be either a strings (such as ``GET``, ``POST``, + ``PUT``, ``DELETE``, or ``HEAD``) representing an HTTP + ``REQUEST_METHOD``, or a tuple containing one or more of these + strings. A view declaration with this argument ensures that the + view will only be called when the ``method`` attribute of the + request (aka the ``REQUEST_METHOD`` of the WSGI environment) matches + a supplied value. Note that use of ``GET`` also implies that the view will respond to ``HEAD`` as of Pyramid 1.4. .. versionchanged:: 1.2 -- cgit v1.2.3 From 58febc5917a82ef1ae25af92b62843ad3c6d5e0e Mon Sep 17 00:00:00 2001 From: Paul Winkler Date: Fri, 7 Mar 2014 11:19:29 -0500 Subject: Minor punctuation and grammar changes to Quick Intro --- docs/quick_tour.rst | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index 2d4e679f8..4ab39bb11 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -73,14 +73,14 @@ This simple example is easy to run. Save this as ``app.py`` and run it: Next, open `http://localhost:6543/ `_ in a browser and you will see the ``Hello World!`` message. -New to Python web programming? If so, some lines in module merit +New to Python web programming? If so, some lines in the module merit explanation: #. *Line 10*. The ``if __name__ == '__main__':`` is Python's way of saying "Start here when running from the command line". #. *Lines 11-13*. Use Pyramid's :term:`configurator` to connect - :term:`view` code to particular URL :term:`route`. + :term:`view` code to a particular URL :term:`route`. #. *Lines 6-7*. Implement the view code that generates the :term:`response`. @@ -148,15 +148,15 @@ So far our examples place everything in one file: - the WSGI application launcher Let's move the views out to their own ``views.py`` module and change -the ``app.py`` to scan that module, looking for decorators that setup +the ``app.py`` to scan that module, looking for decorators that set up the views. First, our revised ``app.py``: .. literalinclude:: quick_tour/views/app.py :linenos: We added some more routes, but we also removed the view code. -Our views, and their registrations (via decorators) are now in a module -``views.py`` which is scanned via ``config.scan('views')``. +Our views and their registrations (via decorators) are now in a module +``views.py``, which is scanned via ``config.scan('views')``. We now have a ``views.py`` module that is focused on handling requests and responses: @@ -167,7 +167,7 @@ and responses: We have 4 views, each leading to the other. If you start at ``http://localhost:6543/``, you get a response with a link to the next view. The ``hello_view`` (available at the URL ``/howdy``) has a link -to the ``redirect_view``, which shows issuing a redirect to the final +to the ``redirect_view``, which issues a redirect to the final view. Earlier we saw ``config.add_view`` as one way to configure a view. This @@ -267,7 +267,7 @@ Now lets change our views.py file: Ahh, that looks better. We have a view that is focused on Python code. Our ``@view_config`` decorator specifies a :term:`renderer` that points -our template file. Our view then simply returns data which is then +to our template file. Our view then simply returns data which is then supplied to our template: .. literalinclude:: quick_tour/templating/hello_world.pt @@ -303,7 +303,7 @@ our configuration: config.include('pyramid_jinja2') -The only change in our view...point the renderer at the ``.jinja2`` file: +The only change in our view is to point the renderer at the ``.jinja2`` file: .. literalinclude:: quick_tour/jinja2/views.py :start-after: Start View 1 @@ -356,8 +356,8 @@ template: This link presumes that our CSS is at a URL starting with ``/static/``. What if the site is later moved under ``/somesite/static/``? Or perhaps -web developer changes the arrangement on disk? Pyramid gives a helper -that provides flexibility on URL generation: +a web developer changes the arrangement on disk? Pyramid provides a helper +to allow flexibility on URL generation: .. literalinclude:: quick_tour/static_assets/hello_world.pt :language: html @@ -454,7 +454,7 @@ have much more to offer: Quick Project Startup with Scaffolds ==================================== -So far we have done all of our *Quick Glance* as a single Python file. +So far we have done all of our *Quick Tour* as a single Python file. No Python packages, no structure. Most Pyramid projects, though, aren't developed this way. @@ -479,7 +479,7 @@ let's use that scaffold to make our project: $ pcreate --scaffold pyramid_jinja2_starter hello_world -We next use the normal Python development to setup our package for +We next use the normal Python command to set up our package for development: .. code-block:: bash @@ -534,7 +534,7 @@ take a look at this configuration file. Configuration with ``.ini`` Files ================================= -Earlier in *Quick Glance* we first met Pyramid's configuration system. +Earlier in *Quick Tour* we first met Pyramid's configuration system. At that point we did all configuration in Python code. For example, the port number chosen for our HTTP server was right there in Python code. Our scaffold has moved this decision, and more, into the @@ -556,8 +556,8 @@ into sections: We have a few decisions made for us in this configuration: -#. *Choice of web server*. The ``use = egg:pyramid#wsgiref`` tell - ``pserve`` to the ``wsgiref`` server that is wrapped in the Pyramid +#. *Choice of web server*. The ``use = egg:pyramid#wsgiref`` tells + ``pserve`` to use the ``wsgiref`` server that is wrapped in the Pyramid package. #. *Port number*. ``port = 6543`` tells ``wsgiref`` to listen on port @@ -574,7 +574,7 @@ We have a few decisions made for us in this configuration: Additionally, the ``development.ini`` generated by this scaffold wired up Python's standard logging. We'll now see in the console, for example, -a log on every request that comes in, as well traceback information. +a log on every request that comes in, as well as traceback information. .. seealso:: See also: :ref:`Quick Tutorial Application Configuration `, @@ -585,7 +585,7 @@ a log on every request that comes in, as well traceback information. Easier Development with ``debugtoolbar`` ======================================== -As we introduce the basics we also want to show how to be productive in +As we introduce the basics, we also want to show how to be productive in development and debugging. For example, we just discussed template reloading and earlier we showed ``--reload`` for application reloading. @@ -700,12 +700,12 @@ we might need to detect situations when other people use the site. We need *logging*. Fortunately Pyramid uses the normal Python approach to logging. The -scaffold generated, in your ``development.ini``, a number of lines that +scaffold generated in your ``development.ini`` a number of lines that configure the logging for you to some reasonable defaults. You then see -messages sent by Pyramid (for example, when a new request comes in.) +messages sent by Pyramid (for example, when a new request comes in). Maybe you would like to log messages in your code? In your Python -module, import and setup the logging: +module, import and set up the logging: .. literalinclude:: quick_tour/package/hello_world/views.py :start-after: Start Logging 1 @@ -726,7 +726,7 @@ controls that? These sections in the configuration file: :start-after: Start Sphinx Include :end-before: End Sphinx Include -Our application, a package named ``hello_world``, is setup as a logger +Our application, a package named ``hello_world``, is set up as a logger and configured to log messages at a ``DEBUG`` or higher level. When you visit ``http://localhost:6543`` your console will now show:: @@ -789,7 +789,7 @@ Databases ========= Web applications mean data. Data means databases. Frequently SQL -databases. SQL Databases frequently mean an "ORM" +databases. SQL databases frequently mean an "ORM" (object-relational mapper.) In Python, ORM usually leads to the mega-quality *SQLAlchemy*, a Python package that greatly eases working with databases. -- cgit v1.2.3 From 5c5edadd089f3c3f13751bde3f691ad19339b551 Mon Sep 17 00:00:00 2001 From: sbivol Date: Mon, 10 Mar 2014 16:28:56 +0200 Subject: Remove extra concatenation from request.static_url() fixes issue #1266 --- docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt index d1fea0d7f..3292dfd90 100644 --- a/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt +++ b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt @@ -4,10 +4,10 @@ WikiPage: Add/Edit + href="${request.static_url(reqt)}"/> - -- cgit v1.2.3 From 49bcc8e86ded6785c3bddd6972f870b2d2d858a8 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Mon, 10 Mar 2014 09:15:08 -0700 Subject: Add integration tests for Unicode in URL --- pyramid/tests/test_integration.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 9d3a9e004..bc22c2e54 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -640,6 +640,44 @@ class RendererScanAppTest(IntegrationBase, unittest.TestCase): res = testapp.get('/two', status=200) self.assertTrue(b'Two!' in res.body) +class UnicodeInURLTest(unittest.TestCase): + def _makeConfig(self): + from pyramid.config import Configurator + config = Configurator() + return config + + def _makeTestApp(self, config): + from webtest import TestApp + app = config.make_wsgi_app() + return TestApp(app) + + def test_unicode_in_url_404(self): + request_path = b'/avalia%C3%A7%C3%A3o_participante/' + request_path_unicode = u'/avalia\xe7\xe3o_participante/' + + config = self._makeConfig() + testapp = self._makeTestApp(config) + + res = testapp.get(request_path, status=404) + self.assertTrue(request_path_unicode in res.text) + + def test_unicode_in_url_200(self): + request_path = b'/avalia%C3%A7%C3%A3o_participante' + request_path_unicode = u'/avalia\xe7\xe3o_participante' + + def myview(request): + return 'XXX' + + config = self._makeConfig() + config.add_route('myroute', request_path_unicode) + config.add_view(myview, route_name='myroute', renderer='json') + testapp = self._makeTestApp(config) + + res = testapp.get(request_path, status=200) + + self.assertEqual(res.text, '"XXX"') + + class AcceptContentTypeTest(unittest.TestCase): def setUp(self): def hello_view(request): -- cgit v1.2.3 From 0c425da09d966bafd2f4043fa8919f3da4d8abc4 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 11 Mar 2014 08:27:56 -0700 Subject: Remove 'b'; WebTest or WebOb doesn't like Python 3 bytes for URLs --- pyramid/tests/test_integration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index bc22c2e54..48199c419 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -652,7 +652,7 @@ class UnicodeInURLTest(unittest.TestCase): return TestApp(app) def test_unicode_in_url_404(self): - request_path = b'/avalia%C3%A7%C3%A3o_participante/' + request_path = '/avalia%C3%A7%C3%A3o_participante/' request_path_unicode = u'/avalia\xe7\xe3o_participante/' config = self._makeConfig() @@ -662,7 +662,7 @@ class UnicodeInURLTest(unittest.TestCase): self.assertTrue(request_path_unicode in res.text) def test_unicode_in_url_200(self): - request_path = b'/avalia%C3%A7%C3%A3o_participante' + request_path = '/avalia%C3%A7%C3%A3o_participante' request_path_unicode = u'/avalia\xe7\xe3o_participante' def myview(request): -- cgit v1.2.3 From bc87edd3d8df7c5bd4c8d267c626f4fd22ef7443 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 11 Mar 2014 10:10:10 -0700 Subject: Convert from u prefix to byte string and decode because the u prefix is not supported in Python 3.2 --- pyramid/tests/test_integration.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 48199c419..0ab18de7a 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -652,8 +652,8 @@ class UnicodeInURLTest(unittest.TestCase): return TestApp(app) def test_unicode_in_url_404(self): - request_path = '/avalia%C3%A7%C3%A3o_participante/' - request_path_unicode = u'/avalia\xe7\xe3o_participante/' + request_path = '/avalia%C3%A7%C3%A3o_participante' + request_path_unicode = b'/avalia\xc3\xa7\xc3\xa3o_participante'.decode('utf-8') config = self._makeConfig() testapp = self._makeTestApp(config) @@ -663,7 +663,7 @@ class UnicodeInURLTest(unittest.TestCase): def test_unicode_in_url_200(self): request_path = '/avalia%C3%A7%C3%A3o_participante' - request_path_unicode = u'/avalia\xe7\xe3o_participante' + request_path_unicode = b'/avalia\xc3\xa7\xc3\xa3o_participante'.decode('utf-8') def myview(request): return 'XXX' -- cgit v1.2.3 From a99abf808a15fbc02da4c27ab7d46d03668b62ed Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 11 Mar 2014 11:12:13 -0700 Subject: test_unicode_in_url_404: Add comment to clarify why request_path_unicode is expected to be in res.txt --- pyramid/tests/test_integration.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 0ab18de7a..35648ed38 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -659,6 +659,10 @@ class UnicodeInURLTest(unittest.TestCase): testapp = self._makeTestApp(config) res = testapp.get(request_path, status=404) + + # Pyramid default 404 handler outputs: + # u'404 Not Found\n\nThe resource could not be found.\n\n\n' + # u'/avalia\xe7\xe3o_participante\n\n' self.assertTrue(request_path_unicode in res.text) def test_unicode_in_url_200(self): -- cgit v1.2.3 From ee824a80bab0b30f7f8f466a9c93765b35c1c677 Mon Sep 17 00:00:00 2001 From: thapar Date: Mon, 17 Mar 2014 20:16:44 -0400 Subject: Added note to place login/logout route definitions before `/{pagename}` route Resolves https://github.com/Pylons/pyramid/issues/1274 --- docs/tutorials/wiki2/authorization.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index 1e5d0dcbf..1417dbdd8 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -207,6 +207,19 @@ routes: :linenos: :language: python +.. note:: These lines must be added `before` this ``view_page`` route + definition: + .. literalinclude:: src/authorization/tutorial/__init__.py + :lines: 32 + :linenos: + :language: python + This is because ``view_page``'s route definition uses a catch-all + "replacement marker" ``/{pagename}`` (see :ref:_route_pattern_syntax ) + which will catch any route that was not already caught by any + route listed above it in ``__init__.py``. Hence, for ``login`` and + ``logout`` views to have the opportunity of being matched + (or "caught"), they must be above ``/{pagename}``. + Add Login and Logout Views ~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From b6d8cf9c2eb8f7dd03fa3487b8e401940c314fb4 Mon Sep 17 00:00:00 2001 From: thapar Date: Mon, 17 Mar 2014 21:26:01 -0400 Subject: Corrected inline markup to use * for italics --- docs/tutorials/wiki2/authorization.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index 1417dbdd8..e3811b338 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -207,7 +207,7 @@ routes: :linenos: :language: python -.. note:: These lines must be added `before` this ``view_page`` route +.. note:: These lines must be added *before* this ``view_page`` route definition: .. literalinclude:: src/authorization/tutorial/__init__.py :lines: 32 -- cgit v1.2.3 From cf4023ad25637720ed5ccd1e7cdffd24da4c47cb Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 21 Mar 2014 06:19:27 -0700 Subject: - correct bad .rst syntax --- docs/tutorials/wiki2/authorization.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index e3811b338..2e35574fd 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -207,14 +207,16 @@ routes: :linenos: :language: python -.. note:: These lines must be added *before* this ``view_page`` route - definition: +.. note:: The preceding lines must be added *before* the following + ``view_page`` route definition: + .. literalinclude:: src/authorization/tutorial/__init__.py :lines: 32 :linenos: :language: python + This is because ``view_page``'s route definition uses a catch-all - "replacement marker" ``/{pagename}`` (see :ref:_route_pattern_syntax ) + "replacement marker" ``/{pagename}`` (see :ref:`route_pattern_syntax`) which will catch any route that was not already caught by any route listed above it in ``__init__.py``. Hence, for ``login`` and ``logout`` views to have the opportunity of being matched -- cgit v1.2.3 From 49923dbf430fc6becc92133ba7672e93f1bc955e Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 21 Mar 2014 06:47:55 -0700 Subject: - fix indentation of code block --- docs/quick_tutorial/scaffolds.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/quick_tutorial/scaffolds.rst b/docs/quick_tutorial/scaffolds.rst index 8ca2d27df..4f2694100 100644 --- a/docs/quick_tutorial/scaffolds.rst +++ b/docs/quick_tutorial/scaffolds.rst @@ -63,11 +63,11 @@ Steps On startup, ``pserve`` logs some output: - .. code-block:: bash + .. code-block:: bash - Starting subprocess with file monitor - Starting server in PID 72213. - Starting HTTP server on http://0.0.0.0:6543 + Starting subprocess with file monitor + Starting server in PID 72213. + Starting HTTP server on http://0.0.0.0:6543 #. Open http://localhost:6543/ in your browser. -- cgit v1.2.3 From 32200c3af84c352c066eb2c402496305375912e4 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 27 Mar 2014 22:03:59 -0500 Subject: broadcast 3.4 support --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 3c17af20d..bb443330d 100644 --- a/setup.py +++ b/setup.py @@ -79,6 +79,7 @@ setup(name='pyramid', "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: Pyramid", -- cgit v1.2.3