diff --git a/.gitignore b/.gitignore index f250e7a5..59d86186 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ *.pyo env dist -website/_mailinglist/* +_mailinglist/* *.egg-info diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 2c125e11..00000000 --- a/AUTHORS +++ /dev/null @@ -1,14 +0,0 @@ -Flask is written and maintained by Armin Ronacher and -various contributors: - -Development Lead -```````````````` - -- Armin Ronacher - -Patches and Suggestions -``````````````````````` - -- Chris Edgemon -- Chris Grindstaff -- Florent Xicluna diff --git a/CHANGES b/CHANGES deleted file mode 100644 index ce7289da..00000000 --- a/CHANGES +++ /dev/null @@ -1,21 +0,0 @@ -Flask Changelog -=============== - -Here you can see the full list of changes between each Flask release. - -Version 0.2 ------------ - -[unreleased; current development version] - -- various bugfixes -- integrated JSON support -- added :func:`~flask.get_template_attribute` helper function. -- :meth:`~flask.Flask.add_url_rule` can now also register a - view function. -- server listens on 127.0.0.1 by default now to fix issues with chrome. - -Version 0.1 ------------ - -First public preview release. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 6a8df19e..00000000 --- a/LICENSE +++ /dev/null @@ -1,32 +0,0 @@ -Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS -for more details. - -Some rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -* The names of the contributors may not be used to endorse or - promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile deleted file mode 100644 index 0be5e6bc..00000000 --- a/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: clean-pyc test - -all: clean-pyc test - -test: - python tests/flask_tests.py - -clean-pyc: - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - -upload-website: - scp -r website/* pocoo.org:/var/www/flask.pocoo.org/ - -upload-docs: - $(MAKE) -C docs dirhtml && scp -r docs/_build/dirhtml/* pocoo.org:/var/www/flask.pocoo.org/docs/ diff --git a/README b/README deleted file mode 100644 index 73e0bbf1..00000000 --- a/README +++ /dev/null @@ -1,31 +0,0 @@ - - // Flask // - - because sometimes a pocket knife is not enough - - - ~ What is Flask? - - Flask is a microframework for Python based on Werkzeug - and Jinja2. It's intended for small scale applications - and was developped with best intentions in mind. - - ~ Is it ready? - - A preview release is out now, and I'm hoping for some - input about what you want from a microframework and - how it should look like. Consider the API to slightly - improve over time. - - ~ What do I need? - - Jinja 2.4 and Werkzeug 0.6.1. `easy_install` will - install them for you if you do `easy_install Flask==dev`. - I encourage you to use a virtualenv. Check the docs for - complete installation and usage instructions. - - ~ Where are the docs? - - Go to http://flask.pocoo.org/ for a prebuilt version of - the current documentation. Otherwise build them yourself - from the sphinx sources in the docs folder. diff --git a/website/_mailinglist/.ignore b/_mailinglist/.ignore similarity index 100% rename from website/_mailinglist/.ignore rename to _mailinglist/.ignore diff --git a/artwork/logo-full.svg b/artwork/logo-full.svg deleted file mode 100755 index 43465a4d..00000000 --- a/artwork/logo-full.svg +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index e35d8850..00000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 52d78d9e..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,118 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask" - @echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -latexpdf: latex - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex - @echo "Running LaTeX files through pdflatex..." - make -C _build/latex all-pdf - @echo "pdflatex finished; the PDF files are in _build/latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/_static/debugger.png b/docs/_static/debugger.png deleted file mode 100644 index 4f47229d..00000000 Binary files a/docs/_static/debugger.png and /dev/null differ diff --git a/docs/_static/flask.png b/docs/_static/flask.png deleted file mode 100644 index 5c603cc2..00000000 Binary files a/docs/_static/flask.png and /dev/null differ diff --git a/docs/_static/flaskr.png b/docs/_static/flaskr.png deleted file mode 100644 index 07d027dd..00000000 Binary files a/docs/_static/flaskr.png and /dev/null differ diff --git a/docs/_static/logo-full.png b/docs/_static/logo-full.png deleted file mode 100644 index f255eece..00000000 Binary files a/docs/_static/logo-full.png and /dev/null differ diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html deleted file mode 100644 index b8381d7d..00000000 --- a/docs/_templates/sidebarintro.html +++ /dev/null @@ -1,13 +0,0 @@ -

About Flask

-

- Flask is a micro webdevelopment framework for Python. You are currently - looking at the documentation of the development version. Things are - not stable yet, but if you have some feedback, - let me know. -

-

Useful Links

- diff --git a/docs/_templates/sidebarlogo.html b/docs/_templates/sidebarlogo.html deleted file mode 100644 index 3bc7f762..00000000 --- a/docs/_templates/sidebarlogo.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/_themes/flasky/static/flasky.css_t b/docs/_themes/flasky/static/flasky.css_t deleted file mode 100644 index 04c5c5f6..00000000 --- a/docs/_themes/flasky/static/flasky.css_t +++ /dev/null @@ -1,344 +0,0 @@ -/* - * flasky.css_t - * ~~~~~~~~~~~~ - * - * Sphinx stylesheet -- flasky theme based on nature theme. - * - * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'Georgia', serif; - font-size: 17px; - background-color: #ddd; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - background: #fafafa; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 30px 30px; - min-height: 34em; -} - -img.floatingflask { - padding: 0 0 10px 10px; - float: right; -} - -div.footer { - position: absolute; - right: 0; - margin-top: -70px; - text-align: right; - color: #888; - padding: 10px; - font-size: 14px; -} - -div.footer a { - color: #888; - text-decoration: underline; -} - -div.related { - line-height: 32px; - color: #888; -} - -div.related ul { - padding: 0 0 0 10px; -} - -div.related a { - color: #444; -} - -div.sphinxsidebar { - font-size: 14px; - line-height: 1.5; -} - -div.sphinxsidebarwrapper { - padding: 0 20px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 20px 0 10px 0; - margin: 0; - text-align: center; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: 'Garamond', 'Georgia', serif; - color: #222; - font-size: 24px; - font-weight: normal; - margin: 20px 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar a { - color: #444; - text-decoration: none; -} - -div.sphinxsidebar a:hover { - text-decoration: underline; -} - -div.sphinxsidebar input { - border: 1px solid #ccc; - font-family: 'Georgia', serif; - font-size: 1em; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body { - padding-bottom: 40px; /* saved for footer */ -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 20px; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: white; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #eaeaea; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - background: #fafafa; - margin: 20px -30px; - padding: 10px 30px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; -} - -div.admonition p.admonition-title { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight{ - background-color: white; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt { - font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -img.screenshot { -} - -tt.descname, tt.descclassname { - font-size: 0.95em; -} - -tt.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #eee; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.footnote td { - padding: 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -pre { - background: #eee; - padding: 7px 30px; - margin: 15px -30px; - line-height: 1.3em; -} - -dl pre { - margin-left: -60px; - padding-left: 60px; -} - -dl dl pre { - margin-left: -90px; - padding-left: 90px; -} - -tt { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, a tt { - background-color: #FBFBFB; -} - -a:hover tt { - background: #EEE; -} diff --git a/docs/_themes/flasky/theme.conf b/docs/_themes/flasky/theme.conf deleted file mode 100644 index cb9eb465..00000000 --- a/docs/_themes/flasky/theme.conf +++ /dev/null @@ -1,3 +0,0 @@ -[theme] -inherit = basic -stylesheet = flasky.css diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index d285dbfd..00000000 --- a/docs/api.rst +++ /dev/null @@ -1,262 +0,0 @@ -.. _api: - -API -=== - -.. module:: flask - -This part of the documentation covers all the interfaces of Flask. For -parts where Flask depends on external libraries, we document the most -important right here and provide links to the canonical documentation. - - -Application Object ------------------- - -.. autoclass:: Flask - :members: - -Incoming Request Data ---------------------- - -.. autoclass:: Request - -.. class:: request - - To access incoming request data, you can use the global `request` - object. Flask parses incoming request data for you and gives you - access to it through that global object. Internally Flask makes - sure that you always get the correct data for the active thread if you - are in a multithreaded environment. - - The request object is an instance of a :class:`~werkzeug.Request` - subclass and provides all of the attributes Werkzeug defines. This - just shows a quick overview of the most important ones. - - .. attribute:: form - - A :class:`~werkzeug.MultiDict` with the parsed form data from `POST` - or `PUT` requests. Please keep in mind that file uploads will not - end up here, but instead in the :attr:`files` attribute. - - .. attribute:: args - - A :class:`~werkzeug.MultiDict` with the parsed contents of the query - string. (The part in the URL after the question mark). - - .. attribute:: values - - A :class:`~werkzeug.CombinedMultiDict` with the contents of both - :attr:`form` and :attr:`args`. - - .. attribute:: cookies - - A :class:`dict` with the contents of all cookies transmitted with - the request. - - .. attribute:: stream - - If the incoming form data was not encoded with a known mimetype - the data is stored unmodified in this stream for consumption. Most - of the time it is a better idea to use :attr:`data` which will give - you that data as a string. The stream only returns the data once. - - .. attribute:: data - - Contains the incoming request data as string in case it came with - a mimetype Flask does not handle. - - .. attribute:: files - - A :class:`~werkzeug.MultiDict` with files uploaded as part of a - `POST` or `PUT` request. Each file is stored as - :class:`~werkzeug.FileStorage` object. It basically behaves like a - standard file object you know from Python, with the difference that - it also has a :meth:`~werkzeug.FileStorage.save` function that can - store the file on the filesystem. - - .. attribute:: environ - - The underlying WSGI environment. - - .. attribute:: method - - The current request method (``POST``, ``GET`` etc.) - - .. attribute:: path - .. attribute:: script_root - .. attribute:: url - .. attribute:: base_url - .. attribute:: url_root - - Provides different ways to look at the current URL. Imagine your - application is listening on the following URL:: - - http://www.example.com/myapplication - - And a user requests the following URL:: - - http://www.example.com/myapplication/page.html?x=y - - In this case the values of the above mentioned attributes would be - the following: - - ============= ====================================================== - `path` ``/page.html`` - `script_root` ``/myapplication`` - `url` ``http://www.example.com/myapplication/page.html`` - `base_url` ``http://www.example.com/myapplication/page.html?x=y`` - `url_root` ``http://www.example.com/myapplication/`` - ============= ====================================================== - - .. attribute:: is_xhr - - `True` if the request was triggered via a JavaScript - `XMLHttpRequest`. This only works with libraries that support the - ``X-Requested-With`` header and set it to `XMLHttpRequest`. - Libraries that do that are prototype, jQuery and Mochikit and - probably some more. - - .. attribute:: json - - Contains the parsed body of the JSON request if the mimetype of - the incoming data was `application/json`. This requires Python 2.6 - or an installed version of simplejson. - -Response Objects ----------------- - -.. autoclass:: flask.Response - :members: set_cookie, data, mimetype - - .. attribute:: headers - - A :class:`Headers` object representing the response headers. - - .. attribute:: status_code - - The response status as integer. - - -Sessions --------- - -If you have the :attr:`Flask.secret_key` set you can use sessions in Flask -applications. A session basically makes it possible to remember -information from one request to another. The way Flask does this is by -using a signed cookie. So the user can look at the session contents, but -not modify it unless he knows the secret key, so make sure to set that to -something complex and unguessable. - -To access the current session you can use the :class:`session` object: - -.. class:: session - - The session object works pretty much like an ordinary dict, with the - difference that it keeps track on modifications. - - The following attributes are interesting: - - .. attribute:: new - - `True` if the session is new, `False` otherwise. - - .. attribute:: modified - - `True` if the session object detected a modification. Be advised - that modifications on mutable structures are not picked up - automatically, in that situation you have to explicitly set the - attribute to `True` yourself. Here an example:: - - # this change is not picked up because a mutable object (here - # a list) is changed. - session['objects'].append(42) - # so mark it as modified yourself - session.modified = True - - -Application Globals -------------------- - -To share data that is valid for one request only from one function to -another, a global variable is not good enough because it would break in -threaded environments. Flask provides you with a special object that -ensures it is only valid for the active request and that will return -different values for each request. In a nutshell: it does the right -thing, like it does for :class:`request` and :class:`session`. - -.. data:: g - - Just store on this whatever you want. For example a database - connection or the user that is currently logged in. - - -Useful Functions and Classes ----------------------------- - -.. autofunction:: url_for - -.. function:: abort(code) - - Raises an :exc:`~werkzeug.exception.HTTPException` for the given - status code. For example to abort request handling with a page not - found exception, you would call ``abort(404)``. - - :param code: the HTTP error code. - -.. autofunction:: redirect - -.. autofunction:: escape - -.. autoclass:: Markup - :members: escape, unescape, striptags - -Message Flashing ----------------- - -.. autofunction:: flash - -.. autofunction:: get_flashed_messages - -Returning JSON --------------- - -.. autofunction:: jsonify - -.. data:: json - - If JSON support is picked up, this will be the module that Flask is - using to parse and serialize JSON. So instead of doing this yourself:: - - try: - import simplejson as json - except ImportError: - import json - - You can instead just do this:: - - from flask import json - - For usage examples, read the :mod:`json` documentation. - - The :func:`~json.dumps` function of this json module is also available - as filter called ``|tojson`` in Jinja2. Note that inside `script` - tags no escaping must take place, so make sure to disable escaping - with ``|safe`` if you intend to use it inside `script` tags: - - .. sourcecode:: html+jinja - - - - Note that the ``|tojson`` filter escapes forward slashes properly. - -Template Rendering ------------------- - -.. autofunction:: render_template - -.. autofunction:: render_template_string - -.. autofunction:: get_template_attribute diff --git a/docs/becomingbig.rst b/docs/becomingbig.rst deleted file mode 100644 index 02344720..00000000 --- a/docs/becomingbig.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. _becomingbig: - -Becoming Big -============ - -Your application is becoming more and more complex? Flask is really not -designed for large scale applications and does not attempt to do so, but -that does not mean you picked the wrong tool in the first place. - -Flask is powered by Werkzeug and Jinja2, two libraries that are in use at -a number of large websites out there and all Flask does is bring those -two together. Being a microframework, Flask is literally a single file. -What that means for large applications is that it's probably a good idea -to take the code from Flask and put it into a new module within the -applications and expand on that. - -What Could Be Improved? ------------------------ - -For instance it makes a lot of sense to change the way endpoints (the -names of the functions / URL rules) are handled to also take the module -name into account. Right now the function name is the URL name, but -imagine you have a large application consisting of multiple components. -In that case, it makes a lot of sense to use dotted names for the URL -endpoints. - -Here are some suggestions for how Flask can be modified to better -accomodate large-scale applications: - -- implement dotted names for URL endpoints -- get rid of the decorator function registering which causes a lot - of troubles for applications that have circular dependencies. It - also requires that the whole application is imported when the system - initializes or certain URLs will not be available right away. A - better solution would be to have one module with all URLs in there and - specifing the target functions explicitly or by name and importing - them when needed. -- switch to explicit request object passing. This requires more typing - (because you now have something to pass around) but it makes it a - whole lot easier to debug hairy situations and to test the code. -- integrate the `Babel`_ i18n package or `SQLAlchemy`_ directly into the - core framework. - -.. _Babel: http://babel.edgewall.org/ -.. _SQLAlchemy: http://www.sqlalchemy.org/ - -Why does Flask not do all that by Default? ------------------------------------------- - -There is a huge difference between a small application that only has to -handle a couple of requests per second and with an overall code complexity -of less than 4000 lines of code and something of larger scale. At some -point it becomes important to integrate external systems, different -storage backends and more. - -If Flask was designed with all these contingencies in mind, it would be a -much more complex framework and harder to get started with. diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index d6c5f48c..00000000 --- a/docs/changelog.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CHANGES diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 03e27217..00000000 --- a/docs/conf.py +++ /dev/null @@ -1,247 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Flask documentation build configuration file, created by -# sphinx-quickstart on Tue Apr 6 15:24:58 2010. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Flask' -copyright = u'2010, Armin Ronacher' - -import pkg_resources - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -release = __import__('pkg_resources').get_distribution('Flask').version -version = '.'.join(release.split('.')[:2]) - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'flaskext.FlaskyStyle' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'flasky' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = ['_themes'] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. Do not set, template magic! -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], - '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', - 'sourcelink.html', 'searchbox.html'] -} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_use_modindex = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Flaskdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'Flask.tex', u'Flask Documentation', - u'Armin Ronacher', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True - - -# -- Options for Epub output --------------------------------------------------- - -# Bibliographic Dublin Core info. -#epub_title = '' -#epub_author = '' -#epub_publisher = '' -#epub_copyright = '' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -#epub_identifier = '' - -# A unique identification for the text. -#epub_uid = '' - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -#epub_exclude_files = [] - -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 - -intersphinx_mapping = { - 'http://docs.python.org/dev': None, - 'http://werkzeug.pocoo.org/documentation/dev/': None, - 'http://www.sqlalchemy.org/docs/': None, - 'http://wtforms.simplecodes.com/docs/0.5/': None -} diff --git a/docs/deploying/cgi.rst b/docs/deploying/cgi.rst deleted file mode 100644 index 15b5ff1d..00000000 --- a/docs/deploying/cgi.rst +++ /dev/null @@ -1,42 +0,0 @@ -CGI -=== - -If all other deployment methods do not work, CGI will work for sure. CGI -is supported by all major servers but usually has a less-than-optimal -performance. - -This is also the way you can use a Flask application on Google's -`AppEngine`_, there however the execution does happen in a CGI-like -environment. The application's performance is unaffected because of that. - -.. _AppEngine: http://code.google.com/appengine/ - -Creating a `.cgi` file ----------------------- - -First you need to create the CGI application file. Let's call it -`yourapplication.cgi`:: - - #!/usr/bin/python - from wsgiref.handlers import CGIHandler - from yourapplication import app - - CGIHandler().run(app) - -If you're running Python 2.4 you will need the :mod:`wsgiref` package. Python -2.5 and higher ship this as part of the standard library. - -Server Setup ------------- - -Usually there are two ways to configure the server. Either just copy the -`.cgi` into a `cgi-bin` (and use `mod_rerwite` or something similar to -rewrite the URL) or let the server point to the file directly. - -In Apache for example you can put a like like this into the config: - -.. sourcecode:: apache - - ScriptAlias /app /path/to/the/application.cgi - -For more information consult the documentation of your webserver. diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst deleted file mode 100644 index b549ddfd..00000000 --- a/docs/deploying/fastcgi.rst +++ /dev/null @@ -1,128 +0,0 @@ -FastCGI -======= - -A very popular deployment setup on servers like `lighttpd`_ and `nginx`_ -is FastCGI. To use your WSGI application with any of them you will need -a FastCGI server first. - -The most popular one is `flup`_ which we will use for this guide. Make -sure to have it installed. - -Creating a `.fcgi` file ------------------------ - -First you need to create the FastCGI server file. Let's call it -`yourapplication.fcgi`:: - - #!/usr/bin/python - from flup.server.fcgi import WSGIServer - from yourapplication import app - - WSGIServer(app).run() - -This is enough for Apache to work, however lighttpd and nginx need a -socket to communicate with the FastCGI server. For that to work you -need to pass the path to the socket to the -:class:`~flup.server.fcgi.WSGIServer`:: - - WSGIServer(application, bindAddress='/path/to/fcgi.sock').run() - -The path has to be the exact same path you define in the server -config. - -Save the `yourapplication.fcgi` file somewhere you will find it again. -It makes sense to have that in `/var/www/yourapplication` or something -similar. - -Make sure to set the executable bit on that file so that the servers -can execute it:: - - # chmod +x /var/www/yourapplication/yourapplication.fcgi - -Configuring lighttpd --------------------- - -A basic FastCGI configuration for lighttpd looks like that:: - - fastcgi.server = ("/yourapplication" => - "yourapplication" => ( - "socket" => "/tmp/yourapplication-fcgi.sock", - "bin-path" => "/var/www/yourapplication/yourapplication.fcgi", - "check-local" => "disable" - ) - ) - -This configuration binds the application to `/yourapplication`. If you -want the application to work in the URL root you have to work around a -lighttpd bug with the :class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix` -middleware. - -Make sure to apply it only if you are mounting the application the URL -root. - -Configuring nginx ------------------ - -Installing FastCGI applications on nginx is a bit tricky because by default -some FastCGI parameters are not properly forwarded. - -A basic FastCGI configuration for nginx looks like this:: - - location /yourapplication/ { - include fastcgi_params; - if ($uri ~ ^/yourapplication/(.*)?) { - set $path_url $1; - } - fastcgi_param PATH_INFO $path_url; - fastcgi_param SCRIPT_NAME /yourapplication; - fastcgi_pass unix:/tmp/yourapplication-fcgi.sock; - } - -This configuration binds the application to `/yourapplication`. If you want -to have it in the URL root it's a bit easier because you don't have to figure -out how to calculate `PATH_INFO` and `SCRIPT_NAME`:: - - location /yourapplication/ { - include fastcgi_params; - fastcgi_param PATH_INFO $fastcgi_script_name; - fastcgi_param SCRIPT_NAME ""; - fastcgi_pass unix:/tmp/yourapplication-fcgi.sock; - } - -Since Nginx doesn't load FastCGI apps, you have to do it by yourself. You -can either write an `init.d` script for that or execute it inside a screen -session:: - - $ screen - $ /var/www/yourapplication/yourapplication.fcgi - -Debugging ---------- - -FastCGI deployments tend to be hard to debug on most webservers. Very often the -only thing the server log tells you is something along the lines of "premature -end of headers". In order to debug the application the only thing that can -really give you ideas why it breaks is switching to the correct user and -executing the application by hand. - -This example assumes your application is called `application.fcgi` and that your -webserver user is `www-data`:: - - $ su www-data - $ cd /var/www/yourapplication - $ python application.fcgi - Traceback (most recent call last): - File "yourapplication.fcg", line 4, in - ImportError: No module named yourapplication - -In this case the error seems to be "yourapplication" not being on the python -path. Common problems are: - -- relative paths being used. Don't rely on the current working directory -- the code depending on environment variables that are not set by the - web server. -- different python interpreters being used. - -.. _lighttpd: http://www.lighttpd.net/ -.. _nginx: http://nginx.net/ -.. _flup: http://trac.saddi.com/flup diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst deleted file mode 100644 index a59e4e9a..00000000 --- a/docs/deploying/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -Deployment Options -================== - -Depending on what you have available there are multiple ways to run Flask -applications. A very common method is to use the builtin server during -development and maybe behind a proxy for simple applications, but there -are more options available. - -If you have a different WSGI server look up the server documentation about -how to use a WSGI app with it. Just remember that your application object -is the actual WSGI application. - -.. toctree:: - :maxdepth: 2 - - mod_wsgi - cgi - fastcgi - others diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst deleted file mode 100644 index a7bbc114..00000000 --- a/docs/deploying/mod_wsgi.rst +++ /dev/null @@ -1,80 +0,0 @@ -mod_wsgi (Apache) -================= - -If you are using the `Apache`_ webserver you should consider using `mod_wsgi`_. - -.. _Apache: http://httpd.apache.org/ - -Installing `mod_wsgi` ---------------------- - -If you don't have `mod_wsgi` installed yet you have to either install it using -a package manager or compile it yourself. - -The mod_wsgi `installation instructions`_ cover source installations on UNIX -systems. - -If you are using ubuntu / debian you can apt-get it and activate it as follows:: - - # apt-get install libapache2-mod-wsgi - -On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by using -pkg_add:: - - # pkg_add -r mod_wsgi - -If you are using pkgsrc you can install `mod_wsgi` by compiling the -`www/ap2-wsgi` package. - -If you encounter segfaulting child processes after the first apache reload you -can safely ignore them. Just restart the server. - -Creating a `.wsgi` file ------------------------ - -To run your application you need a `yourapplication.wsgi` file. This file -contains the code `mod_wsgi` is executing on startup to get the application -object. The object called `application` in that file is then used as -application. - -For most applications the following file should be sufficient:: - - from yourapplication import app as application - -If you don't have a factory function for application creation but a singleton -instance you can directly import that one as `application`. - -Store that file somewhere that you will find it again (e.g.: -`/var/www/yourapplication`) and make sure that `yourapplication` and all -the libraries that are in use are on the python load path. If you don't -want to install it system wide consider using a `virtual python`_ instance. - -Configuring Apache ------------------- - -The last thing you have to do is to create an Apache configuration file for -your application. In this example we are telling `mod_wsgi` to execute the -application under a different user for security reasons: - -.. sourcecode:: apache - - - ServerName example.com - - WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5 - WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi - - - WSGIProcessGroup yourapplication - WSGIApplicationGroup %{GLOBAL} - Order deny,allow - Allow from all - - - -For more information consult the `mod_wsgi wiki`_. - -.. _mod_wsgi: http://code.google.com/p/modwsgi/ -.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide -.. _virtual python: http://pypi.python.org/pypi/virtualenv -.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/ diff --git a/docs/deploying/others.rst b/docs/deploying/others.rst deleted file mode 100644 index 4e2f966c..00000000 --- a/docs/deploying/others.rst +++ /dev/null @@ -1,48 +0,0 @@ -Other Servers -============= - -There are popular servers written in Python that allow the execution of -WSGI applications as well. Keep in mind though that some of these servers -were written for very specific applications and might not work as well for -standard WSGI application such as Flask powered ones. - - -Tornado --------- - -`Tornado`_ is an open source version of the scalable, non-blocking web -server and tools that power `FriendFeed`_. Because it is non-blocking and -uses epoll, it can handle thousands of simultaneous standing connections, -which means it is ideal for real-time web services. Integrating this -service with Flask is a trivial task:: - - from tornado.wsgi import WSGIContainer - from tornado.httpserver import HTTPServer - from tornado.ioloop import IOLoop - from yourapplication import app - - http_server = HTTPServer(WSGIContainer(app)) - http_server.listen(5000) - IOLoop.instance().start() - - -.. _Tornado: http://www.tornadoweb.org/ -.. _FriendFeed: http://friendfeed.com/ - - -Gevent -------- - -`Gevent`_ is a coroutine-based Python networking library that uses -`greenlet`_ to provide a high-level synchronous API on top of `libevent`_ -event loop:: - - from gevent.wsgi import WSGIServer - from yourapplication import app - - http_server = WSGIServer(('', 5000), app) - http_server.serve_forever() - -.. _Gevent: http://www.gevent.org/ -.. _greenlet: http://codespeak.net/py/0.9.2/greenlet.html -.. _libevent: http://monkey.org/~provos/libevent/ diff --git a/docs/design.rst b/docs/design.rst deleted file mode 100644 index c4fd32dd..00000000 --- a/docs/design.rst +++ /dev/null @@ -1,147 +0,0 @@ -Design Decisions in Flask -========================= - -If you are curious why Flask does certain things the way it does and not -differently, this section is for you. This should give you an idea about -some of the design decisions that may appear arbitrary and surprising at -first, especially in direct comparison with other frameworks. - - -The Explicit Application Object -------------------------------- - -A Python web application based on WSGI has to have one central callable -object that implements the actual application. In Flask this is an -instance of the :class:`~flask.Flask` class. Each Flask application has -to create an instance of this class itself and pass it the name of the -module, but why can't Flask do that itself? - -Without such an explicit application object the following code:: - - from flask import Flask - app = Flask(__name__) - - @app.route('/') - def index(): - return 'Hello World!' - -Would look like this instead:: - - from hypothetical_flask import route - - @route('/') - def index(): - return 'Hello World!' - -There are three major reasons for this. The most important one is that -implicit application objects require that there may only be one class at -the time. There are ways to fake multiple application with a single -application object, like maintaining a stack of applications, but this -causes some problems I won't outline here in detail. Now the question is: -when does a microframework need more than one application at the same -time? A good example for this is unittesting. When you want to test -something it can be very helpful to create a minimal application to test -specific behavior. When the application object is deleted everything it -allocated will be freed again. - -Another thing that becomes possible when you have an explicit object laying -around in your code is that you can subclass the base class -(:class:`~flask.Flask`) to alter specific behaviour. This would not be -possible without hacks if the object were created ahead of time for you -based on a class that is not exposed to you. - -But there is another very important reason why Flask depends on an -explicit instanciation of that class: the package name. Whenever you -create a Flask instance you usually pass it `__name__` as package name. -Flask depends on that information to properly load resources relative -to your module. With Python's outstanding support for reflection it can -then access the package to figure out where the templates and static files -are stored (see :meth:`~flask.Flask.open_resource`). Now obviously there -are frameworks around that do not need any configuration and will still be -able to load templates relative to your application module. But they have -to use the current working directory for that, which is a very unreliable -way to determine where the application is. The current working directory -is process-wide and if you are running multiple applications in one -process (which could happen in a webserver without you knowing) the paths -will be off. Worse: many webservers do not set the working directory to -the directory of your application but to the document root which does not -have to be the same folder. - -The third reason is "explicit is better than implicit". That object is -your WSGI application, you don't have to remember anything else. If you -want to apply a WSGI middleware, just wrap it and you're done (though -there are better ways to do that so that you do not lose the reference -to the application object :meth:`~flask.Flask.wsgi_app`). - -One Template Engine -------------------- - -Flask decides on one template engine: Jinja2. Why doesn't Flask have a -pluggable template engine interface? You can obviously use a different -template engine, but Flask will still configure Jinja2 for you. While -that limitation that Jinja2 is *always* configured will probably go away, -the decision to bundle one template engine and use that will not. - -Template engines are like programming languages and each of those engines -has a certain understanding about how things work. On the surface they -all work the same: you tell the engine to evaluate a template with a set -of variables and take the return value as string. - -But that's about where similarities end. Jinja2 for example has an -extensive filter system, a certain way to do template inheritance, support -for reusable blocks (macros) that can be used from inside templates and -also from Python code, uses unicode for all operations, supports -iterative template rendering, configurable syntax and more. On the other -hand an engine like Genshi is based on XML stream evaluation, template -inheritance by taking the availability of XPath into account and more. -Mako on the other hand treats templates similar to Python modules. - -When it comes to connecting a template engine with an application or -framework there is more than just rendering templates. For instance, -Flask uses Jinja2's extensive autoescaping support. Also it provides -ways to access macros from Jinja2 templates. - -A template abstraction layer that would not take the unique features of -the template engines away is a science on its own and a too large -undertaking for a microframework like Flask. - - -Micro with Dependencies ------------------------ - -Why does Flask call itself a microframework and yet it depends on two -libraries (namely Werkzeug and Jinja2). Why shouldn't it? If we look -over to the Ruby side of web development there we have a protocol very -similar to WSGI. Just that it's called Rack there, but besides that it -looks very much like a WSGI rendition for Ruby. But nearly all -applications in Ruby land do not work with Rack directly, but on top of a -library with the same name. This Rack library has two equivalents in -Python: WebOb (formerly Paste) and Werkzeug. Paste is still around but -from my understanding it's sort of deprecated in favour of WebOb. The -development of WebOb and Werkzeug started side by side with similar ideas -in mind: be a good implementation of WSGI for other applications to take -advantage. - -Flask is a framework that takes advantage of the work already done by -Werkzeug to properly interface WSGI (which can be a complex task at -times). Thanks to recent developments in the Python package -infrastructure, packages with depencencies are no longer an issue and -there are very few reasons against having libraries that depend on others. - - -Thread Locals -------------- - -Flask uses thread local objects (context local objects in fact, they -support greenlet contexts as well) for request, session and an extra -object you can put your own things on (:data:`~flask.g`). Why is that and -isn't that a bad idea? - -Yes it is usually not such a bright idea to use thread locals. They cause -troubles for servers that are not based on the concept of threads and make -large applications harder to maintain. However Flask is just not designed -for large applications or asyncronous servers. Flask wants to make it -quick and easy to write a traditional web application. - -Also see the :ref:`becomingbig` section of the documentation for some -inspiration for larger applications based on Flask. diff --git a/docs/flaskext.py b/docs/flaskext.py deleted file mode 100644 index 33f47449..00000000 --- a/docs/flaskext.py +++ /dev/null @@ -1,86 +0,0 @@ -# flasky extensions. flasky pygments style based on tango style -from pygments.style import Style -from pygments.token import Keyword, Name, Comment, String, Error, \ - Number, Operator, Generic, Whitespace, Punctuation, Other, Literal - - -class FlaskyStyle(Style): - background_color = "#f8f8f8" - default_style = "" - - styles = { - # No corresponding class for the following: - #Text: "", # class: '' - Whitespace: "underline #f8f8f8", # class: 'w' - Error: "#a40000 border:#ef2929", # class: 'err' - Other: "#000000", # class 'x' - - Comment: "italic #8f5902", # class: 'c' - Comment.Preproc: "noitalic", # class: 'cp' - - Keyword: "bold #004461", # class: 'k' - Keyword.Constant: "bold #004461", # class: 'kc' - Keyword.Declaration: "bold #004461", # class: 'kd' - Keyword.Namespace: "bold #004461", # class: 'kn' - Keyword.Pseudo: "bold #004461", # class: 'kp' - Keyword.Reserved: "bold #004461", # class: 'kr' - Keyword.Type: "bold #004461", # class: 'kt' - - Operator: "#582800", # class: 'o' - Operator.Word: "bold #004461", # class: 'ow' - like keywords - - Punctuation: "bold #000000", # class: 'p' - - # because special names such as Name.Class, Name.Function, etc. - # are not recognized as such later in the parsing, we choose them - # to look the same as ordinary variables. - Name: "#000000", # class: 'n' - Name.Attribute: "#c4a000", # class: 'na' - to be revised - Name.Builtin: "#004461", # class: 'nb' - Name.Builtin.Pseudo: "#3465a4", # class: 'bp' - Name.Class: "#000000", # class: 'nc' - to be revised - Name.Constant: "#000000", # class: 'no' - to be revised - Name.Decorator: "#888", # class: 'nd' - to be revised - Name.Entity: "#ce5c00", # class: 'ni' - Name.Exception: "bold #cc0000", # class: 'ne' - Name.Function: "#000000", # class: 'nf' - Name.Property: "#000000", # class: 'py' - Name.Label: "#f57900", # class: 'nl' - Name.Namespace: "#000000", # class: 'nn' - to be revised - Name.Other: "#000000", # class: 'nx' - Name.Tag: "bold #004461", # class: 'nt' - like a keyword - Name.Variable: "#000000", # class: 'nv' - to be revised - Name.Variable.Class: "#000000", # class: 'vc' - to be revised - Name.Variable.Global: "#000000", # class: 'vg' - to be revised - Name.Variable.Instance: "#000000", # class: 'vi' - to be revised - - Number: "#990000", # class: 'm' - - Literal: "#000000", # class: 'l' - Literal.Date: "#000000", # class: 'ld' - - String: "#4e9a06", # class: 's' - String.Backtick: "#4e9a06", # class: 'sb' - String.Char: "#4e9a06", # class: 'sc' - String.Doc: "italic #8f5902", # class: 'sd' - like a comment - String.Double: "#4e9a06", # class: 's2' - String.Escape: "#4e9a06", # class: 'se' - String.Heredoc: "#4e9a06", # class: 'sh' - String.Interpol: "#4e9a06", # class: 'si' - String.Other: "#4e9a06", # class: 'sx' - String.Regex: "#4e9a06", # class: 'sr' - String.Single: "#4e9a06", # class: 's1' - String.Symbol: "#4e9a06", # class: 'ss' - - Generic: "#000000", # class: 'g' - Generic.Deleted: "#a40000", # class: 'gd' - Generic.Emph: "italic #000000", # class: 'ge' - Generic.Error: "#ef2929", # class: 'gr' - Generic.Heading: "bold #000080", # class: 'gh' - Generic.Inserted: "#00A000", # class: 'gi' - Generic.Output: "#888", # class: 'go' - Generic.Prompt: "#745334", # class: 'gp' - Generic.Strong: "bold #000000", # class: 'gs' - Generic.Subheading: "bold #800080", # class: 'gu' - Generic.Traceback: "bold #a40000", # class: 'gt' - } diff --git a/docs/foreword.rst b/docs/foreword.rst deleted file mode 100644 index deeff8ca..00000000 --- a/docs/foreword.rst +++ /dev/null @@ -1,94 +0,0 @@ -Foreword -======== - -Read this before you get started with Flask. This hopefully answers some -questions about the intention of the project, what it aims at and when you -should or should not be using it. - -What does Micro Mean? ---------------------- - -The micro in microframework for me means on the one hand being small in -size and complexity but on the other hand also that the complexity of the -applications that are written with these frameworks do not exceed a -certain size. A microframework like Flask sacrifices a few things in -order to be approachable and to be as concise as possible. - -For example Flask uses thread local objects internally so that you don't -have to pass objects around from function to function within a request in -order to stay threadsafe. While this is a really easy approach and saves -you a lot of time, it also does not scale well to large applications. -It's especially painful for more complex unittests and when you suddenly -have to deal with code being executed outside of the context of a request -(for example if you have cronjobs). - -Flask provides some tools to deal with the downsides of this approach but -the core problem of this approach obviously stays. It is also based on -convention over configuration which means that a lot of things are -preconfigured in Flask and will work well for smaller applications but not -so much for larger ones (where and how it looks for templates, static -files etc.) - -But don't worry if your application suddenly grows larger than it was -initially and you're afraid Flask might not grow with it. Even with -larger frameworks you sooner or later will find out that you need -something the framework just cannot do for you without modification. -If you are ever in that situation, check out the :ref:`becomingbig` -chapter. - -A Framework and An Example --------------------------- - -Flask is not only a microframework, it is also an example. Based on -Flask, there will be a series of blog posts that explain how to create a -framework. Flask itself is just one way to implement a framework on top -of existing libraries. Unlike many other microframeworks Flask does not -try to implement anything on its own, it reuses existing code. - -Web Development is Dangerous ----------------------------- - -I'm not even joking. Well, maybe a little. If you write a web -application you are probably allowing users to register and leave their -data on your server. The users are entrusting you with data. And even if -you are the only user that might leave data in your application, you still -want that data to be stored in a secure manner. - -Unfortunately there are many ways security of a web application can be -compromised. Flask protects you against one of the most common security -problems of modern web applications: cross site scripting (XSS). Unless -you deliberately mark insecure HTML as secure Flask (and the underlying -Jinja2 template engine) have you covered. But there are many more ways to -cause security problems. - -Whenever something is dangerous where you have to watch out, the -documentation will tell you so. Some of the security concerns of web -development are far more complex than one might think and often we all end -up in situations where we think "well, this is just far fetched, how could -that possibly be exploited" and then an intelligent guy comes along and -figures a way out to exploit that application. And don't think, your -application is not important enough for hackers to take notice. Depending -ont he kind of attack, chances are there are automated botnets out there -trying to figure out how to fill your database with viagra adverisments. - -So always keep that in mind when doing web development. - -Target Audience ---------------- - -Is Flask for you? Is your application small-ish (less than 4000 lines of -Python code) and does not depend on too complex database structures, Flask -is the Framework for you. It was designed from the ground up to be easy -to use, based on established principles, good intentions and on top of two -established libraries in widespread usage. - -Flask serves two purposes: it's an example of how to create a minimal and -opinionated framework on top of Werkzeug to show how this can be done, and -to provide people with a simple tool to prototype larger applications or -to implement small and medium sized applications. - -If you suddenly discover that your application grows larger than -originally intended, head over to the :ref:`becomingbig` section to see -some possible solutions for larger applications. - -Satisfied? Then head over to the :ref:`installation`. diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 1f3b6460..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,68 +0,0 @@ -Welcome to Flask -================ - -.. image:: _static/logo-full.png - :alt: The Flask Logo with Subtitle - :class: floatingflask - -Welcome to Flask's documentation. This documentation is divided in -different parts. I would suggest to get started with the -:ref:`installation` and then heading over to the :ref:`quickstart`. -Besides the quickstart there is also a more detailed :ref:`tutorial` that -shows how to create a complete (albeit small) application with Flask. If -you rather want to dive into all the internal parts of Flask, check out -the :ref:`api` documentation. Common patterns are described in the -:ref:`patterns` section. - -Flask also depends on two external libraries: the `Jinja2`_ template -engine and the `Werkzeug`_ WSGI toolkit. both of which are not documented -here. If you want to dive into their documentation check out the -following links: - -- `Jinja2 Documentation `_ -- `Werkzeug Documentation `_ - -.. _Jinja2: http://jinja.pocoo.org/2/ -.. _Werkzeug: http://werkzeug.pocoo.org/ - -User's Guide ------------- - -This part of the documentation is written text and should give you an idea -how to work with Flask. It's a series of step-by-step instructions for -web development. - -.. toctree:: - :maxdepth: 2 - - foreword - installation - quickstart - tutorial/index - testing - patterns/index - deploying/index - becomingbig - -Additional Notes ----------------- - -Design notes, legal information and changelog are here for the interested: - -.. toctree:: - :maxdepth: 2 - - design - license - changelog - -API Reference -------------- - -If you are looking for information on a specific function, class or -method, this part of the documentation is for you: - -.. toctree:: - :maxdepth: 2 - - api diff --git a/docs/installation.rst b/docs/installation.rst deleted file mode 100644 index bb85b405..00000000 --- a/docs/installation.rst +++ /dev/null @@ -1,169 +0,0 @@ -.. _installation: - -Installation -============ - -Flask is a microframework and yet it depends on external libraries. There -are various ways how you can install that library and this explains each -way and why there are multiple ways. - -Flask depends on two external libraries: `Werkzeug -`_ and `Jinja2 `_. -The first on is responsible for interfacing WSGI the latter to render -templates. Now you are maybe asking, what is WSGI? WSGI is a standard -in Python that is basically responsible for ensuring that your application -is behaving in a specific way that you can run it on different -environments (for example on a local development server, on an Apache2, on -lighttpd, on Google's App Engine or whatever you have in mind). - -So how do you get all that on your computer in no time? The most kick-ass -method is virtualenv, so let's look at that first. - -virtualenv ----------- - -Virtualenv is what you want to use during development and in production if -you have shell access. So first: what does virtualenv do? If you are -like me and you like Python, chances are you want to use it for another -project as well. Now the more projects you have, the more likely it is -that you will be working with different versions of Python itself or a -library involved. Because let's face it: quite often libraries break -backwards compatibility and it's unlikely that your application will -not have any dependencies, that just won't happen. So virtualenv for the -rescue! - -It basically makes it possible to have multiple side-by-side -"installations" of Python, each for your own project. It's not actually -an installation but a clever way to keep things separated. - -So let's see how that works! - -If you are on OS X or Linux chances are that one of the following two -commands will for for you:: - - $ sudo easy_install virtualenv - -or even better:: - - $ sudo pip install virtualenv - -Changes are you have virtualenv installed on your system then. Maybe it's -even in your package manager (on ubuntu try ``sudo apt-get install -python-virtualenv``). - -If you are on Windows and missing the `easy_install` command you have to -install it first. Check the :ref:`windows-easy-install` section for more -information about how to do that. Once you have it installed, run the -same commands as above, but without the `sudo` part. - -So now that you have virtualenv running just fire up a shell and create -your own environment. I usually create a folder and a `env` folder -within:: - - $ mkdir myproject - $ cd myproject - $ virtualenv env - New python executable in env/bin/python - Installing setuptools............done. - -Now you only have to activate it, whenever you work with it. On OS X and -Linux do the following:: - - $ source env/bin/activate - -If you are a Windows user, the following command is for you:: - - $ env\scripts\activate - -Either way, you should now be using your virtualenv (see how the prompt of -your shell has changed to show the virtualenv). - -Now you can just enter the following command to get Flask activated in -your virtualenv:: - - $ easy_install Flask - -A few seconds later you are good to go. - - -System Wide Installation ------------------------- - -This is possible as well, but I would not recommend it. Just run -`easy_install` with root rights:: - - sudo easy_install Flask - -(Run it in an Admin shell on Windows systems and without the `sudo`). - - -Leaving on the Edge -------------------- - -You want to work with the latest version of Flask, there are two ways: you -can either let `easy_install` pull in the development version or tell it -to operate on a git checkout. Either way it's recommended to do that in a -virtualenv. - -Get the git checkout in a new virtualenv and run in develop mode:: - - $ git clone http://github.com/mitsuhiko/flask.git - Initialized empty Git repository in ~/dev/flask/.git/ - $ cd flask - $ virtualenv env - $ source env/bin/activate - New python executable in env/bin/python - Installing setuptools............done. - $ python setup.py develop - ... - Finished processing dependencies for Flask - -This will pull in the depdenencies and activate the git head as current -version. Then you just have to ``git pull origin`` to get the latest -version. - -To just get the development version without git, do this instead:: - - $ mkdir flask - $ cd flask - $ virtualenv env - $ source env/bin/activate - New python executable in env/bin/python - Installing setuptools............done. - $ easy_install Flask==dev - ... - Finished processing dependencies for Flask==dev - -.. _windows-easy-install: - -`easy_install` on Windows -------------------------- - -On Windows installation of `easy_install` is a little bit tricker because -on Windows slightly different rules apply, but it's not a biggy. The -easiest way to accomplish that is downloading the `ez_setup.py`_ file and -running it. (Double clicking should do the trick) - -Once you have done that it's important to add the `easy_install` command -and other Python scripts to the path. To do that you have to add the -Python installation's Script folder to the `PATH` variable. - -To do that, click right on your "Computer" desktop icon and click -"Properties". On Windows Vista and Windows 7 then click on "Advanced System -settings", on Windows XP click on the "Advanced" tab instead. Then click -on the "Environment variables" button and double click on the "Path" -variable in the "System variables" section. - -There append the path of your Python interpreter's Script folder to the -end of the last (make sure you delimit it from existing values with a -semicolon). Assuming you are using Python 2.6 on the default path, add -the following value:: - - ;C:\Python26\Scripts - -Then you are done. To check if it worked, open the cmd and execute -"easy_install". If you have UAC enabled it should prompt you for admin -privileges. - - -.. _ez_setup.py: http://peak.telecommunity.com/dist/ez_setup.py diff --git a/docs/license.rst b/docs/license.rst deleted file mode 100644 index 918a75b1..00000000 --- a/docs/license.rst +++ /dev/null @@ -1,21 +0,0 @@ -License -======= - -Flask is licensed under a three clause `BSD License`_. It basically -means: do whatever you want with it as long as the copyright in Flask -sticks around, the conditions are not modified and the disclaimer is -present. Furthermore you must not use the names of the authors to promote -derivates of the software without written consent. - -.. _BSD License: - http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29 - -Authors -------- - -.. include:: ../AUTHORS - -License Text ------------- - -.. include:: ../LICENSE diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 3ad12879..00000000 --- a/docs/make.bat +++ /dev/null @@ -1,139 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Flask.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Flask.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% _build/devhelp - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/docs/patterns/flashing.rst b/docs/patterns/flashing.rst deleted file mode 100644 index fca9a9e1..00000000 --- a/docs/patterns/flashing.rst +++ /dev/null @@ -1,81 +0,0 @@ -.. _message-flashing-pattern: - -Message Flashing -================ - -Good applications and user interfaces are all about feedback. If the user -does not get enough feedback he will probably end up hating the -application. Flask provides a really simple way to give feedback to a -user with the flashing system. The flashing system basically makes it -possible to record a message at the end of a request and access it next -request and only next request. This is usually combined with a layout -template that does this. - -So here a full example:: - - from flask import flash, redirect, url_for, render_template - - @app.route('/') - def index(): - return render_template('index.html') - - @app.route('/login', methods=['GET', 'POST']) - def login(): - error = None - if request.method == 'POST': - if request.form['username'] != 'admin' or \ - request.form['password'] != 'secret': - error = 'Invalid credentials' - else: - flash('You were sucessfully logged in') - return redirect(url_for('index')) - return render_template('login.html', error=error) - -And here the ``layout.html`` template which does the magic: - -.. sourcecode:: html+jinja - - - My Application - {% with messages = get_flashed_messages() %} - {% if messages %} -
    - {% for message in messages %} -
  • {{ message }}
  • - {% endfor %} -
- {% endif %} - {% endwith %} - {% block body %}{% endblock %} - -And here the index.html template: - -.. sourcecode:: html+jinja - - {% extends "layout.html" %} - {% block body %} -

Overview

-

Do you want to log in? - {% endblock %} - -And of course the login template: - -.. sourcecode:: html+jinja - - {% extends "layout.html" %} - {% block body %} -

Login

- {% if error %} -

Error: {{ error }} - {% endif %} -

-
-
Username: -
-
Password: -
-
-

-

- {% endblock %} diff --git a/docs/patterns/index.rst b/docs/patterns/index.rst deleted file mode 100644 index 8122deb7..00000000 --- a/docs/patterns/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _patterns: - -Patterns for Flask -================== - -Certain things are common enough that the changes are high you will find -them in most web applications. For example quite a lot of applications -are using relational databases and user authentication. In that case, -changes are they will open a database connection at the beginning of the -request and get the information of the currently logged in user. At the -end of the request, the database connection is closed again. - -.. toctree:: - :maxdepth: 2 - - packages - sqlite3 - sqlalchemy - wtforms - templateinheritance - flashing - jquery diff --git a/docs/patterns/jquery.rst b/docs/patterns/jquery.rst deleted file mode 100644 index f087e6f0..00000000 --- a/docs/patterns/jquery.rst +++ /dev/null @@ -1,167 +0,0 @@ -AJAX with jQuery -================ - -`jQuery`_ is a small JavaScript library commonly used to simplify working -with the DOM and JavaScript in general. It is the perfect tool to make -web applications more dynamic by exchanging JSON between server and -client. - -JSON itself is a very lightweight transport format, very similar to how -Python primitives (numbers, strings, dicts and lists) look like which is -widely supported and very easy to parse. It became popular a few years -ago and quickly replaced XML as transport format in web applications. - -If you have Python 2.6 JSON will work out of the box, in Python 2.5 you -will have to install the `simplejson`_ library from PyPI. - -.. _jQuery: http://jquery.com/ -.. _simplejson: http://pypi.python.org/pypi/simplejson - -Loading jQuery --------------- - -In order to use jQuery, you have to download it first and place it in the -static folder of your application and then ensure it's loaded. Ideally -you have a layout template that is used for all pages where you just have -to add a script statement to your `head` to load jQuery: - -.. sourcecode:: html - - - -Another method is using Google's `AJAX Libraries API -`_ to load jQuery: - -.. sourcecode:: html - - - -In this case you don't have to put jQuery into your static folder, it will -instead be loaded from Google directly. This has the advantage that your -website will probably load faster for users if they were to at least one -other website before using the same jQuery version from Google because it -will already be in the browser cache. Downside is that if you don't have -network connectivity during development jQuery will not load. - -Where is My Site? ------------------ - -Do you know where your application is? If you are developing the answer -is quite simple: it's on localhost port something and directly on the root -of that server. But what if you later decide to move your application to -a different location? For example to ``http://example.com/myapp``? On -the server side this never was a problem because we were using the handy -:func:`~flask.url_for` function that did could answer that question for -us, but if we are using jQuery we should better not hardcode the path to -the application but make that dynamic, so how can we do that? - -A simple method would be to add a script tag to our page that sets a -global variable to the prefix to the root of the application. Something -like this: - -.. sourcecode:: html+jinja - - - -The ``|safe`` is necessary so that Jinja does not escape the JSON encoded -string with HTML rules. Usually this would be necessary, but we are -inside a `script` block here where different rules apply. - -.. admonition:: Information for Pros - - In HTML the `script` tag is declared `CDATA` which means that entities - will not be parsed. Everything until ```` is handled as script. - This also means that there must never be any ``"|tojson|safe }`` is rendered as - ``"<\/script>"``). - - -JSON View Functions -------------------- - -Now let's create a server side function that accepts two URL arguments of -numbers which should be added together and then sent back to the -application in a JSON object. This is a really ridiculous example and is -something you usually would do on the client side alone, but a simple -example that shows how you would use jQuery and Flask nonetheless:: - - from flask import Flask, jsonify, render_template, request - app = Flask(__name__) - - @app.route('/_add_numbers') - def add_numbers(): - a = request.args.get('a', 0, type=int) - b = request.args.get('b', 0, type=int) - return jsonify(result=a + b) - - @app.route('/') - def index(): - return render_template('index.html') - -As you can see I also added an `index` method here that renders a -template. This template will load jQuery as above and have a little form -we can add two numbers and a link to trigger the function on the server -side. - -Note that we are using the :meth:`~werkzeug.MultiDict.get` method here -which will never fail. If the key is missing a default value (here ``0``) -is returned. Furthermore it can convert values to a specific type (like -in our case `int`). This is especially handy for code that is -triggered by a script (APIs, JavaScript etc.) because you don't need -special error reporting in that case. - -The HTML --------- - -You index.html template either has to extend a `layout.html` template with -jQuery loaded and the `$SCRIPT_ROOT` variable set, or do that on the top. -Here the HTML code needed for our little application (`index.html`). -Notice that we also drop the script directly into the HTML here. It is -usually a better idea to have that in a separate script file: - -.. sourcecode:: html - - -

jQuery Example

-

+ - = - ? -

calculate server side - -I won't got into detail here about how jQuery works, just a very quick -explanation of the little bit of code above: - -1. ``$(function() { ... })`` specifies code that should run once the - browser is done loading the basic parts of the page. -2. ``#('selector')`` selects an element and lets you operate on it. -3. ``element.bind('event', func)`` specifies a function that should run - when the user clicked on the element. If that function returns - `false`, the default behaviour will not kick in (in this case, navigate - to the `#` URL). -4. ``$.getJSON(url, data, func)`` sends a `GET` request to `url` and will - send the contents of the `data` object as query parameters. Once the - data arrived, it will call the given function with the return value as - argument. Note that we can use the `$SCRIPT_ROOT` variable here that - we set earlier. - -If you don't get the whole picture, download the `sourcecode -for this example -`_ -from github. diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst deleted file mode 100644 index 4d54e49c..00000000 --- a/docs/patterns/packages.rst +++ /dev/null @@ -1,86 +0,0 @@ -.. _larger-applications: - -Larger Applications -=================== - -For larger applications it's a good idea to use a package instead of a -module. That is quite simple. Imagine a small application looks like -this:: - - /yourapplication - /yourapplication.py - /static - /style.css - /templates - layout.html - index.html - login.html - ... - -To convert that into a larger one, just create a new folder -`yourapplication` inside the existing one and move everything below it. -Then rename `yourapplication.py` to `__init__.py`. (Make sure to delete -all `.pyc` files first, otherwise things would most likely break) - -You should then end up with something like that:: - - /yourapplication - /yourapplication - /__init__.py - /static - /style.css - /templates - layout.html - index.html - login.html - ... - -But how do you run your application now? The naive ``python -yourapplication/__init__.py`` will not work. Let's just say that Python -does not want modules in packages to be the startup file. But that is not -a big problem, just add a new file called `runserver.py` next to the inner -`yourapplication` folder with the following contents:: - - from yourapplication import app - app.run(debug=True) - -What did we gain from this? Now we can restructure the application a bit -into multiple modules. The only thing you have to remember is the -following quick checklist: - -1. the `Flask` application object creation has to be in the - `__init__.py` file. That way each module can import it safely and the - `__name__` variable will resolve to the correct package. -2. all the view functions (the ones with a :meth:`~flask.Flask.route` - decorator on top) have to be imported when in the `__init__.py` file. - Not the object itself, but the module it is in. Do the importing at - the *bottom* of the file. - -Here an example `__init__.py`:: - - from flask import Flask - app = Flask(__name__) - - import yourapplication.views - -And this is what `views.py` would look like:: - - from yourapplication import app - - @app.route('/') - def index(): - return 'Hello World!' - -.. admonition:: Circular Imports - - Every Python programmer hates them, and yet we just added some: - circular imports (That's when two modules depend on each other. In this - case `views.py` depends on `__init__.py`). Be advised that this is a - bad idea in general but here it is actually fine. The reason for this is - that we are not actually using the views in `__init__.py` and just - ensuring the module is imported and we are doing that at the bottom of - the file. - - There are still some problems with that approach but if you want to use - decorators there is no way around that. Check out the - :ref:`becomingbig` section for some inspiration how to deal with that. diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst deleted file mode 100644 index 32d41c08..00000000 --- a/docs/patterns/sqlalchemy.rst +++ /dev/null @@ -1,193 +0,0 @@ -.. _sqlalchemy-pattern: - -SQLAlchemy in Flask -=================== - -Many people prefer `SQLAlchemy`_ for database access. In this case it's -encouraged to use a package instead of a module for your flask application -and drop the models into a separate module (:ref:`larger-applications`). -While that is not necessary, it makes a lot of sense. - -There are three very common ways to use SQLAlchemy. I will outline each -of them here: - -Declarative ------------ - -The declarative extension in SQLAlchemy is the most recent method of using -SQLAlchemy. It allows you to define tables and models in one go, similar -to how Django works. In addition to the following text I recommend the -official documentation on the `declarative`_ extension. - -Here the example `database.py` module for your application:: - - from sqlalchemy import create_engine - from sqlalchemy.orm import scoped_session, sessionmaker - from sqlalchemy.ext.declarative import declarative_base - - engine = create_engine('sqlite:////tmp/test.db') - db_session = scoped_session(sessionmaker(autocommit=False, - autoflush=False, - bind=engine)) - Base = declarative_base() - Base.query = db_session.query_property() - - def init_db(): - Base.metadata.create_all(bind=engine) - -To define your models, just subclass the `Base` class that was created by -the code above. If you are wondering why we don't have to care about -threads here (like we did in the SQLite3 example above with the -:data:`~flask.g` object): that's because SQLAlchemy does that for us -already with the :class:`~sqlalchemy.orm.scoped_session`. - -To use SQLAlchemy in a declarative way with your application, you just -have to put the following code into your application module. Flask will -automatically remove database sessions at the end of the request for you:: - - from yourapplication.database import db_session - - @app.after_request - def shutdown_session(response): - db_session.remove() - return response - -Here is an example model (put this into `models.py`, e.g.):: - - from sqlalchemy import Column, Integer, String - from yourapplication.database import Base - - class User(Base): - __tablename__ = 'users' - id = Column(Integer, primary_key=True) - name = Column(String(50), unique=True) - email = Column(String(120), unique=True) - - def __init__(self, name=None, email=None): - self.name = name - self.email = email - - def __repr__(self): - return '' % (self.name, self.email) - -You can insert entries into the database like this: - ->>> from yourapplication.database import db_session ->>> from yourapplication.models import User ->>> u = User('admin', 'admin@localhost') ->>> db_session.add(u) ->>> db_session.commit() - -Querying is simple as well: - ->>> User.query.all() -[] ->>> User.query.filter(User.name == 'admin').first() - - -.. _SQLAlchemy: http://www.sqlalchemy.org/ -.. _declarative: - http://www.sqlalchemy.org/docs/reference/ext/declarative.html - -Manual Object Relational Mapping --------------------------------- - -Manual object relational mapping has a few upsides and a few downsides -versus the declarative approach from above. The main difference is that -you define tables and classes separately and map them together. It's more -flexible but a little more to type. In general it works like the -declarative approach, so make sure to also split up your application into -multiple modules in a package. - -Here is an example `database.py` module for your application:: - - from sqlalchemy import create_engine, MetaData - from sqlalchemy.orm import scoped_session, sessionmaker - - engine = create_engine('sqlite:////tmp/test.db') - metadata = MetaData() - db_session = scoped_session(sessionmaker(autocommit=False, - autoflush=False, - bind=engine)) - def init_db(): - metadata.create_all(bind=engine) - -As for the declarative approach you need to close the session after -each request. Put this into your application module:: - - from yourapplication.database import db_session - - @app.after_request - def shutdown_session(response): - db_session.remove() - return response - -Here is an example table and model (put this into `models.py`):: - - from sqlalchemy import Table, Column, Integer, String - from sqlalchemy.orm import mapper - from yourapplication.database import metadata, db_session - - class User(object): - query = db_session.query_property() - - def __init__(self, name=None, email=None): - self.name = name - self.email = email - - def __repr__(self): - return '' % (self.name, self.email) - - users = Table('users', metadata, - Column('id', Integer, primary_key=True), - Column('name', String(50), unique=True), - Column('email', String(120), unique=True) - ) - mapper(User, users) - -Querying and inserting works exactly the same as in the example above. - - -SQL Abstraction Layer ---------------------- - -If you just want to use the database system (and SQL) abstraction layer -you basically only need the engine:: - - from sqlalchemy import create_engine, MetaData - - engine = create_engine('sqlite:////tmp/test.db') - metadata = MetaData(bind=engine) - -Then you can either declare the tables in your code like in the examples -above, or automatically load them:: - - users = Table('users', metadata, autoload=True) - -To insert data you can use the `insert` method. We have to get a -connection first so that we can use a transaction: - ->>> con = engine.connect() ->>> con.execute(users.insert(name='admin', email='admin@localhost')) - -SQLAlchemy will automatically commit for us. - -To query your database, you use the engine directly or use a connection: - ->>> users.select(users.c.id == 1).execute().first() -(1, u'admin', u'admin@localhost') - -These results are also dict-like tuples: - ->>> r = users.select(users.c.id == 1).execute().first() ->>> r['name'] -u'admin' - -You can also pass strings of SQL statements to the -:meth:`~sqlalchemy.engine.base.Connection.execute` method: - ->>> engine.execute('select * from users where id = :1', [1]).first() -(1, u'admin', u'admin@localhost') - -For more information about SQLAlchemy, head over to the -`website `_. diff --git a/docs/patterns/sqlite3.rst b/docs/patterns/sqlite3.rst deleted file mode 100644 index c11e837d..00000000 --- a/docs/patterns/sqlite3.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. _sqlite3: - -Using SQLite 3 with Flask -========================= - -In Flask you can implement opening of database connections at the beginning -of the request and closing at the end with the -:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.after_request` -decorators in combination with the special :class:`~flask.g` object. - -So here a simple example of how you can use SQLite 3 with Flask:: - - import sqlite3 - from flask import g - - DATABASE = '/path/to/database.db' - - def connect_db(): - return sqlite3.connect(DATABASE) - - @app.before_request - def before_request(): - g.db = connect_db() - - @app.after_request - def after_request(response): - g.db.close() - return response - -.. _easy-querying: - -Easy Querying -------------- - -Now in each request handling function you can access `g.db` to get the -current open database connection. To simplify working with SQLite a -helper function can be useful:: - - def query_db(query, args=(), one=False): - cur = g.db.execute(query, args) - rv = [dict((cur.description[idx][0], value) - for idx, value in enumerate(row)) for row in cur.fetchall()] - return (rv[0] if rv else None) if one else rv - -This handy little function makes working with the database much more -pleasant than it is by just using the raw cursor and connection objects. - -Here is how you can use it:: - - for user in query_db('select * from users'): - print user['username'], 'has the id', user['user_id'] - -Or if you just want a single result:: - - user = query_db('select * from users where username = ?', - [the_username], one=True) - if user is None: - print 'No such user' - else: - print the_username, 'has the id', user['user_id'] - -To pass variable parts to the SQL statement, use a question mark in the -statement and pass in the arguments as a list. Never directly add them to -the SQL statement with string formattings because this makes it possible -to attack the application using `SQL Injections -`_. - -Initial Schemas ---------------- - -Relational databases need schemas, so applications often ship a -`schema.sql` file that creates the database. It's a good idea to provide -a function that creates the database based on that schema. This function -can do that for you:: - - from contextlib import closing - - def init_db(): - with closing(connect_db()) as db: - with app.open_resource('schema.sql') as f: - db.cursor().executescript(f.read()) - db.commit() - -You can then create such a database from the python shell: - ->>> from yourapplication import init_db ->>> init_db() diff --git a/docs/patterns/templateinheritance.rst b/docs/patterns/templateinheritance.rst deleted file mode 100644 index 8a1a306d..00000000 --- a/docs/patterns/templateinheritance.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. _template-inheritance: - -Template Inheritance -==================== - -The most powerful part of Jinja is template inheritance. Template inheritance -allows you to build a base "skeleton" template that contains all the common -elements of your site and defines **blocks** that child templates can override. - -Sounds complicated but is very basic. It's easiest to understand it by starting -with an example. - - -Base Template -------------- - -This template, which we'll call ``layout.html``, defines a simple HTML skeleton -document that you might use for a simple two-column page. It's the job of -"child" templates to fill the empty blocks with content: - -.. sourcecode:: html+jinja - - - - - {% block head %} - - {% block title %}{% endblock %} - My Webpage - {% endblock %} - - -

{% block content %}{% endblock %}
- - - -In this example, the ``{% block %}`` tags define four blocks that child templates -can fill in. All the `block` tag does is to tell the template engine that a -child template may override those portions of the template. - -Child Template --------------- - -A child template might look like this: - -.. sourcecode:: html+jinja - - {% extends "layout.html" %} - {% block title %}Index{% endblock %} - {% block head %} - {{ super() }} - - {% endblock %} - {% block content %} -

Index

-

- Welcome on my awesome homepage. - {% endblock %} - -The ``{% extends %}`` tag is the key here. It tells the template engine that -this template "extends" another template. When the template system evaluates -this template, first it locates the parent. The extends tag must be the -first tag in the template. To render the contents of a block defined in -the parent template, use ``{{ super() }}``. diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst deleted file mode 100644 index 4a836975..00000000 --- a/docs/patterns/wtforms.rst +++ /dev/null @@ -1,113 +0,0 @@ -Form Validation with WTForms -============================ - -When you have to work with form data submitted by a browser view code -quickly becomes very hard to read. There are libraries out there designed -to make this process easier to manage. One of them is `WTForms`_ which we -will handle here. If you find yourself in the situation of having many -forms, you might want to give it a try. - -When you are working with WTForms you have to define your forms as classes -first. I recommend breaking up the application into multiple modules -(:ref:`larger-applications`) for that and adding a separate module for the -forms. - -The Forms ---------- - -This is an example form for a typical registration page:: - - from wtforms import Form, BooleanField, TextField, validators - - class RegistrationForm(Form): - username = TextField('Username', [validators.Length(min=4, max=25)]) - email = TextField('Email Address', [validators.Length(min=6, max=35)]) - password = PasswordField('New Password', [validators.Required()]) - confirm = PasswordField('Repeat Password', [validators.EqualTo( - 'confirm', message='Passwords must match')]) - accept_tos = BooleanField('I accept the TOS', [validators.Required()]) - -In the View ------------ - -In the view function, the usage of this form looks like this:: - - @app.route('/register', methods=['GET', 'POST']) - def register(): - form = RegistrationForm(request.form) - if request.method == 'POST' and form.validate(): - user = User(form.username.data, form.email.data, - form.password.data) - db_session.add(user) - flash('Thanks for registering') - redirect(url_for('login')) - return render_template('register.html', form=form) - -Notice that we are implying that the view is using SQLAlchemy here -(:ref:`sqlalchemy-pattern`) but this is no requirement of course. Adapt -the code as necessary. - -Things to remember: - -1. create the form from the request :attr:`~flask.request.form` value if - the data is submitted via the HTTP `POST` method and - :attr:`~flask.request.args` if the data is submitted as `GET`. -2. to validate the data, call the :func:`~wtforms.form.Form.validate` - method which will return `True` if the data validates, `False` - otherwise. -3. to access individual values from the form, access `form..data`. - -Forms in Templates ------------------- - -Now to the template side. When you pass the form to the templates you can -easily render them there. Look at the following example template to see -how easy this is. WTForms does half the form generation for us already. -To make it even nicer, we can write a macro that renders a field with -label and a list of errors if there are any. - -Here an example `_formhelpers.html` template with such a macro: - -.. sourcecode:: html+jinja - - {% macro render_field(field) %} -

{{ field.label }} -
{{ field(**kwargs)|safe }} - {% if field.errors %} -
    - {% for error in field.errors %}
  • {{ error }}{% endfor %} -
- {% endif %} -
- {% endmacro %} - -This macro accepts a couple of keyword arguments that are forwarded to -WTForm's field function that renders the field for us. They keyword -arguments will be inserted as HTML attributes. So for example you can -call ``render_field(form.username, class='username')`` to add a class to -the input element. Note that WTForms returns standard Python unicode -strings, so we have to tell Jinja2 that this data is already HTML escaped -with the `|safe` filter. - -Here the `register.html` template for the function we used above which -takes advantage of the `_formhelpers.html` template: - -.. sourcecode:: html+jinja - - {% from "_formhelpers.html" import render_field %} -
-
- {{ render_field(form.username) }} - {{ render_field(form.email) }} - {{ render_field(form.password) }} - {{ render_field(form.confirm) }} - {{ render_field(form.accept_tos) }} -
-

-

- -For more information about WTForms, head over to the `WTForms -website`_. - -.. _WTForms: http://wtforms.simplecodes.com/ -.. _WTForms website: http://wtforms.simplecodes.com/ diff --git a/docs/quickstart.rst b/docs/quickstart.rst deleted file mode 100644 index e0f0749f..00000000 --- a/docs/quickstart.rst +++ /dev/null @@ -1,618 +0,0 @@ -.. _quickstart: - -Quickstart -========== - -Eager to get started? This page gives a good introduction in how to gets -started with Flask. This assumes you already have Flask installed. If -you do not, head over to the :ref:`installation` section. - - -A Minimal Application ---------------------- - -A minimal Flask application looks something like that:: - - from flask import Flask - app = Flask(__name__) - - @app.route('/') - def hello_world(): - return "Hello World!" - - if __name__ == '__main__': - app.run() - -Just save it as `hello.py` or something similar and run it with your -Python interpreter. Make sure to not call your application `flask.py` -because this would conflict with Flask itself. - -:: - - $ python hello.py - * Running on http://127.0.0.1:5000/ - -Head over to `http://127.0.0.1:5000/ `_, you should -see your hello world greeting. - -So what did that code do? - -1. first we imported the :class:`~flask.Flask` class. An instance of this - class will be our WSGI application. -2. next we create an instance of it. We pass it the name of the module / - package. This is needed so that Flask knows where it should look for - templates, static files and so on. -3. Then we use the :meth:`~flask.Flask.route` decorator to tell Flask - what URL should trigger our function. -4. The function then has a name which is also used to generate URLs to - that particular function, and returns the message we want to display in - the user's browser. -5. Finally we use the :meth:`~flask.Flask.run` function to run the - local server with our application. The ``if __name__ == '__main__':`` - makes sure the server only runs if the script is executed directly from - the Python interpreter and not used as imported module. - -To stop the server, hit control-C. - - -Debug Mode ----------- - -Now that :meth:`~flask.Flask.run` method is nice to start a local -development server, but you would have to restart it manually after each -change you do to code. That is not very nice and Flask can do better. If -you enable the debug support the server will reload itself on code changes -and also provide you with a helpful debugger if things go wrong. - -There are two ways to enable debugging. Either set that flag on the -applciation object:: - - app.debug = True - app.run() - -Or pass it to run:: - - app.run(debug=True) - -Both will have exactly the same effect. - -.. admonition:: Attention - - The interactive debugger however does not work in forking environments - which makes it nearly impossible to use on production servers but the - debugger still allows the execution of arbitrary code which makes it a - major security risk and **must never be used on production machines** - because of that. - -Screenshot of the debugger in action: - -.. image:: _static/debugger.png - :align: center - :class: screenshot - :alt: screenshot of debugger in action - - -Routing -------- - -As you have seen above, the :meth:`~flask.Flask.route` decorator is used -to bind a function to a URL. But there is more to it! You can make -certain parts of the URL dynamic and attach multiple rules to a function. - -Here some examples:: - - @app.route('/') - def index(): - return 'Index Page' - - @app.route('/hello') - def hello(): - return 'Hello World' - - -Variable Rules -`````````````` - -Modern web applications have beautiful URLs. This helps people remember -the URLs which is especially handy for applications that are used from -mobile devices with slower network connections. If the user can directly -go to the desired page without having to hit the index page it is more -likely he will like the page and come back next time. - -To add variable parts to a URL you can mark these special sections as -````. Such a part is then passed as keyword argument to -your function. Optionally a converter can be specifed by specifying a -rule with ````. Here some nice examples:: - - @app.route('/user/') - def show_user_profile(username): - # show the user profile for that user - pass - - @app.route('/post/') - def show_post(post_id): - # show the post with the given id, the id is an integer - pass - -The following converters exist: - -=========== =========================================== -`int` accepts integers -`float` like `int` but for floating point values -`path` like the default but also accepts slashes -=========== =========================================== - -URL Building -```````````` - -If it can match URLs, can it also generate them? Of course you can. To -build a URL to a specific function you can use the :func:`~flask.url_for` -function. It accepts the name of the function as first argument and a -number of keyword arguments, each corresponding to the variable part of -the URL rule. Here some examples: - ->>> from flask import Flask, url_for ->>> app = Flask(__name__) ->>> @app.route('/') -... def index(): pass -... ->>> @app.route('/login') -... def login(): pass -... ->>> @app.route('/user/') -... def profile(username): pass -... ->>> with app.test_request_context(): -... print url_for('index') -... print url_for('login') -... print url_for('profile', username='John Doe') -... -/ -/login -/user/John%20Doe - -(This also uses the :meth:`~flask.Flask.test_request_context` method -explained below. It basically tells flask to think we are handling a -request even though we are not, we are in an interactive Python shell. -Have a look at the explanation below. :ref:`context-locals`). - -Why would you want to build URLs instead of hardcoding them in your -templates? There are three good reasons for this: - -1. reversing is often more descriptive than hardcoding the URLs. Also and - more importantly you can change URLs in one go without having to change - the URLs all over the place. -2. URL building will handle escaping of special characters and unicode - data transparently for you, you don't have to deal with that. -3. If your application is placed outside the URL root (so say in - ``/myapplication`` instead of ``/``), :func:`~flask.url_for` will - handle that properly for you. - - -HTTP Methods -```````````` - -HTTP (the protocol web applications are speaking) knows different methods -to access URLs. By default a route only answers to `GET` requests, but -that can be changed by providing the `methods` argument to the -:meth:`~flask.Flask.route` decorator. Here some examples:: - - @app.route('/login', methods=['GET', 'POST']) - def login(): - if request.method == 'POST': - do_the_login() - else: - show_the_login_form() - -If `GET` is present, `HEAD` will be added automatically for you. You -don't have to deal with that. It will also make sure that `HEAD` requests -are handled like the `HTTP RFC`_ (the document describing the HTTP -protocol) demands, so you can completely ignore that part of the HTTP -specification. - -You have no idea what an HTTP method is? Worry not, here quick -introduction in HTTP methods and why they matter: - -The HTTP method (also often called "the verb") tells the server what the -clients wants to *do* with the requested page. The following methods are -very common: - -`GET` - The Browser tells the server: just *get* me the information stored on - that page and send them to me. This is probably the most common - method. - -`HEAD` - The Browser tells the server: get me the information, but I am only - interested in the *headers*, not the content of the page. An - application is supposed to handle that as if a `GET` request was - received but not deliver the actual contents. In Flask you don't have - to deal with that at all, the underlying Werkzeug library handles that - for you. - -`POST` - The browser tells the server that it wants to *post* some new - information to that URL and that the server must ensure the data is - stored and only stored once. This is how HTML forms are usually - transmitting data to the server. - -`PUT` - Similar to `POST` but the server might trigger the store procedure - multiple times by overwriting the old values more than once. Now you - might be asking why this is any useful, but there are some good - reasons to do that. Consider the connection is lost during - transmission, in that situation a system between the browser and the - server might sent the request safely a second time without breaking - things. With `POST` that would not be possible because it must only - be triggered once. - -`DELETE` - Remove the information that the given location. - -Now the interesting part is that in HTML4 and XHTML1, the only methods a -form might submit to the server are `GET` and `POST`. But with JavaScript -and future HTML standards you can use other methods as well. Furthermore -HTTP became quite popular lately and there are more things than browsers -that are speaking HTTP. (Your revision control system for instance might -speak HTTP) - -.. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt - -Static Files ------------- - -Dynamic web applications need static files as well. That's usually where -the CSS and JavaScript files are coming from. Ideally your web server is -configured to serve them for you, but during development Flask can do that -as well. Just create a folder called `static` in your package or next to -your module and it will be available at `/static` on the application. - -To generate URLs to that part of the URL, use the special ``'static'`` URL -name:: - - url_for('static', filename='style.css') - -The file has to be stored on the filesystem as ``static/style.css``. - -Rendering Templates -------------------- - -Generating HTML from within Python is not fun, and actually pretty -cumbersome because you have to do the HTML escaping on your own to keep -the application secure. Because of that Flask configures the `Jinja2 -`_ template engine for you automatically. - -To render a template you can use the :func:`~flask.render_template` -method. All you have to do is to provide the name of the template and the -variables you want to pass to the template engine as keyword arguments. -Here a simple example of how to render a template:: - - from flask import render_template - - @app.route('/hello/') - @app.route('/hello/') - def hello(name=None): - return render_template('hello.html', name=name) - -Flask will look for templates in the `templates` folder. So if your -application is a module, that folder is next to that module, if it's a -pacakge it's actually inside your package: - -**Case 1**: a module:: - - /application.py - /templates - /hello.html - -**Case 2**: a package:: - - /application - /__init__.py - /templates - /hello.html - -For templates you can use the full power of Jinja2 templates. Head over -to the `Jinja2 Template Documentation -`_ for more information. - -Here an example template: - -.. sourcecode:: html+jinja - - - Hello from Flask - {% if name %} -

Hello {{ name }}!

- {% else %} -

Hello World!

- {% endif %} - -Inside templates you also have access to the :class:`~flask.request`, -:class:`~flask.session` and :class:`~flask.g` [#]_ objects -as well as the :func:`~flask.get_flashed_messages` function. - -Templates are especially useful if inheritance is used. If you want to -know how that works, head over to the :ref:`template-inheritance` pattern -documentation. Basically template inheritance makes it possible to keep -certain elements on each page (like header, navigation and footer). - -Automatic escaping is enabled, so if name contains HTML it will be escaped -automatically. If you can trust a variable and you know that it will be -safe HTML (because for example it came from a module that converts wiki -markup to HTML) you can mark it as safe by using the -:class:`~jinja2.Markup` class or by using the ``|safe`` filter in the -template. Head over to the Jinja 2 documentation for more examples. - -Here a basic introduction in how the :class:`~jinja2.Markup` class works: - ->>> from flask import Markup ->>> Markup('Hello %s!') % 'hacker' -Markup(u'Hello <blink>hacker</blink>!') ->>> Markup.escape('hacker') -Markup(u'<blink>hacker</blink>') ->>> Markup('Marked up » HTML').striptags() -u'Marked up \xbb HTML' - -.. [#] Unsure what that :class:`~flask.g` object is? It's something you - can store information on yourself, check the documentation of that - object (:class:`~flask.g`) and the :ref:`sqlite3` for more - information. - - -Accessing Request Data ----------------------- - -For web applications it's crucial to react to the data a client sent to -the server. In Flask this information is provided by the global -:class:`~flask.request` object. If you have some experience with Python -you might be wondering how that object can be global and how Flask -manages to still be threadsafe. The answer are context locals: - - -.. _context-locals: - -Context Locals -`````````````` - -.. admonition:: Insider Information - - If you want to understand how that works and how you can implement - tests with context locals, read this section, otherwise just skip it. - -Certain objects in Flask are global objects, but not just a standard -global object, but actually a proxy to an object that is local to a -specific context. What a mouthful. But that is actually quite easy to -understand. - -Imagine the context being the handling thread. A request comes in and the -webserver decides to spawn a new thread (or something else, the -underlying object is capable of dealing with other concurrency systems -than threads as well). When Flask starts its internal request handling it -figures out that the current thread is the active context and binds the -current application and the WSGI environments to that context (thread). -It does that in an intelligent way that one application can invoke another -application without breaking. - -So what does this mean to you? Basically you can completely ignore that -this is the case unless you are unittesting or something different. You -will notice that code that depends on a request object will suddenly break -because there is no request object. The solution is creating a request -object yourself and binding it to the context. The easiest solution for -unittesting is by using the :meth:`~flask.Flask.test_request_context` -context manager. In combination with the `with` statement it will bind a -test request so that you can interact with it. Here an example:: - - from flask import request - - with app.test_request_context('/hello', method='POST'): - # now you can do something with the request until the - # end of the with block, such as basic assertions: - assert request.path == '/hello' - assert request.method == 'POST' - -The other possibility is passing a whole WSGI environment to the -:meth:`~flask.Flask.request_context` method:: - - from flask import request - - with app.request_context(environ): - assert request.method == 'POST' - -The Request Object -`````````````````` - -The request object is documented in the API section and we will not cover -it here in detail (see :class:`~flask.request`), but just mention some of -the most common operations. First of all you have to import it from the -the `flask` module:: - - from flask import request - -The current request method is available by using the -:attr:`~flask.request.method` attribute. To access form data (data -transmitted in a `POST` or `PUT` request) you can use the -:attr:`~flask.request.form` attribute. Here a full example of the two -attributes mentioned above:: - - @app.route('/login', method=['POST', 'GET']) - def login(): - error = None - if request.method == 'POST': - if valid_login(request.form['username'], - request.form['password']): - return log_the_user_in(request.form['username']) - else: - error = 'Invalid username/password' - # this is executed if the request method was GET or the - # credentials were invalid - -What happens if the key does not exist in the `form` attribute? In that -case a special :exc:`KeyError` is raised. You can catch it like a -standard :exc:`KeyError` but if you don't do that, a HTTP 400 Bad Request -error page is shown instead. So for many situations you don't have to -deal with that problem. - -To access parameters submitted in the URL (``?key=value``) you can use the -:attr:`~flask.request.args` attribute:: - - searchword = request.args.get('q', '') - -We recommend accessing URL parameters with `get` or by catching the -`KeyError` because users might change the URL and presenting them a 400 -bad request page in that case is a bit user unfriendly. - -For a full list of methods and attribtues on that object, head over to the -:class:`~flask.request` documentation. - - -File Uploads -```````````` - -Obviously you can handle uploaded files with Flask just as easy. Just -make sure not to forget to set the ``enctype="multipart/form-data"`` -attribtue on your HTML form, otherwise the browser will not transmit your -files at all. - -Uploaded files are stored in memory or at a temporary location on the -filesystem. You can access those files by looking at the -:attr:`~flask.request.files` attribute on the request object. Each -uploaded file is stored in that dictionary. It behaves just like a -standard Python :class:`file` object, but it also has a -:meth:`~werkzeug.FileStorage.save` method that allows you to store that -file on the filesystem of the server. Here a simple example how that -works:: - - from flask import request - - @app.route('/upload', methods=['GET', 'POST']) - def upload_file(): - if request.method == 'POST': - f = request.files['the_file'] - f.save('/var/www/uploads/uploaded_file.txt') - ... - -If you want to know how the file was named on the client before it was -uploaded to your application, you can access the -:attr:`~werkzeug.FileStorage.filename` attribute. However please keep in -mind that this value can be forged so never ever trust that value. If you -want to use the filename of the client to store the file on the server, -pass it through the :func:`~werkzeug.secure_filename` function that -Werkzeug provides for you:: - - from flask import request - from werkzeug import secure_filename - - @app.route('/upload', methods=['GET', 'POST']) - def upload_file(): - if request.method == 'POST': - f= request.files['the_file'] - f.save('/var/www/uploads/' + secure_filename(f.filename)) - ... - -Cookies -``````` - -To access cookies you can use the :attr:`~flask.request.cookies` -attribute. Again this is a dictionary with all the cookies the client -transmits. If you want to use sessions, do not use the cookies directly -but instead use the :ref:`sessions` in Flask that add some security on top -of cookies for you. - - -Redirects and Errors --------------------- - -To redirect a user to somewhere else you can use the -:func:`~flask.redirect` function, to abort a request early with an error -code the :func:`~flask.abort` function. Here an example how this works:: - - from flask import abort, redirect, url_for - - @app.route('/') - def index(): - return redirect(url_for('login')) - - @app.route('/login') - def login(): - abort(401) - this_is_never_executed() - -This is a rather pointless example because a user will be redirected from -the index to a page he cannot access (401 means access denied) but it -shows how that works. - -By default a black and white error page is shown for each error code. If -you want to customize the error page, you can use the -:meth:`~flask.Flask.errorhandler` decorator:: - - from flask import render_template - - @app.errorhandler(404) - def page_not_found(error): - return render_template('page_not_found.html'), 404 - -Note the ``404`` after the :func:`~flask.render_template` call. This -tells Flask that the status code of that page should be 404 which means -not found. By default 200 is assumed which translats to: all went well. - -.. _sessions: - -Sessions --------- - -Besides the request object there is also a second object called -:class:`~flask.session` that allows you to store information specific to a -user from one request to the next. This is implemented on top of cookies -for you and signes the cookies cryptographically. What this means is that -the user could look at the contents of your cookie but not modify it, -unless he knows the secret key used for signing. - -In order to use sessions you have to set a secret key. Here is how -sessions work:: - - from flask import session, redirect, url_for, escape - - @app.route('/') - def index(): - if 'username' in session: - return 'Logged in as %s' % escape(session['username']) - return 'You are not logged in' - - @app.route('/login', methods=['GET', 'POST']) - def login(): - if request.method == 'POST': - session['username'] = request.form['username'] - return redirect(url_for('index')) - return ''' -
-

-

-

- ''' - - @app.route('/logout') - def logout(): - # remove the username from the session if its there - session.pop('username', None) - - # set the secret key. keep this really secret: - app.secret_key = 'the secret key' - -The here mentioned :func:`~flask.escape` does escaping for you if you are -not using the template engine (like in this example). - -Message Flashing ----------------- - -Good applications and user interfaces are all about feedback. If the user -does not get enough feedback he will probably end up hating the -application. Flask provides a really simple way to give feedback to a -user with the flashing system. The flashing system basically makes it -possible to record a message at the end of a request and access it next -request and only next request. This is usually combined with a layout -template that does this. - -To flash a message use the :func:`~flask.flash` method, to get hold of the -messages you can use :func:`~flask.get_flashed_messages` which is also -available in the templates. Check out the :ref:`message-flashing-pattern` -for a full example. diff --git a/docs/testing.rst b/docs/testing.rst deleted file mode 100644 index 0901792b..00000000 --- a/docs/testing.rst +++ /dev/null @@ -1,197 +0,0 @@ -.. _testing: - -Testing Flask Applications -========================== - - **Something that is untested is broken.** - -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 -something. - -Flask gives you a couple of ways to test applications. It mainly does -that by exposing the Werkzeug test :class:`~werkzeug.Client` class to your -code and handling the context locals for you. You can then use that with -your favourite testing solution. In this documentation we will use the -:mod:`unittest` package that comes preinstalled with each Python -installation. - -The Application ---------------- - -First we need an application to test for functionality. For the testing -we will use the application from the :ref:`tutorial`. If you don't have -that application yet, get the sources from `the examples`_. - -.. _the examples: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ - -The Testing Skeleton --------------------- - -In order to test that, we add a second module ( -`flaskr_tests.py`) and create a unittest skeleton there:: - - import os - import flaskr - import unittest - import tempfile - - class FlaskrTestCase(unittest.TestCase): - - def setUp(self): - self.db_fd, flaskr.DATABASE = tempfile.mkstemp() - self.app = flaskr.app.test_client() - flaskr.init_db() - - def tearDown(self): - os.close(self.db_fd) - os.unlink(flaskr.DATABASE) - - if __name__ == '__main__': - unittest.main() - -The code in the :meth:`~unittest.TestCase.setUp` method creates a new test -client and initializes a new database. That function is called before -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 -for us. - -Because SQLite3 is filesystem-based we can easily use the tempfile module -to create a temporary database and initialize it. The -:func:`~tempfile.mkstemp` function does two things for us: it returns a -low-level file handle and a random file name, the latter we use as -database name. We just have to keep the `db_fd` around so that we can use -the :func:`os.close` function to close the file. - -If we now run that testsuite, we should see the following output:: - - $ python flaskr_tests.py - - ---------------------------------------------------------------------- - Ran 0 tests in 0.000s - - OK - -Even though it did not run any tests, we already know that our flaskr -application is syntactically valid, otherwise the import would have died -with an exception. - -The First Test --------------- - -Now we can add the first test. Let's check that the application shows -"No entries here so far" if we access the root of the application (``/``). -For that we modify our created test case class so that it looks like -this:: - - class FlaskrTestCase(unittest.TestCase): - - def setUp(self): - self.db_fd, flaskr.DATABASE = tempfile.mkstemp() - self.app = flaskr.app.test_client() - flaskr.init_db() - - def tearDown(self): - os.close(self.db_fd) - os.unlink(flaskr.DATABASE) - - def test_empty_db(self): - rv = self.app.get('/') - assert 'No entries here so far' in rv.data - -Test functions begin with the word `test`. Every function named like that -will be picked up automatically. By using `self.app.get` we can send an -HTTP `GET` request to the application with the given path. The return -value will be a :class:`~flask.Flask.response_class` object. We can now -use the :attr:`~werkzeug.BaseResponse.data` attribute to inspect the -return value (as string) from the application. In this case, we ensure -that ``'No entries here so far'`` is part of the output. - -Run it again and you should see one passing test:: - - $ python flaskr_tests.py - . - ---------------------------------------------------------------------- - Ran 1 test in 0.034s - - OK - -Of course you can submit forms with the test client as well, which we will -use now to log our user in. - -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 -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 -client to `follow_redirects`. - -Add the following two methods to your `FlaskrTestCase` class:: - - def login(self, username, password): - return self.app.post('/login', data=dict( - username=username, - password=password - ), follow_redirects=True) - - def logout(self): - return self.app.get('/logout', follow_redirects=True) - -Now we can easily test if logging in and out works and that it fails with -invalid credentials. Add this new test to the class:: - - def test_login_logout(self): - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD) - assert 'You were logged in' in rv.data - rv = self.logout() - assert 'You were logged out' in rv.data - rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD) - assert 'Invalid username' in rv.data - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x') - assert 'Invalid password' in rv.data - -Test Adding Messages --------------------- - -Now we can also test that adding messages works. Add a new test method -like this:: - - def test_messages(self): - self.login(flaskr.USERNAME, flaskr.PASSWORD) - rv = self.app.post('/add', data=dict( - title='', - text='HTML allowed here' - ), follow_redirects=True) - assert 'No entries here so far' not in rv.data - assert '<Hello>' in rv.data - assert 'HTML allowed here' in rv.data - -Here we check that HTML is allowed in the text but not in the title, -which is the intended behavior. - -Running that should now give us three passing tests:: - - $ python flaskr_tests.py - ... - ---------------------------------------------------------------------- - Ran 3 tests in 0.332s - - OK - -For more complex tests with headers and status codes, check out the -`MiniTwit Example`_ from the sources. That one contains a larger test -suite. - - -.. _MiniTwit Example: - http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/ diff --git a/docs/tutorial/css.rst b/docs/tutorial/css.rst deleted file mode 100644 index c2a6ba5b..00000000 --- a/docs/tutorial/css.rst +++ /dev/null @@ -1,27 +0,0 @@ -Step 7: Adding Style -==================== - -Now that everything else works, it's time to add some style to the -application. Just create a stylesheet called `style.css` in the `static` -folder we created before: - -.. sourcecode:: css - - body { font-family: sans-serif; background: #eee; } - a, h1, h2 { color: #377BA8; } - h1, h2 { font-family: 'Georgia', serif; margin: 0; } - h1 { border-bottom: 2px solid #eee; } - h2 { font-size: 1.2em; } - - .page { margin: 2em auto; width: 35em; border: 5px solid #ccc; - padding: 0.8em; background: white; } - .entries { list-style: none; margin: 0; padding: 0; } - .entries li { margin: 0.8em 1.2em; } - .entries li h2 { margin-left: -1em; } - .add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } - .add-entry dl { font-weight: bold; } - .metanav { text-align: right; font-size: 0.8em; padding: 0.3em; - margin-bottom: 1em; background: #fafafa; } - .flash { background: #CEE5F5; padding: 0.5em; - border: 1px solid #AACBE2; } - .error { background: #F0D6D6; padding: 0.5em; } diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst deleted file mode 100644 index 9741dabb..00000000 --- a/docs/tutorial/dbcon.rst +++ /dev/null @@ -1,33 +0,0 @@ -Step 4: Request Database Connections ------------------------------------- - -Now we know how we can open database connections and use them for scripts, -but how can we elegantly do that for requests? We will need the database -connection in all our functions so it makes sense to initialize them -before each request and shut them down afterwards. - -Flask allows us to do that with the :meth:`~flask.Flask.before_request` and -:meth:`~flask.Flask.after_request` decorators:: - - @app.before_request - def before_request(): - g.db = connect_db() - - @app.after_request - def after_request(response): - g.db.close() - return response - -Functions marked with :meth:`~flask.Flask.before_request` are called before -a request and passed no arguments, functions marked with -:meth:`~flask.Flask.after_request` are called after a request and -passed the response that will be sent to the client. They have to return -that response object or a different one. In this case we just return it -unchanged. - -We store our current database connection on the special :data:`~flask.g` -object that flask provides for us. This object stores information for one -request only and is available from within each function. Never store such -things on other objects because this would not work with threaded -environments. That special :data:`~flask.g` object does some magic behind -the scenes to ensure it does the right thing. diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst deleted file mode 100644 index 0dc87d58..00000000 --- a/docs/tutorial/dbinit.rst +++ /dev/null @@ -1,63 +0,0 @@ -Step 3: Creating The Database -============================= - -Flaskr is a database powered application as outlined earlier, and more -precisely, an application powered by a relational database system. Such -systems need a schema that tells them how to store that information. So -before starting the server for the first time it's important to create -that schema. - -Such a schema can be created by piping the `schema.sql` file into the -`sqlite3` command as follows:: - - sqlite3 /tmp/flaskr.db < schema.sql - -The downside of this is that it requires the sqlite3 command to be -installed which is not necessarily the case on every system. Also one has -to provide the path to the database there which leaves some place for -errors. It's a good idea to add a function that initializes the database -for you to the application. - -If you want to do that, you first have to import the -:func:`contextlib.closing` function from the contextlib package. If you -want to use Python 2.5 it's also necessary to enable the `with` statement -first (`__future__` imports must be the very first import):: - - from __future__ import with_statement - from contextlib import closing - -Next we can create a function called `init_db` that initializes the -database. For this we can use the `connect_db` function we defined -earlier. Just add that function below the `connect_db` function:: - - def init_db(): - with closing(connect_db()) as db: - with app.open_resource('schema.sql') as f: - db.cursor().executescript(f.read()) - db.commit() - -The :func:`~contextlib.closing` helper function allows us to keep a -connection open for the duration of the `with` block. The -:func:`~flask.Flask.open_resource` method of the application object -supports that functionality out of the box, so it can be used in the -`with` block directly. This function opens a file from the resource -location (your `flaskr` folder) and allows you to read from it. We are -using this here to execute a script on the database connection. - -When we connect to a database we get a connection object (here called -`db`) that can give us a cursor. On that cursor there is a method to -execute a complete script. Finally we only have to commit the changes. -SQLite 3 and other transactional databases will not commit unless you -explicitly tell it to. - -Now it is possible to create a database by starting up a Python shell and -importing and calling that function:: - ->>> from flaskr import init_db ->>> init_db() - -.. admonition:: Troubleshooting - - If you get an exception later that a table cannot be found check that - you did call the `init_db` function and that your table names are - correct (singular vs. plural for example). diff --git a/docs/tutorial/folders.rst b/docs/tutorial/folders.rst deleted file mode 100644 index 80697a94..00000000 --- a/docs/tutorial/folders.rst +++ /dev/null @@ -1,19 +0,0 @@ -Step 0: Creating The Folders -============================ - -Before we get started, let's create the folders needed for this -application:: - - /flaskr - /static - /templates - -The `flaskr` folder is not a python package, but just something where we -drop our files. Directly into this folder we will then put our database -schema as well as main module in the following steps. The files inside -the `static` folder are available to users of the application via `HTTP`. -This is the place where css and javascript files go. Inside the -`templates` folder Flask will look for `Jinja2`_ templates. Drop all the -templates there. - -.. _Jinja2: http://jinja.pocoo.org/2/ diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst deleted file mode 100644 index 3f2d659e..00000000 --- a/docs/tutorial/index.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. _tutorial: - -Tutorial -======== - -You want to develop an application with Python and Flask? Here you have -the chance to learn that by example. In this tutorial we will create a -simple microblog application. It only supports one user that can create -text-only entries and there are no feeds or comments, but it still -features everything you need to get started. We will use Flask and SQLite -as database which comes out of the box with Python, so there is nothing -else you need. - -If you want the full sourcecode in advance or for comparison, check out -the `example source`_. - -.. _example source: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ - -.. toctree:: - :maxdepth: 2 - - introduction - folders - schema - setup - dbinit - dbcon - views - templates - css - testing diff --git a/docs/tutorial/introduction.rst b/docs/tutorial/introduction.rst deleted file mode 100644 index 04396a9d..00000000 --- a/docs/tutorial/introduction.rst +++ /dev/null @@ -1,29 +0,0 @@ -Introducing Flaskr -================== - -We will call our blogging application flaskr here, feel free to chose a -less web-2.0-ish name ;) Basically we want it to do the following things: - -1. let the user sign in and out with credentials specified in the - configuration. Only one user is supported. -2. when the user is logged in he or she can add new entries to the page - consisting of a text-only title and some HTML for the text. This HTML - is not sanitized because we trust the user here. -3. the page shows all entries so far in reverse order (newest on top) and - the user can add new ones from there if logged in. - -We will be using SQlite3 directly for that application because it's good -enough for an application of that size. For larger applications however -it makes a lot of sense to use `SQLAlchemy`_ that handles database -connections in a more intelligent way, allows you to target different -relational databases at once and more. You might also want to consider -one of the popular NoSQL databases if your data is more suited for those. - -Here a screenshot from the final application: - -.. image:: ../_static/flaskr.png - :align: center - :class: screenshot - :alt: screenshot of the final application - -.. _SQLAlchemy: http://www.sqlalchemy.org/ diff --git a/docs/tutorial/schema.rst b/docs/tutorial/schema.rst deleted file mode 100644 index ed329539..00000000 --- a/docs/tutorial/schema.rst +++ /dev/null @@ -1,21 +0,0 @@ -Step 1: Database Schema -======================= - -First we want to create the database schema. For this application only a -single table is needed and we only want to support SQLite so that is quite -easy. Just put the following contents into a file named `schema.sql` in -the just created `flaskr` folder: - -.. sourcecode:: sql - - drop table if exists entries; - create table entries ( - id integer primary key autoincrement, - title string not null, - text string not null - ); - -This schema consists of a single table called `entries` and each row in -this table has an `id`, a `title` and a `text`. The `id` is an -automatically incrementing integer and a primary key, the other two are -strings that must not be null. diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst deleted file mode 100644 index 24b76561..00000000 --- a/docs/tutorial/setup.rst +++ /dev/null @@ -1,69 +0,0 @@ -Step 2: Application Setup Code -============================== - -Now that we have the schema in place we can create the application module. -Let's call it `flaskr.py` inside the `flaskr` folder. For starters we -will add the imports we will need as well as the config section. For -small applications it's a possibility to drop the configuration directly -into the module which we will be doing here. However a cleaner solution -would be to create a separate `.ini` or `.py` file and load that or import -the values from there. - -:: - - # all the imports - import sqlite3 - from flask import Flask, request, session, g, redirect, url_for, \ - abort, render_template, flash - - # configuration - DATABASE = '/tmp/flaskr.db' - DEBUG = True - SECRET_KEY = 'development key' - USERNAME = 'admin' - PASSWORD = 'default' - -Next we can create our actual application and initialize it with the -config:: - - # create our little application :) - app = Flask(__name__) - app.secret_key = SECRET_KEY - app.debug = DEBUG - -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! - -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 -Python shell or a script. This will come in handy later - -:: - - def connect_db(): - return sqlite3.connect(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:: - - if __name__ == '__main__': - app.run() - -With that out of the way you should be able to start up the application -without problems. When you head over to the server you will get an 404 -page not found error because we don't have any views yet. But we will -focus on that a little later. First we should get the database working. - -.. admonition:: Troubleshooting - - If you notice later that the browser cannot connect to the server - during development, you might want to try this line instead:: - - app.run(host='127.0.0.1') - - In a nutshell: Werkzeug starts up as IPv6 on many operating systems by - default and not every browser is happy with that. This forces IPv4 - usage. diff --git a/docs/tutorial/templates.rst b/docs/tutorial/templates.rst deleted file mode 100644 index 66b1dec6..00000000 --- a/docs/tutorial/templates.rst +++ /dev/null @@ -1,107 +0,0 @@ -Step 6: The Templates -===================== - -Now we should start working on the templates. If we request the URLs now -we would only get an exception that Flask cannot find the templates. The -templates are using `Jinja2`_ syntax and have autoescaping enabled by -default. This means that unless you mark a value in the code with -:class:`~flask.Markup` or with the ``|safe`` filter in the template, -Jinja2 will ensure that special characters such as ``<`` or ``>`` are -escaped with their XML equivalents. - -We are also using template inheritance which makes it possible to reuse -the layout of the website in all pages. - -Put the following templates into the `templates` folder: - -.. _Jinja2: http://jinja.pocoo.org/2/documentation/templates - -layout.html ------------ - -This template contains the HTML skeleton, the header and a link to log in -(or log out if the user was already logged in). It also displays the -flashed messages if there are any. The ``{% block body %}`` block can be -replaced by a block of the same name (``body``) in a child template. - -The :class:`~flask.session` dict is available in the template as well and -you can use that to check if the user is logged in or not. Note that in -Jinja you can access missing attributes and items of objects / dicts which -makes the following code work, even if there is no ``'logged_in'`` key in -the session: - -.. sourcecode:: html+jinja - - - Flaskr - -
-

Flaskr

-
- {% if not session.logged_in %} - log in - {% else %} - log out - {% endif %} -
- {% for message in get_flashed_messages() %} -
{{ message }}
- {% endfor %} - {% block body %}{% endblock %} -
- -show_entries.html ------------------ - -This template extends the `layout.html` template from above to display the -messages. Note that the `for` loop iterates over the messages we passed -in with the :func:`~flask.render_template` function. We also tell the -form to submit to your `add_entry` function and use `POST` as `HTTP` -method: - -.. sourcecode:: html+jinja - - {% extends "layout.html" %} - {% block body %} - {% if session.logged_in %} -
-
-
Title: -
-
Text: -
-
-
-
- {% endif %} -
    - {% for entry in entries %} -
  • {{ entry.title }}

    {{ entry.text|safe }} - {% else %} -
  • Unbelievable. No entries here so far - {% endfor %} -
- {% endblock %} - -login.html ----------- - -Finally the login template which basically just displays a form to allow -the user to login: - -.. sourcecode:: html+jinja - - {% extends "layout.html" %} - {% block body %} -

Login

- {% if error %}

Error: {{ error }}{% endif %} -

-
-
Username: -
-
Password: -
-
-
-
- {% endblock %} diff --git a/docs/tutorial/testing.rst b/docs/tutorial/testing.rst deleted file mode 100644 index 3d1aa806..00000000 --- a/docs/tutorial/testing.rst +++ /dev/null @@ -1,9 +0,0 @@ -Bonus: Testing the Application -=============================== - -Now that you have finished the application and everything works as -expected, it's probably not the best idea to add automated tests to -simplify modifications in the future. The application above is used as a -basic example of how to perform unittesting in the :ref:`testing` section -of the documentation. Go there to see how easy it is to test Flask -applications. diff --git a/docs/tutorial/views.rst b/docs/tutorial/views.rst deleted file mode 100644 index 29be65fa..00000000 --- a/docs/tutorial/views.rst +++ /dev/null @@ -1,87 +0,0 @@ -Step 5: The View Functions -========================== - -Now that the database connections are working we can start writing the -view functions. We will need four of them: - -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 view function will pass the entries as dicts to the -`show_entries.html` template and return the rendered one:: - - @app.route('/') - def show_entries(): - cur = g.db.execute('select title, text from entries order by id desc') - entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()] - return render_template('show_entries.html', entries=entries) - -Add New Entry -------------- - -This view lets the user add new entries if he's logged in. This only -responds to `POST` requests, the actual form is shown on the -`show_entries` page. If everything worked out well we will -:func:`~flask.flash` an information message to the next request and -redirect back to the `show_entries` page:: - - @app.route('/add', methods=['POST']) - def add_entry(): - if not session.get('logged_in'): - abort(401) - g.db.execute('insert into entries (title, text) values (?, ?)', - [request.form['title'], request.form['text']]) - g.db.commit() - flash('New entry was successfully posted') - return redirect(url_for('show_entries')) - -Note that we check that the user is logged in here (the `logged_in` key is -present in the session and `True`). - -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:: - - @app.route('/login', methods=['GET', 'POST']) - def login(): - error = None - if request.method == 'POST': - if request.form['username'] != USERNAME: - error = 'Invalid username' - elif request.form['password'] != PASSWORD: - error = 'Invalid password' - else: - session['logged_in'] = True - flash('You were logged in') - 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 -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 -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. - -:: - - @app.route('/logout') - def logout(): - session.pop('logged_in', None) - flash('You were logged out') - return redirect(url_for('show_entries')) diff --git a/examples/flaskr/README b/examples/flaskr/README deleted file mode 100644 index 4a9a02c6..00000000 --- a/examples/flaskr/README +++ /dev/null @@ -1,26 +0,0 @@ - - / Flaskr / - - a minimal blog application - - - ~ What is Flaskr? - - A sqlite powered thumble blog application - - ~ How do I use it? - - 1. edit the configuration in the flaskr.py file - - 2. fire up a python shell and run this: - - >>> from flaskr import init_db; init_db() - - 3. now you can run the flaskr.py file with your - python interpreter and the application will - greet you on http://localhost:5000/ - - ~ Is it tested? - - You betcha. Run the `flaskr_tests.py` file to see - the tests pass. diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py deleted file mode 100644 index f2a8b341..00000000 --- a/examples/flaskr/flaskr.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- -""" - Flaskr - ~~~~~~ - - A microblog example application written as Flask tutorial with - Flask and sqlite3. - - :copyright: (c) 2010 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -from __future__ import with_statement -import sqlite3 -from contextlib import closing -from flask import Flask, request, session, g, redirect, url_for, abort, \ - render_template, flash - -# configuration -DATABASE = '/tmp/flaskr.db' -DEBUG = True -SECRET_KEY = 'development key' -USERNAME = 'admin' -PASSWORD = 'default' - -# create our little application :) -app = Flask(__name__) -app.secret_key = SECRET_KEY -app.debug = DEBUG - - -def connect_db(): - """Returns a new connection to the database.""" - return sqlite3.connect(DATABASE) - - -def init_db(): - """Creates the database tables.""" - with closing(connect_db()) as db: - with app.open_resource('schema.sql') as f: - db.cursor().executescript(f.read()) - db.commit() - - -@app.before_request -def before_request(): - """Make sure we are connected to the database each request.""" - g.db = connect_db() - - -@app.after_request -def after_request(response): - """Closes the database again at the end of the request.""" - g.db.close() - return response - - -@app.route('/') -def show_entries(): - cur = g.db.execute('select title, text from entries order by id desc') - entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()] - return render_template('show_entries.html', entries=entries) - - -@app.route('/add', methods=['POST']) -def add_entry(): - if not session.get('logged_in'): - abort(401) - g.db.execute('insert into entries (title, text) values (?, ?)', - [request.form['title'], request.form['text']]) - g.db.commit() - flash('New entry was successfully posted') - return redirect(url_for('show_entries')) - - -@app.route('/login', methods=['GET', 'POST']) -def login(): - error = None - if request.method == 'POST': - if request.form['username'] != USERNAME: - error = 'Invalid username' - elif request.form['password'] != PASSWORD: - error = 'Invalid password' - else: - session['logged_in'] = True - flash('You were logged in') - return redirect(url_for('show_entries')) - return render_template('login.html', error=error) - - -@app.route('/logout') -def logout(): - session.pop('logged_in', None) - flash('You were logged out') - return redirect(url_for('show_entries')) - - -if __name__ == '__main__': - app.run() diff --git a/examples/flaskr/flaskr_tests.py b/examples/flaskr/flaskr_tests.py deleted file mode 100644 index 9421cca6..00000000 --- a/examples/flaskr/flaskr_tests.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -""" - Flaskr Tests - ~~~~~~~~~~~~ - - Tests the Flaskr application. - - :copyright: (c) 2010 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import os -import flaskr -import unittest -import tempfile - - -class FlaskrTestCase(unittest.TestCase): - - def setUp(self): - """Before each test, set up a blank database""" - self.db_fd, flaskr.DATABASE = tempfile.mkstemp() - self.app = flaskr.app.test_client() - flaskr.init_db() - - def tearDown(self): - """Get rid of the database again after each test.""" - os.close(self.db_fd) - os.unlink(flaskr.DATABASE) - - def login(self, username, password): - return self.app.post('/login', data=dict( - username=username, - password=password - ), follow_redirects=True) - - def logout(self): - return self.app.get('/logout', follow_redirects=True) - - # testing functions - - def test_empty_db(self): - """Start with a blank database.""" - rv = self.app.get('/') - assert 'No entries here so far' in rv.data - - def test_login_logout(self): - """Make sure login and logout works""" - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD) - assert 'You were logged in' in rv.data - rv = self.logout() - assert 'You were logged out' in rv.data - rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD) - assert 'Invalid username' in rv.data - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x') - assert 'Invalid password' in rv.data - - def test_messages(self): - """Test that messages work""" - self.login(flaskr.USERNAME, flaskr.PASSWORD) - rv = self.app.post('/add', data=dict( - title='', - text='HTML allowed here' - ), follow_redirects=True) - assert 'No entries here so far' not in rv.data - assert '<Hello>' in rv.data - assert 'HTML allowed here' in rv.data - - -if __name__ == '__main__': - unittest.main() diff --git a/examples/flaskr/schema.sql b/examples/flaskr/schema.sql deleted file mode 100644 index 970cca77..00000000 --- a/examples/flaskr/schema.sql +++ /dev/null @@ -1,6 +0,0 @@ -drop table if exists entries; -create table entries ( - id integer primary key autoincrement, - title string not null, - text string not null -); diff --git a/examples/flaskr/static/style.css b/examples/flaskr/static/style.css deleted file mode 100644 index 4f3b71d8..00000000 --- a/examples/flaskr/static/style.css +++ /dev/null @@ -1,18 +0,0 @@ -body { font-family: sans-serif; background: #eee; } -a, h1, h2 { color: #377BA8; } -h1, h2 { font-family: 'Georgia', serif; margin: 0; } -h1 { border-bottom: 2px solid #eee; } -h2 { font-size: 1.2em; } - -.page { margin: 2em auto; width: 35em; border: 5px solid #ccc; - padding: 0.8em; background: white; } -.entries { list-style: none; margin: 0; padding: 0; } -.entries li { margin: 0.8em 1.2em; } -.entries li h2 { margin-left: -1em; } -.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } -.add-entry dl { font-weight: bold; } -.metanav { text-align: right; font-size: 0.8em; padding: 0.3em; - margin-bottom: 1em; background: #fafafa; } -.flash { background: #CEE5F5; padding: 0.5em; - border: 1px solid #AACBE2; } -.error { background: #F0D6D6; padding: 0.5em; } diff --git a/examples/flaskr/templates/layout.html b/examples/flaskr/templates/layout.html deleted file mode 100644 index cbdb9650..00000000 --- a/examples/flaskr/templates/layout.html +++ /dev/null @@ -1,17 +0,0 @@ - -Flaskr - -
-

Flaskr

-
- {% if not session.logged_in %} - log in - {% else %} - log out - {% endif %} -
- {% for message in get_flashed_messages() %} -
{{ message }}
- {% endfor %} - {% block body %}{% endblock %} -
diff --git a/examples/flaskr/templates/login.html b/examples/flaskr/templates/login.html deleted file mode 100644 index 6f70bb76..00000000 --- a/examples/flaskr/templates/login.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "layout.html" %} -{% block body %} -

Login

- {% if error %}

Error: {{ error }}{% endif %} -

-
-
Username: -
-
Password: -
-
-
-
-{% endblock %} diff --git a/examples/flaskr/templates/show_entries.html b/examples/flaskr/templates/show_entries.html deleted file mode 100644 index fabe65ec..00000000 --- a/examples/flaskr/templates/show_entries.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "layout.html" %} -{% block body %} - {% if session.logged_in %} -
-
-
Title: -
-
Text: -
-
-
-
- {% endif %} -
    - {% for entry in entries %} -
  • {{ entry.title }}

    {{ entry.text|safe }} - {% else %} -
  • Unbelievable. No entries here so far - {% endfor %} -
-{% endblock %} diff --git a/examples/jqueryexample/jqueryexample.py b/examples/jqueryexample/jqueryexample.py deleted file mode 100644 index 0e8caf3e..00000000 --- a/examples/jqueryexample/jqueryexample.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jQuery Example - ~~~~~~~~~~~~~~ - - A simple application that shows how Flask and jQuery get along. - - :copyright: (c) 2010 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -from flask import Flask, jsonify, render_template, request -app = Flask(__name__) - - -@app.route('/_add_numbers') -def add_numbers(): - """Add two numbers server side, ridiculous but well...""" - a = request.args.get('a', 0, type=int) - b = request.args.get('b', 0, type=int) - return jsonify(result=a + b) - - -@app.route('/') -def index(): - return render_template('index.html') - - -if __name__ == '__main__': - app.run() diff --git a/examples/jqueryexample/templates/index.html b/examples/jqueryexample/templates/index.html deleted file mode 100644 index 0545516d..00000000 --- a/examples/jqueryexample/templates/index.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "layout.html" %} -{% block body %} - -

jQuery Example

-

+ - = - ? -

calculate server side -{% endblock %} diff --git a/examples/jqueryexample/templates/layout.html b/examples/jqueryexample/templates/layout.html deleted file mode 100644 index 0b5f3a7e..00000000 --- a/examples/jqueryexample/templates/layout.html +++ /dev/null @@ -1,10 +0,0 @@ - -jQuery Example - - - -{% block body %}{% endblock %} diff --git a/examples/minitwit/README b/examples/minitwit/README deleted file mode 100644 index 065674a9..00000000 --- a/examples/minitwit/README +++ /dev/null @@ -1,26 +0,0 @@ - - / MiniTwit / - - because writing todo lists is not fun - - - ~ What is MiniTwit? - - A SQLite and Flask powered twitter clone - - ~ How do I use it? - - 1. edit the configuration in the minitwit.py file - - 2. fire up a python shell and run this: - - >>> from minitwit import init_db; init_db() - - 3. now you can run the minitwit.py file with your - python interpreter and the application will - greet you on http://localhost:5000/ - - ~ Is it tested? - - You betcha. Run the `minitwit_tests.py` file to - see the tests pass. diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py deleted file mode 100644 index 07ffe4c7..00000000 --- a/examples/minitwit/minitwit.py +++ /dev/null @@ -1,249 +0,0 @@ -# -*- coding: utf-8 -*- -""" - MiniTwit - ~~~~~~~~ - - A microblogging application written with Flask and sqlite3. - - :copyright: (c) 2010 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -from __future__ import with_statement -import time -import sqlite3 -from hashlib import md5 -from datetime import datetime -from contextlib import closing -from flask import Flask, request, session, url_for, redirect, \ - render_template, abort, g, flash -from werkzeug import check_password_hash, generate_password_hash - - -# configuration -DATABASE = '/tmp/minitwit.db' -PER_PAGE = 30 -DEBUG = True -SECRET_KEY = 'development key' - -# create our little application :) -app = Flask(__name__) - - -def connect_db(): - """Returns a new connection to the database.""" - return sqlite3.connect(DATABASE) - - -def init_db(): - """Creates the database tables.""" - with closing(connect_db()) as db: - with app.open_resource('schema.sql') as f: - db.cursor().executescript(f.read()) - db.commit() - - -def query_db(query, args=(), one=False): - """Queries the database and returns a list of dictionaries.""" - cur = g.db.execute(query, args) - rv = [dict((cur.description[idx][0], value) - for idx, value in enumerate(row)) for row in cur.fetchall()] - return (rv[0] if rv else None) if one else rv - - -def get_user_id(username): - """Convenience method to look up the id for a username.""" - rv = g.db.execute('select user_id from user where username = ?', - [username]).fetchone() - return rv[0] if rv else None - - -def format_datetime(timestamp): - """Format a timestamp for display.""" - return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d @ %H:%M') - - -def gravatar_url(email, size=80): - """Return the gravatar image for the given email address.""" - return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \ - (md5(email.strip().lower().encode('utf-8')).hexdigest(), size) - - -@app.before_request -def before_request(): - """Make sure we are connected to the database each request and look - up the current user so that we know he's there. - """ - g.db = connect_db() - g.user = None - if 'user_id' in session: - g.user = query_db('select * from user where user_id = ?', - [session['user_id']], one=True) - - -@app.after_request -def after_request(response): - """Closes the database again at the end of the request.""" - g.db.close() - return response - - -@app.route('/') -def timeline(): - """Shows a users timeline or if no user is logged in it will - redirect to the public timeline. This timeline shows the user's - messages as well as all the messages of followed users. - """ - if not g.user: - return redirect(url_for('public_timeline')) - return render_template('timeline.html', messages=query_db(''' - select message.*, user.* from message, user - where message.author_id = user.user_id and ( - user.user_id = ? or - user.user_id in (select whom_id from follower - where who_id = ?)) - order by message.pub_date desc limit ?''', - [session['user_id'], session['user_id'], PER_PAGE])) - - -@app.route('/public') -def public_timeline(): - """Displays the latest messages of all users.""" - return render_template('timeline.html', messages=query_db(''' - select message.*, user.* from message, user - where message.author_id = user.user_id - order by message.pub_date desc limit ?''', [PER_PAGE])) - - -@app.route('/') -def user_timeline(username): - """Display's a users tweets.""" - profile_user = query_db('select * from user where username = ?', - [username], one=True) - if profile_user is None: - abort(404) - followed = False - if g.user: - followed = query_db('''select 1 from follower where - follower.who_id = ? and follower.whom_id = ?''', - [session['user_id'], profile_user['user_id']], - one=True) is not None - return render_template('timeline.html', messages=query_db(''' - select message.*, user.* from message, user where - user.user_id = message.author_id and user.user_id = ? - order by message.pub_date desc limit ?''', - [profile_user['user_id'], PER_PAGE]), followed=followed, - profile_user=profile_user) - - -@app.route('//follow') -def follow_user(username): - """Adds the current user as follower of the given user.""" - if not g.user: - abort(401) - whom_id = get_user_id(username) - if whom_id is None: - abort(404) - g.db.execute('insert into follower (who_id, whom_id) values (?, ?)', - [session['user_id'], whom_id]) - g.db.commit() - flash('You are now following "%s"' % username) - return redirect(url_for('user_timeline', username=username)) - - -@app.route('//unfollow') -def unfollow_user(username): - """Removes the current user as follower of the given user.""" - if not g.user: - abort(401) - whom_id = get_user_id(username) - if whom_id is None: - abort(404) - g.db.execute('delete from follower where who_id=? and whom_id=?', - [session['user_id'], whom_id]) - g.db.commit() - flash('You are no longer following "%s"' % username) - return redirect(url_for('user_timeline', username=username)) - - -@app.route('/add_message', methods=['POST']) -def add_message(): - """Registers a new message for the user.""" - if 'user_id' not in session: - abort(401) - if request.form['text']: - g.db.execute('''insert into message (author_id, text, pub_date) - values (?, ?, ?)''', (session['user_id'], request.form['text'], - int(time.time()))) - g.db.commit() - flash('Your message was recorded') - return redirect(url_for('timeline')) - - -@app.route('/login', methods=['GET', 'POST']) -def login(): - """Logs the user in.""" - if g.user: - return redirect(url_for('timeline')) - error = None - if request.method == 'POST': - user = query_db('''select * from user where - username = ?''', [request.form['username']], one=True) - if user is None: - error = 'Invalid username' - elif not check_password_hash(user['pw_hash'], - request.form['password']): - error = 'Invalid password' - else: - flash('You were logged in') - session['user_id'] = user['user_id'] - return redirect(url_for('timeline')) - return render_template('login.html', error=error) - - -@app.route('/register', methods=['GET', 'POST']) -def register(): - """Registers the user.""" - if g.user: - return redirect(url_for('timeline')) - error = None - if request.method == 'POST': - if not request.form['username']: - error = 'You have to enter a username' - elif not request.form['email'] or \ - '@' not in request.form['email']: - error = 'You have to enter a valid email address' - elif not request.form['password']: - error = 'You have to enter a password' - elif request.form['password'] != request.form['password2']: - error = 'The two passwords do not match' - elif get_user_id(request.form['username']) is not None: - error = 'The username is already taken' - else: - g.db.execute('''insert into user ( - username, email, pw_hash) values (?, ?, ?)''', - [request.form['username'], request.form['email'], - generate_password_hash(request.form['password'])]) - g.db.commit() - flash('You were successfully registered and can login now') - return redirect(url_for('login')) - return render_template('register.html', error=error) - - -@app.route('/logout') -def logout(): - """Logs the user out.""" - flash('You were logged out') - session.pop('user_id', None) - return redirect(url_for('public_timeline')) - - -# add some filters to jinja and set the secret key and debug mode -# from the configuration. -app.jinja_env.filters['datetimeformat'] = format_datetime -app.jinja_env.filters['gravatar'] = gravatar_url -app.secret_key = SECRET_KEY -app.debug = DEBUG - - -if __name__ == '__main__': - app.run() diff --git a/examples/minitwit/minitwit_tests.py b/examples/minitwit/minitwit_tests.py deleted file mode 100644 index 10962142..00000000 --- a/examples/minitwit/minitwit_tests.py +++ /dev/null @@ -1,149 +0,0 @@ -# -*- coding: utf-8 -*- -""" - MiniTwit Tests - ~~~~~~~~~~~~~~ - - Tests the MiniTwit application. - - :copyright: (c) 2010 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import os -import minitwit -import unittest -import tempfile - - -class MiniTwitTestCase(unittest.TestCase): - - def setUp(self): - """Before each test, set up a blank database""" - self.db_fd, minitwit.DATABASE = tempfile.mkstemp() - self.app = minitwit.app.test_client() - minitwit.init_db() - - def tearDown(self): - """Get rid of the database again after each test.""" - os.close(self.db_fd) - os.unlink(minitwit.DATABASE) - - # helper functions - - def register(self, username, password, password2=None, email=None): - """Helper function to register a user""" - if password2 is None: - password2 = password - if email is None: - email = username + '@example.com' - return self.app.post('/register', data={ - 'username': username, - 'password': password, - 'password2': password2, - 'email': email, - }, follow_redirects=True) - - def login(self, username, password): - """Helper function to login""" - return self.app.post('/login', data={ - 'username': username, - 'password': password - }, follow_redirects=True) - - def register_and_login(self, username, password): - """Registers and logs in in one go""" - self.register(username, password) - return self.login(username, password) - - def logout(self): - """Helper function to logout""" - return self.app.get('/logout', follow_redirects=True) - - def add_message(self, text): - """Records a message""" - rv = self.app.post('/add_message', data={'text': text}, - follow_redirects=True) - if text: - assert 'Your message was recorded' in rv.data - return rv - - # testing functions - - def test_register(self): - """Make sure registering works""" - rv = self.register('user1', 'default') - assert 'You were successfully registered ' \ - 'and can login now' in rv.data - rv = self.register('user1', 'default') - assert 'The username is already taken' in rv.data - rv = self.register('', 'default') - assert 'You have to enter a username' in rv.data - rv = self.register('meh', '') - assert 'You have to enter a password' in rv.data - rv = self.register('meh', 'x', 'y') - assert 'The two passwords do not match' in rv.data - rv = self.register('meh', 'foo', email='broken') - assert 'You have to enter a valid email address' in rv.data - - def test_login_logout(self): - """Make sure logging in and logging out works""" - rv = self.register_and_login('user1', 'default') - assert 'You were logged in' in rv.data - rv = self.logout() - assert 'You were logged out' in rv.data - rv = self.login('user1', 'wrongpassword') - assert 'Invalid password' in rv.data - rv = self.login('user2', 'wrongpassword') - assert 'Invalid username' in rv.data - - def test_message_recording(self): - """Check if adding messages works""" - self.register_and_login('foo', 'default') - self.add_message('test message 1') - self.add_message('') - rv = self.app.get('/') - assert 'test message 1' in rv.data - assert '<test message 2>' in rv.data - - def test_timelines(self): - """Make sure that timelines work""" - self.register_and_login('foo', 'default') - self.add_message('the message by foo') - self.logout() - self.register_and_login('bar', 'default') - self.add_message('the message by bar') - rv = self.app.get('/public') - assert 'the message by foo' in rv.data - assert 'the message by bar' in rv.data - - # bar's timeline should just show bar's message - rv = self.app.get('/') - assert 'the message by foo' not in rv.data - assert 'the message by bar' in rv.data - - # now let's follow foo - rv = self.app.get('/foo/follow', follow_redirects=True) - assert 'You are now following "foo"' in rv.data - - # we should now see foo's message - rv = self.app.get('/') - assert 'the message by foo' in rv.data - assert 'the message by bar' in rv.data - - # but on the user's page we only want the user's message - rv = self.app.get('/bar') - assert 'the message by foo' not in rv.data - assert 'the message by bar' in rv.data - rv = self.app.get('/foo') - assert 'the message by foo' in rv.data - assert 'the message by bar' not in rv.data - - # now unfollow and check if that worked - rv = self.app.get('/foo/unfollow', follow_redirects=True) - assert 'You are no longer following "foo"' in rv.data - rv = self.app.get('/') - assert 'the message by foo' not in rv.data - assert 'the message by bar' in rv.data - - -if __name__ == '__main__': - unittest.main() diff --git a/examples/minitwit/schema.sql b/examples/minitwit/schema.sql deleted file mode 100644 index b64afbed..00000000 --- a/examples/minitwit/schema.sql +++ /dev/null @@ -1,21 +0,0 @@ -drop table if exists user; -create table user ( - user_id integer primary key autoincrement, - username string not null, - email string not null, - pw_hash string not null -); - -drop table if exists follower; -create table follower ( - who_id integer, - whom_id integer -); - -drop table if exists message; -create table message ( - message_id integer primary key autoincrement, - author_id integer not null, - text string not null, - pub_date integer -); diff --git a/examples/minitwit/static/style.css b/examples/minitwit/static/style.css deleted file mode 100644 index ebbed8c9..00000000 --- a/examples/minitwit/static/style.css +++ /dev/null @@ -1,178 +0,0 @@ -body { - background: #CAECE9; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; -} - -a { - color: #26776F; -} - -a:hover { - color: #333; -} - -input[type="text"], -input[type="password"] { - background: white; - border: 1px solid #BFE6E2; - padding: 2px; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - color: #105751; -} - -input[type="submit"] { - background: #105751; - border: 1px solid #073B36; - padding: 1px 3px; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; - font-weight: bold; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - color: white; -} - -div.page { - background: white; - border: 1px solid #6ECCC4; - width: 700px; - margin: 30px auto; -} - -div.page h1 { - background: #6ECCC4; - margin: 0; - padding: 10px 14px; - color: white; - letter-spacing: 1px; - text-shadow: 0 0 3px #24776F; - font-weight: normal; -} - -div.page div.navigation { - background: #DEE9E8; - padding: 4px 10px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #eee; - color: #888; - font-size: 12px; - letter-spacing: 0.5px; -} - -div.page div.navigation a { - color: #444; - font-weight: bold; -} - -div.page h2 { - margin: 0 0 15px 0; - color: #105751; - text-shadow: 0 1px 2px #ccc; -} - -div.page div.body { - padding: 10px; -} - -div.page div.footer { - background: #eee; - color: #888; - padding: 5px 10px; - font-size: 12px; -} - -div.page div.followstatus { - border: 1px solid #ccc; - background: #E3EBEA; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 3px; - font-size: 13px; -} - -div.page ul.messages { - list-style: none; - margin: 0; - padding: 0; -} - -div.page ul.messages li { - margin: 10px 0; - padding: 5px; - background: #F0FAF9; - border: 1px solid #DBF3F1; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - min-height: 48px; -} - -div.page ul.messages p { - margin: 0; -} - -div.page ul.messages li img { - float: left; - padding: 0 10px 0 0; -} - -div.page ul.messages li small { - font-size: 0.9em; - color: #888; -} - -div.page div.twitbox { - margin: 10px 0; - padding: 5px; - background: #F0FAF9; - border: 1px solid #94E2DA; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; -} - -div.page div.twitbox h3 { - margin: 0; - font-size: 1em; - color: #2C7E76; -} - -div.page div.twitbox p { - margin: 0; -} - -div.page div.twitbox input[type="text"] { - width: 585px; -} - -div.page div.twitbox input[type="submit"] { - width: 70px; - margin-left: 5px; -} - -ul.flashes { - list-style: none; - margin: 10px 10px 0 10px; - padding: 0; -} - -ul.flashes li { - background: #B9F3ED; - border: 1px solid #81CEC6; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 4px; - font-size: 13px; -} - -div.error { - margin: 10px 0; - background: #FAE4E4; - border: 1px solid #DD6F6F; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 4px; - font-size: 13px; -} diff --git a/examples/minitwit/templates/layout.html b/examples/minitwit/templates/layout.html deleted file mode 100644 index 668e3895..00000000 --- a/examples/minitwit/templates/layout.html +++ /dev/null @@ -1,32 +0,0 @@ - -{% block title %}Welcome{% endblock %} | MiniTwit - -

-

MiniTwit

- - {% with flashes = get_flashed_messages() %} - {% if flashes %} -
    - {% for message in flashes %} -
  • {{ message }} - {% endfor %} -
- {% endif %} - {% endwith %} -
- {% block body %}{% endblock %} -
- -
diff --git a/examples/minitwit/templates/login.html b/examples/minitwit/templates/login.html deleted file mode 100644 index ae776714..00000000 --- a/examples/minitwit/templates/login.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "layout.html" %} -{% block title %}Sign In{% endblock %} -{% block body %} -

Sign In

- {% if error %}
Error: {{ error }}
{% endif %} -
-
-
Username: -
-
Password: -
-
-
-
-{% endblock %} - diff --git a/examples/minitwit/templates/register.html b/examples/minitwit/templates/register.html deleted file mode 100644 index ccb345d5..00000000 --- a/examples/minitwit/templates/register.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "layout.html" %} -{% block title %}Sign Up{% endblock %} -{% block body %} -

Sign Up

- {% if error %}
Error: {{ error }}
{% endif %} -
-
-
Username: -
-
E-Mail: -
-
Password: -
-
Password (repeat): -
-
-
-
-{% endblock %} diff --git a/examples/minitwit/templates/timeline.html b/examples/minitwit/templates/timeline.html deleted file mode 100644 index ea7d751b..00000000 --- a/examples/minitwit/templates/timeline.html +++ /dev/null @@ -1,49 +0,0 @@ -{% extends "layout.html" %} -{% block title %} - {% if request.endpoint == 'public_timeline' %} - Public Timeline - {% elif request.endpoint == 'user_timeline' %} - {{ profile_user.username }}'s Timeline - {% else %} - My Timeline - {% endif %} -{% endblock %} -{% block body %} -

{{ self.title() }}

- {% if g.user %} - {% if request.endpoint == 'user_timeline' %} -
- {% if g.user.user_id == profile_user.user_id %} - This is you! - {% elif followed %} - You are currently following this user. - Unfollow user. - {% else %} - You are not yet following this user. - . - {% endif %} -
- {% elif request.endpoint == 'timeline' %} -
-

What's on your mind {{ g.user.username }}?

-
-

-

-
- {% endif %} - {% endif %} -
    - {% for message in messages %} -
  • - {{ message.username }} - {{ message.text }} - — {{ message.pub_date|datetimeformat }} - {% else %} -

  • There's no message so far. - {% endfor %} -
-{% endblock %} diff --git a/flask.py b/flask.py deleted file mode 100644 index 1f0fb7e6..00000000 --- a/flask.py +++ /dev/null @@ -1,801 +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 - -from jinja2 import Environment, PackageLoader, FileSystemLoader -from werkzeug import Request as RequestBase, Response as ResponseBase, \ - LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \ - ImmutableDict, cached_property -from werkzeug.routing import Map, Rule -from werkzeug.exceptions import HTTPException -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 = None - - @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.request_class` to your subclass. - """ - default_mimetype = 'text/html' - - -class _RequestGlobals(object): - pass - - -class _NullSession(SecureCookie): - """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 - - def __enter__(self): - _request_ctx_stack.push(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: - _request_ctx_stack.pop() - - -def url_for(endpoint, **values): - """Generates a URL to the given endpoint with the method provided. - - :param endpoint: the endpoint of the URL (name of the function) - :param values: the variable arguments of the URL rule - """ - return _request_ctx_stack.top.url_adapter.build(endpoint, values) - - -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): - """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`. - - :param message: the message to be flashed. - """ - session.setdefault('_flashes', []).append(message) - - -def get_flashed_messages(): - """Pulls all flashed messages from the session and returns them. - Further calls in the same request to the function will return - the same messages. - """ - flashes = _request_ctx_stack.top.flashes - if flashes is None: - _request_ctx_stack.top.flashes = flashes = session.pop('_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 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 Flask(object): - """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' - - #: 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. - secret_key = None - - #: The secure cookie uses this for the name of the session cookie - session_cookie_name = 'session' - - #: options that are passed directly to the Jinja2 environment - jinja_options = ImmutableDict( - autoescape=True, - extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] - ) - - def __init__(self, package_name): - #: 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. - self.debug = False - - #: the name of the package or module. Do not change this once - #: it was set by the constructor. - self.package_name = package_name - - #: where is the app root located? - self.root_path = _get_package_path(self.package_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 list of functions that should be called at the beginning - #: of the request before request dispatching kicks in. 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 list of functions that are called at the end of the - #: request. The function is passed the current response - #: object and modify it in place or replace it. - #: To register a function here use the :meth:`after_request` - #: decorator. - self.after_request_funcs = [] - - #: a list of functions that are called without arguments - #: to populate the template context. 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 = [_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.package_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 - - 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.package_name) - - def update_template_context(self, context): - """Update the template context with some commonly used variables. - This injects request, session and g into the template context. - - :param context: the context as a dictionary that is updated in place - to add extra variables. - """ - for func in self.template_context_processors: - 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_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.package_name, resource) - - 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 SecureCookie.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` - """ - session.save_cookie(response, self.session_cookie_name) - - def add_url_rule(self, rule, endpoint, 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 - """ - 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, f.__name__, 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(): - 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(): - 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 before_request(self, f): - """Registers a function to run before each request.""" - self.before_request_funcs.append(f) - return f - - def after_request(self, f): - """Register a function to be run after each request.""" - self.after_request_funcs.append(f) - return f - - def context_processor(self, f): - """Registers a template context processor function.""" - self.template_context_processors.append(f) - return f - - def match_request(self): - """Matches the current request against the URL map and also - stores the endpoint and view arguments on the request object - is successful, otherwise the exception is stored. - """ - rv = _request_ctx_stack.top.url_adapter.match() - request.endpoint, request.view_args = rv - return rv - - 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`. - """ - try: - endpoint, values = self.match_request() - return self.view_functions[endpoint](**values) - except HTTPException, e: - handler = self.error_handlers.get(e.code) - if handler is None: - return e - return handler(e) - except Exception, e: - handler = self.error_handlers.get(500) - if self.debug or handler is None: - raise - return handler(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 allowd for `rv`: - - ======================= =========================================== - :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 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. - """ - for func in self.before_request_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`. - """ - session = _request_ctx_stack.top.session - if not isinstance(session, _NullSession): - self.save_session(session, response) - for handler in self.after_request_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): - rv = self.preprocess_request() - if rv is None: - rv = self.dispatch_request() - response = self.make_response(rv) - response = self.process_response(response) - return response(environ, start_response) - - 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) - - :params 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/website/flask_website.py b/flask_website.py similarity index 100% rename from website/flask_website.py rename to flask_website.py diff --git a/setup.py b/setup.py deleted file mode 100644 index ae68c49e..00000000 --- a/setup.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Flask ------ - -Flask is a microframework for Python based on Werkzeug, Jinja 2 and good -intentions. And before you ask: It's BSD licensed! - -Flask is Fun -```````````` - -:: - - from flask import Flask - app = Flask(__name__) - - @app.route("/") - def hello(): - return "Hello World!" - - if __name__ == "__main__": - app.run() - -And Easy to Setup -````````````````` - -:: - - $ easy_install Flask - $ python hello.py - * Running on http://localhost:5000/ - -Links -````` - -* `website `_ -* `documentation `_ -* `development version - `_ - -""" -from setuptools import setup - - -setup( - name='Flask', - version='0.2', - url='http://github.com/mitsuhiko/flask/', - license='BSD', - author='Armin Ronacher', - author_email='armin.ronacher@active-4.com', - description='A microframework based on Werkzeug, Jinja2 ' - 'and good intentions', - long_description=__doc__, - py_modules=['flask'], - zip_safe=False, - platforms='any', - install_requires=[ - 'Werkzeug>=0.6.1', - 'Jinja2>=2.4' - ], - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - 'Topic :: Software Development :: Libraries :: Python Modules' - ] -) diff --git a/website/static/logo.png b/static/logo.png similarity index 100% rename from website/static/logo.png rename to static/logo.png diff --git a/website/static/mailinglist.js b/static/mailinglist.js similarity index 100% rename from website/static/mailinglist.js rename to static/mailinglist.js diff --git a/website/static/mailinglist.png b/static/mailinglist.png similarity index 100% rename from website/static/mailinglist.png rename to static/mailinglist.png diff --git a/website/static/mask.png b/static/mask.png similarity index 100% rename from website/static/mask.png rename to static/mask.png diff --git a/website/static/ship.png b/static/ship.png similarity index 100% rename from website/static/ship.png rename to static/ship.png diff --git a/website/static/style.css b/static/style.css similarity index 100% rename from website/static/style.css rename to static/style.css diff --git a/website/sync-librelist.py b/sync-librelist.py similarity index 100% rename from website/sync-librelist.py rename to sync-librelist.py diff --git a/website/templates/404.html b/templates/404.html similarity index 100% rename from website/templates/404.html rename to templates/404.html diff --git a/website/templates/index.html b/templates/index.html similarity index 100% rename from website/templates/index.html rename to templates/index.html diff --git a/website/templates/layout.html b/templates/layout.html similarity index 100% rename from website/templates/layout.html rename to templates/layout.html diff --git a/website/templates/mailinglist/archive.html b/templates/mailinglist/archive.html similarity index 100% rename from website/templates/mailinglist/archive.html rename to templates/mailinglist/archive.html diff --git a/website/templates/mailinglist/index.html b/templates/mailinglist/index.html similarity index 100% rename from website/templates/mailinglist/index.html rename to templates/mailinglist/index.html diff --git a/website/templates/mailinglist/layout.html b/templates/mailinglist/layout.html similarity index 100% rename from website/templates/mailinglist/layout.html rename to templates/mailinglist/layout.html diff --git a/website/templates/mailinglist/show_thread.html b/templates/mailinglist/show_thread.html similarity index 100% rename from website/templates/mailinglist/show_thread.html rename to templates/mailinglist/show_thread.html diff --git a/tests/flask_tests.py b/tests/flask_tests.py deleted file mode 100644 index 5f07fbe8..00000000 --- a/tests/flask_tests.py +++ /dev/null @@ -1,316 +0,0 @@ -# -*- coding: utf-8 -*- -""" - Flask Tests - ~~~~~~~~~~~ - - Tests Flask itself. The majority of Flask is already tested - as part of Werkzeug. - - :copyright: (c) 2010 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -from __future__ import with_statement -import os -import sys -import flask -import unittest -import tempfile - - -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')) - - -class ContextTestCase(unittest.TestCase): - - def test_context_binding(self): - app = flask.Flask(__name__) - @app.route('/') - def index(): - return 'Hello %s!' % flask.request.args['name'] - @app.route('/meh') - def meh(): - return flask.request.url - - with app.test_request_context('/?name=World'): - assert index() == 'Hello World!' - with app.test_request_context('/meh'): - assert meh() == 'http://localhost/meh' - - -class BasicFunctionalityTestCase(unittest.TestCase): - - def test_request_dispatching(self): - app = flask.Flask(__name__) - @app.route('/') - def index(): - return flask.request.method - @app.route('/more', methods=['GET', 'POST']) - def more(): - return flask.request.method - - c = app.test_client() - assert c.get('/').data == 'GET' - rv = c.post('/') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD'] - rv = c.head('/') - assert rv.status_code == 200 - assert not rv.data # head truncates - assert c.post('/more').data == 'POST' - assert c.get('/more').data == 'GET' - rv = c.delete('/more') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'POST'] - - def test_url_mapping(self): - app = flask.Flask(__name__) - def index(): - return flask.request.method - def more(): - return flask.request.method - - app.add_url_rule('/', 'index', index) - app.add_url_rule('/more', 'more', more, methods=['GET', 'POST']) - - c = app.test_client() - assert c.get('/').data == 'GET' - rv = c.post('/') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD'] - rv = c.head('/') - assert rv.status_code == 200 - assert not rv.data # head truncates - assert c.post('/more').data == 'POST' - assert c.get('/more').data == 'GET' - rv = c.delete('/more') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'POST'] - - def test_session(self): - app = flask.Flask(__name__) - app.secret_key = 'testkey' - @app.route('/set', methods=['POST']) - def set(): - flask.session['value'] = flask.request.form['value'] - return 'value set' - @app.route('/get') - def get(): - return flask.session['value'] - - c = app.test_client() - assert c.post('/set', data={'value': '42'}).data == 'value set' - assert c.get('/get').data == '42' - - def test_missing_session(self): - app = flask.Flask(__name__) - def expect_exception(f, *args, **kwargs): - try: - f(*args, **kwargs) - except RuntimeError, e: - assert e.args and 'session is unavailable' in e.args[0] - else: - assert False, 'expected exception' - with app.test_request_context(): - assert flask.session.get('missing_key') is None - expect_exception(flask.session.__setitem__, 'foo', 42) - expect_exception(flask.session.pop, 'foo') - - def test_flashes(self): - app = flask.Flask(__name__) - app.secret_key = 'testkey' - - with app.test_request_context(): - assert not flask.session.modified - flask.flash('Zap') - flask.session.modified = False - flask.flash('Zip') - assert flask.session.modified - assert list(flask.get_flashed_messages()) == ['Zap', 'Zip'] - - def test_request_processing(self): - app = flask.Flask(__name__) - evts = [] - @app.before_request - def before_request(): - evts.append('before') - @app.after_request - def after_request(response): - response.data += '|after' - evts.append('after') - return response - @app.route('/') - def index(): - assert 'before' in evts - assert 'after' not in evts - return 'request' - assert 'after' not in evts - rv = app.test_client().get('/').data - assert 'after' in evts - assert rv == 'request|after' - - def test_error_handling(self): - app = flask.Flask(__name__) - @app.errorhandler(404) - def not_found(e): - return 'not found', 404 - @app.errorhandler(500) - def internal_server_error(e): - return 'internal server error', 500 - @app.route('/') - def index(): - flask.abort(404) - @app.route('/error') - def error(): - 1/0 - 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' in rv.data - - def test_response_creation(self): - app = flask.Flask(__name__) - @app.route('/unicode') - def from_unicode(): - return u'Hällo Wörld' - @app.route('/string') - def from_string(): - return u'Hällo Wörld'.encode('utf-8') - @app.route('/args') - def from_tuple(): - return 'Meh', 400, {'X-Foo': 'Testing'}, 'text/plain' - c = app.test_client() - assert c.get('/unicode').data == u'Hällo Wörld'.encode('utf-8') - assert c.get('/string').data == u'Hällo Wörld'.encode('utf-8') - rv = c.get('/args') - assert rv.data == 'Meh' - assert rv.headers['X-Foo'] == 'Testing' - assert rv.status_code == 400 - assert rv.mimetype == 'text/plain' - - def test_url_generation(self): - app = flask.Flask(__name__) - @app.route('/hello/', methods=['POST']) - def hello(): - pass - with app.test_request_context(): - assert flask.url_for('hello', name='test x') == '/hello/test%20x' - - def test_custom_converters(self): - from werkzeug.routing import BaseConverter - class ListConverter(BaseConverter): - def to_python(self, value): - return value.split(',') - def to_url(self, value): - return ','.join(super(ListConverter, self).to_url(x) for x in value) - app = flask.Flask(__name__) - app.url_map.converters['list'] = ListConverter - @app.route('/') - def index(args): - return '|'.join(args) - c = app.test_client() - assert c.get('/1,2,3').data == '1|2|3' - - def test_static_files(self): - app = flask.Flask(__name__) - rv = app.test_client().get('/static/index.html') - assert rv.status_code == 200 - assert rv.data.strip() == '

Hello World!

' - with app.test_request_context(): - assert flask.url_for('static', filename='index.html') \ - == '/static/index.html' - - -class JSONTestCase(unittest.TestCase): - - def test_jsonify(self): - d = dict(a=23, b=42, c=[1, 2, 3]) - app = flask.Flask(__name__) - @app.route('/kw') - def return_kwargs(): - return flask.jsonify(**d) - @app.route('/dict') - def return_dict(): - return flask.jsonify(d) - c = app.test_client() - for url in '/kw', '/dict': - rv = c.get(url) - assert rv.mimetype == 'application/json' - assert flask.json.loads(rv.data) == d - - def test_json_attr(self): - app = flask.Flask(__name__) - @app.route('/add', methods=['POST']) - def add(): - return unicode(flask.request.json['a'] + flask.request.json['b']) - c = app.test_client() - rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}), - content_type='application/json') - assert rv.data == '3' - - def test_template_escaping(self): - app = flask.Flask(__name__) - with app.test_request_context(): - rv = flask.render_template_string('{{ ""|tojson|safe }}') - assert rv == '"<\\/script>"' - rv = flask.render_template_string('{{ "<\0/script>"|tojson|safe }}') - assert rv == '"<\\u0000\\/script>"' - - -class TemplatingTestCase(unittest.TestCase): - - def test_context_processing(self): - app = flask.Flask(__name__) - @app.context_processor - def context_processor(): - return {'injected_value': 42} - @app.route('/') - def index(): - return flask.render_template('context_template.html', value=23) - rv = app.test_client().get('/') - assert rv.data == '

23|42' - - def test_escaping(self): - text = '

Hello World!' - app = flask.Flask(__name__) - @app.route('/') - def index(): - return flask.render_template('escaping_template.html', text=text, - html=flask.Markup(text)) - lines = app.test_client().get('/').data.splitlines() - assert lines == [ - '<p>Hello World!', - '

Hello World!', - '

Hello World!', - '

Hello World!', - '<p>Hello World!', - '

Hello World!' - ] - - def test_macros(self): - app = flask.Flask(__name__) - with app.test_request_context(): - macro = flask.get_template_attribute('_macro.html', 'hello') - assert macro('World') == 'Hello World!' - - -def suite(): - from minitwit_tests import MiniTwitTestCase - from flaskr_tests import FlaskrTestCase - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ContextTestCase)) - suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase)) - suite.addTest(unittest.makeSuite(TemplatingTestCase)) - if flask.json_available: - suite.addTest(unittest.makeSuite(JSONTestCase)) - suite.addTest(unittest.makeSuite(MiniTwitTestCase)) - suite.addTest(unittest.makeSuite(FlaskrTestCase)) - return suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/tests/static/index.html b/tests/static/index.html deleted file mode 100644 index de8b69b6..00000000 --- a/tests/static/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello World!

diff --git a/tests/templates/_macro.html b/tests/templates/_macro.html deleted file mode 100644 index 3460ae2e..00000000 --- a/tests/templates/_macro.html +++ /dev/null @@ -1 +0,0 @@ -{% macro hello(name) %}Hello {{ name }}!{% endmacro %} diff --git a/tests/templates/context_template.html b/tests/templates/context_template.html deleted file mode 100644 index fadf3e5d..00000000 --- a/tests/templates/context_template.html +++ /dev/null @@ -1 +0,0 @@ -

{{ value }}|{{ injected_value }} diff --git a/tests/templates/escaping_template.html b/tests/templates/escaping_template.html deleted file mode 100644 index dc47644d..00000000 --- a/tests/templates/escaping_template.html +++ /dev/null @@ -1,6 +0,0 @@ -{{ text }} -{{ html }} -{% autoescape false %}{{ text }} -{{ html }}{% endautoescape %} -{% autoescape true %}{{ text }} -{{ html }}{% endautoescape %}