`_ for
+more information.
+
+Jinja Setup
+-----------
+
+Unless customized, Jinja2 is configured by Flask as follows:
+
+- autoescaping is enabled for all templates ending in ``.html``,
+ ``.htm``, ``.xml`` as well as ``.xhtml``
+- a template has the ability to opt in/out autoescaping with the
+ ``{% autoescape %}`` tag.
+- Flask inserts a couple of global functions and helpers into the
+ Jinja2 context, additionally to the values that are present by
+ default.
+
+Standard Context
+----------------
+
+The following global variables are available within Jinja2 templates
+by default:
+
+.. data:: config
+ :noindex:
+
+ The current configuration object (:data:`flask.config`)
+
+ .. versionadded:: 0.6
+
+.. data:: request
+ :noindex:
+
+ The current request object (:class:`flask.request`)
+
+.. data:: session
+ :noindex:
+
+ The current session object (:class:`flask.session`)
+
+.. data:: g
+ :noindex:
+
+ The request-bound object for global variables (:data:`flask.g`)
+
+.. function:: url_for
+ :noindex:
+
+ The :func:`flask.url_for` function.
+
+.. function:: get_flashed_messages
+ :noindex:
+
+ The :func:`flask.get_flashed_messages` function.
+
+.. admonition:: The Jinja Context Behaviour
+
+ These variables are added to the context of variables, they are not
+ global variables. The difference is that by default these will not
+ show up in the context of imported templates. This is partially caused
+ by performance considerations, partially to keep things explicit.
+
+ What does this mean for you? If you have a macro you want to import,
+ that needs to access the request object you have two possibilities:
+
+ 1. you explicitly pass the request to the macro as parameter, or
+ the attribute of the request object you are interested in.
+ 2. you import the macro "with context".
+
+ Importing with context looks like this:
+
+ .. sourcecode:: jinja
+
+ {% from '_helpers.html' import my_macro with context %}
+
+Standard Filters
+----------------
+
+These filters are available in Jinja2 additionally to the filters provided
+by Jinja2 itself:
+
+.. function:: tojson
+ :noindex:
+
+ This function converts the given object into JSON representation. This
+ is for example very helpful if you try to generate JavaScript on the
+ fly.
+
+ 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
+
+
+
+ That the ``|tojson`` filter escapes forward slashes properly for you.
+
+Controlling Autoescaping
+------------------------
+
+Autoescaping is the concept of automatically escaping special characters
+of you. Special characters in the sense of HTML (or XML, and thus XHTML)
+are ``&``, ``>``, ``<``, ``"`` as well as ``'``. Because these characters
+carry specific meanings in documents on their own you have to replace them
+by so called "entities" if you want to use them for text. Not doing so
+would not only cause user frustration by the inability to use these
+characters in text, but can also lead to security problems. (see
+:ref:`xss`)
+
+Sometimes however you will need to disable autoescaping in templates.
+This can be the case if you want to explicitly inject HTML into pages, for
+example if they come from a system that generate secure HTML like a
+markdown to HTML converter.
+
+There are three ways to accomplish that:
+
+- In the Python code, wrap the HTML string in a :class:`~flask.Markup`
+ object before passing it to the template. This is in general the
+ recommended way.
+- Inside the template, use the ``|safe`` filter to explicitly mark a
+ string as safe HTML (``{{ myvariable|safe }}``)
+- Temporarily disable the autoescape system altogether.
+
+To disable the autoescape system in templates, you can use the ``{%
+autoescape %}`` block:
+
+.. sourcecode:: html+jinja
+
+ {% autoescape false %}
+ autoescaping is disabled here
+
{{ will_not_be_escaped }}
+ {% endautoescape %}
+
+Whenever you do this, please be very cautious about the varibles you are
+using in this block.
+
+Registering Filters
+-------------------
+
+If you want to register your own filters in Jinja2 you have two ways to do
+that. You can either put them by hand into the
+:attr:`~flask.Flask.jinja_env` of the application or use the
+:meth:`~flask.Flask.template_filter` decorator.
+
+The two following examples work the same and both reverse an object::
+
+ @app.template_filter('reverse')
+ def reverse_filter(s):
+ return s[::-1]
+
+ def reverse_filter(s):
+ return s[::-1]
+ app.jinja_env.filters['reverse'] = reverse_filter
+
+In case of the decorator the argument is optional if you want to use the
+function name as name of the filter.
+
+Context Processors
+------------------
+
+To inject new variables automatically into the context of a template
+context processors exist in Flask. Context processors run before the
+template is rendered and have the ability to inject new values into the
+template context. A context processor is a function that returns a
+dictionary. The keys and values of this dictionary are then merged with
+the template context::
+
+ @app.context_processor
+ def inject_user():
+ return dict(user=g.user)
+
+The context processor above makes a variable called `user` available in
+the template with the value of `g.user`. This example is not very
+interesting because `g` is available in templates anyways, but it gives an
+idea how this works.
diff --git a/docs/testing.rst b/docs/testing.rst
index db2b4188..d131c673 100644
--- a/docs/testing.rst
+++ b/docs/testing.rst
@@ -8,8 +8,8 @@ Testing Flask Applications
Not sure where that is coming from, and it's not entirely correct, but
also not that far from the truth. Untested applications make it hard to
improve existing code and developers of untested applications tend to
-become pretty paranoid. If an application however has automated tests, you
-can safely change things and you will instantly know if your change broke
+become pretty paranoid. If an application has automated tests, you can
+safely change things, and you will instantly know if your change broke
something.
Flask gives you a couple of ways to test applications. It mainly does
@@ -60,7 +60,7 @@ each individual test function. To delete the database after the test, we
close the file and remove it from the filesystem in the
:meth:`~unittest.TestCase.tearDown` method. What the test client does is
give us a simple interface to the application. We can trigger test
-requests to the application and the client will also keep track of cookies
+requests to the application, and the client will also keep track of cookies
for us.
Because SQLite3 is filesystem-based we can easily use the tempfile module
@@ -130,7 +130,7 @@ Logging In and Out
------------------
The majority of the functionality of our application is only available for
-the administration user. So we need a way to log our test client in to the
+the administrative user, so we need a way to log our test client in to the
application and out of it again. For that we fire some requests to the
login and logout pages with the required form data (username and
password). Because the login and logout pages redirect, we tell the
@@ -200,12 +200,12 @@ suite.
Other Testing Tricks
--------------------
-Besides using the test client we used above there is also the
+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::
+functions. Here's a full example that showcases this::
app = flask.Flask(__name__)
@@ -218,3 +218,27 @@ All the other objects that are context bound can be used the same.
If you want to test your application with different configurations and
there does not seem to be a good way to do that, consider switching to
application factories (see :ref:`app-factories`).
+
+
+Keeping the Context Around
+--------------------------
+
+.. versionadded:: 0.4
+
+Sometimes it can be helpful to trigger a regular request but keep the
+context around for a little longer so that additional introspection can
+happen. With Flask 0.4 this is possible by using the
+:meth:`~flask.Flask.test_client` with a `with` block::
+
+ app = flask.Flask(__name__)
+
+ with app.test_client() as c:
+ rv = c.get('/?tequila=42')
+ assert request.args['tequila'] == '42'
+
+If you would just be using the :meth:`~flask.Flask.test_client` without
+the `with` block, the `assert` would fail with an error because `request`
+is no longer available (because used outside of an actual request).
+Keep in mind however that :meth:`~flask.Flask.after_request` functions
+are already called at that point so your database connection and
+everything involved is probably already closed down.
diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst
index 790cc41e..b5ae2a0e 100644
--- a/docs/tutorial/setup.rst
+++ b/docs/tutorial/setup.rst
@@ -52,7 +52,7 @@ The `secret_key` is needed to keep the client-side sessions secure.
Choose that key wisely and as hard to guess and complex as possible. The
debug flag enables or disables the interactive debugger. Never leave
debug mode activated in a production system because it will allow users to
-executed code on the server!
+execute code on the server!
We also add a method to easily connect to the database specified. That
can be used to open a connection on request and also from the interactive
@@ -64,7 +64,7 @@ Python shell or a script. This will come in handy later
return sqlite3.connect(app.config['DATABASE'])
Finally we just add a line to the bottom of the file that fires up the
-server if we run that file as standalone application::
+server if we want to run that file as a standalone application::
if __name__ == '__main__':
app.run()
diff --git a/docs/tutorial/testing.rst b/docs/tutorial/testing.rst
index ed9fc3e3..34edd791 100644
--- a/docs/tutorial/testing.rst
+++ b/docs/tutorial/testing.rst
@@ -4,7 +4,7 @@ Bonus: Testing the Application
==============================
Now that you have finished the application and everything works as
-expected, it's probably not a good idea to add automated tests to simplify
+expected, it's probably not a bad 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.
diff --git a/docs/tutorial/views.rst b/docs/tutorial/views.rst
index c7cc1ed4..f2871257 100644
--- a/docs/tutorial/views.rst
+++ b/docs/tutorial/views.rst
@@ -11,11 +11,11 @@ Show Entries
This view shows all the entries stored in the database. It listens on the
root of the application and will select title and text from the database.
-The one with the highest id (the newest entry) on top. The rows returned
-from the cursor are tuples with the columns ordered like specified in the
-select statement. This is good enough for small applications like here,
-but you might want to convert them into a dict. If you are interested how
-to do that, check out the :ref:`easy-querying` example.
+The one with the highest id (the newest entry) will be on top. The rows
+returned from the cursor are tuples with the columns ordered like specified
+in the select statement. This is good enough for small applications like
+here, but you might want to convert them into a dict. If you are
+interested in how to do that, check out the :ref:`easy-querying` example.
The view function will pass the entries as dicts to the
`show_entries.html` template and return the rendered one::
@@ -48,16 +48,23 @@ redirect back to the `show_entries` page::
Note that we check that the user is logged in here (the `logged_in` key is
present in the session and `True`).
+.. admonition:: Security Note
+
+ Be sure to use question marks when building SQL statements, as done in the
+ example above. Otherwise, your app will be vulnerable to SQL injection when
+ you use string formatting to build SQL statements.
+ See :ref:`sqlite3` for more.
+
Login and Logout
----------------
These functions are used to sign the user in and out. Login checks the
username and password against the ones from the configuration and sets the
-`logged_in` key in the session. If the user logged in successfully that
-key is set to `True` and the user is redirected back to the `show_entries`
-page. In that case also a message is flashed that informs the user he or
-she was logged in successfully. If an error occoured the template is
-notified about that and the user asked again::
+`logged_in` key in the session. If the user logged in successfully, that
+key is set to `True`, and the user is redirected back to the `show_entries`
+page. In addition, a message is flashed that informs the user that he or
+she was logged in successfully. If an error occurred, the template is
+notified about that, and the user is asked again::
@app.route('/login', methods=['GET', 'POST'])
def login():
@@ -73,12 +80,12 @@ notified about that and the user asked again::
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
-The logout function on the other hand removes that key from the session
+The logout function, on the other hand, removes that key from the session
again. We use a neat trick here: if you use the :meth:`~dict.pop` method
-of the dict and pass a second parameter to it (the default) the method
+of the dict and pass a second parameter to it (the default), the method
will delete the key from the dictionary if present or do nothing when that
-key was not in there. This is helpful because we don't have to check in
-that case if the user was logged in.
+key is not in there. This is helpful because now we don't have to check
+if the user was logged in.
::
diff --git a/docs/unicode.rst b/docs/unicode.rst
new file mode 100644
index 00000000..5b4202ad
--- /dev/null
+++ b/docs/unicode.rst
@@ -0,0 +1,107 @@
+Unicode in Flask
+================
+
+Flask like Jinja2 and Werkzeug is totally unicode based when it comes to
+text. Not only these libraries, also the majority of web related Python
+libraries that deal with text. If you don't know unicode so far, you
+should probably read `The Absolute Minimum Every Software Developer
+Absolutely, Positively Must Know About Unicode and Character Sets
+`_. This part of the
+documentation just tries to cover the very basics so that you have a
+pleasant experience with unicode related things.
+
+Automatic Conversion
+--------------------
+
+Flask has a few assumptions about your application (which you can change
+of course) that give you basic and painless unicode support:
+
+- the encoding for text on your website is UTF-8
+- internally you will always use unicode exclusively for text except
+ for literal strings with only ASCII character points.
+- encoding and decoding happens whenever you are talking over a protocol
+ that requires bytes to be transmitted.
+
+So what does this mean to you?
+
+HTTP is based on bytes. Not only the protocol, also the system used to
+address documents on servers (so called URIs or URLs). However HTML which
+is usually transmitted on top of HTTP supports a large variety of
+character sets and which ones are used, are transmitted in an HTTP header.
+To not make this too complex Flask just assumes that if you are sending
+unicode out you want it to be UTF-8 encoded. Flask will do the encoding
+and setting of the appropriate headers for you.
+
+The same is true if you are talking to databases with the help of
+SQLAlchemy or a similar ORM system. Some databases have a protocol that
+already transmits unicode and if they do not, SQLAlchemy or your other ORM
+should take care of that.
+
+The Golden Rule
+---------------
+
+So the rule of thumb: if you are not dealing with binary data, work with
+unicode. What does working with unicode in Python 2.x mean?
+
+- as long as you are using ASCII charpoints only (basically numbers,
+ some special characters of latin letters without umlauts or anything
+ fancy) you can use regular string literals (``'Hello World'``).
+- if you need anything else than ASCII in a string you have to mark
+ this string as unicode string by prefixing it with a lowercase `u`.
+ (like ``u'Hänsel und Gretel'``)
+- if you are using non-unicode characters in your Python files you have
+ to tell Python which encoding your file uses. Again, I recommend
+ UTF-8 for this purpose. To tell the interpreter your encoding you can
+ put the ``# -*- coding: utf-8 -*-`` into the first or second line of
+ your Python source file.
+- Jinja is configured to decode the template files from UTF-8. So make
+ sure to tell your editor to save the file as UTF-8 there as well.
+
+Encoding and Decoding Yourself
+------------------------------
+
+If you are talking with a filesystem or something that is not really based
+on unicode you will have to ensure that you decode properly when working
+with unicode interface. So for example if you want to load a file on the
+filesystem and embed it into a Jinja2 template you will have to decode it
+from the encoding of that file. Here the old problem that text files do
+not specify their encoding comes into play. So do yourself a favour and
+limit yourself to UTF-8 for text files as well.
+
+Anyways. To load such a file with unicode you can use the built-in
+:meth:`str.decode` method::
+
+ def read_file(filename, charset='utf-8'):
+ with open(filename, 'r') as f:
+ return f.read().decode(charset)
+
+To go from unicode into a specific charset such as UTF-8 you can use the
+:meth:`unicode.encode` method::
+
+ def write_file(filename, contents, charset='utf-8'):
+ with open(filename, 'w') as f:
+ f.write(contents.encode(charset))
+
+Configuring Editors
+-------------------
+
+Most editors save as UTF-8 by default nowadays but in case your editor is
+not configured to do this you have to change it. Here some common ways to
+set your editor to store as UTF-8:
+
+- Vim: put ``set enc=utf-8`` to your ``.vimrc`` file.
+
+- Emacs: either use an encoding cookie or put this into your ``.emacs``
+ file::
+
+ (prefer-coding-system 'utf-8)
+ (setq default-buffer-file-coding-system 'utf-8)
+
+- Notepad++:
+
+ 1. Go to *Settings -> Preferences ...*
+ 2. Select the "New Document/Default Directory" tab
+ 3. Select "UTF-8 without BOM" as encoding
+
+ It is also recommended to use the Unix newline format, you can select
+ it in the same panel but this is not a requirement.
diff --git a/docs/upgrading.rst b/docs/upgrading.rst
index f06523e9..747fdb72 100644
--- a/docs/upgrading.rst
+++ b/docs/upgrading.rst
@@ -14,6 +14,62 @@ This section of the documentation enumerates all the changes in Flask from
release to release and how you can change your code to have a painless
updating experience.
+If you want to use the `easy_install` command to upgrade your Flask
+installation, make sure to pass it the ``-U`` parameter::
+
+ $ easy_install -U Flask
+
+Version 0.6
+-----------
+
+Flask 0.6 comes with a backwards incompatible change which affects the
+order of after-request handlers. Previously they were called in the order
+of the registration, now they are called in reverse order. This change
+was made so that Flask behaves more like people expected it to work and
+how other systems handle request pre- and postprocessing. If you
+dependend on the order of execution of post-request functions, be sure to
+change the order.
+
+Another change that breaks backwards compatibility is that context
+processors will no longer override values passed directly to the template
+rendering function. If for example `request` is as variable passed
+directly to the template, the default context processor will not override
+it with the current request object. This makes it easier to extend
+context processors later to inject additional variables without breaking
+existing template not expecting them.
+
+Version 0.5
+-----------
+
+Flask 0.5 is the first release that comes as a Python package instead of a
+single module. There were a couple of internal refactoring so if you
+depend on undocumented internal details you probably have to adapt the
+imports.
+
+The following changes may be relevant to your application:
+
+- autoescaping no longer happens for all templates. Instead it is
+ configured to only happen on files ending with ``.html``, ``.htm``,
+ ``.xml`` and ``.xhtml``. If you have templates with different
+ extensions you should override the
+ :meth:`~flask.Flask.select_jinja_autoescape` method.
+- Flask no longer supports zipped applications in this release. This
+ functionality might come back in future releases if there is demand
+ for this feature. Removing support for this makes the Flask internal
+ code easier to understand and fixes a couple of small issues that make
+ debugging harder than necessary.
+- The `create_jinja_loader` function is gone. If you want to customize
+ the Jinja loader now, use the
+ :meth:`~flask.Flask.create_jinja_environment` method instead.
+
+Version 0.4
+-----------
+
+For application developers there are no changes that require changes in
+your code. In case you are developing on a Flask extension however, and
+that extension has a unittest-mode you might want to link the activation
+of that mode to the new ``TESTING`` flag.
+
Version 0.3
-----------
diff --git a/examples/jqueryexample/templates/layout.html b/examples/jqueryexample/templates/layout.html
index 0b5f3a7e..3e2ed69b 100644
--- a/examples/jqueryexample/templates/layout.html
+++ b/examples/jqueryexample/templates/layout.html
@@ -2,8 +2,6 @@
jQuery Example
-
diff --git a/flask.py b/flask.py
deleted file mode 100644
index bf4451c7..00000000
--- a/flask.py
+++ /dev/null
@@ -1,1426 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- flask
- ~~~~~
-
- A microframework based on Werkzeug. It's extensively documented
- and follows best practice patterns.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-from __future__ import with_statement
-import os
-import sys
-import mimetypes
-from datetime import datetime, timedelta
-
-from itertools import chain
-from jinja2 import Environment, PackageLoader, FileSystemLoader
-from werkzeug import Request as RequestBase, Response as ResponseBase, \
- LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
- ImmutableDict, cached_property, wrap_file, Headers, \
- import_string
-from werkzeug.routing import Map, Rule
-from werkzeug.exceptions import HTTPException, InternalServerError
-from werkzeug.contrib.securecookie import SecureCookie
-
-# try to load the best simplejson implementation available. If JSON
-# is not installed, we add a failing class.
-json_available = True
-try:
- import simplejson as json
-except ImportError:
- try:
- import json
- except ImportError:
- json_available = False
-
-# utilities we import from Werkzeug and Jinja2 that are unused
-# in the module but are exported as public interface.
-from werkzeug import abort, redirect
-from jinja2 import Markup, escape
-
-# use pkg_resource if that works, otherwise fall back to cwd. The
-# current working directory is generally not reliable with the notable
-# exception of google appengine.
-try:
- import pkg_resources
- pkg_resources.resource_stream
-except (ImportError, AttributeError):
- pkg_resources = None
-
-
-class Request(RequestBase):
- """The request object used by default in flask. Remembers the
- matched endpoint and view arguments.
-
- It is what ends up as :class:`~flask.request`. If you want to replace
- the request object used you can subclass this and set
- :attr:`~flask.Flask.request_class` to your subclass.
- """
-
- endpoint = view_args = routing_exception = None
-
- @property
- def module(self):
- """The name of the current module"""
- if self.endpoint and '.' in self.endpoint:
- return self.endpoint.rsplit('.', 1)[0]
-
- @cached_property
- def json(self):
- """If the mimetype is `application/json` this will contain the
- parsed JSON data.
- """
- if __debug__:
- _assert_have_json()
- if self.mimetype == 'application/json':
- return json.loads(self.data)
-
-
-class Response(ResponseBase):
- """The response object that is used by default in flask. Works like the
- response object from Werkzeug but is set to have a HTML mimetype by
- default. Quite often you don't have to create this object yourself because
- :meth:`~flask.Flask.make_response` will take care of that for you.
-
- If you want to replace the response object used you can subclass this and
- set :attr:`~flask.Flask.response_class` to your subclass.
- """
- default_mimetype = 'text/html'
-
-
-class _RequestGlobals(object):
- pass
-
-
-class Session(SecureCookie):
- """Expands the session with 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.
- """
-
- def _fail(self, *args, **kwargs):
- raise RuntimeError('the session is unavailable because no secret '
- 'key was set. Set the secret_key on the '
- 'application to something unique and secret')
- __setitem__ = __delitem__ = clear = pop = popitem = \
- update = setdefault = _fail
- del _fail
-
-
-class _RequestContext(object):
- """The request context contains all request relevant information. It is
- created at the beginning of the request and pushed to the
- `_request_ctx_stack` and removed at the end of it. It will create the
- URL adapter and request object for the WSGI environment provided.
- """
-
- def __init__(self, app, environ):
- self.app = app
- self.url_adapter = app.url_map.bind_to_environ(environ)
- self.request = app.request_class(environ)
- self.session = app.open_session(self.request)
- if self.session is None:
- self.session = _NullSession()
- self.g = _RequestGlobals()
- self.flashes = None
-
- try:
- self.request.endpoint, self.request.view_args = \
- self.url_adapter.match()
- except HTTPException, e:
- self.request.routing_exception = e
-
- def push(self):
- """Binds the request context."""
- _request_ctx_stack.push(self)
-
- def pop(self):
- """Pops the request context."""
- _request_ctx_stack.pop()
-
- def __enter__(self):
- self.push()
- return self
-
- def __exit__(self, exc_type, exc_value, tb):
- # do not pop the request stack if we are in debug mode and an
- # exception happened. This will allow the debugger to still
- # access the request object in the interactive shell.
- if tb is None or not self.app.debug:
- self.pop()
-
-
-def url_for(endpoint, **values):
- """Generates a URL to the given endpoint with the method provided.
- The endpoint is relative to the active module if modules are in use.
-
- Here some examples:
-
- ==================== ======================= =============================
- Active Module Target Endpoint Target Function
- ==================== ======================= =============================
- `None` ``'index'`` `index` of the application
- `None` ``'.index'`` `index` of the application
- ``'admin'`` ``'index'`` `index` of the `admin` module
- any ``'.index'`` `index` of the application
- 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 `.
-
- :param endpoint: the endpoint of the URL (name of the function)
- :param values: the variable arguments of the URL rule
- :param _external: if set to `True`, an absolute URL is generated.
- """
- ctx = _request_ctx_stack.top
- if '.' not in endpoint:
- mod = ctx.request.module
- if mod is not None:
- endpoint = mod + '.' + endpoint
- elif endpoint.startswith('.'):
- endpoint = endpoint[1:]
- external = values.pop('_external', False)
- return ctx.url_adapter.build(endpoint, values, force_external=external)
-
-
-def get_template_attribute(template_name, attribute):
- """Loads a macro (or variable) a template exports. This can be used to
- invoke a macro from within Python code. If you for example have a
- template named `_foo.html` with the following contents:
-
- .. sourcecode:: html+jinja
-
- {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
-
- You can access this from Python code like this::
-
- hello = get_template_attribute('_foo.html', 'hello')
- return hello('World')
-
- .. versionadded:: 0.2
-
- :param template_name: the name of the template
- :param attribute: the name of the variable of macro to acccess
- """
- return getattr(current_app.jinja_env.get_template(template_name).module,
- attribute)
-
-
-def flash(message, category='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,
- the template has to call :func:`get_flashed_messages`.
-
- .. versionchanged: 0.3
- `category` parameter added.
-
- :param message: the message to be flashed.
- :param category: the category for the message. The following values
- are recommended: ``'message'`` for any kind of message,
- ``'error'`` for errors, ``'info'`` for information
- messages and ``'warning'`` for warnings. However any
- kind of string can be used as category.
- """
- session.setdefault('_flashes', []).append((category, message))
-
-
-def get_flashed_messages(with_categories=False):
- """Pulls all flashed messages from the session and returns them.
- Further calls in the same request to the function will return
- the same messages. By default just the messages are returned,
- but when `with_categories` is set to `True`, the return value will
- be a list of tuples in the form ``(category, message)`` instead.
-
- Example usage:
-
- .. sourcecode:: html+jinja
-
- {% for category, msg in get_flashed_messages(with_categories=true) %}
- {{ msg }}
- {% endfor %}
-
- .. versionchanged:: 0.3
- `with_categories` parameter added.
-
- :param with_categories: set to `True` to also receive categories.
- """
- flashes = _request_ctx_stack.top.flashes
- if flashes is None:
- _request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
- if not with_categories:
- return [x[1] for x in flashes]
- return flashes
-
-
-def jsonify(*args, **kwargs):
- """Creates a :class:`~flask.Response` with the JSON representation of
- the given arguments with an `application/json` mimetype. The arguments
- to this function are the same as to the :class:`dict` constructor.
-
- Example usage::
-
- @app.route('/_get_current_user')
- def get_current_user():
- return jsonify(username=g.user.username,
- email=g.user.email,
- id=g.user.id)
-
- This will send a JSON response like this to the browser::
-
- {
- "username": "admin",
- "email": "admin@localhost",
- "id": 42
- }
-
- This requires Python 2.6 or an installed version of simplejson.
-
- .. versionadded:: 0.2
- """
- if __debug__:
- _assert_have_json()
- return current_app.response_class(json.dumps(dict(*args, **kwargs),
- indent=None if request.is_xhr else 2), mimetype='application/json')
-
-
-def send_file(filename_or_fp, mimetype=None, as_attachment=False,
- attachment_filename=None):
- """Sends the contents of a file to the client. This will use the
- most efficient method available and configured. By default it will
- try to use the WSGI server's file_wrapper support. Alternatively
- you can set the application's :attr:`~Flask.use_x_sendfile` attribute
- to ``True`` to directly emit an `X-Sendfile` header. This however
- requires support of the underlying webserver for `X-Sendfile`.
-
- By default it will try to guess the mimetype for you, but you can
- also explicitly provide one. For extra security you probably want
- to sent certain files as attachment (HTML for instance).
-
- Please never pass filenames to this function from user sources without
- checking them first. Something like this is usually sufficient to
- avoid security problems::
-
- if '..' in filename or filename.startswith('/'):
- abort(404)
-
- .. versionadded:: 0.2
-
- :param filename_or_fp: the filename of the file to send. This is
- relative to the :attr:`~Flask.root_path` if a
- relative path is specified.
- Alternatively a file object might be provided
- in which case `X-Sendfile` might not work and
- fall back to the traditional method.
- :param mimetype: the mimetype of the file if provided, otherwise
- auto detection happens.
- :param as_attachment: set to `True` if you want to send this file with
- a ``Content-Disposition: attachment`` header.
- :param attachment_filename: the filename for the attachment if it
- differs from the file's filename.
- """
- if isinstance(filename_or_fp, basestring):
- filename = filename_or_fp
- file = None
- else:
- file = filename_or_fp
- filename = getattr(file, 'name', None)
- if filename is not None:
- filename = os.path.join(current_app.root_path, filename)
- if mimetype is None and (filename or attachment_filename):
- mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
- if mimetype is None:
- mimetype = 'application/octet-stream'
-
- headers = Headers()
- if as_attachment:
- if attachment_filename is None:
- if filename is None:
- raise TypeError('filename unavailable, required for '
- 'sending as attachment')
- attachment_filename = os.path.basename(filename)
- headers.add('Content-Disposition', 'attachment',
- filename=attachment_filename)
-
- if current_app.use_x_sendfile and filename:
- if file is not None:
- file.close()
- headers['X-Sendfile'] = filename
- data = None
- else:
- if file is None:
- file = open(filename, 'rb')
- data = wrap_file(request.environ, file)
-
- return Response(data, mimetype=mimetype, headers=headers,
- direct_passthrough=True)
-
-
-def render_template(template_name, **context):
- """Renders a template from the template folder with the given
- context.
-
- :param template_name: the name of the template to be rendered
- :param context: the variables that should be available in the
- context of the template.
- """
- 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.
-
- :param template_name: the sourcecode of the template to be
- rendered
- :param context: the variables that should be available in the
- context of the template.
- """
- current_app.update_template_context(context)
- return current_app.jinja_env.from_string(source).render(context)
-
-
-def _default_template_ctx_processor():
- """Default template context processor. Injects `request`,
- `session` and `g`.
- """
- reqctx = _request_ctx_stack.top
- return dict(
- request=reqctx.request,
- session=reqctx.session,
- g=reqctx.g
- )
-
-
-def _assert_have_json():
- """Helper function that fails if JSON is unavailable."""
- if not json_available:
- raise RuntimeError('simplejson not installed')
-
-
-def _get_package_path(name):
- """Returns the path to a package or cwd if that cannot be found."""
- try:
- return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
- except (KeyError, AttributeError):
- return os.getcwd()
-
-
-# figure out if simplejson escapes slashes. This behaviour was changed
-# from one version to another without reason.
-if not json_available or '\\/' not in json.dumps('/'):
-
- def _tojson_filter(*args, **kwargs):
- if __debug__:
- _assert_have_json()
- return json.dumps(*args, **kwargs).replace('/', '\\/')
-else:
- _tojson_filter = json.dumps
-
-
-class _PackageBoundObject(object):
-
- def __init__(self, import_name):
- #: the name of the package or module. Do not change this once
- #: it was set by the constructor.
- self.import_name = import_name
-
- #: where is the app root located?
- self.root_path = _get_package_path(self.import_name)
-
- def open_resource(self, resource):
- """Opens a resource from the application's resource folder. To see
- how this works, consider the following folder structure::
-
- /myapplication.py
- /schemal.sql
- /static
- /style.css
- /template
- /layout.html
- /index.html
-
- If you want to open the `schema.sql` file you would do the
- following::
-
- with app.open_resource('schema.sql') as f:
- contents = f.read()
- do_something_with(contents)
-
- :param resource: the name of the resource. To access resources within
- subfolders use forward slashes as separator.
- """
- if pkg_resources is None:
- return open(os.path.join(self.root_path, resource), 'rb')
- return pkg_resources.resource_stream(self.import_name, resource)
-
-
-class _ModuleSetupState(object):
-
- def __init__(self, app, url_prefix=None):
- self.app = app
- self.url_prefix = url_prefix
-
-
-class Module(_PackageBoundObject):
- """Container object that enables pluggable applications. A module can
- be used to organize larger applications. They represent blueprints that,
- in combination with a :class:`Flask` object are used to create a large
- application.
-
- A module is like an application bound to an `import_name`. Multiple
- modules can share the same import names, but in that case a `name` has
- to be provided to keep them apart. If different import names are used,
- the rightmost part of the import name is used as name.
-
- Here an example structure for a larger appliation::
-
- /myapplication
- /__init__.py
- /views
- /__init__.py
- /admin.py
- /frontend.py
-
- The `myapplication/__init__.py` can look like this::
-
- from flask import Flask
- from myapplication.views.admin import admin
- from myapplication.views.frontend import frontend
-
- app = Flask(__name__)
- app.register_module(admin, url_prefix='/admin')
- app.register_module(frontend)
-
- And here an example view module (`myapplication/views/admin.py`)::
-
- from flask import Module
-
- admin = Module(__name__)
-
- @admin.route('/')
- def index():
- pass
-
- @admin.route('/login')
- def login():
- pass
-
- For a gentle introduction into modules, checkout the
- :ref:`working-with-modules` section.
- """
-
- def __init__(self, import_name, name=None, url_prefix=None):
- if name is None:
- assert '.' in import_name, 'name required if package name ' \
- 'does not point to a submodule'
- name = import_name.rsplit('.', 1)[1]
- _PackageBoundObject.__init__(self, import_name)
- self.name = name
- self.url_prefix = url_prefix
- self._register_events = []
-
- def route(self, rule, **options):
- """Like :meth:`Flask.route` but for a module. The endpoint for the
- :func:`url_for` function is prefixed with the name of the module.
- """
- def decorator(f):
- self.add_url_rule(rule, f.__name__, f, **options)
- return f
- return decorator
-
- def add_url_rule(self, rule, endpoint, view_func=None, **options):
- """Like :meth:`Flask.add_url_rule` but for a module. The endpoint for
- the :func:`url_for` function is prefixed with the name of the module.
- """
- def register_rule(state):
- the_rule = rule
- if state.url_prefix:
- the_rule = state.url_prefix + rule
- state.app.add_url_rule(the_rule, '%s.%s' % (self.name, endpoint),
- view_func, **options)
- self._record(register_rule)
-
- def before_request(self, f):
- """Like :meth:`Flask.before_request` but for a module. This function
- is only executed before each request that is handled by a function of
- that module.
- """
- self._record(lambda s: s.app.before_request_funcs
- .setdefault(self.name, []).append(f))
- return f
-
- def before_app_request(self, f):
- """Like :meth:`Flask.before_request`. Such a function is executed
- before each request, even if outside of a module.
- """
- self._record(lambda s: s.app.before_request_funcs
- .setdefault(None, []).append(f))
- return f
-
- def after_request(self, f):
- """Like :meth:`Flask.after_request` but for a module. This function
- is only executed after each request that is handled by a function of
- that module.
- """
- self._record(lambda s: s.app.after_request_funcs
- .setdefault(self.name, []).append(f))
- return f
-
- def after_app_request(self, f):
- """Like :meth:`Flask.after_request` but for a module. Such a function
- is executed after each request, even if outside of the module.
- """
- self._record(lambda s: s.app.after_request_funcs
- .setdefault(None, []).append(f))
- return f
-
- def context_processor(self, f):
- """Like :meth:`Flask.context_processor` but for a module. This
- function is only executed for requests handled by a module.
- """
- self._record(lambda s: s.app.template_context_processors
- .setdefault(self.name, []).append(f))
- return f
-
- def app_context_processor(self, f):
- """Like :meth:`Flask.context_processor` but for a module. Such a
- function is executed each request, even if outside of the module.
- """
- self._record(lambda s: s.app.template_context_processors
- .setdefault(None, []).append(f))
- return f
-
- def _record(self, func):
- self._register_events.append(func)
-
-
-class ConfigAttribute(object):
- """Makes an attribute forward to the config"""
-
- def __init__(self, name):
- self.__name__ = name
-
- def __get__(self, obj, type=None):
- if obj is None:
- return self
- return obj.config[self.__name__]
-
- def __set__(self, obj, value):
- obj.config[self.__name__] = value
-
-
-class Config(dict):
- """Works exactly like a dict but provides ways to fill it from files
- or special dictionaries. There are two common patterns to populate the
- config.
-
- Either you can fill the config from a config file::
-
- app.config.from_pyfile('yourconfig.cfg')
-
- Or alternatively you can define the configuration options in the
- module that calls :meth:`from_object` or provide an import path to
- a module that should be loaded. It is also possible to tell it to
- use the same module and with that provide the configuration values
- just before the call::
-
- DEBUG = True
- SECRET_KEY = 'development key'
- app.config.from_object(__name__)
-
- In both cases (loading from any Python file or loading from modules),
- only uppercase keys are added to the config. This makes it possible to use
- lowercase values in the config file for temporary values that are not added
- to the config or to define the config keys in the same file that implements
- the application.
-
- Probably the most interesting way to load configurations is from an
- environment variable pointing to a file::
-
- app.config.from_envvar('YOURAPPLICATION_SETTINGS')
-
- In this case before launching the application you have to set this
- environment variable to the file you want to use. On Linux and OS X
- use the export statement::
-
- export YOURAPPLICATION_SETTINGS='/path/to/config/file'
-
- On windows use `set` instead.
-
- :param root_path: path to which files are read relative from. When the
- config object is created by the application, this is
- the application's :attr:`~flask.Flask.root_path`.
- :param defaults: an optional dictionary of default values
- """
-
- def __init__(self, root_path, defaults=None):
- dict.__init__(self, defaults or {})
- self.root_path = root_path
-
- def from_envvar(self, variable_name, silent=False):
- """Loads a configuration from an environment variable pointing to
- a configuration file. This basically is just a shortcut with nicer
- error messages for this line of code::
-
- app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
-
- :param variable_name: name of the environment variable
- :param silent: set to `True` if you want silent failing for missing
- files.
- :return: bool. `True` if able to load config, `False` otherwise.
- """
- rv = os.environ.get(variable_name)
- if not rv:
- if silent:
- return False
- raise RuntimeError('The environment variable %r is not set '
- 'and as such configuration could not be '
- 'loaded. Set this variable and make it '
- 'point to a configuration file' %
- variable_name)
- self.from_pyfile(rv)
- return True
-
- def from_pyfile(self, filename):
- """Updates the values in the config from a Python file. This function
- behaves as if the file was imported as module with the
- :meth:`from_object` function.
-
- :param filename: the filename of the config. This can either be an
- absolute filename or a filename relative to the
- root path.
- """
- filename = os.path.join(self.root_path, filename)
- d = type(sys)('config')
- d.__file__ = filename
- execfile(filename, d.__dict__)
- self.from_object(d)
-
- def from_object(self, obj):
- """Updates the values from the given object. An object can be of one
- of the following two types:
-
- - a string: in this case the object with that name will be imported
- - an actual object reference: that object is used directly
-
- Objects are usually either modules or classes.
-
- Just the uppercase variables in that object are stored in the config
- after lowercasing. Example usage::
-
- app.config.from_object('yourapplication.default_config')
- from yourapplication import default_config
- app.config.from_object(default_config)
-
- You should not use this function to load the actual configuration but
- rather configuration defaults. The actual config should be loaded
- with :meth:`from_pyfile` and ideally from a location not within the
- package because the package might be installed system wide.
-
- :param obj: an import name or object
- """
- if isinstance(obj, basestring):
- obj = import_string(obj)
- for key in dir(obj):
- if key.isupper():
- self[key] = getattr(obj, key)
-
- def __repr__(self):
- return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
-
-
-class Flask(_PackageBoundObject):
- """The flask object implements a WSGI application and acts as the central
- object. It is passed the name of the module or package of the
- application. Once it is created it will act as a central registry for
- the view functions, the URL rules, template configuration and much more.
-
- The name of the package is used to resolve resources from inside the
- package or the folder the module is contained in depending on if the
- package parameter resolves to an actual python package (a folder with
- an `__init__.py` file inside) or a standard module (just a `.py` file).
-
- For more information about resource loading, see :func:`open_resource`.
-
- Usually you create a :class:`Flask` instance in your main module or
- in the `__init__.py` file of your package like this::
-
- from flask import Flask
- app = Flask(__name__)
- """
-
- #: the class that is used for request objects. See :class:`~flask.Request`
- #: for more information.
- request_class = Request
-
- #: the class that is used for response objects. See
- #: :class:`~flask.Response` for more information.
- response_class = Response
-
- #: path for the static files. If you don't want to use static files
- #: you can set this value to `None` in which case no URL rule is added
- #: and the development server will no longer serve any static files.
- static_path = '/static'
-
- #: the debug flag. Set this to `True` to enable debugging of the
- #: application. In debug mode the debugger will kick in when an unhandled
- #: exception ocurrs and the integrated server will automatically reload
- #: the application if changes in the code are detected.
- #:
- #: This attribute can also be configured from the config with the `DEBUG`
- #: configuration key. Defaults to `False`.
- debug = ConfigAttribute('DEBUG')
-
- #: if a secret key is set, cryptographic components can use this to
- #: sign cookies and other things. Set this to a complex random value
- #: when you want to use the secure cookie for instance.
- #:
- #: This attribute can also be configured from the config with the
- #: `SECRET_KEY` configuration key. Defaults to `None`.
- secret_key = ConfigAttribute('SECRET_KEY')
-
- #: The secure cookie uses this for the name of the session cookie
- #:
- #: This attribute can also be configured from the config with the
- #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'``
- session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
-
- #: 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.
- #:
- #: This attribute can also be configured from the config with the
- #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to
- #: ``timedelta(days=31)``
- permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME')
-
- #: Enable this if you want to use the X-Sendfile feature. Keep in
- #: mind that the server has to support this. This only affects files
- #: sent with the :func:`send_file` method.
- #:
- #: .. versionadded:: 0.2
- #:
- #: This attribute can also be configured from the config with the
- #: `USE_X_SENDFILE` configuration key. Defaults to `False`.
- use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
-
- #: the logging format used for the debug logger. This is only used when
- #: the application is in debug mode, otherwise the attached logging
- #: handler does the formatting.
- #:
- #: .. versionadded:: 0.3
- debug_log_format = (
- '-' * 80 + '\n' +
- '%(levelname)s in %(module)s, %(pathname)s:%(lineno)d]:\n' +
- '%(message)s\n' +
- '-' * 80
- )
-
- #: options that are passed directly to the Jinja2 environment
- jinja_options = ImmutableDict(
- autoescape=True,
- extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
- )
-
- #: default configuration parameters
- default_config = ImmutableDict({
- 'DEBUG': False,
- 'SECRET_KEY': None,
- 'SESSION_COOKIE_NAME': 'session',
- 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
- 'USE_X_SENDFILE': False
- })
-
- def __init__(self, import_name):
- _PackageBoundObject.__init__(self, import_name)
-
- #: the configuration dictionary as :class:`Config`. This behaves
- #: exactly like a regular dictionary but supports additional methods
- #: to load a config from files.
- self.config = Config(self.root_path, self.default_config)
-
- #: a dictionary of all view functions registered. The keys will
- #: be function names which are also used to generate URLs and
- #: the values are the function objects themselves.
- #: to register a view function, use the :meth:`route` decorator.
- self.view_functions = {}
-
- #: a dictionary of all registered error handlers. The key is
- #: be the error code as integer, the value the function that
- #: should handle that error.
- #: To register a error handler, use the :meth:`errorhandler`
- #: decorator.
- self.error_handlers = {}
-
- #: a dictionary with lists of functions that should be called at the
- #: beginning of the request. The key of the dictionary is the name of
- #: the module this function is active for, `None` for all requests.
- #: This can for example be used to open database connections or
- #: getting hold of the currently logged in user. To register a
- #: function here, use the :meth:`before_request` decorator.
- self.before_request_funcs = {}
-
- #: a dictionary with lists of functions that should be called after
- #: each request. The key of the dictionary is the name of the module
- #: this function is active for, `None` for all requests. This can for
- #: example be used to open database connections or getting hold of the
- #: currently logged in user. To register a function here, use the
- #: :meth:`before_request` decorator.
- self.after_request_funcs = {}
-
- #: a dictionary with list of functions that are called without argument
- #: to populate the template context. They key of the dictionary is the
- #: name of the module this function is active for, `None` for all
- #: requests. Each returns a dictionary that the template context is
- #: updated with. To register a function here, use the
- #: :meth:`context_processor` decorator.
- self.template_context_processors = {
- None: [_default_template_ctx_processor]
- }
-
- #: the :class:`~werkzeug.routing.Map` for this instance. You can use
- #: this to change the routing converters after the class was created
- #: but before any routes are connected. Example::
- #:
- #: from werkzeug import BaseConverter
- #:
- #: class ListConverter(BaseConverter):
- #: def to_python(self, value):
- #: return value.split(',')
- #: def to_url(self, values):
- #: return ','.join(BaseConverter.to_url(value)
- #: for value in values)
- #:
- #: app = Flask(__name__)
- #: app.url_map.converters['list'] = ListConverter
- self.url_map = Map()
-
- if self.static_path is not None:
- self.add_url_rule(self.static_path + '/',
- build_only=True, endpoint='static')
- if pkg_resources is not None:
- target = (self.import_name, 'static')
- else:
- target = os.path.join(self.root_path, 'static')
- self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
- self.static_path: target
- })
-
- #: the Jinja2 environment. It is created from the
- #: :attr:`jinja_options` and the loader that is returned
- #: by the :meth:`create_jinja_loader` function.
- self.jinja_env = Environment(loader=self.create_jinja_loader(),
- **self.jinja_options)
- self.jinja_env.globals.update(
- url_for=url_for,
- get_flashed_messages=get_flashed_messages
- )
- self.jinja_env.filters['tojson'] = _tojson_filter
-
- @cached_property
- def logger(self):
- """A :class:`logging.Logger` object for this application. The
- default configuration is to log to stderr if the application is
- in debug mode. This logger can be used to (surprise) log messages.
- Here some examples::
-
- app.logger.debug('A value for debugging')
- app.logger.warning('A warning ocurred (%d apples)', 42)
- app.logger.error('An error occoured')
-
- .. versionadded:: 0.3
- """
- from logging import getLogger, StreamHandler, Formatter, DEBUG
- class DebugHandler(StreamHandler):
- def emit(x, record):
- if self.debug:
- StreamHandler.emit(x, record)
- handler = DebugHandler()
- handler.setLevel(DEBUG)
- handler.setFormatter(Formatter(self.debug_log_format))
- logger = getLogger(self.import_name)
- logger.addHandler(handler)
- return logger
-
- def create_jinja_loader(self):
- """Creates the Jinja loader. By default just a package loader for
- the configured package is returned that looks up templates in the
- `templates` folder. To add other loaders it's possible to
- override this method.
- """
- if pkg_resources is None:
- return FileSystemLoader(os.path.join(self.root_path, 'templates'))
- return PackageLoader(self.import_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.
-
- :param context: the context as a dictionary that is updated in place
- to add extra variables.
- """
- funcs = self.template_context_processors[None]
- mod = _request_ctx_stack.top.request.module
- if mod is not None and mod in self.template_context_processors:
- funcs = chain(funcs, self.template_context_processors[mod])
- for func in funcs:
- context.update(func())
-
- def run(self, host='127.0.0.1', port=5000, **options):
- """Runs the application on a local development server. If the
- :attr:`debug` flag is set the server will automatically reload
- for code changes and show a debugger in case an exception happened.
-
- :param host: the hostname to listen on. set this to ``'0.0.0.0'``
- to have the server available externally as well.
- :param port: the port of the webserver
- :param options: the options to be forwarded to the underlying
- Werkzeug server. See :func:`werkzeug.run_simple`
- for more information.
- """
- from werkzeug import run_simple
- if 'debug' in options:
- self.debug = options.pop('debug')
- options.setdefault('use_reloader', self.debug)
- options.setdefault('use_debugger', self.debug)
- return run_simple(host, port, self, **options)
-
- def test_client(self):
- """Creates a test client for this application. For information
- about unit testing head over to :ref:`testing`.
- """
- from werkzeug import Client
- return Client(self, self.response_class, use_cookies=True)
-
- def open_session(self, request):
- """Creates or opens a new session. Default implementation stores all
- session data in a signed cookie. This requires that the
- :attr:`secret_key` is set.
-
- :param request: an instance of :attr:`request_class`.
- """
- key = self.secret_key
- if key is not None:
- 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
- implementation, check :meth:`open_session`.
-
- :param session: the session to be saved (a
- :class:`~werkzeug.contrib.securecookie.SecureCookie`
- object)
- :param response: an instance of :attr:`response_class`
- """
- 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
- of this function are the same as the ones for the constructor of the
- :class:`Module` class and will override the values of the module if
- provided.
- """
- options.setdefault('url_prefix', module.url_prefix)
- state = _ModuleSetupState(self, **options)
- for func in module._register_events:
- func(state)
-
- def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
- """Connects a URL rule. Works exactly like the :meth:`route`
- decorator. If a view_func is provided it will be registered with the
- endpoint.
-
- Basically this example::
-
- @app.route('/')
- def index():
- pass
-
- Is equivalent to the following::
-
- def index():
- pass
- app.add_url_rule('/', 'index', index)
-
- If the view_func is not provided you will need to connect the endpoint
- to a view function like so::
-
- app.view_functions['index'] = index
-
- .. versionchanged:: 0.2
- `view_func` parameter added.
-
- :param rule: the URL rule as string
- :param endpoint: the endpoint for the registered URL rule. Flask
- itself assumes the name of the view function as
- endpoint
- :param view_func: the function to call when serving a request to the
- provided endpoint
- :param options: the options to be forwarded to the underlying
- :class:`~werkzeug.routing.Rule` object
- """
- if endpoint is None:
- assert view_func is not None, 'expected view func if endpoint ' \
- 'is not provided.'
- endpoint = view_func.__name__
- options['endpoint'] = endpoint
- options.setdefault('methods', ('GET',))
- self.url_map.add(Rule(rule, **options))
- if view_func is not None:
- self.view_functions[endpoint] = view_func
-
- def route(self, rule, **options):
- """A decorator that is used to register a view function for a
- given URL rule. Example::
-
- @app.route('/')
- def index():
- return 'Hello World'
-
- Variables parts in the route can be specified with angular
- brackets (``/user/``). By default a variable part
- in the URL accepts any string without a slash however a different
- converter can be specified as well by using ````.
-
- Variable parts are passed to the view function as keyword
- arguments.
-
- The following converters are possible:
-
- =========== ===========================================
- `int` accepts integers
- `float` like `int` but for floating point values
- `path` like the default but also accepts slashes
- =========== ===========================================
-
- Here some examples::
-
- @app.route('/')
- def index():
- pass
-
- @app.route('/')
- def show_user(username):
- pass
-
- @app.route('/post/')
- def show_post(post_id):
- pass
-
- An important detail to keep in mind is how Flask deals with trailing
- slashes. The idea is to keep each URL unique so the following rules
- apply:
-
- 1. If a rule ends with a slash and is requested without a slash
- by the user, the user is automatically redirected to the same
- page with a trailing slash attached.
- 2. If a rule does not end with a trailing slash and the user request
- the page with a trailing slash, a 404 not found is raised.
-
- This is consistent with how web servers deal with static files. This
- also makes it possible to use relative link targets safely.
-
- The :meth:`route` decorator accepts a couple of other arguments
- as well:
-
- :param rule: the URL rule as string
- :param methods: a list of methods this rule should be limited
- to (``GET``, ``POST`` etc.). By default a rule
- just listens for ``GET`` (and implicitly ``HEAD``).
- :param subdomain: specifies the rule for the subdoain in case
- subdomain matching is in use.
- :param strict_slashes: can be used to disable the strict slashes
- setting for this rule. See above.
- :param options: other options to be forwarded to the underlying
- :class:`~werkzeug.routing.Rule` object.
- """
- def decorator(f):
- self.add_url_rule(rule, None, f, **options)
- return f
- return decorator
-
- def errorhandler(self, code):
- """A decorator that is used to register a function give a given
- error code. Example::
-
- @app.errorhandler(404)
- def page_not_found(error):
- return 'This page does not exist', 404
-
- You can also register a function as error handler without using
- the :meth:`errorhandler` decorator. The following example is
- equivalent to the one above::
-
- def page_not_found(error):
- return 'This page does not exist', 404
- app.error_handlers[404] = page_not_found
-
- :param code: the code as integer for the handler
- """
- def decorator(f):
- self.error_handlers[code] = f
- return f
- return decorator
-
- def template_filter(self, name=None):
- """A decorator that is used to register custom template filter.
- You can specify a name for the filter, otherwise the function
- name will be used. Example::
-
- @app.template_filter()
- def reverse(s):
- return s[::-1]
-
- :param name: the optional name of the filter, otherwise the
- function name will be used.
- """
- def decorator(f):
- self.jinja_env.filters[name or f.__name__] = f
- return f
- return decorator
-
- def before_request(self, f):
- """Registers a function to run before each request."""
- self.before_request_funcs.setdefault(None, []).append(f)
- return f
-
- def after_request(self, f):
- """Register a function to be run after each request."""
- self.after_request_funcs.setdefault(None, []).append(f)
- return f
-
- def context_processor(self, f):
- """Registers a template context processor function."""
- self.template_context_processors[None].append(f)
- return f
-
- def handle_http_exception(self, e):
- """Handles an HTTP exception. By default this will invoke the
- registered error handlers and fall back to returning the
- exception as response.
-
- .. versionadded: 0.3
- """
- handler = self.error_handlers.get(e.code)
- if handler is None:
- return e
- return handler(e)
-
- def handle_exception(self, e):
- """Default exception handling that kicks in when an exception
- occours that is not catched. In debug mode the exception will
- be re-raised immediately, otherwise it is logged an the handler
- for an 500 internal server error is used. If no such handler
- exists, a default 500 internal server error message is displayed.
-
- .. versionadded: 0.3
- """
- handler = self.error_handlers.get(500)
- if self.debug:
- raise
- self.logger.exception('Exception on %s [%s]' % (
- request.path,
- request.method
- ))
- if handler is None:
- return InternalServerError()
- return handler(e)
-
- def dispatch_request(self):
- """Does the request dispatching. Matches the URL and returns the
- return value of the view or error handler. This does not have to
- be a response object. In order to convert the return value to a
- proper response object, call :func:`make_response`.
- """
- req = _request_ctx_stack.top.request
- try:
- if req.routing_exception is not None:
- raise req.routing_exception
- return self.view_functions[req.endpoint](**req.view_args)
- except HTTPException, e:
- return self.handle_http_exception(e)
-
- def make_response(self, rv):
- """Converts the return value from a view function to a real
- response object that is an instance of :attr:`response_class`.
-
- The following types are allowed for `rv`:
-
- .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
-
- ======================= ===========================================
- :attr:`response_class` the object is returned unchanged
- :class:`str` a response object is created with the
- string as body
- :class:`unicode` a response object is created with the
- string encoded to utf-8 as body
- :class:`tuple` the response object is created with the
- contents of the tuple as arguments
- a WSGI function the function is called as WSGI application
- and buffered as response object
- ======================= ===========================================
-
- :param rv: the return value from the view function
- """
- if rv is None:
- raise ValueError('View function did not return a response')
- if isinstance(rv, self.response_class):
- return rv
- if isinstance(rv, basestring):
- return self.response_class(rv)
- if isinstance(rv, tuple):
- return self.response_class(*rv)
- return self.response_class.force_type(rv, request.environ)
-
- def preprocess_request(self):
- """Called before the actual request dispatching and will
- call every as :meth:`before_request` decorated function.
- If any of these function returns a value it's handled as
- if it was the return value from the view and further
- request handling is stopped.
- """
- funcs = self.before_request_funcs.get(None, ())
- mod = request.module
- if mod and mod in self.before_request_funcs:
- funcs = chain(funcs, self.before_request_funcs[mod])
- for func in funcs:
- rv = func()
- if rv is not None:
- return rv
-
- def process_response(self, response):
- """Can be overridden in order to modify the response object
- before it's sent to the WSGI server. By default this will
- call all the :meth:`after_request` decorated functions.
-
- :param response: a :attr:`response_class` object.
- :return: a new response object or the same, has to be an
- instance of :attr:`response_class`.
- """
- ctx = _request_ctx_stack.top
- mod = ctx.request.module
- if not isinstance(ctx.session, _NullSession):
- self.save_session(ctx.session, response)
- funcs = ()
- if mod and mod in self.after_request_funcs:
- funcs = chain(funcs, self.after_request_funcs[mod])
- if None in self.after_request_funcs:
- funcs = chain(funcs, self.after_request_funcs[None])
- for handler in funcs:
- response = handler(response)
- return response
-
- def wsgi_app(self, environ, start_response):
- """The actual WSGI application. This is not implemented in
- `__call__` so that middlewares can be applied without losing a
- reference to the class. So instead of doing this::
-
- app = MyMiddleware(app)
-
- It's a better idea to do this instead::
-
- app.wsgi_app = MyMiddleware(app.wsgi_app)
-
- Then you still have the original application object around and
- can continue to call methods on it.
-
- :param environ: a WSGI environment
- :param start_response: a callable accepting a status code,
- a list of headers and an optional
- exception context to start the response
- """
- with self.request_context(environ):
- try:
- rv = self.preprocess_request()
- if rv is None:
- rv = self.dispatch_request()
- response = self.make_response(rv)
- response = self.process_response(response)
- except Exception, e:
- response = self.make_response(self.handle_exception(e))
- return response(environ, start_response)
-
- 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)
-
- The object returned can also be used without the `with` statement
- which is useful for working in the shell. The example above is
- doing exactly the same as this code::
-
- ctx = app.request_context(environ)
- ctx.push()
- try:
- do_something_with(request)
- finally:
- ctx.pop()
-
- The big advantage of this approach is that you can use it without
- the try/finally statement in a shell for interactive testing:
-
- >>> ctx = app.test_request_context()
- >>> ctx.bind()
- >>> request.path
- u'/'
- >>> ctx.unbind()
-
- .. versionchanged:: 0.3
- Added support for non-with statement usage and `with` statement
- is now passed the ctx object.
-
- :param environ: a WSGI environment
- """
- return _RequestContext(self, environ)
-
- def test_request_context(self, *args, **kwargs):
- """Creates a WSGI environment from the given values (see
- :func:`werkzeug.create_environ` for more information, this
- function accepts the same arguments).
- """
- 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)
-
-
-# context locals
-_request_ctx_stack = LocalStack()
-current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
-request = LocalProxy(lambda: _request_ctx_stack.top.request)
-session = LocalProxy(lambda: _request_ctx_stack.top.session)
-g = LocalProxy(lambda: _request_ctx_stack.top.g)
diff --git a/flask/__init__.py b/flask/__init__.py
new file mode 100644
index 00000000..ee8508bc
--- /dev/null
+++ b/flask/__init__.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+"""
+ flask
+ ~~~~~
+
+ A microframework based on Werkzeug. It's extensively documented
+ and follows best practice patterns.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+# utilities we import from Werkzeug and Jinja2 that are unused
+# in the module but are exported as public interface.
+from werkzeug import abort, redirect
+from jinja2 import Markup, escape
+
+from .app import Flask, Request, Response
+from .config import Config
+from .helpers import url_for, jsonify, json_available, flash, \
+ send_file, send_from_directory, get_flashed_messages, \
+ get_template_attribute, make_response
+from .globals import current_app, g, request, session, _request_ctx_stack
+from .module import Module
+from .templating import render_template, render_template_string
+from .session import Session
+
+# the signals
+from .signals import signals_available, template_rendered, request_started, \
+ request_finished, got_request_exception
+
+# only import json if it's available
+if json_available:
+ from .helpers import json
diff --git a/flask/app.py b/flask/app.py
new file mode 100644
index 00000000..151525e6
--- /dev/null
+++ b/flask/app.py
@@ -0,0 +1,875 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.app
+ ~~~~~~~~~
+
+ This module implements the central WSGI application object.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+from threading import Lock
+from datetime import timedelta, datetime
+from itertools import chain
+
+from jinja2 import Environment
+
+from werkzeug import ImmutableDict
+from werkzeug.routing import Map, Rule
+from werkzeug.exceptions import HTTPException, InternalServerError, NotFound
+
+from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
+ _tojson_filter, _endpoint_from_view_func
+from .wrappers import Request, Response
+from .config import ConfigAttribute, Config
+from .ctx import _RequestContext
+from .globals import _request_ctx_stack, request
+from .session import Session, _NullSession
+from .module import _ModuleSetupState
+from .templating import _DispatchingJinjaLoader, \
+ _default_template_ctx_processor
+from .signals import request_started, request_finished, got_request_exception
+
+# a lock used for logger initialization
+_logger_lock = Lock()
+
+
+class Flask(_PackageBoundObject):
+ """The flask object implements a WSGI application and acts as the central
+ object. It is passed the name of the module or package of the
+ application. Once it is created it will act as a central registry for
+ the view functions, the URL rules, template configuration and much more.
+
+ The name of the package is used to resolve resources from inside the
+ package or the folder the module is contained in depending on if the
+ package parameter resolves to an actual python package (a folder with
+ an `__init__.py` file inside) or a standard module (just a `.py` file).
+
+ For more information about resource loading, see :func:`open_resource`.
+
+ Usually you create a :class:`Flask` instance in your main module or
+ in the `__init__.py` file of your package like this::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ .. admonition:: About the First Parameter
+
+ The idea of the first parameter is to give Flask an idea what
+ belongs to your application. This name is used to find resources
+ on the file system, can be used by extensions to improve debugging
+ information and a lot more.
+
+ So it's important what you provide there. If you are using a single
+ module, `__name__` is always the correct value. If you however are
+ using a package, it's usually recommended to hardcode the name of
+ your package there.
+
+ For example if your application is defined in `yourapplication/app.py`
+ you should create it with one of the two versions below::
+
+ app = Flask('yourapplication')
+ app = Flask(__name__.split('.')[0])
+
+ Why is that? The application will work even with `__name__`, thanks
+ to how resources are looked up. However it will make debugging more
+ painful. Certain extensions can make assumptions based on the
+ import name of your application. For example the Flask-SQLAlchemy
+ extension will look for the code in your application that triggered
+ an SQL query in debug mode. If the import name is not properly set
+ up, that debugging information is lost. (For example it would only
+ pick up SQL queries in `yourapplicaiton.app` and not
+ `yourapplication.views.frontend`)
+
+ .. versionadded:: 0.5
+ The `static_path` parameter was added.
+
+ :param import_name: the name of the application package
+ :param static_path: can be used to specify a different path for the
+ static files on the web. Defaults to ``/static``.
+ This does not affect the folder the files are served
+ *from*.
+ """
+
+ #: The class that is used for request objects. See :class:`~flask.Request`
+ #: for more information.
+ request_class = Request
+
+ #: The class that is used for response objects. See
+ #: :class:`~flask.Response` for more information.
+ response_class = Response
+
+ #: Path for the static files. If you don't want to use static files
+ #: you can set this value to `None` in which case no URL rule is added
+ #: and the development server will no longer serve any static files.
+ #:
+ #: This is the default used for application and modules unless a
+ #: different value is passed to the constructor.
+ static_path = '/static'
+
+ #: The debug flag. Set this to `True` to enable debugging of the
+ #: application. In debug mode the debugger will kick in when an unhandled
+ #: exception ocurrs and the integrated server will automatically reload
+ #: the application if changes in the code are detected.
+ #:
+ #: This attribute can also be configured from the config with the `DEBUG`
+ #: configuration key. Defaults to `False`.
+ debug = ConfigAttribute('DEBUG')
+
+ #: The testing flask. Set this to `True` to enable the test mode of
+ #: Flask extensions (and in the future probably also Flask itself).
+ #: For example this might activate unittest helpers that have an
+ #: additional runtime cost which should not be enabled by default.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `TESTING` configuration key. Defaults to `False`.
+ testing = ConfigAttribute('TESTING')
+
+ #: If a secret key is set, cryptographic components can use this to
+ #: sign cookies and other things. Set this to a complex random value
+ #: when you want to use the secure cookie for instance.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SECRET_KEY` configuration key. Defaults to `None`.
+ secret_key = ConfigAttribute('SECRET_KEY')
+
+ #: The secure cookie uses this for the name of the session cookie.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'``
+ session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
+
+ #: 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.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to
+ #: ``timedelta(days=31)``
+ permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME')
+
+ #: Enable this if you want to use the X-Sendfile feature. Keep in
+ #: mind that the server has to support this. This only affects files
+ #: sent with the :func:`send_file` method.
+ #:
+ #: .. versionadded:: 0.2
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `USE_X_SENDFILE` configuration key. Defaults to `False`.
+ use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
+
+ #: The name of the logger to use. By default the logger name is the
+ #: package name passed to the constructor.
+ #:
+ #: .. versionadded:: 0.4
+ logger_name = ConfigAttribute('LOGGER_NAME')
+
+ #: The logging format used for the debug logger. This is only used when
+ #: the application is in debug mode, otherwise the attached logging
+ #: handler does the formatting.
+ #:
+ #: .. versionadded:: 0.3
+ debug_log_format = (
+ '-' * 80 + '\n' +
+ '%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' +
+ '%(message)s\n' +
+ '-' * 80
+ )
+
+ #: Options that are passed directly to the Jinja2 environment.
+ jinja_options = ImmutableDict(
+ extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
+ )
+
+ #: Default configuration parameters.
+ default_config = ImmutableDict({
+ 'DEBUG': False,
+ 'TESTING': False,
+ 'SECRET_KEY': None,
+ 'SESSION_COOKIE_NAME': 'session',
+ 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
+ 'USE_X_SENDFILE': False,
+ 'LOGGER_NAME': None,
+ 'SERVER_NAME': None,
+ 'MAX_CONTENT_LENGTH': None
+ })
+
+ def __init__(self, import_name, static_path=None):
+ _PackageBoundObject.__init__(self, import_name)
+ if static_path is not None:
+ self.static_path = static_path
+
+ #: The configuration dictionary as :class:`Config`. This behaves
+ #: exactly like a regular dictionary but supports additional methods
+ #: to load a config from files.
+ self.config = Config(self.root_path, self.default_config)
+
+ #: Prepare the deferred setup of the logger.
+ self._logger = None
+ self.logger_name = self.import_name
+
+ #: A dictionary of all view functions registered. The keys will
+ #: be function names which are also used to generate URLs and
+ #: the values are the function objects themselves.
+ #: to register a view function, use the :meth:`route` decorator.
+ self.view_functions = {}
+
+ #: A dictionary of all registered error handlers. The key is
+ #: be the error code as integer, the value the function that
+ #: should handle that error.
+ #: To register a error handler, use the :meth:`errorhandler`
+ #: decorator.
+ self.error_handlers = {}
+
+ #: A dictionary with lists of functions that should be called at the
+ #: beginning of the request. The key of the dictionary is the name of
+ #: the module this function is active for, `None` for all requests.
+ #: This can for example be used to open database connections or
+ #: getting hold of the currently logged in user. To register a
+ #: function here, use the :meth:`before_request` decorator.
+ self.before_request_funcs = {}
+
+ #: A dictionary with lists of functions that should be called after
+ #: each request. The key of the dictionary is the name of the module
+ #: this function is active for, `None` for all requests. This can for
+ #: example be used to open database connections or getting hold of the
+ #: currently logged in user. To register a function here, use the
+ #: :meth:`before_request` decorator.
+ self.after_request_funcs = {}
+
+ #: A dictionary with list of functions that are called without argument
+ #: to populate the template context. They key of the dictionary is the
+ #: name of the module this function is active for, `None` for all
+ #: requests. Each returns a dictionary that the template context is
+ #: updated with. To register a function here, use the
+ #: :meth:`context_processor` decorator.
+ self.template_context_processors = {
+ None: [_default_template_ctx_processor]
+ }
+
+ #: all the loaded modules in a dictionary by name.
+ #:
+ #: .. versionadded:: 0.5
+ self.modules = {}
+
+ #: The :class:`~werkzeug.routing.Map` for this instance. You can use
+ #: this to change the routing converters after the class was created
+ #: but before any routes are connected. Example::
+ #:
+ #: from werkzeug import BaseConverter
+ #:
+ #: class ListConverter(BaseConverter):
+ #: def to_python(self, value):
+ #: return value.split(',')
+ #: def to_url(self, values):
+ #: return ','.join(BaseConverter.to_url(value)
+ #: for value in values)
+ #:
+ #: app = Flask(__name__)
+ #: app.url_map.converters['list'] = ListConverter
+ self.url_map = Map()
+
+ # register the static folder for the application. Do that even
+ # if the folder does not exist. First of all it might be created
+ # while the server is running (usually happens during development)
+ # but also because google appengine stores static files somewhere
+ # else when mapped with the .yml file.
+ self.add_url_rule(self.static_path + '/',
+ endpoint='static',
+ view_func=self.send_static_file)
+
+ #: The Jinja2 environment. It is created from the
+ #: :attr:`jinja_options`.
+ self.jinja_env = self.create_jinja_environment()
+ self.init_jinja_globals()
+
+ @property
+ def logger(self):
+ """A :class:`logging.Logger` object for this application. The
+ default configuration is to log to stderr if the application is
+ in debug mode. This logger can be used to (surprise) log messages.
+ Here some examples::
+
+ app.logger.debug('A value for debugging')
+ app.logger.warning('A warning ocurred (%d apples)', 42)
+ app.logger.error('An error occoured')
+
+ .. versionadded:: 0.3
+ """
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ with _logger_lock:
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ from flask.logging import create_logger
+ self._logger = rv = create_logger(self)
+ return rv
+
+ def create_jinja_environment(self):
+ """Creates the Jinja2 environment based on :attr:`jinja_options`
+ and :meth:`select_jinja_autoescape`.
+
+ .. versionadded:: 0.5
+ """
+ options = dict(self.jinja_options)
+ if 'autoescape' not in options:
+ options['autoescape'] = self.select_jinja_autoescape
+ return Environment(loader=_DispatchingJinjaLoader(self), **options)
+
+ def init_jinja_globals(self):
+ """Called directly after the environment was created to inject
+ some defaults (like `url_for`, `get_flashed_messages` and the
+ `tojson` filter.
+
+ .. versionadded:: 0.5
+ """
+ self.jinja_env.globals.update(
+ url_for=url_for,
+ get_flashed_messages=get_flashed_messages
+ )
+ self.jinja_env.filters['tojson'] = _tojson_filter
+
+ def select_jinja_autoescape(self, filename):
+ """Returns `True` if autoescaping should be active for the given
+ template name.
+
+ .. versionadded:: 0.5
+ """
+ if filename is None:
+ return False
+ return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))
+
+ def update_template_context(self, context):
+ """Update the template context with some commonly used variables.
+ This injects request, session, config and g into the template
+ context as well as everything template context processors want
+ to inject. Note that the as of Flask 0.6, the original values
+ in the context will not be overriden if a context processor
+ decides to return a value with the same key.
+
+ :param context: the context as a dictionary that is updated in place
+ to add extra variables.
+ """
+ funcs = self.template_context_processors[None]
+ mod = _request_ctx_stack.top.request.module
+ if mod is not None and mod in self.template_context_processors:
+ funcs = chain(funcs, self.template_context_processors[mod])
+ orig_ctx = context.copy()
+ for func in funcs:
+ context.update(func())
+ # make sure the original values win. This makes it possible to
+ # easier add new variables in context processors without breaking
+ # existing views.
+ context.update(orig_ctx)
+
+ def run(self, host='127.0.0.1', port=5000, **options):
+ """Runs the application on a local development server. If the
+ :attr:`debug` flag is set the server will automatically reload
+ for code changes and show a debugger in case an exception happened.
+
+ If you want to run the application in debug mode, but disable the
+ code execution on the interactive debugger, you can pass
+ ``use_evalex=False`` as parameter. This will keep the debugger's
+ traceback screen active, but disable code execution.
+
+ .. admonition:: Keep in Mind
+
+ Flask will suppress any server error with a generic error page
+ unless it is in debug mode. As such to enable just the
+ interactive debugger without the code reloading, you have to
+ invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
+ Setting ``use_debugger`` to `True` without being in debug mode
+ won't catch any exceptions because there won't be any to
+ catch.
+
+ :param host: the hostname to listen on. set this to ``'0.0.0.0'``
+ to have the server available externally as well.
+ :param port: the port of the webserver
+ :param options: the options to be forwarded to the underlying
+ Werkzeug server. See :func:`werkzeug.run_simple`
+ for more information.
+ """
+ from werkzeug import run_simple
+ if 'debug' in options:
+ self.debug = options.pop('debug')
+ options.setdefault('use_reloader', self.debug)
+ options.setdefault('use_debugger', self.debug)
+ return run_simple(host, port, self, **options)
+
+ def test_client(self):
+ """Creates a test client for this application. For information
+ about unit testing head over to :ref:`testing`.
+
+ The test client can be used in a `with` block to defer the closing down
+ of the context until the end of the `with` block. This is useful if
+ you want to access the context locals for testing::
+
+ with app.test_client() as c:
+ rv = c.get('/?vodka=42')
+ assert request.args['vodka'] == '42'
+
+ .. versionchanged:: 0.4
+ added support for `with` block usage for the client.
+ """
+ from flask.testing import FlaskClient
+ return FlaskClient(self, self.response_class, use_cookies=True)
+
+ def open_session(self, request):
+ """Creates or opens a new session. Default implementation stores all
+ session data in a signed cookie. This requires that the
+ :attr:`secret_key` is set.
+
+ :param request: an instance of :attr:`request_class`.
+ """
+ key = self.secret_key
+ if key is not None:
+ 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
+ implementation, check :meth:`open_session`.
+
+ :param session: the session to be saved (a
+ :class:`~werkzeug.contrib.securecookie.SecureCookie`
+ object)
+ :param response: an instance of :attr:`response_class`
+ """
+ expires = domain = None
+ if session.permanent:
+ expires = datetime.utcnow() + self.permanent_session_lifetime
+ if self.config['SERVER_NAME'] is not None:
+ domain = '.' + self.config['SERVER_NAME']
+ session.save_cookie(response, self.session_cookie_name,
+ expires=expires, httponly=True, domain=domain)
+
+ def register_module(self, module, **options):
+ """Registers a module with this application. The keyword argument
+ of this function are the same as the ones for the constructor of the
+ :class:`Module` class and will override the values of the module if
+ provided.
+ """
+ options.setdefault('url_prefix', module.url_prefix)
+ options.setdefault('subdomain', module.subdomain)
+ state = _ModuleSetupState(self, **options)
+ for func in module._register_events:
+ func(state)
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Connects a URL rule. Works exactly like the :meth:`route`
+ decorator. If a view_func is provided it will be registered with the
+ endpoint.
+
+ Basically this example::
+
+ @app.route('/')
+ def index():
+ pass
+
+ Is equivalent to the following::
+
+ def index():
+ pass
+ app.add_url_rule('/', 'index', index)
+
+ If the view_func is not provided you will need to connect the endpoint
+ to a view function like so::
+
+ app.view_functions['index'] = index
+
+ .. versionchanged:: 0.2
+ `view_func` parameter added.
+
+ .. versionchanged:: 0.6
+ `OPTIONS` is added automatically as method.
+
+ :param rule: the URL rule as string
+ :param endpoint: the endpoint for the registered URL rule. Flask
+ itself assumes the name of the view function as
+ endpoint
+ :param view_func: the function to call when serving a request to the
+ provided endpoint
+ :param options: the options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object. A change
+ to Werkzeug is handling of method options. methods
+ is a list of methods this rule should be limited
+ to (`GET`, `POST` etc.). By default a rule
+ just listens for `GET` (and implicitly `HEAD`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ """
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ options['endpoint'] = endpoint
+ methods = options.pop('methods', ('GET',))
+ provide_automatic_options = False
+ if 'OPTIONS' not in methods:
+ methods = tuple(methods) + ('OPTIONS',)
+ provide_automatic_options = True
+ rule = Rule(rule, methods=methods, **options)
+ rule.provide_automatic_options = provide_automatic_options
+ self.url_map.add(rule)
+ if view_func is not None:
+ self.view_functions[endpoint] = view_func
+
+ def route(self, rule, **options):
+ """A decorator that is used to register a view function for a
+ given URL rule. Example::
+
+ @app.route('/')
+ def index():
+ return 'Hello World'
+
+ Variables parts in the route can be specified with angular
+ brackets (``/user/``). By default a variable part
+ in the URL accepts any string without a slash however a different
+ converter can be specified as well by using ````.
+
+ Variable parts are passed to the view function as keyword
+ arguments.
+
+ The following converters are possible:
+
+ =========== ===========================================
+ `int` accepts integers
+ `float` like `int` but for floating point values
+ `path` like the default but also accepts slashes
+ =========== ===========================================
+
+ Here some examples::
+
+ @app.route('/')
+ def index():
+ pass
+
+ @app.route('/')
+ def show_user(username):
+ pass
+
+ @app.route('/post/')
+ def show_post(post_id):
+ pass
+
+ An important detail to keep in mind is how Flask deals with trailing
+ slashes. The idea is to keep each URL unique so the following rules
+ apply:
+
+ 1. If a rule ends with a slash and is requested without a slash
+ by the user, the user is automatically redirected to the same
+ page with a trailing slash attached.
+ 2. If a rule does not end with a trailing slash and the user request
+ the page with a trailing slash, a 404 not found is raised.
+
+ This is consistent with how web servers deal with static files. This
+ also makes it possible to use relative link targets safely.
+
+ The :meth:`route` decorator accepts a couple of other arguments
+ as well:
+
+ :param rule: the URL rule as string
+ :param methods: a list of methods this rule should be limited
+ to (`GET`, `POST` etc.). By default a rule
+ just listens for `GET` (and implicitly `HEAD`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ :param subdomain: specifies the rule for the subdomain in case
+ subdomain matching is in use.
+ :param strict_slashes: can be used to disable the strict slashes
+ setting for this rule. See above.
+ :param options: other options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object.
+ """
+ def decorator(f):
+ self.add_url_rule(rule, None, f, **options)
+ return f
+ return decorator
+
+ def errorhandler(self, code):
+ """A decorator that is used to register a function give a given
+ error code. Example::
+
+ @app.errorhandler(404)
+ def page_not_found(error):
+ return 'This page does not exist', 404
+
+ You can also register a function as error handler without using
+ the :meth:`errorhandler` decorator. The following example is
+ equivalent to the one above::
+
+ def page_not_found(error):
+ return 'This page does not exist', 404
+ app.error_handlers[404] = page_not_found
+
+ :param code: the code as integer for the handler
+ """
+ def decorator(f):
+ self.error_handlers[code] = f
+ return f
+ return decorator
+
+ def template_filter(self, name=None):
+ """A decorator that is used to register custom template filter.
+ You can specify a name for the filter, otherwise the function
+ name will be used. Example::
+
+ @app.template_filter()
+ def reverse(s):
+ return s[::-1]
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.jinja_env.filters[name or f.__name__] = f
+ return f
+ return decorator
+
+ def before_request(self, f):
+ """Registers a function to run before each request."""
+ self.before_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ def after_request(self, f):
+ """Register a function to be run after each request."""
+ self.after_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ def context_processor(self, f):
+ """Registers a template context processor function."""
+ self.template_context_processors[None].append(f)
+ return f
+
+ def handle_http_exception(self, e):
+ """Handles an HTTP exception. By default this will invoke the
+ registered error handlers and fall back to returning the
+ exception as response.
+
+ .. versionadded: 0.3
+ """
+ handler = self.error_handlers.get(e.code)
+ if handler is None:
+ return e
+ return handler(e)
+
+ def handle_exception(self, e):
+ """Default exception handling that kicks in when an exception
+ occours that is not catched. In debug mode the exception will
+ be re-raised immediately, otherwise it is logged and the handler
+ for a 500 internal server error is used. If no such handler
+ exists, a default 500 internal server error message is displayed.
+
+ .. versionadded: 0.3
+ """
+ got_request_exception.send(self, exception=e)
+ handler = self.error_handlers.get(500)
+ if self.debug:
+ raise
+ self.logger.exception('Exception on %s [%s]' % (
+ request.path,
+ request.method
+ ))
+ if handler is None:
+ return InternalServerError()
+ return handler(e)
+
+ def dispatch_request(self):
+ """Does the request dispatching. Matches the URL and returns the
+ return value of the view or error handler. This does not have to
+ be a response object. In order to convert the return value to a
+ proper response object, call :func:`make_response`.
+ """
+ req = _request_ctx_stack.top.request
+ try:
+ if req.routing_exception is not None:
+ raise req.routing_exception
+ rule = req.url_rule
+ # if we provide automatic options for this URL and the
+ # request came with the OPTIONS method, reply automatically
+ if rule.provide_automatic_options and req.method == 'OPTIONS':
+ rv = self.response_class()
+ rv.allow.update(rule.methods)
+ return rv
+ # otherwise dispatch to the handler for that endpoint
+ return self.view_functions[rule.endpoint](**req.view_args)
+ except HTTPException, e:
+ return self.handle_http_exception(e)
+
+ def make_response(self, rv):
+ """Converts the return value from a view function to a real
+ response object that is an instance of :attr:`response_class`.
+
+ The following types are allowed for `rv`:
+
+ .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
+
+ ======================= ===========================================
+ :attr:`response_class` the object is returned unchanged
+ :class:`str` a response object is created with the
+ string as body
+ :class:`unicode` a response object is created with the
+ string encoded to utf-8 as body
+ :class:`tuple` the response object is created with the
+ contents of the tuple as arguments
+ a WSGI function the function is called as WSGI application
+ and buffered as response object
+ ======================= ===========================================
+
+ :param rv: the return value from the view function
+ """
+ if rv is None:
+ raise ValueError('View function did not return a response')
+ if isinstance(rv, self.response_class):
+ return rv
+ if isinstance(rv, basestring):
+ return self.response_class(rv)
+ if isinstance(rv, tuple):
+ return self.response_class(*rv)
+ return self.response_class.force_type(rv, request.environ)
+
+ def create_url_adapter(self, request):
+ """Creates a URL adapter for the given request. The URL adapter
+ is created at a point where the request context is not yet set up
+ so the request is passed explicitly.
+
+ .. versionadded:: 0.6
+ """
+ return self.url_map.bind_to_environ(request.environ,
+ server_name=self.config['SERVER_NAME'])
+
+ def preprocess_request(self):
+ """Called before the actual request dispatching and will
+ call every as :meth:`before_request` decorated function.
+ If any of these function returns a value it's handled as
+ if it was the return value from the view and further
+ request handling is stopped.
+ """
+ funcs = self.before_request_funcs.get(None, ())
+ mod = request.module
+ if mod and mod in self.before_request_funcs:
+ funcs = chain(funcs, self.before_request_funcs[mod])
+ for func in funcs:
+ rv = func()
+ if rv is not None:
+ return rv
+
+ def process_response(self, response):
+ """Can be overridden in order to modify the response object
+ before it's sent to the WSGI server. By default this will
+ call all the :meth:`after_request` decorated functions.
+
+ .. versionchanged:: 0.5
+ As of Flask 0.5 the functions registered for after request
+ execution are called in reverse order of registration.
+
+ :param response: a :attr:`response_class` object.
+ :return: a new response object or the same, has to be an
+ instance of :attr:`response_class`.
+ """
+ ctx = _request_ctx_stack.top
+ mod = ctx.request.module
+ if not isinstance(ctx.session, _NullSession):
+ self.save_session(ctx.session, response)
+ funcs = ()
+ if mod and mod in self.after_request_funcs:
+ funcs = reversed(self.after_request_funcs[mod])
+ if None in self.after_request_funcs:
+ funcs = chain(funcs, reversed(self.after_request_funcs[None]))
+ for handler in funcs:
+ response = handler(response)
+ return response
+
+ 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)
+
+ The object returned can also be used without the `with` statement
+ which is useful for working in the shell. The example above is
+ doing exactly the same as this code::
+
+ ctx = app.request_context(environ)
+ ctx.push()
+ try:
+ do_something_with(request)
+ finally:
+ ctx.pop()
+
+ The big advantage of this approach is that you can use it without
+ the try/finally statement in a shell for interactive testing:
+
+ >>> ctx = app.test_request_context()
+ >>> ctx.bind()
+ >>> request.path
+ u'/'
+ >>> ctx.unbind()
+
+ .. versionchanged:: 0.3
+ Added support for non-with statement usage and `with` statement
+ is now passed the ctx object.
+
+ :param environ: a WSGI environment
+ """
+ return _RequestContext(self, environ)
+
+ def test_request_context(self, *args, **kwargs):
+ """Creates a WSGI environment from the given values (see
+ :func:`werkzeug.create_environ` for more information, this
+ function accepts the same arguments).
+ """
+ from werkzeug import create_environ
+ return self.request_context(create_environ(*args, **kwargs))
+
+ def wsgi_app(self, environ, start_response):
+ """The actual WSGI application. This is not implemented in
+ `__call__` so that middlewares can be applied without losing a
+ reference to the class. So instead of doing this::
+
+ app = MyMiddleware(app)
+
+ It's a better idea to do this instead::
+
+ app.wsgi_app = MyMiddleware(app.wsgi_app)
+
+ Then you still have the original application object around and
+ can continue to call methods on it.
+
+ .. versionchanged:: 0.4
+ The :meth:`after_request` functions are now called even if an
+ error handler took over request processing. This ensures that
+ even if an exception happens database have the chance to
+ properly close the connection.
+
+ :param environ: a WSGI environment
+ :param start_response: a callable accepting a status code,
+ a list of headers and an optional
+ exception context to start the response
+ """
+ with self.request_context(environ):
+ try:
+ request_started.send(self)
+ rv = self.preprocess_request()
+ if rv is None:
+ rv = self.dispatch_request()
+ response = self.make_response(rv)
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ try:
+ response = self.process_response(response)
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ request_finished.send(self, response=response)
+ return response(environ, start_response)
+
+ def __call__(self, environ, start_response):
+ """Shortcut for :attr:`wsgi_app`."""
+ return self.wsgi_app(environ, start_response)
diff --git a/flask/config.py b/flask/config.py
new file mode 100644
index 00000000..cf9ad541
--- /dev/null
+++ b/flask/config.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.config
+ ~~~~~~~~~~~~
+
+ Implements the configuration related objects.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import sys
+
+from werkzeug import import_string
+
+
+class ConfigAttribute(object):
+ """Makes an attribute forward to the config"""
+
+ def __init__(self, name):
+ self.__name__ = name
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return obj.config[self.__name__]
+
+ def __set__(self, obj, value):
+ obj.config[self.__name__] = value
+
+
+class Config(dict):
+ """Works exactly like a dict but provides ways to fill it from files
+ or special dictionaries. There are two common patterns to populate the
+ config.
+
+ Either you can fill the config from a config file::
+
+ app.config.from_pyfile('yourconfig.cfg')
+
+ Or alternatively you can define the configuration options in the
+ module that calls :meth:`from_object` or provide an import path to
+ a module that should be loaded. It is also possible to tell it to
+ use the same module and with that provide the configuration values
+ just before the call::
+
+ DEBUG = True
+ SECRET_KEY = 'development key'
+ app.config.from_object(__name__)
+
+ In both cases (loading from any Python file or loading from modules),
+ only uppercase keys are added to the config. This makes it possible to use
+ lowercase values in the config file for temporary values that are not added
+ to the config or to define the config keys in the same file that implements
+ the application.
+
+ Probably the most interesting way to load configurations is from an
+ environment variable pointing to a file::
+
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+ In this case before launching the application you have to set this
+ environment variable to the file you want to use. On Linux and OS X
+ use the export statement::
+
+ export YOURAPPLICATION_SETTINGS='/path/to/config/file'
+
+ On windows use `set` instead.
+
+ :param root_path: path to which files are read relative from. When the
+ config object is created by the application, this is
+ the application's :attr:`~flask.Flask.root_path`.
+ :param defaults: an optional dictionary of default values
+ """
+
+ def __init__(self, root_path, defaults=None):
+ dict.__init__(self, defaults or {})
+ self.root_path = root_path
+
+ def from_envvar(self, variable_name, silent=False):
+ """Loads a configuration from an environment variable pointing to
+ a configuration file. This basically is just a shortcut with nicer
+ error messages for this line of code::
+
+ app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
+
+ :param variable_name: name of the environment variable
+ :param silent: set to `True` if you want silent failing for missing
+ files.
+ :return: bool. `True` if able to load config, `False` otherwise.
+ """
+ rv = os.environ.get(variable_name)
+ if not rv:
+ if silent:
+ return False
+ raise RuntimeError('The environment variable %r is not set '
+ 'and as such configuration could not be '
+ 'loaded. Set this variable and make it '
+ 'point to a configuration file' %
+ variable_name)
+ self.from_pyfile(rv)
+ return True
+
+ def from_pyfile(self, filename):
+ """Updates the values in the config from a Python file. This function
+ behaves as if the file was imported as module with the
+ :meth:`from_object` function.
+
+ :param filename: the filename of the config. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ """
+ filename = os.path.join(self.root_path, filename)
+ d = type(sys)('config')
+ d.__file__ = filename
+ execfile(filename, d.__dict__)
+ self.from_object(d)
+
+ def from_object(self, obj):
+ """Updates the values from the given object. An object can be of one
+ of the following two types:
+
+ - a string: in this case the object with that name will be imported
+ - an actual object reference: that object is used directly
+
+ Objects are usually either modules or classes.
+
+ Just the uppercase variables in that object are stored in the config
+ after lowercasing. Example usage::
+
+ app.config.from_object('yourapplication.default_config')
+ from yourapplication import default_config
+ app.config.from_object(default_config)
+
+ You should not use this function to load the actual configuration but
+ rather configuration defaults. The actual config should be loaded
+ with :meth:`from_pyfile` and ideally from a location not within the
+ package because the package might be installed system wide.
+
+ :param obj: an import name or object
+ """
+ if isinstance(obj, basestring):
+ obj = import_string(obj)
+ for key in dir(obj):
+ if key.isupper():
+ self[key] = getattr(obj, key)
+
+ def __repr__(self):
+ return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
diff --git a/flask/ctx.py b/flask/ctx.py
new file mode 100644
index 00000000..1b17086c
--- /dev/null
+++ b/flask/ctx.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ctx
+ ~~~~~~~~~
+
+ Implements the objects required to keep the context.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.exceptions import HTTPException
+
+from .globals import _request_ctx_stack
+from .session import _NullSession
+
+
+class _RequestGlobals(object):
+ pass
+
+
+class _RequestContext(object):
+ """The request context contains all request relevant information. It is
+ created at the beginning of the request and pushed to the
+ `_request_ctx_stack` and removed at the end of it. It will create the
+ URL adapter and request object for the WSGI environment provided.
+ """
+
+ def __init__(self, app, environ):
+ self.app = app
+ self.request = app.request_class(environ)
+ self.url_adapter = app.create_url_adapter(self.request)
+ self.session = app.open_session(self.request)
+ if self.session is None:
+ self.session = _NullSession()
+ self.g = _RequestGlobals()
+ self.flashes = None
+
+ try:
+ url_rule, self.request.view_args = \
+ self.url_adapter.match(return_rule=True)
+ self.request.url_rule = url_rule
+ except HTTPException, e:
+ self.request.routing_exception = e
+
+ def push(self):
+ """Binds the request context."""
+ _request_ctx_stack.push(self)
+
+ def pop(self):
+ """Pops the request context."""
+ _request_ctx_stack.pop()
+
+ def __enter__(self):
+ self.push()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ # do not pop the request stack if we are in debug mode and an
+ # exception happened. This will allow the debugger to still
+ # access the request object in the interactive shell. Furthermore
+ # the context can be force kept alive for the test client.
+ # See flask.testing for how this works.
+ if not self.request.environ.get('flask._preserve_context') and \
+ (tb is None or not self.app.debug):
+ self.pop()
diff --git a/flask/globals.py b/flask/globals.py
new file mode 100644
index 00000000..aac46555
--- /dev/null
+++ b/flask/globals.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.globals
+ ~~~~~~~~~~~~~
+
+ Defines all the global objects that are proxies to the current
+ active context.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug import LocalStack, LocalProxy
+
+# context locals
+_request_ctx_stack = LocalStack()
+current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
+request = LocalProxy(lambda: _request_ctx_stack.top.request)
+session = LocalProxy(lambda: _request_ctx_stack.top.session)
+g = LocalProxy(lambda: _request_ctx_stack.top.g)
diff --git a/flask/helpers.py b/flask/helpers.py
new file mode 100644
index 00000000..39214a10
--- /dev/null
+++ b/flask/helpers.py
@@ -0,0 +1,464 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.helpers
+ ~~~~~~~~~~~~~
+
+ Implements various helpers.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import sys
+import posixpath
+import mimetypes
+from time import time
+from zlib import adler32
+from functools import wraps
+
+# try to load the best simplejson implementation available. If JSON
+# is not installed, we add a failing class.
+json_available = True
+json = None
+try:
+ import simplejson as json
+except ImportError:
+ try:
+ import json
+ except ImportError:
+ try:
+ # Google Appengine offers simplejson via django
+ from django.utils import simplejson as json
+ except ImportError:
+ json_available = False
+
+
+from werkzeug import Headers, wrap_file, is_resource_modified, cached_property
+from werkzeug.exceptions import NotFound
+
+from jinja2 import FileSystemLoader
+
+from .globals import session, _request_ctx_stack, current_app, request
+
+
+def _assert_have_json():
+ """Helper function that fails if JSON is unavailable."""
+ if not json_available:
+ raise RuntimeError('simplejson not installed')
+
+# figure out if simplejson escapes slashes. This behaviour was changed
+# from one version to another without reason.
+if not json_available or '\\/' not in json.dumps('/'):
+
+ def _tojson_filter(*args, **kwargs):
+ if __debug__:
+ _assert_have_json()
+ return json.dumps(*args, **kwargs).replace('/', '\\/')
+else:
+ _tojson_filter = json.dumps
+
+
+def _endpoint_from_view_func(view_func):
+ """Internal helper that returns the default endpoint for a given
+ function. This always is the function name.
+ """
+ assert view_func is not None, 'expected view func if endpoint ' \
+ 'is not provided.'
+ return view_func.__name__
+
+
+def jsonify(*args, **kwargs):
+ """Creates a :class:`~flask.Response` with the JSON representation of
+ the given arguments with an `application/json` mimetype. The arguments
+ to this function are the same as to the :class:`dict` constructor.
+
+ Example usage::
+
+ @app.route('/_get_current_user')
+ def get_current_user():
+ return jsonify(username=g.user.username,
+ email=g.user.email,
+ id=g.user.id)
+
+ This will send a JSON response like this to the browser::
+
+ {
+ "username": "admin",
+ "email": "admin@localhost",
+ "id": 42
+ }
+
+ This requires Python 2.6 or an installed version of simplejson. For
+ security reasons only objects are supported toplevel. For more
+ information about this, have a look at :ref:`json-security`.
+
+ .. versionadded:: 0.2
+ """
+ if __debug__:
+ _assert_have_json()
+ return current_app.response_class(json.dumps(dict(*args, **kwargs),
+ indent=None if request.is_xhr else 2), mimetype='application/json')
+
+
+def make_response(*args):
+ """Sometimes it is necessary to set additional headers in a view. Because
+ views do not have to return response objects but can return a value that
+ is converted into a response object by Flask itself, it becomes tricky to
+ add headers to it. This function can be called instead of using a return
+ and you will get a response object which you can use to attach headers.
+
+ If view looked like this and you want to add a new header::
+
+ def index():
+ return render_template('index.html', foo=42)
+
+ You can now do something like this::
+
+ def index():
+ response = make_response(render_template('index.html', foo=42))
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+ return response
+
+ This function accepts the very same arguments you can return from a
+ view function. This for example creates a response with a 404 error
+ code::
+
+ response = make_response(render_template('not_found.html'), 404)
+
+ Internally this function does the following things:
+
+ - if no arguments are passed, it creates a new response argument
+ - if one argument is passed, :meth:`flask.Flask.make_response`
+ is invoked with it.
+ - if more than one argument is passed, the arguments are passed
+ to the :meth:`flask.Flask.make_response` function as tuple.
+
+ .. versionadded:: 0.6
+ """
+ if not args:
+ return current_app.response_class()
+ if len(args) == 1:
+ args = args[0]
+ return current_app.make_response(args)
+
+
+def url_for(endpoint, **values):
+ """Generates a URL to the given endpoint with the method provided.
+ The endpoint is relative to the active module if modules are in use.
+
+ Here some examples:
+
+ ==================== ======================= =============================
+ Active Module Target Endpoint Target Function
+ ==================== ======================= =============================
+ `None` ``'index'`` `index` of the application
+ `None` ``'.index'`` `index` of the application
+ ``'admin'`` ``'index'`` `index` of the `admin` module
+ any ``'.index'`` `index` of the application
+ 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 `.
+
+ :param endpoint: the endpoint of the URL (name of the function)
+ :param values: the variable arguments of the URL rule
+ :param _external: if set to `True`, an absolute URL is generated.
+ """
+ ctx = _request_ctx_stack.top
+ if '.' not in endpoint:
+ mod = ctx.request.module
+ if mod is not None:
+ endpoint = mod + '.' + endpoint
+ elif endpoint.startswith('.'):
+ endpoint = endpoint[1:]
+ external = values.pop('_external', False)
+ return ctx.url_adapter.build(endpoint, values, force_external=external)
+
+
+def get_template_attribute(template_name, attribute):
+ """Loads a macro (or variable) a template exports. This can be used to
+ invoke a macro from within Python code. If you for example have a
+ template named `_cider.html` with the following contents:
+
+ .. sourcecode:: html+jinja
+
+ {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
+
+ You can access this from Python code like this::
+
+ hello = get_template_attribute('_cider.html', 'hello')
+ return hello('World')
+
+ .. versionadded:: 0.2
+
+ :param template_name: the name of the template
+ :param attribute: the name of the variable of macro to acccess
+ """
+ return getattr(current_app.jinja_env.get_template(template_name).module,
+ attribute)
+
+
+def flash(message, category='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,
+ the template has to call :func:`get_flashed_messages`.
+
+ .. versionchanged: 0.3
+ `category` parameter added.
+
+ :param message: the message to be flashed.
+ :param category: the category for the message. The following values
+ are recommended: ``'message'`` for any kind of message,
+ ``'error'`` for errors, ``'info'`` for information
+ messages and ``'warning'`` for warnings. However any
+ kind of string can be used as category.
+ """
+ session.setdefault('_flashes', []).append((category, message))
+
+
+def get_flashed_messages(with_categories=False):
+ """Pulls all flashed messages from the session and returns them.
+ Further calls in the same request to the function will return
+ the same messages. By default just the messages are returned,
+ but when `with_categories` is set to `True`, the return value will
+ be a list of tuples in the form ``(category, message)`` instead.
+
+ Example usage:
+
+ .. sourcecode:: html+jinja
+
+ {% for category, msg in get_flashed_messages(with_categories=true) %}
+ {{ msg }}
+ {% endfor %}
+
+ .. versionchanged:: 0.3
+ `with_categories` parameter added.
+
+ :param with_categories: set to `True` to also receive categories.
+ """
+ flashes = _request_ctx_stack.top.flashes
+ if flashes is None:
+ _request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
+ if not with_categories:
+ return [x[1] for x in flashes]
+ return flashes
+
+
+def send_file(filename_or_fp, mimetype=None, as_attachment=False,
+ attachment_filename=None, add_etags=True,
+ cache_timeout=60 * 60 * 12, conditional=False):
+ """Sends the contents of a file to the client. This will use the
+ most efficient method available and configured. By default it will
+ try to use the WSGI server's file_wrapper support. Alternatively
+ you can set the application's :attr:`~Flask.use_x_sendfile` attribute
+ to ``True`` to directly emit an `X-Sendfile` header. This however
+ requires support of the underlying webserver for `X-Sendfile`.
+
+ By default it will try to guess the mimetype for you, but you can
+ also explicitly provide one. For extra security you probably want
+ to sent certain files as attachment (HTML for instance).
+
+ Please never pass filenames to this function from user sources without
+ checking them first. Something like this is usually sufficient to
+ avoid security problems::
+
+ if '..' in filename or filename.startswith('/'):
+ abort(404)
+
+ .. versionadded:: 0.2
+
+ .. versionadded:: 0.5
+ The `add_etags`, `cache_timeout` and `conditional` parameters were
+ added. The default behaviour is now to attach etags.
+
+ :param filename_or_fp: the filename of the file to send. This is
+ relative to the :attr:`~Flask.root_path` if a
+ relative path is specified.
+ Alternatively a file object might be provided
+ in which case `X-Sendfile` might not work and
+ fall back to the traditional method.
+ :param mimetype: the mimetype of the file if provided, otherwise
+ auto detection happens.
+ :param as_attachment: set to `True` if you want to send this file with
+ a ``Content-Disposition: attachment`` header.
+ :param attachment_filename: the filename for the attachment if it
+ differs from the file's filename.
+ :param add_etags: set to `False` to disable attaching of etags.
+ :param conditional: set to `True` to enable conditional responses.
+ :param cache_timeout: the timeout in seconds for the headers.
+ """
+ mtime = None
+ if isinstance(filename_or_fp, basestring):
+ filename = filename_or_fp
+ file = None
+ else:
+ file = filename_or_fp
+ filename = getattr(file, 'name', None)
+ if filename is not None:
+ if not os.path.isabs(filename):
+ filename = os.path.join(current_app.root_path, filename)
+ if mimetype is None and (filename or attachment_filename):
+ mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
+ if mimetype is None:
+ mimetype = 'application/octet-stream'
+
+ headers = Headers()
+ if as_attachment:
+ if attachment_filename is None:
+ if filename is None:
+ raise TypeError('filename unavailable, required for '
+ 'sending as attachment')
+ attachment_filename = os.path.basename(filename)
+ headers.add('Content-Disposition', 'attachment',
+ filename=attachment_filename)
+
+ if current_app.use_x_sendfile and filename:
+ if file is not None:
+ file.close()
+ headers['X-Sendfile'] = filename
+ data = None
+ else:
+ if file is None:
+ file = open(filename, 'rb')
+ mtime = os.path.getmtime(filename)
+ data = wrap_file(request.environ, file)
+
+ rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
+ direct_passthrough=True)
+
+ # if we know the file modification date, we can store it as the
+ # current time to better support conditional requests. Werkzeug
+ # as of 0.6.1 will override this value however in the conditional
+ # response with the current time. This will be fixed in Werkzeug
+ # with a new release, however many WSGI servers will still emit
+ # a separate date header.
+ if mtime is not None:
+ rv.date = int(mtime)
+
+ rv.cache_control.public = True
+ if cache_timeout:
+ rv.cache_control.max_age = cache_timeout
+ rv.expires = int(time() + cache_timeout)
+
+ if add_etags and filename is not None:
+ rv.set_etag('flask-%s-%s-%s' % (
+ os.path.getmtime(filename),
+ os.path.getsize(filename),
+ adler32(filename) & 0xffffffff
+ ))
+ if conditional:
+ rv = rv.make_conditional(request)
+ # make sure we don't send x-sendfile for servers that
+ # ignore the 304 status code for x-sendfile.
+ if rv.status_code == 304:
+ rv.headers.pop('x-sendfile', None)
+ return rv
+
+
+def send_from_directory(directory, filename, **options):
+ """Send a file from a given directory with :func:`send_file`. This
+ is a secure way to quickly expose static files from an upload folder
+ or something similar.
+
+ Example usage::
+
+ @app.route('/uploads/')
+ def download_file(filename):
+ return send_from_directory(app.config['UPLOAD_FOLDER'],
+ filename, as_attachment=True)
+
+ .. admonition:: Sending files and Performance
+
+ It is strongly recommended to activate either `X-Sendfile` support in
+ your webserver or (if no authentication happens) to tell the webserver
+ to serve files for the given path on its own without calling into the
+ web application for improved performance.
+
+ .. versionadded:: 0.5
+
+ :param directory: the directory where all the files are stored.
+ :param filename: the filename relative to that directory to
+ download.
+ :param options: optional keyword arguments that are directly
+ forwarded to :func:`send_file`.
+ """
+ filename = posixpath.normpath(filename)
+ if filename.startswith(('/', '../')):
+ raise NotFound()
+ filename = os.path.join(directory, filename)
+ if not os.path.isfile(filename):
+ raise NotFound()
+ return send_file(filename, conditional=True, **options)
+
+
+def _get_package_path(name):
+ """Returns the path to a package or cwd if that cannot be found."""
+ try:
+ return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
+ except (KeyError, AttributeError):
+ return os.getcwd()
+
+
+class _PackageBoundObject(object):
+
+ def __init__(self, import_name):
+ #: The name of the package or module. Do not change this once
+ #: it was set by the constructor.
+ self.import_name = import_name
+
+ #: Where is the app root located?
+ self.root_path = _get_package_path(self.import_name)
+
+ @property
+ def has_static_folder(self):
+ """This is `True` if the package bound object's container has a
+ folder named ``'static'``.
+
+ .. versionadded:: 0.5
+ """
+ return os.path.isdir(os.path.join(self.root_path, 'static'))
+
+ @cached_property
+ def jinja_loader(self):
+ """The Jinja loader for this package bound object.
+
+ .. versionadded:: 0.5
+ """
+ return FileSystemLoader(os.path.join(self.root_path, 'templates'))
+
+ def send_static_file(self, filename):
+ """Function used internally to send static files from the static
+ folder to the browser.
+
+ .. versionadded:: 0.5
+ """
+ return send_from_directory(os.path.join(self.root_path, 'static'),
+ filename)
+
+ def open_resource(self, resource):
+ """Opens a resource from the application's resource folder. To see
+ how this works, consider the following folder structure::
+
+ /myapplication.py
+ /schemal.sql
+ /static
+ /style.css
+ /templates
+ /layout.html
+ /index.html
+
+ If you want to open the `schema.sql` file you would do the
+ following::
+
+ with app.open_resource('schema.sql') as f:
+ contents = f.read()
+ do_something_with(contents)
+
+ :param resource: the name of the resource. To access resources within
+ subfolders use forward slashes as separator.
+ """
+ return open(os.path.join(self.root_path, resource), 'rb')
diff --git a/flask/logging.py b/flask/logging.py
new file mode 100644
index 00000000..29caadce
--- /dev/null
+++ b/flask/logging.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.logging
+ ~~~~~~~~~~~~~
+
+ Implements the logging support for Flask.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import absolute_import
+
+from logging import getLogger, StreamHandler, Formatter, Logger, DEBUG
+
+
+def create_logger(app):
+ """Creates a logger for the given application. This logger works
+ similar to a regular Python logger but changes the effective logging
+ level based on the application's debug flag. Furthermore this
+ function also removes all attached handlers in case there was a
+ logger with the log name before.
+ """
+
+ class DebugLogger(Logger):
+ def getEffectiveLevel(x):
+ return DEBUG if app.debug else Logger.getEffectiveLevel(x)
+
+ class DebugHandler(StreamHandler):
+ def emit(x, record):
+ StreamHandler.emit(x, record) if app.debug else None
+
+ handler = DebugHandler()
+ handler.setLevel(DEBUG)
+ handler.setFormatter(Formatter(app.debug_log_format))
+ logger = getLogger(app.logger_name)
+ # just in case that was not a new logger, get rid of all the handlers
+ # already attached to it.
+ del logger.handlers[:]
+ logger.__class__ = DebugLogger
+ logger.addHandler(handler)
+ return logger
diff --git a/flask/module.py b/flask/module.py
new file mode 100644
index 00000000..e6e1ee36
--- /dev/null
+++ b/flask/module.py
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.module
+ ~~~~~~~~~~~~
+
+ Implements a class that represents module blueprints.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from .helpers import _PackageBoundObject, _endpoint_from_view_func
+
+
+def _register_module(module, static_path):
+ """Internal helper function that returns a function for recording
+ that registers the `send_static_file` function for the module on
+ the application if necessary. It also registers the module on
+ the application.
+ """
+ def _register(state):
+ state.app.modules[module.name] = module
+ # do not register the rule if the static folder of the
+ # module is the same as the one from the application.
+ if state.app.root_path == module.root_path:
+ return
+ path = static_path
+ if path is None:
+ path = state.app.static_path
+ if state.url_prefix:
+ path = state.url_prefix + path
+ state.app.add_url_rule(path + '/',
+ endpoint='%s.static' % module.name,
+ view_func=module.send_static_file)
+ return _register
+
+
+class _ModuleSetupState(object):
+
+ def __init__(self, app, url_prefix=None, subdomain=None):
+ self.app = app
+ self.url_prefix = url_prefix
+ self.subdomain = subdomain
+
+
+class Module(_PackageBoundObject):
+ """Container object that enables pluggable applications. A module can
+ be used to organize larger applications. They represent blueprints that,
+ in combination with a :class:`Flask` object are used to create a large
+ application.
+
+ A module is like an application bound to an `import_name`. Multiple
+ modules can share the same import names, but in that case a `name` has
+ to be provided to keep them apart. If different import names are used,
+ the rightmost part of the import name is used as name.
+
+ Here an example structure for a larger appliation::
+
+ /myapplication
+ /__init__.py
+ /views
+ /__init__.py
+ /admin.py
+ /frontend.py
+
+ The `myapplication/__init__.py` can look like this::
+
+ from flask import Flask
+ from myapplication.views.admin import admin
+ from myapplication.views.frontend import frontend
+
+ app = Flask(__name__)
+ app.register_module(admin, url_prefix='/admin')
+ app.register_module(frontend)
+
+ And here an example view module (`myapplication/views/admin.py`)::
+
+ from flask import Module
+
+ admin = Module(__name__)
+
+ @admin.route('/')
+ def index():
+ pass
+
+ @admin.route('/login')
+ def login():
+ pass
+
+ For a gentle introduction into modules, checkout the
+ :ref:`working-with-modules` section.
+
+ .. versionadded:: 0.5
+ The `static_path` parameter was added and it's now possible for
+ modules to refer to their own templates and static files. See
+ :ref:`modules-and-resources` for more information.
+
+ .. versionadded:: 0.6
+ The `subdomain` parameter was added.
+
+ :param import_name: the name of the Python package or module
+ implementing this :class:`Module`.
+ :param name: the internal short name for the module. Unless specified
+ the rightmost part of the import name
+ :param url_prefix: an optional string that is used to prefix all the
+ URL rules of this module. This can also be specified
+ when registering the module with the application.
+ :param subdomain: used to set the subdomain setting for URL rules that
+ do not have a subdomain setting set.
+ :param static_path: can be used to specify a different path for the
+ static files on the web. Defaults to ``/static``.
+ This does not affect the folder the files are served
+ *from*.
+ """
+
+ def __init__(self, import_name, name=None, url_prefix=None,
+ static_path=None, subdomain=None):
+ if name is None:
+ assert '.' in import_name, 'name required if package name ' \
+ 'does not point to a submodule'
+ name = import_name.rsplit('.', 1)[1]
+ _PackageBoundObject.__init__(self, import_name)
+ self.name = name
+ self.url_prefix = url_prefix
+ self.subdomain = subdomain
+ self._register_events = [_register_module(self, static_path)]
+
+ def route(self, rule, **options):
+ """Like :meth:`Flask.route` but for a module. The endpoint for the
+ :func:`url_for` function is prefixed with the name of the module.
+ """
+ def decorator(f):
+ self.add_url_rule(rule, f.__name__, f, **options)
+ return f
+ return decorator
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Like :meth:`Flask.add_url_rule` but for a module. The endpoint for
+ the :func:`url_for` function is prefixed with the name of the module.
+
+ .. versionchanged:: 0.6
+ The `endpoint` argument is now optional and will default to the
+ function name to consistent with the function of the same name
+ on the application object.
+ """
+ def register_rule(state):
+ the_rule = rule
+ if state.url_prefix:
+ the_rule = state.url_prefix + rule
+ options.setdefault('subdomain', state.subdomain)
+ the_endpoint = endpoint
+ if the_endpoint is None:
+ the_endpoint = _endpoint_from_view_func(view_func)
+ state.app.add_url_rule(the_rule, '%s.%s' % (self.name,
+ the_endpoint),
+ view_func, **options)
+ self._record(register_rule)
+
+ def before_request(self, f):
+ """Like :meth:`Flask.before_request` but for a module. This function
+ is only executed before each request that is handled by a function of
+ that module.
+ """
+ self._record(lambda s: s.app.before_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def before_app_request(self, f):
+ """Like :meth:`Flask.before_request`. Such a function is executed
+ before each request, even if outside of a module.
+ """
+ self._record(lambda s: s.app.before_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def after_request(self, f):
+ """Like :meth:`Flask.after_request` but for a module. This function
+ is only executed after each request that is handled by a function of
+ that module.
+ """
+ self._record(lambda s: s.app.after_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def after_app_request(self, f):
+ """Like :meth:`Flask.after_request` but for a module. Such a function
+ is executed after each request, even if outside of the module.
+ """
+ self._record(lambda s: s.app.after_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a module. This
+ function is only executed for requests handled by a module.
+ """
+ self._record(lambda s: s.app.template_context_processors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a module. Such a
+ function is executed each request, even if outside of the module.
+ """
+ self._record(lambda s: s.app.template_context_processors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_errorhandler(self, code):
+ """Like :meth:`Flask.errorhandler` but for a module. This
+ handler is used for all requests, even if outside of the module.
+
+ .. versionadded:: 0.4
+ """
+ def decorator(f):
+ self._record(lambda s: s.app.errorhandler(code)(f))
+ return f
+ return decorator
+
+ def _record(self, func):
+ self._register_events.append(func)
diff --git a/flask/session.py b/flask/session.py
new file mode 100644
index 00000000..df2d8773
--- /dev/null
+++ b/flask/session.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.session
+ ~~~~~~~~~~~~~
+
+ Implements cookie based sessions based on Werkzeug's secure cookie
+ system.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.contrib.securecookie import SecureCookie
+
+
+class Session(SecureCookie):
+ """Expands the session with 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.
+ """
+
+ def _fail(self, *args, **kwargs):
+ raise RuntimeError('the session is unavailable because no secret '
+ 'key was set. Set the secret_key on the '
+ 'application to something unique and secret.')
+ __setitem__ = __delitem__ = clear = pop = popitem = \
+ update = setdefault = _fail
+ del _fail
diff --git a/flask/signals.py b/flask/signals.py
new file mode 100644
index 00000000..22447c7c
--- /dev/null
+++ b/flask/signals.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.signals
+ ~~~~~~~~~~~~~
+
+ Implements signals based on blinker if available, otherwise
+ falls silently back to a noop
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+signals_available = False
+try:
+ from blinker import Namespace
+ signals_available = True
+except ImportError:
+ class Namespace(object):
+ def signal(self, name, doc=None):
+ return _FakeSignal(name, doc)
+
+ class _FakeSignal(object):
+ """If blinker is unavailable, create a fake class with the same
+ interface that allows sending of signals but will fail with an
+ error on anything else. Instead of doing anything on send, it
+ will just ignore the arguments and do nothing instead.
+ """
+
+ def __init__(self, name, doc=None):
+ self.name = name
+ self.__doc__ = doc
+ def _fail(self, *args, **kwargs):
+ raise RuntimeError('signalling support is unavailable '
+ 'because the blinker library is '
+ 'not installed.')
+ send = lambda *a, **kw: None
+ connect = disconnect = has_receivers_for = receivers_for = \
+ temporarily_connected_to = _fail
+ del _fail
+
+# the namespace for code signals. If you are not flask code, do
+# not put signals in here. Create your own namespace instead.
+_signals = Namespace()
+
+
+# core signals. For usage examples grep the sourcecode or consult
+# the API documentation in docs/api.rst as well as docs/signals.rst
+template_rendered = _signals.signal('template-rendered')
+request_started = _signals.signal('request-started')
+request_finished = _signals.signal('request-finished')
+got_request_exception = _signals.signal('got-request-exception')
diff --git a/flask/templating.py b/flask/templating.py
new file mode 100644
index 00000000..41c0d39b
--- /dev/null
+++ b/flask/templating.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.templating
+ ~~~~~~~~~~~~~~~~
+
+ Implements the bridge to Jinja2.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound
+
+from .globals import _request_ctx_stack
+from .signals import template_rendered
+
+
+def _default_template_ctx_processor():
+ """Default template context processor. Injects `request`,
+ `session` and `g`.
+ """
+ reqctx = _request_ctx_stack.top
+ return dict(
+ config=reqctx.app.config,
+ request=reqctx.request,
+ session=reqctx.session,
+ g=reqctx.g
+ )
+
+
+class _DispatchingJinjaLoader(BaseLoader):
+ """A loader that looks for templates in the application and all
+ the module folders.
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def get_source(self, environment, template):
+ loader = None
+ try:
+ module, name = template.split('/', 1)
+ loader = self.app.modules[module].jinja_loader
+ except (ValueError, KeyError):
+ pass
+ # if there was a module and it has a loader, try this first
+ if loader is not None:
+ try:
+ return loader.get_source(environment, name)
+ except TemplateNotFound:
+ pass
+ # fall back to application loader if module failed
+ return self.app.jinja_loader.get_source(environment, template)
+
+ def list_templates(self):
+ result = self.app.jinja_loader.list_templates()
+ for name, module in self.app.modules.iteritems():
+ if module.jinja_loader is not None:
+ for template in module.jinja_loader.list_templates():
+ result.append('%s/%s' % (name, template))
+ return result
+
+
+def _render(template, context, app):
+ """Renders the template and fires the signal"""
+ rv = template.render(context)
+ template_rendered.send(app, template=template, context=context)
+ return rv
+
+
+def render_template(template_name, **context):
+ """Renders a template from the template folder with the given
+ context.
+
+ :param template_name: the name of the template to be rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _request_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.get_template(template_name),
+ context, ctx.app)
+
+
+def render_template_string(source, **context):
+ """Renders a template from the given template source string
+ with the given context.
+
+ :param template_name: the sourcecode of the template to be
+ rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _request_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.from_string(source),
+ context, ctx.app)
diff --git a/flask/testing.py b/flask/testing.py
new file mode 100644
index 00000000..ee4bd28e
--- /dev/null
+++ b/flask/testing.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testing
+ ~~~~~~~~~~~~~
+
+ Implements test support helpers. This module is lazily imported
+ and usually not used in production environments.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug import Client
+from flask import _request_ctx_stack
+
+
+class FlaskClient(Client):
+ """Works like a regular Werkzeug test client but has some
+ knowledge about how Flask works to defer the cleanup of the
+ request context stack to the end of a with body when used
+ in a with statement.
+ """
+
+ preserve_context = context_preserved = False
+
+ def open(self, *args, **kwargs):
+ if self.context_preserved:
+ _request_ctx_stack.pop()
+ self.context_preserved = False
+ kwargs.setdefault('environ_overrides', {}) \
+ ['flask._preserve_context'] = self.preserve_context
+ old = _request_ctx_stack.top
+ try:
+ return Client.open(self, *args, **kwargs)
+ finally:
+ self.context_preserved = _request_ctx_stack.top is not old
+
+ def __enter__(self):
+ self.preserve_context = True
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ self.preserve_context = False
+ if self.context_preserved:
+ _request_ctx_stack.pop()
diff --git a/flask/wrappers.py b/flask/wrappers.py
new file mode 100644
index 00000000..c578170c
--- /dev/null
+++ b/flask/wrappers.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.wrappers
+ ~~~~~~~~~~~~~~
+
+ Implements the WSGI wrappers (request and response).
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug import Request as RequestBase, Response as ResponseBase, \
+ cached_property
+
+from .helpers import json, _assert_have_json
+from .globals import _request_ctx_stack
+
+
+class Request(RequestBase):
+ """The request object used by default in flask. Remembers the
+ matched endpoint and view arguments.
+
+ It is what ends up as :class:`~flask.request`. If you want to replace
+ the request object used you can subclass this and set
+ :attr:`~flask.Flask.request_class` to your subclass.
+ """
+
+ #: the internal URL rule that matched the request. This can be
+ #: useful to inspect which methods are allowed for the URL from
+ #: a before/after handler (``request.url_rule.methods``) etc.
+ #:
+ #: .. versionadded:: 0.6
+ url_rule = None
+
+ #: a dict of view arguments that matched the request. If an exception
+ #: happened when matching, this will be `None`.
+ view_args = None
+
+ #: if matching the URL failed, this is the exception that will be
+ #: raised / was raised as part of the request handling. This is
+ #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
+ #: something similar.
+ routing_exception = None
+
+ @property
+ def max_content_length(self):
+ """Read-only view of the `MAX_CONTENT_LENGTH` config key."""
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.app.config['MAX_CONTENT_LENGTH']
+
+ @property
+ def endpoint(self):
+ """The endpoint that matched the request. This in combination with
+ :attr:`view_args` can be used to reconstruct the same or a
+ modified URL. If an exception happened when matching, this will
+ be `None`.
+ """
+ if self.url_rule is not None:
+ return self.url_rule.endpoint
+
+ @property
+ def module(self):
+ """The name of the current module"""
+ if self.url_rule and '.' in self.url_rule.endpoint:
+ return self.url_rule.endpoint.rsplit('.', 1)[0]
+
+ @cached_property
+ def json(self):
+ """If the mimetype is `application/json` this will contain the
+ parsed JSON data.
+ """
+ if __debug__:
+ _assert_have_json()
+ if self.mimetype == 'application/json':
+ return json.loads(self.data)
+
+
+class Response(ResponseBase):
+ """The response object that is used by default in flask. Works like the
+ response object from Werkzeug but is set to have a HTML mimetype by
+ default. Quite often you don't have to create this object yourself because
+ :meth:`~flask.Flask.make_response` will take care of that for you.
+
+ If you want to replace the response object used you can subclass this and
+ set :attr:`~flask.Flask.response_class` to your subclass.
+ """
+ default_mimetype = 'text/html'
diff --git a/setup.py b/setup.py
index 66ef6a66..b97a33f7 100644
--- a/setup.py
+++ b/setup.py
@@ -41,9 +41,16 @@ Links
from setuptools import setup
+def run_tests():
+ import os, sys
+ sys.path.append(os.path.join(os.path.dirname(__file__), 'tests'))
+ from flask_tests import suite
+ return suite()
+
+
setup(
name='Flask',
- version='0.4',
+ version='0.6',
url='http://github.com/mitsuhiko/flask/',
license='BSD',
author='Armin Ronacher',
@@ -51,7 +58,7 @@ setup(
description='A microframework based on Werkzeug, Jinja2 '
'and good intentions',
long_description=__doc__,
- py_modules=['flask'],
+ packages=['flask'],
zip_safe=False,
platforms='any',
install_requires=[
@@ -67,5 +74,6 @@ setup(
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
- ]
+ ],
+ test_suite='__main__.run_tests'
)
diff --git a/tests/flask_tests.py b/tests/flask_tests.py
index ff95364f..3000e41c 100644
--- a/tests/flask_tests.py
+++ b/tests/flask_tests.py
@@ -16,12 +16,14 @@ import sys
import flask
import unittest
import tempfile
+from logging import StreamHandler
from contextlib import contextmanager
from datetime import datetime
-from werkzeug import parse_date, parse_options_header
+from werkzeug import parse_date, parse_options_header, http_date
+from werkzeug.exceptions import NotFound
+from jinja2 import TemplateNotFound
from cStringIO import StringIO
-
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
sys.path.append(os.path.join(example_path, 'flaskr'))
sys.path.append(os.path.join(example_path, 'minitwit'))
@@ -57,6 +59,7 @@ class ContextTestCase(unittest.TestCase):
assert index() == 'Hello World!'
with app.test_request_context('/meh'):
assert meh() == 'http://localhost/meh'
+ assert flask._request_ctx_stack.top is None
def test_manual_context_binding(self):
app = flask.Flask(__name__)
@@ -75,9 +78,48 @@ class ContextTestCase(unittest.TestCase):
else:
assert 0, 'expected runtime error'
+ def test_test_client_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ flask.g.value = 42
+ return 'Hello World!'
+
+ @app.route('/other')
+ def other():
+ 1/0
+
+ with app.test_client() as c:
+ resp = c.get('/')
+ assert flask.g.value == 42
+ assert resp.data == 'Hello World!'
+ assert resp.status_code == 200
+
+ resp = c.get('/other')
+ assert not hasattr(flask.g, 'value')
+ assert 'Internal Server Error' in resp.data
+ assert resp.status_code == 500
+ flask.g.value = 23
+
+ try:
+ flask.g.value
+ except (AttributeError, RuntimeError):
+ pass
+ else:
+ raise AssertionError('some kind of exception expected')
+
class BasicFunctionalityTestCase(unittest.TestCase):
+ def test_options_work(self):
+ app = flask.Flask(__name__)
+ @app.route('/', methods=['GET', 'POST'])
+ def index():
+ return 'Hello World'
+ rv = app.test_client().open('/', method='OPTIONS')
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
+ assert rv.data == ''
+
def test_request_dispatching(self):
app = flask.Flask(__name__)
@app.route('/')
@@ -91,7 +133,7 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert c.get('/').data == 'GET'
rv = c.post('/')
assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD']
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS']
rv = c.head('/')
assert rv.status_code == 200
assert not rv.data # head truncates
@@ -99,7 +141,7 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert c.get('/more').data == 'GET'
rv = c.delete('/more')
assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD', 'POST']
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
def test_url_mapping(self):
app = flask.Flask(__name__)
@@ -115,7 +157,7 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert c.get('/').data == 'GET'
rv = c.post('/')
assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD']
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS']
rv = c.head('/')
assert rv.status_code == 200
assert not rv.data # head truncates
@@ -123,7 +165,7 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert c.get('/more').data == 'GET'
rv = c.delete('/more')
assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD', 'POST']
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
def test_session(self):
app = flask.Flask(__name__)
@@ -140,6 +182,20 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert c.post('/set', data={'value': '42'}).data == 'value set'
assert c.get('/get').data == '42'
+ def test_session_using_server_name(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SECRET_KEY='foo',
+ SERVER_NAME='example.com'
+ )
+ @app.route('/')
+ def index():
+ flask.session['testing'] = 42
+ return 'Hello World'
+ rv = app.test_client().get('/', 'http://example.com/')
+ assert 'domain=.example.com' in rv.headers['set-cookie'].lower()
+ assert 'httponly' in rv.headers['set-cookie'].lower()
+
def test_missing_session(self):
app = flask.Flask(__name__)
def expect_exception(f, *args, **kwargs):
@@ -240,6 +296,61 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert 'after' in evts
assert rv == 'request|after'
+ def test_after_request_errors(self):
+ app = flask.Flask(__name__)
+ called = []
+ @app.after_request
+ def after_request(response):
+ called.append(True)
+ return response
+ @app.route('/')
+ def fails():
+ 1/0
+ rv = app.test_client().get('/')
+ assert rv.status_code == 500
+ assert 'Internal Server Error' in rv.data
+ assert len(called) == 1
+
+ def test_after_request_handler_error(self):
+ called = []
+ app = flask.Flask(__name__)
+ @app.after_request
+ def after_request(response):
+ called.append(True)
+ 1/0
+ return response
+ @app.route('/')
+ def fails():
+ 1/0
+ rv = app.test_client().get('/')
+ assert rv.status_code == 500
+ assert 'Internal Server Error' in rv.data
+ assert len(called) == 1
+
+ def test_before_after_request_order(self):
+ called = []
+ app = flask.Flask(__name__)
+ @app.before_request
+ def before1():
+ called.append(1)
+ @app.before_request
+ def before2():
+ called.append(2)
+ @app.after_request
+ def after1(response):
+ called.append(4)
+ return response
+ @app.after_request
+ def after1(response):
+ called.append(3)
+ return response
+ @app.route('/')
+ def index():
+ return '42'
+ rv = app.test_client().get('/')
+ assert rv.data == '42'
+ assert called == [1, 2, 3, 4]
+
def test_error_handling(self):
app = flask.Flask(__name__)
@app.errorhandler(404)
@@ -260,7 +371,7 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert rv.data == 'not found'
rv = c.get('/error')
assert rv.status_code == 500
- assert 'internal server error' in rv.data
+ assert 'internal server error' == rv.data
def test_response_creation(self):
app = flask.Flask(__name__)
@@ -282,6 +393,24 @@ class BasicFunctionalityTestCase(unittest.TestCase):
assert rv.status_code == 400
assert rv.mimetype == 'text/plain'
+ def test_make_response(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ rv = flask.make_response()
+ assert rv.status_code == 200
+ assert rv.data == ''
+ assert rv.mimetype == 'text/html'
+
+ rv = flask.make_response('Awesome')
+ assert rv.status_code == 200
+ assert rv.data == 'Awesome'
+ assert rv.mimetype == 'text/html'
+
+ rv = flask.make_response('W00t', 404)
+ assert rv.status_code == 404
+ assert rv.data == 'W00t'
+ assert rv.mimetype == 'text/html'
+
def test_url_generation(self):
app = flask.Flask(__name__)
@app.route('/hello/', methods=['POST'])
@@ -367,6 +496,21 @@ class JSONTestCase(unittest.TestCase):
rv = render('{{ "<\0/script>"|tojson|safe }}')
assert rv == '"<\\u0000\\/script>"'
+ def test_modified_url_encoding(self):
+ class ModifiedRequest(flask.Request):
+ url_charset = 'euc-kr'
+ app = flask.Flask(__name__)
+ app.request_class = ModifiedRequest
+ app.url_map.charset = 'euc-kr'
+
+ @app.route('/')
+ def index():
+ return flask.request.args['foo']
+
+ rv = app.test_client().get(u'/?foo=정상처리'.encode('euc-kr'))
+ assert rv.status_code == 200
+ assert rv.data == u'정상처리'.encode('utf-8')
+
class TemplatingTestCase(unittest.TestCase):
@@ -381,6 +525,30 @@ class TemplatingTestCase(unittest.TestCase):
rv = app.test_client().get('/')
assert rv.data == '23|42'
+ def test_original_win(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template_string('{{ config }}', config=42)
+ rv = app.test_client().get('/')
+ assert rv.data == '42'
+
+ def test_standard_context(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'development key'
+ @app.route('/')
+ def index():
+ flask.g.foo = 23
+ flask.session['test'] = 'aha'
+ return flask.render_template_string('''
+ {{ request.args.foo }}
+ {{ g.foo }}
+ {{ config.DEBUG }}
+ {{ session.test }}
+ ''')
+ rv = app.test_client().get('/?foo=42')
+ assert rv.data.split() == ['42', '23', 'False', 'aha']
+
def test_escaping(self):
text = '
Hello World!'
app = flask.Flask(__name__)
@@ -398,6 +566,14 @@ class TemplatingTestCase(unittest.TestCase):
'
Hello World!'
]
+ def test_no_escaping(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ assert flask.render_template_string('{{ foo }}',
+ foo='') == ''
+ assert flask.render_template('mail.txt', foo='') \
+ == ' Mail'
+
def test_macros(self):
app = flask.Flask(__name__)
with app.test_request_context():
@@ -469,6 +645,18 @@ class ModuleTestCase(unittest.TestCase):
assert c.get('/admin/login').data == 'admin login'
assert c.get('/admin/logout').data == 'admin logout'
+ def test_default_endpoint_name(self):
+ app = flask.Flask(__name__)
+ mod = flask.Module(__name__, 'frontend')
+ def index():
+ return 'Awesome'
+ mod.add_url_rule('/', view_func=index)
+ app.register_module(mod)
+ rv = app.test_client().get('/')
+ assert rv.data == 'Awesome'
+ with app.test_request_context():
+ assert flask.url_for('frontend.index') == '/'
+
def test_request_processing(self):
catched = []
app = flask.Flask(__name__)
@@ -536,6 +724,77 @@ class ModuleTestCase(unittest.TestCase):
app.register_module(admin, url_prefix='/admin')
assert app.test_client().get('/admin/').data == '42'
+ def test_error_handling(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin')
+ @admin.app_errorhandler(404)
+ def not_found(e):
+ return 'not found', 404
+ @admin.app_errorhandler(500)
+ def internal_server_error(e):
+ return 'internal server error', 500
+ @admin.route('/')
+ def index():
+ flask.abort(404)
+ @admin.route('/error')
+ def error():
+ 1 // 0
+ app.register_module(admin)
+ c = app.test_client()
+ rv = c.get('/')
+ assert rv.status_code == 404
+ assert rv.data == 'not found'
+ rv = c.get('/error')
+ assert rv.status_code == 500
+ assert 'internal server error' == rv.data
+
+ def test_templates_and_static(self):
+ from moduleapp import app
+ c = app.test_client()
+
+ rv = c.get('/')
+ assert rv.data == 'Hello from the Frontend'
+ rv = c.get('/admin/')
+ assert rv.data == 'Hello from the Admin'
+ rv = c.get('/admin/static/test.txt')
+ assert rv.data.strip() == 'Admin File'
+ rv = c.get('/admin/static/css/test.css')
+ assert rv.data.strip() == '/* nested file */'
+
+ with app.test_request_context():
+ assert flask.url_for('admin.static', filename='test.txt') \
+ == '/admin/static/test.txt'
+
+ with app.test_request_context():
+ try:
+ flask.render_template('missing.html')
+ except TemplateNotFound, e:
+ assert e.name == 'missing.html'
+ else:
+ assert 0, 'expected exception'
+
+ with flask.Flask(__name__).test_request_context():
+ assert flask.render_template('nested/nested.txt') == 'I\'m nested'
+
+ def test_safe_access(self):
+ from moduleapp import app
+
+ with app.test_request_context():
+ f = app.view_functions['admin.static']
+
+ try:
+ rv = f('/etc/passwd')
+ except NotFound:
+ pass
+ else:
+ assert 0, 'expected exception'
+ try:
+ rv = f('../__init__.py')
+ except NotFound:
+ pass
+ else:
+ assert 0, 'expected exception'
+
class SendfileTestCase(unittest.TestCase):
@@ -620,12 +879,22 @@ class SendfileTestCase(unittest.TestCase):
class LoggingTestCase(unittest.TestCase):
+ def test_logger_cache(self):
+ app = flask.Flask(__name__)
+ logger1 = app.logger
+ assert app.logger is logger1
+ assert logger1.name == __name__
+ app.logger_name = __name__ + '/test_logger_cache'
+ assert app.logger is not logger1
+
def test_debug_log(self):
app = flask.Flask(__name__)
app.debug = True
+
@app.route('/')
def index():
app.logger.warning('the standard library is dead')
+ app.logger.debug('this is a debug statement')
return ''
@app.route('/exc')
@@ -636,9 +905,10 @@ class LoggingTestCase(unittest.TestCase):
with catch_stderr() as err:
rv = c.get('/')
out = err.getvalue()
- assert 'WARNING in flask_tests,' in out
+ assert 'WARNING in flask_tests [' in out
assert 'flask_tests.py' in out
assert 'the standard library is dead' in out
+ assert 'this is a debug statement' in out
with catch_stderr() as err:
try:
@@ -649,9 +919,9 @@ class LoggingTestCase(unittest.TestCase):
assert False, 'debug log ate the exception'
def test_exception_logging(self):
- from logging import StreamHandler
out = StringIO()
app = flask.Flask(__name__)
+ app.logger_name = 'flask_tests/test_exception_logging'
app.logger.addHandler(StreamHandler(out))
@app.route('/')
@@ -738,6 +1008,141 @@ class ConfigTestCase(unittest.TestCase):
os.environ = env
+class SubdomainTestCase(unittest.TestCase):
+
+ def test_basic_support(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'localhost'
+ @app.route('/')
+ def normal_index():
+ return 'normal index'
+ @app.route('/', subdomain='test')
+ def test_index():
+ return 'test index'
+
+ c = app.test_client()
+ rv = c.get('/', 'http://localhost/')
+ assert rv.data == 'normal index'
+
+ rv = c.get('/', 'http://test.localhost/')
+ assert rv.data == 'test index'
+
+ def test_subdomain_matching(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'localhost'
+ @app.route('/', subdomain='')
+ def index(user):
+ return 'index for %s' % user
+
+ c = app.test_client()
+ rv = c.get('/', 'http://mitsuhiko.localhost/')
+ assert rv.data == 'index for mitsuhiko'
+
+ def test_module_subdomain_support(self):
+ app = flask.Flask(__name__)
+ mod = flask.Module(__name__, 'test', subdomain='testing')
+ app.config['SERVER_NAME'] = 'localhost'
+
+ @mod.route('/test')
+ def test():
+ return 'Test'
+
+ @mod.route('/outside', subdomain='xtesting')
+ def bar():
+ return 'Outside'
+
+ app.register_module(mod)
+
+ c = app.test_client()
+ rv = c.get('/test', 'http://testing.localhost/')
+ assert rv.data == 'Test'
+ rv = c.get('/outside', 'http://xtesting.localhost/')
+ assert rv.data == 'Outside'
+
+
+class TestSignals(unittest.TestCase):
+
+ def test_template_rendered(self):
+ app = flask.Flask(__name__)
+
+ @app.route('/')
+ def index():
+ return flask.render_template('simple_template.html', whiskey=42)
+
+ recorded = []
+ def record(sender, template, context):
+ recorded.append((template, context))
+
+ flask.template_rendered.connect(record, app)
+ try:
+ rv = app.test_client().get('/')
+ assert len(recorded) == 1
+ template, context = recorded[0]
+ assert template.name == 'simple_template.html'
+ assert context['whiskey'] == 42
+ finally:
+ flask.template_rendered.disconnect(record, app)
+
+ def test_request_signals(self):
+ app = flask.Flask(__name__)
+ calls = []
+
+ def before_request_signal(sender):
+ calls.append('before-signal')
+
+ def after_request_signal(sender, response):
+ assert response.data == 'stuff'
+ calls.append('after-signal')
+
+ @app.before_request
+ def before_request_handler():
+ calls.append('before-handler')
+
+ @app.after_request
+ def after_request_handler(response):
+ calls.append('after-handler')
+ response.data = 'stuff'
+ return response
+
+ @app.route('/')
+ def index():
+ calls.append('handler')
+ return 'ignored anyway'
+
+ flask.request_started.connect(before_request_signal, app)
+ flask.request_finished.connect(after_request_signal, app)
+
+ try:
+ rv = app.test_client().get('/')
+ assert rv.data == 'stuff'
+
+ assert calls == ['before-signal', 'before-handler',
+ 'handler', 'after-handler',
+ 'after-signal']
+ finally:
+ flask.request_started.disconnect(before_request_signal, app)
+ flask.request_finished.disconnect(after_request_signal, app)
+
+ def test_request_exception_signal(self):
+ app = flask.Flask(__name__)
+ recorded = []
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ def record(sender, exception):
+ recorded.append(exception)
+
+ flask.got_request_exception.connect(record, app)
+ try:
+ assert app.test_client().get('/').status_code == 500
+ assert len(recorded) == 1
+ assert isinstance(recorded[0], ZeroDivisionError)
+ finally:
+ flask.got_request_exception.disconnect(record, app)
+
+
def suite():
from minitwit_tests import MiniTwitTestCase
from flaskr_tests import FlaskrTestCase
@@ -749,8 +1154,11 @@ def suite():
suite.addTest(unittest.makeSuite(SendfileTestCase))
suite.addTest(unittest.makeSuite(LoggingTestCase))
suite.addTest(unittest.makeSuite(ConfigTestCase))
+ suite.addTest(unittest.makeSuite(SubdomainTestCase))
if flask.json_available:
suite.addTest(unittest.makeSuite(JSONTestCase))
+ if flask.signals_available:
+ suite.addTest(unittest.makeSuite(TestSignals))
suite.addTest(unittest.makeSuite(MiniTwitTestCase))
suite.addTest(unittest.makeSuite(FlaskrTestCase))
return suite
diff --git a/tests/flaskext_test.py b/tests/flaskext_test.py
new file mode 100644
index 00000000..aa4b7461
--- /dev/null
+++ b/tests/flaskext_test.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+"""
+ Flask Extension Tests
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the Flask extensions.
+
+ :copyright: (c) 2010 by Ali Afshar.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import tempfile, subprocess, urllib2, os
+
+from flask import json
+
+from setuptools.package_index import PackageIndex
+from setuptools.archive_util import unpack_archive
+
+flask_svc_url = 'http://flask.pocoo.org/extensions/'
+tdir = tempfile.mkdtemp()
+
+
+def run_tests(checkout_dir):
+ cmd = ['tox']
+ return subprocess.call(cmd, cwd=checkout_dir,
+ stdout=open(os.path.join(tdir, 'tox.log'), 'w'),
+ stderr=subprocess.STDOUT)
+
+
+def get_test_command(checkout_dir):
+ files = set(os.listdir(checkout_dir))
+ if 'Makefile' in files:
+ return 'make test'
+ elif 'conftest.py' in files:
+ return 'py.test'
+ else:
+ return 'nosetests'
+
+
+def fetch_extensions_list():
+ req = urllib2.Request(flask_svc_url, headers={'accept':'application/json'})
+ d = urllib2.urlopen(req).read()
+ data = json.loads(d)
+ for ext in data['extensions']:
+ yield ext
+
+
+def checkout_extension(ext):
+ name = ext['name']
+ root = os.path.join(tdir, name)
+ os.mkdir(root)
+ checkout_path = PackageIndex().download(ext['name'], root)
+ unpack_archive(checkout_path, root)
+ path = None
+ for fn in os.listdir(root):
+ path = os.path.join(root, fn)
+ if os.path.isdir(path):
+ break
+ return path
+
+
+tox_template = """[tox]
+envlist=py26
+
+[testenv]
+commands=
+%s
+downloadcache=
+%s
+"""
+
+def create_tox_ini(checkout_path):
+ tox_path = os.path.join(checkout_path, 'tox.ini')
+ if not os.path.exists(tox_path):
+ with open(tox_path, 'w') as f:
+ f.write(tox_template % (get_test_command(checkout_path), tdir))
+
+# XXX command line
+only_approved = True
+
+def test_all_extensions(only_approved=only_approved):
+ for ext in fetch_extensions_list():
+ if ext['approved'] or not only_approved:
+ checkout_path = checkout_extension(ext)
+ create_tox_ini(checkout_path)
+ ret = run_tests(checkout_path)
+ yield ext['name'], ret
+
+def main():
+ for name, ret in test_all_extensions():
+ print name, ret
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/moduleapp/__init__.py b/tests/moduleapp/__init__.py
new file mode 100644
index 00000000..35e82d4e
--- /dev/null
+++ b/tests/moduleapp/__init__.py
@@ -0,0 +1,7 @@
+from flask import Flask
+
+app = Flask(__name__)
+from moduleapp.apps.admin import admin
+from moduleapp.apps.frontend import frontend
+app.register_module(admin)
+app.register_module(frontend)
diff --git a/tests/moduleapp/apps/__init__.py b/tests/moduleapp/apps/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/moduleapp/apps/admin/__init__.py b/tests/moduleapp/apps/admin/__init__.py
new file mode 100644
index 00000000..98af2b26
--- /dev/null
+++ b/tests/moduleapp/apps/admin/__init__.py
@@ -0,0 +1,9 @@
+from flask import Module, render_template
+
+
+admin = Module(__name__, url_prefix='/admin')
+
+
+@admin.route('/')
+def index():
+ return render_template('admin/index.html')
diff --git a/tests/moduleapp/apps/admin/static/css/test.css b/tests/moduleapp/apps/admin/static/css/test.css
new file mode 100644
index 00000000..b9f564de
--- /dev/null
+++ b/tests/moduleapp/apps/admin/static/css/test.css
@@ -0,0 +1 @@
+/* nested file */
diff --git a/tests/moduleapp/apps/admin/static/test.txt b/tests/moduleapp/apps/admin/static/test.txt
new file mode 100644
index 00000000..f220d22f
--- /dev/null
+++ b/tests/moduleapp/apps/admin/static/test.txt
@@ -0,0 +1 @@
+Admin File
diff --git a/tests/moduleapp/apps/admin/templates/index.html b/tests/moduleapp/apps/admin/templates/index.html
new file mode 100644
index 00000000..eeec199a
--- /dev/null
+++ b/tests/moduleapp/apps/admin/templates/index.html
@@ -0,0 +1 @@
+Hello from the Admin
diff --git a/tests/moduleapp/apps/frontend/__init__.py b/tests/moduleapp/apps/frontend/__init__.py
new file mode 100644
index 00000000..f83581e7
--- /dev/null
+++ b/tests/moduleapp/apps/frontend/__init__.py
@@ -0,0 +1,9 @@
+from flask import Module, render_template
+
+
+frontend = Module(__name__)
+
+
+@frontend.route('/')
+def index():
+ return render_template('frontend/index.html')
diff --git a/tests/moduleapp/apps/frontend/templates/index.html b/tests/moduleapp/apps/frontend/templates/index.html
new file mode 100644
index 00000000..a062d713
--- /dev/null
+++ b/tests/moduleapp/apps/frontend/templates/index.html
@@ -0,0 +1 @@
+Hello from the Frontend
diff --git a/tests/templates/mail.txt b/tests/templates/mail.txt
new file mode 100644
index 00000000..d6cb92ea
--- /dev/null
+++ b/tests/templates/mail.txt
@@ -0,0 +1 @@
+{{ foo}} Mail
diff --git a/tests/templates/nested/nested.txt b/tests/templates/nested/nested.txt
new file mode 100644
index 00000000..2c8634f9
--- /dev/null
+++ b/tests/templates/nested/nested.txt
@@ -0,0 +1 @@
+I'm nested
diff --git a/tests/templates/simple_template.html b/tests/templates/simple_template.html
new file mode 100644
index 00000000..c24612cb
--- /dev/null
+++ b/tests/templates/simple_template.html
@@ -0,0 +1 @@
+{{ whiskey }}