Browse Source

Fixed merging confict

pull/1638/head
Armin Ronacher 15 years ago
parent
commit
eeb0e94951
  1. 2
      Makefile
  2. 7
      docs/api.rst
  3. 49
      docs/patterns/viewdecorators.rst
  4. 2
      docs/patterns/wtforms.rst
  5. 9
      docs/quickstart.rst
  6. 19
      docs/testing.rst
  7. 9
      docs/tutorial/testing.rst
  8. 36
      flask.py
  9. 27
      tests/flask_tests.py

2
Makefile

@ -1,4 +1,4 @@
.PHONY: clean-pyc test
.PHONY: clean-pyc test upload-docs
all: clean-pyc test

7
docs/api.rst

@ -183,6 +183,13 @@ To access the current session you can use the :class:`session` object:
# so mark it as modified yourself
session.modified = True
.. attribute:: permanent
If set to `True` the session life for
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
default is 31 days. If set to `False` (which is the default) the
session will be deleted when the user closes the browser.
Application Globals
-------------------

49
docs/patterns/viewdecorators.rst

@ -91,3 +91,52 @@ Here the code::
Notice that this assumes an instanciated `cache` object is available, see
:ref:`caching-pattern` for more information.
Templating Decorator
--------------------
A common pattern invented by the TurboGears guys a while back is a
templating decorator. The idea of that decorator is that you return a
dictionary with the values passed to the template from the view function
and the template is automatically rendered. With that, the following
three examples do exactly the same::
@app.route('/')
def index():
return render_template('index.html', value=42)
@app.route('/')
@templated('index.html')
def index():
return dict(value=42)
@app.route('/')
@templated()
def index():
return dict(value=42)
As you can see, if no template name is provided it will use the endpoint
of the URL map + ``'.html'``. Otherwise the provided template name is
used. When the decorated function returns, the dictionary returned is
passed to the template rendering function. If `None` is returned, an
empty dictionary is assumed.
Here the code for that decorator::
from functools import wraps
from flask import request
def templated(template=None):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
template_name = template
if template_name is None:
template_name = request.endpoint + '.html'
ctx = f(*args, **kwargs)
if ctx is None:
ctx = {}
return render_template(template_name, **ctx)
return decorated_function
return decorator

2
docs/patterns/wtforms.rst

@ -42,7 +42,7 @@ In the view function, the usage of this form looks like this::
form.password.data)
db_session.add(user)
flash('Thanks for registering')
redirect(url_for('login'))
return redirect(url_for('login'))
return render_template('register.html', form=form)
Notice that we are implying that the view is using SQLAlchemy here

9
docs/quickstart.rst

@ -160,6 +160,8 @@ The following converters exist:
`path` like the default but also accepts slashes
=========== ===========================================
.. _url-building:
URL Building
````````````
@ -167,7 +169,8 @@ If it can match URLs, can it also generate them? Of course you can. To
build a URL to a specific function you can use the :func:`~flask.url_for`
function. It accepts the name of the function as first argument and a
number of keyword arguments, each corresponding to the variable part of
the URL rule. Here some examples:
the URL rule. Unknown variable parts are appended to the URL as query
parameter. Here some examples:
>>> from flask import Flask, url_for
>>> app = Flask(__name__)
@ -184,9 +187,11 @@ the URL rule. Here some examples:
... print url_for('index')
... print url_for('login')
... print url_for('profile', username='John Doe')
... print url_for('login', next='/')
...
/
/login
/login?next=/
/user/John%20Doe
(This also uses the :meth:`~flask.Flask.test_request_context` method
@ -452,7 +457,7 @@ transmitted in a `POST` or `PUT` request) you can use the
:attr:`~flask.request.form` attribute. Here a full example of the two
attributes mentioned above::
@app.route('/login', method=['POST', 'GET'])
@app.route('/login', methods=['POST', 'GET'])
def login():
error = None
if request.method == 'POST':

19
docs/testing.rst

@ -195,3 +195,22 @@ suite.
.. _MiniTwit Example:
http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/
Other Testing Tricks
--------------------
Besides using the test client we used above there is also the
:meth:`~flask.Flask.test_request_context` method that in combination with
the `with` statement can be used to activate a request context
temporarily. With that you can access the :class:`~flask.request`,
:class:`~flask.g` and :class:`~flask.session` objects like in view
functions. Here a full example that showcases this::
app = flask.Flask(__name__)
with app.test_request_context('/?name=Peter'):
assert flask.request.path == '/'
assert flask.request.args['name'] == 'Peter'
All the other objects that are context bound can be used the same.

9
docs/tutorial/testing.rst

@ -2,8 +2,7 @@ Bonus: Testing the Application
===============================
Now that you have finished the application and everything works as
expected, it's probably not the best idea to add automated tests to
simplify modifications in the future. The application above is used as a
basic example of how to perform unittesting in the :ref:`testing` section
of the documentation. Go there to see how easy it is to test Flask
applications.
expected, it's probably not a good idea to add automated tests to simplify
modifications in the future. The application above is used as a basic
example of how to perform unittesting in the :ref:`testing` section of the
documentation. Go there to see how easy it is to test Flask applications.

36
flask.py

@ -13,6 +13,7 @@ from __future__ import with_statement
import os
import sys
import types
from datetime import datetime, timedelta
from itertools import chain
from jinja2 import Environment, PackageLoader, FileSystemLoader
@ -93,7 +94,20 @@ class _RequestGlobals(object):
pass
class _NullSession(SecureCookie):
class Session(SecureCookie):
"""Expands the session for support for switching between permanent
and non-permanent sessions.
"""
def _get_permanent(self):
return self.get('_permanent', False)
def _set_permanent(self, value):
self['_permanent'] = bool(value)
permanent = property(_get_permanent, _set_permanent)
del _get_permanent, _set_permanent
class _NullSession(Session):
"""Class used to generate nicer error messages if sessions are not
available. Will still allow read-only access to the empty session
but fail on setting.
@ -158,6 +172,11 @@ def url_for(endpoint, **values):
any ``'admin.index'`` `index` of the `admin` module
==================== ======================= =============================
Variable arguments that are unknown to the target endpoint are appended
to the generated URL as query arguments.
For more information, head over to the :ref:`Quickstart <url-building>`.
:param endpoint: the endpoint of the URL (name of the function)
:param values: the variable arguments of the URL rule
"""
@ -511,6 +530,11 @@ class Flask(_PackageBoundObject):
#: The secure cookie uses this for the name of the session cookie
session_cookie_name = 'session'
#: A :class:`~datetime.timedelta` which is used to set the expiration
#: date of a permanent session. The default is 31 days which makes a
#: permanent session survive for roughly one month.
permanent_session_lifetime = timedelta(days=31)
#: options that are passed directly to the Jinja2 environment
jinja_options = ImmutableDict(
autoescape=True,
@ -664,8 +688,8 @@ class Flask(_PackageBoundObject):
"""
key = self.secret_key
if key is not None:
return SecureCookie.load_cookie(request, self.session_cookie_name,
secret_key=key)
return Session.load_cookie(request, self.session_cookie_name,
secret_key=key)
def save_session(self, session, response):
"""Saves the session if it needs updates. For the default
@ -676,7 +700,11 @@ class Flask(_PackageBoundObject):
object)
:param response: an instance of :attr:`response_class`
"""
session.save_cookie(response, self.session_cookie_name)
expires = None
if session.permanent:
expires = datetime.utcnow() + self.permanent_session_lifetime
session.save_cookie(response, self.session_cookie_name,
expires=expires, httponly=True)
def register_module(self, module, **options):
"""Registers a module with this application. The keyword argument

27
tests/flask_tests.py

@ -11,11 +11,14 @@
"""
from __future__ import with_statement
import os
import re
import sys
import flask
import unittest
import tempfile
import warnings
from datetime import datetime
from werkzeug import parse_date
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
@ -118,6 +121,30 @@ class BasicFunctionalityTestCase(unittest.TestCase):
expect_exception(flask.session.__setitem__, 'foo', 42)
expect_exception(flask.session.pop, 'foo')
def test_session_expiration(self):
permanent = True
app = flask.Flask(__name__)
app.secret_key = 'testkey'
@app.route('/')
def index():
flask.session['test'] = 42
flask.session.permanent = permanent
return ''
rv = app.test_client().get('/')
assert 'set-cookie' in rv.headers
match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
expires = parse_date(match.group())
expected = datetime.utcnow() + app.permanent_session_lifetime
assert expires.year == expected.year
assert expires.month == expected.month
assert expires.day == expected.day
permanent = False
rv = app.test_client().get('/')
assert 'set-cookie' in rv.headers
match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
assert match is None
def test_flashes(self):
app = flask.Flask(__name__)
app.secret_key = 'testkey'

Loading…
Cancel
Save