Permalink
Browse files

Merge branch 'master' into py3-dev

Conflicts:
	flask/helpers.py
	flask/testsuite/blueprints.py
	flask/testsuite/templating.py
	tox.ini
  • Loading branch information...
puzzlet committed Oct 18, 2012
2 parents 7ab052c + 8339cb3 commit 709c40a64c6365c9840c7f9d17536b011c4a8cf5
Showing with 1,197 additions and 319 deletions.
  1. +12 −0 .travis.yml
  2. +27 −0 CHANGES
  3. +0 −3 Makefile
  4. +53 −22 docs/api.rst
  5. +46 −2 docs/appcontext.rst
  6. +10 −0 docs/config.rst
  7. +0 −2 docs/deploying/fastcgi.rst
  8. +14 −0 docs/deploying/mod_wsgi.rst
  9. +2 −0 docs/patterns/index.rst
  10. +43 −0 docs/patterns/methodoverrides.rst
  11. +55 −0 docs/patterns/requestchecksum.rst
  12. +3 −3 docs/patterns/sqlalchemy.rst
  13. +59 −48 docs/patterns/sqlite3.rst
  14. +3 −3 docs/quickstart.rst
  15. +1 −1 docs/signals.rst
  16. +12 −3 docs/templating.rst
  17. +1 −1 docs/tutorial/dbinit.rst
  18. +20 −1 docs/upgrading.rst
  19. +22 −19 examples/flaskr/flaskr.py
  20. +44 −37 examples/minitwit/minitwit.py
  21. +12 −7 flask/__init__.py
  22. +73 −16 flask/app.py
  23. +28 −0 flask/blueprints.py
  24. +3 −2 flask/ctx.py
  25. +1 −2 flask/exceptions.py
  26. +1 −1 flask/exthook.py
  27. +5 −71 flask/helpers.py
  28. +167 −0 flask/json.py
  29. +0 −19 flask/session.py
  30. +111 −22 flask/sessions.py
  31. +5 −4 flask/templating.py
  32. +5 −1 flask/testing.py
  33. +4 −1 flask/testsuite/__init__.py
  34. +27 −1 flask/testsuite/basic.py
  35. +112 −4 flask/testsuite/blueprints.py
  36. +45 −0 flask/testsuite/helpers.py
  37. +6 −0 flask/testsuite/regression.py
  38. +4 −4 flask/testsuite/subclassing.py
  39. +3 −0 flask/testsuite/templates/template_test.html
  40. +96 −7 flask/testsuite/templating.py
  41. +39 −0 flask/testsuite/testing.py
  42. +18 −1 flask/testsuite/views.py
  43. +2 −4 flask/wrappers.py
  44. +1 −1 scripts/flaskext_compat.py
  45. +0 −1 setup.cfg
  46. +2 −1 setup.py
  47. +0 −4 tox.ini
View
@@ -13,3 +13,15 @@ script: python setup.py test
branches:
except:
- website
+
+notifications:
+ # Disable travis notifications until they figured out how to hide
+ # their own builder failure from us. Travis currently fails way
+ # too many times by itself.
+ email: false
+
+ irc:
+ channels:
+ - "irc.freenode.org#pocoo"
+ use_notice: true
+ skip_join: true
View
27 CHANGES
@@ -8,6 +8,33 @@ Version 0.10
Release date to be decided.
+- Changed default cookie serialization format from pickle to JSON to
+ limit the impact an attacker can do if the secret key leaks. See
+ :ref:`upgrading-to-010` for more information.
+- Added ``template_test`` methods in addition to the already existing
+ ``template_filter`` method family.
+- Set the content-length header for x-sendfile.
+- ``tojson`` filter now does not escape script blocks in HTML5 parsers.
+- Flask will now raise an error if you attempt to register a new function
+ on an already used endpoint.
+- Added wrapper module around simplejson and added default serialization
+ of datetime objects. This allows much easier customization of how
+ JSON is handled by Flask or any Flask extension.
+- Removed deprecated internal ``flask.session`` module alias. Use
+ ``flask.sessions`` instead to get the session module. This is not to
+ be confused with ``flask.session`` the session proxy.
+- Templates can now be rendered without request context. The behavior is
+ slightly different as the ``request``, ``session`` and ``g`` objects
+ will not be available and blueprint's context processors are not
+ called.
+- The config object is now available to the template as a real global and
+ not through a context processor which makes it available even in imported
+ templates by default.
+- Added an option to generate non-ascii encoded JSON which should result
+ in less bytes being transmitted over the network. It's disabled by
+ default to not cause confusion with existing libraries that might expect
+ ``flask.json.dumps`` to return bytestrings by default.
+
Version 0.9
-----------
View
@@ -11,9 +11,6 @@ audit:
release:
python scripts/make-release.py
-tox-test:
- PYTHONDONTWRITEBYTECODE= tox
-
ext-test:
python tests/flaskext_test.py --browse
View
@@ -215,6 +215,9 @@ implementation that Flask is using.
.. autoclass:: SecureCookieSessionInterface
:members:
+.. autoclass:: SecureCookieSession
+ :members:
+
.. autoclass:: NullSession
:members:
@@ -309,43 +312,71 @@ Message Flashing
.. autofunction:: get_flashed_messages
-Returning JSON
---------------
+JSON Support
+------------
-.. autofunction:: jsonify
+.. module:: flask.json
+
+Flask uses ``simplejson`` for the JSON implementation. Since simplejson
+is provided both by the standard library as well as extension Flask will
+try simplejson first and then fall back to the stdlib json module. On top
+of that it will delegate access to the current application's JSOn encoders
+and decoders for easier customization.
-.. data:: json
+So for starters instead of doing::
- If JSON support is picked up, this will be the module that Flask is
- using to parse and serialize JSON. So instead of doing this yourself::
+ try:
+ import simplejson as json
+ except ImportError:
+ import json
- try:
- import simplejson as json
- except ImportError:
- import json
+You can instead just do this::
- You can instead just do this::
+ from flask import json
- from flask import json
+For usage examples, read the :mod:`json` documentation in the standard
+lirbary. The following extensions are by default applied to the stdlib's
+JSON module:
- For usage examples, read the :mod:`json` documentation.
+1. ``datetime`` objects are serialized as :rfc:`822` strings.
+2. Any object with an ``__html__`` method (like :class:`~flask.Markup`)
+ will ahve that method called and then the return value is serialized
+ as string.
- The :func:`~json.dumps` function of this json module is also available
- as filter called ``|tojson`` in Jinja2. Note that inside `script`
- tags no escaping must take place, so make sure to disable escaping
- with ``|safe`` if you intend to use it inside `script` tags:
+The :func:`~htmlsafe_dumps` function of this json module is also available
+as filter called ``|tojson`` in Jinja2. Note that inside `script`
+tags no escaping must take place, so make sure to disable escaping
+with ``|safe`` if you intend to use it inside `script` tags:
- .. sourcecode:: html+jinja
+.. sourcecode:: html+jinja
+
+ <script type=text/javascript>
+ doSomethingWith({{ user.username|tojson|safe }});
+ </script>
+
+Note that the ``|tojson`` filter escapes forward slashes properly.
+
+.. autofunction:: jsonify
- <script type=text/javascript>
- doSomethingWith({{ user.username|tojson|safe }});
- </script>
+.. autofunction:: dumps
- Note that the ``|tojson`` filter escapes forward slashes properly.
+.. autofunction:: dump
+
+.. autofunction:: loads
+
+.. autofunction:: load
+
+.. autoclass:: JSONEncoder
+ :members:
+
+.. autoclass:: JSONDecoder
+ :members:
Template Rendering
------------------
+.. currentmodule:: flask
+
.. autofunction:: render_template
.. autofunction:: render_template_string
View
@@ -37,7 +37,7 @@ context local.
Purpose of the Application Context
----------------------------------
-The main reason for the application's context existance is that in the
+The main reason for the application's context existence is that in the
past a bunch of functionality was attached to the request context in lack
of a better solution. Since one of the pillar's of Flask's design is that
you can have more than one application in the same Python process.
@@ -58,7 +58,7 @@ Creating an Application Context
To make an application context there are two ways. The first one is the
implicit one: whenever a request context is pushed, an application context
will be created alongside if this is necessary. As a result of that, you
-can ignore the existance of the application context unless you need it.
+can ignore the existence of the application context unless you need it.
The second way is the explicit way using the
:meth:`~flask.Flask.app_context` method::
@@ -85,3 +85,47 @@ Extensions are free to store additional information on the topmost level,
assuming they pick a sufficiently unique name.
For more information about that, see :ref:`extension-dev`.
+
+Context Usage
+-------------
+
+The context is typically used to cache resources on there that need to be
+created on a per-request or usage case. For instance database connects
+are destined to go there. When storing things on the application context
+unique names should be chosen as this is a place that is shared between
+Flask applications and extensions.
+
+The most common usage is to split resource management into two parts:
+
+1. an implicit resource caching on the context.
+2. a context teardown based resource deallocation.
+
+Generally there would be a ``get_X()`` function that creates resource
+``X`` if it does not exist yet and otherwise returns the same resource,
+and a ``teardown_X()`` function that is registered as teardown handler.
+
+This is an example that connects to a database::
+
+ import sqlite3
+ from flask import _app_ctx_stack
+
+ def get_db():
+ top = _app_ctx_stack.top
+ if not hasattr(top, 'database'):
+ top.database = connect_to_database()
+ return top.database
+
+ @app.teardown_appcontext
+ def teardown_db(exception):
+ top = _app_ctx_stack.top
+ if hasattr(top, 'database'):
+ top.database.close()
+
+The first time ``get_db()`` is called the connection will be established.
+To make this implicit a :class:`~werkzeug.local.LocalProxy` can be used::
+
+ from werkzeug.local import LocalProxy
+ db = LocalProxy(get_db)
+
+That way a user can directly access ``db`` which internally calls
+``get_db()``.
View
@@ -142,6 +142,13 @@ The following configuration values are used internally by Flask:
``PREFERRED_URL_SCHEME`` The URL scheme that should be used for
URL generation if no URL scheme is
available. This defaults to ``http``.
+``JSON_AS_ASCII`` By default Flask serialize object to
+ ascii-encoded JSON. If this is set to
+ ``False`` Flask will not encode to ASCII
+ and output strings as-is and return
+ unicode strings. ``jsonfiy`` will
+ automatically encode it in ``utf-8``
+ then for transport for instance.
================================= =========================================
.. admonition:: More on ``SERVER_NAME``
@@ -184,6 +191,9 @@ The following configuration values are used internally by Flask:
.. versionadded:: 0.9
``PREFERRED_URL_SCHEME``
+.. versionadded:: 0.10
+ ``JSON_AS_ASCII``
+
Configuring from Files
----------------------
@@ -95,8 +95,6 @@ Set yourapplication.fcgi::
from yourapplication import app
class ScriptNameStripper(object):
- to_strip = '/yourapplication.fcgi'
-
def __init__(self, app):
self.app = app
@@ -91,6 +91,20 @@ execute the application under a different user for security reasons:
</Directory>
</VirtualHost>
+Note: WSGIDaemonProcess isn't implemented in Windows and Apache will
+refuse to run with the above configuration. On a Windows system, eliminate those lines:
+
+.. sourcecode:: apache
+
+ <VirtualHost *>
+ ServerName example.com
+ WSGIScriptAlias / C:\yourdir\yourapp.wsgi
+ <Directory C:\yourdir>
+ Order deny,allow
+ Allow from all
+ </Directory>
+ </VirtualHost>
+
For more information consult the `mod_wsgi wiki`_.
.. _mod_wsgi: http://code.google.com/p/modwsgi/
View
@@ -37,3 +37,5 @@ Snippet Archives <http://flask.pocoo.org/snippets/>`_.
favicon
streaming
deferredcallbacks
+ methodoverrides
+ requestchecksum
@@ -0,0 +1,43 @@
+Adding HTTP Method Overrides
+============================
+
+Some HTTP proxies do not support arbitrary HTTP methods or newer HTTP
+methods (such as PATCH). In that case it's possible to “proxy” HTTP
+methods through another HTTP method in total violation of the protocol.
+
+The way this works is by letting the client do an HTTP POST request and
+set the ``X-HTTP-Method-Override`` header and set the value to the
+intended HTTP method (such as ``PATCH``).
+
+This can easily be accomplished with an HTTP middleware::
+
+ class HTTPMethodOverrideMiddleware(object):
+ allowed_methods = frozenset([
+ 'GET',
+ 'HEAD',
+ 'POST',
+ 'DELETE',
+ 'PUT',
+ 'PATCH',
+ 'OPTIONS'
+ ])
+ bodyless_methods = frozenset(['GET', 'HEAD', 'OPTIONS', 'DELETE'])
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ method = environ.get('HTTP_X_HTTP_METHOD_OVERRIDE', '').upper()
+ if method in self.allowed_methods:
+ method = method.encode('ascii', 'replace')
+ environ['REQUEST_METHOD'] = method
+ if method in self.bodyless_methods:
+ environ['CONTENT_LENGTH'] = '0'
+ return self.app(environ, start_response)
+
+To use this with Flask this is all that is necessary::
+
+ from flask import Flask
+
+ app = Flask(__name__)
+ app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app)
@@ -0,0 +1,55 @@
+Request Content Checksums
+=========================
+
+Various pieces of code can consume the request data and preprocess it.
+For instance JSON data ends up on the request object already read and
+processed, form data ends up there as well but goes through a different
+code path. This seems inconvenient when you want to calculate the
+checksum of the incoming request data. This is necessary sometimes for
+some APIs.
+
+Fortunately this is however very simple to change by wrapping the input
+stream.
+
+The following example calculates the SHA1 checksum of the incoming data as
+it gets read and stores it in the WSGI environment::
+
+ import hashlib
+
+ class ChecksumCalcStream(object):
+
+ def __init__(self, stream):
+ self._stream = stream
+ self._hash = hashlib.sha1()
+
+ def read(self, bytes):
+ rv = self._stream.read(bytes)
+ self._hash.update(rv)
+ return rv
+
+ def readline(self, size_hint):
+ rv = self._stream.readline(size_hint)
+ self._hash.update(rv)
+ return rv
+
+ def generate_checksum(request):
+ env = request.environ
+ stream = ChecksumCalcStream(env['wsgi.input'])
+ env['wsgi.input'] = stream
+ return stream._hash
+
+To use this, all you need to do is to hook the calculating stream in
+before the request starts consuming data. (Eg: be careful accessing
+``request.form`` or anything of that nature. ``before_request_handlers``
+for instance should be careful not to access it).
+
+Example usage::
+
+ @app.route('/special-api', methods=['POST'])
+ def special_api():
+ hash = generate_checksum(request)
+ # Accessing this parses the input stream
+ files = request.files
+ # At this point the hash is fully constructed.
+ checksum = hash.hexdigest()
+ return 'Hash was: %s' % checksum
Oops, something went wrong.

0 comments on commit 709c40a

Please sign in to comment.