Permalink
Browse files

Improved documentation, added a contextmanager for request binding

  • Loading branch information...
1 parent 44b42e0 commit 3b36bef2e6165bb4dad73d17f23ee1879e99f497 Armin Ronacher committed Apr 8, 2010
Showing with 198 additions and 28 deletions.
  1. +152 −2 docs/api.rst
  2. +6 −1 docs/conf.py
  3. +2 −2 examples/minitwit/minitwit.py
  4. +38 −23 flask.py
View
@@ -8,8 +8,158 @@ parts where Flask depends on external libraries, we document the most
important right here and provide links to the canonical documentation.
-General Structure
------------------
+Application Object
+------------------
.. autoclass:: Flask
:members:
+
+Incoming Request Data
+---------------------
+
+.. class:: request
+
+ To access incoming request data, you can use the global `request`
+ object. Flask parses incoming request data for you and gives you
+ access to it through that global object. Internally Flask makes
+ sure that you always get the correct data for the active thread if you
+ are in a multithreaded environment.
+
+ The request object is an instance of a :class:`~werkzeug.Request`
+ subclass and provides all of the attributes Werkzeug defines. This
+ just shows a quick overview of the most important ones.
+
+ .. attribute:: form
+
+ A :class:`~werkzeug.MultiDict` with the parsed form data from `POST`
+ or `PUT` requests. Please keep in mind that file uploads will not
+ end up here, but instead in the :attr:`files` attribute.
+
+ .. attribute:: args
+
+ A :class:`~werkzeug.MultiDict` with the parsed contents of the query
+ string. (The part in the URL after the question mark).
+
+ .. attribute:: values
+
+ A :class:`~werkzeug.CombinedMultiDict` with the contents of both
+ :attr:`form` and :attr:`args`.
+
+ .. attribute:: cookies
+
+ A :class:`dict` with the contents of all cookies transmitted with
+ the request.
+
+ .. attribute:: stream
+
+ If the incoming form data was not encoded with a known encoding (for
+ example it was transmitted as JSON) the data is stored unmodified in
+ this stream for consumption. For example to read the incoming
+ request data as JSON, one can do the following::
+
+ json_body = simplejson.load(request.stream)
+
+ .. attribute:: files
+
+ A :class:`~werkzeug.MultiDict` with files uploaded as part of a
+ `POST` or `PUT` request. Each file is stored as
+ :class:`~werkzeug.FileStorage` object. It basically behaves like a
+ standard file object you know from Python, with the difference that
+ it also has a :meth:`~werkzeug.FileStorage.save` function that can
+ store the file on the filesystem.
+
+ .. attribute:: method
+
+ The current request method (``POST``, ``GET`` etc.)
+
+ .. attribute:: path
+ .. attribute:: script_root
+ .. attribute:: url
+ .. attribute:: base_url
+ .. attribute:: url_root
+
+ Provides different ways to look at the current URL. Imagine your
+ application is listening on the following URL::
+
+ http://www.example.com/myapplication
+
+ And a user requests the following URL::
+
+ http://www.example.com/myapplication/page.html?x=y
+
+ In this case the values of the above mentioned attributes would be
+ the following:
+
+ ============= ======================================================
+ `path` ``/page.html``
+ `script_root` ``/myapplication``
+ `url` ``http://www.example.com/myapplication/page.html``
+ `base_url` ``http://www.example.com/myapplication/page.html?x=y``
+ `root_url` ``http://www.example.com/myapplication/``
+ ============= ======================================================
+
+
+Sessions
+--------
+
+If you have the :attr:`Flask.secret_key` set you can use sessions in Flask
+applications. A session basically makes it possible to remember
+information from one request to another. The way Flask does this is by
+using a signed cookie. So the user can look at the session contents, but
+not modify it unless he knows the secret key, so make sure to set that to
+something complex and unguessable.
+
+To access the current session you can use the :class:`session` object:
+
+.. class:: session
+
+ The session object works pretty much like an ordinary dict, with the
+ difference that it keeps track on modifications.
+
+ The following attributes are interesting:
+
+ .. attribute:: new
+
+ `True` if the session is new, `False` otherwise.
+
+ .. attribute:: modified
+
+ `True` if the session object detected a modification. Be advised
+ that modifications on mutable structures are not picked up
+ automatically, in that situation you have to explicitly set the
+ attribute to `True` yourself. Here an example::
+
+ # this change is not picked up because a mutable object (here
+ # a list) is changed.
+ session['objects'].append(42)
+ # so mark it as modified yourself
+ session.modified = True
+
+
+Useful Functions and Classes
+----------------------------
+
+.. autofunction:: url_for
+
+.. autofunction:: abort
+
+.. autofunction:: redirect
+
+.. autofunction:: escape
+
+.. autoclass:: Markup
+ :members: escape, unescape, striptags
+
+Message Flashing
+----------------
+
+.. autofunction:: flash
+
+.. autofunction:: get_flashed_messages
+
+Template Rendering
+------------------
+
+.. autofunction:: render_template
+
+.. autofunction:: render_template_string
View
@@ -25,7 +25,7 @@
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -234,3 +234,8 @@
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
+
+intersphinx_mapping = {
+ 'http://docs.python.org/dev': None,
+ 'http://werkzeug.pocoo.org/documentation/dev/': None
+}
@@ -7,8 +7,8 @@
from datetime import datetime
from contextlib import closing
from flask import Flask, request, session, url_for, redirect, \
- render_template, abort, g, flash, generate_password_hash, \
- check_password_hash
+ render_template, abort, g, flash
+from werkzeug import check_password_hash, generate_password_hash
# configuration
View
@@ -13,25 +13,17 @@
import sys
import pkg_resources
from threading import local
+from contextlib import contextmanager
from jinja2 import Environment, PackageLoader
-from werkzeug import Request, Response, LocalStack, LocalProxy
+from werkzeug import Request, Response, LocalStack, LocalProxy, \
+ create_environ, cached_property
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, InternalServerError
from werkzeug.contrib.securecookie import SecureCookie
-# try to import the json helpers
-try:
- from simplejson import loads as load_json, dumps as dump_json
-except ImportError:
- try:
- from json import loads as load_json, dumps as dump_json
- except ImportError:
- pass
-
# utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface.
-from werkzeug import abort, redirect, secure_filename, cached_property, \
- html, import_string, generate_password_hash, check_password_hash
+from werkzeug import abort, redirect
from jinja2 import Markup, escape
@@ -83,12 +75,6 @@ def url_for(endpoint, **values):
return _request_ctx_stack.top.url_adapter.build(endpoint, values)
-def jsonified(**values):
- """Returns a json response"""
- return current_app.response_class(dump_json(values),
- mimetype='application/json')
-
-
def flash(message):
"""Flashes a message to the next request. In order to remove the
flashed message from the session and to display it to the user,
@@ -113,13 +99,15 @@ def render_template(template_name, **context):
"""Renders a template from the template folder with the given
context.
"""
+ current_app.update_template_context(context)
return current_app.jinja_env.get_template(template_name).render(context)
def render_template_string(source, **context):
"""Renders a template from the given template source string
with the given context.
"""
+ current_app.update_template_context(context)
return current_app.jinja_env.from_string(source).render(context)
@@ -220,9 +208,6 @@ def __init__(self, package_name):
**self.jinja_options)
self.jinja_env.globals.update(
url_for=url_for,
- request=request,
- session=session,
- g=g,
get_flashed_messages=get_flashed_messages
)
@@ -234,6 +219,15 @@ def create_jinja_loader(self):
"""
return PackageLoader(self.package_name)
+ def update_template_context(self, context):
+ """Update the template context with some commonly used variables.
+ This injects request, session and g into the template context.
+ """
+ reqctx = _request_ctx_stack.top
+ context['request'] = reqctx.request
+ context['session'] = reqctx.session
+ context['g'] = reqctx.g
+
def run(self, host='localhost', port=5000, **options):
"""Runs the application on a local development server. If the
:attr:`debug` flag is set the server will automatically reload
@@ -443,17 +437,38 @@ def wsgi_app(self, environ, start_response):
app.wsgi_app = MyMiddleware(app.wsgi_app)
"""
- _request_ctx_stack.push(_RequestContext(self, environ))
- try:
+ with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
+
+ @contextmanager
+ def request_context(self, environ):
+ """Creates a request context from the given environment and binds
+ it to the current context. This must be used in combination with
+ the `with` statement because the request is only bound to the
+ current context for the duration of the `with` block.
+
+ Example usage::
+
+ with app.request_context(environ):
+ do_something_with(request)
+ """
+ _request_ctx_stack.push(_RequestContext(self, environ))
+ try:
+ yield
finally:
_request_ctx_stack.pop()
+ def test_request_context(self, *args, **kwargs):
+ """Creates a WSGI environment from the given values (see
+ :func:`werkzeug.create_environ` for more information).
+ """
+ return self.request_context(create_environ(*args, **kwargs))
+
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`"""
return self.wsgi_app(environ, start_response)

0 comments on commit 3b36bef

Please sign in to comment.