Browse Source

Initial checkin of stuff that exists so far.

pull/1638/head
Armin Ronacher 15 years ago
commit
33850c0ebd
  1. 4
      .gitignore
  2. 30
      examples/apishowcase/apishowcase.py
  3. 7
      examples/apishowcase/static/style.css
  4. 12
      examples/apishowcase/templates/counter.html
  5. 13
      examples/apishowcase/templates/hello.html
  6. 11
      examples/apishowcase/templates/index.html
  7. 8
      examples/apishowcase/templates/layout.html
  8. 228
      examples/minitwit/minitwit.py
  9. 21
      examples/minitwit/schema.sql
  10. 178
      examples/minitwit/static/style.css
  11. 32
      examples/minitwit/templates/layout.html
  12. 16
      examples/minitwit/templates/login.html
  13. 19
      examples/minitwit/templates/register.html
  14. 49
      examples/minitwit/templates/timeline.html
  15. 356
      flask.py

4
.gitignore vendored

@ -0,0 +1,4 @@
.DS_Store
*.pyc
*.pyo
env

30
examples/apishowcase/apishowcase.py

@ -0,0 +1,30 @@
from flask import Flask, abort, redirect, request, session, \
render_template, url_for
#: create a new flask applications. We pass it the name of our module
#: so that flask knows where to look for templates and static files.
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
"""Show an overview page"""
return render_template('index.html')
@app.route('/hello/', methods=['GET', 'POST'])
def hello_user():
"""Ask the user for a name and redirect to :func:`hello`"""
if request.method == 'POST':
return redirect(url_for('hello', name=request.form['name']))
return render_template('hello.html', name=None)
@app.route('/hello/<name>', methods=['GET'])
def hello(name):
"""Greet name friendly"""
return render_template('hello.html', name=name)
if __name__ == '__main__':
app.run(debug=True)

7
examples/apishowcase/static/style.css

@ -0,0 +1,7 @@
body {
font-family: 'Trebuchet MS', sans-serif;
}
a {
color: #44AD80;
}

12
examples/apishowcase/templates/counter.html

@ -0,0 +1,12 @@
{% extends "layout.html" %}
{% block body %}
<p>
This is an example application that shows how
the Werkzeug powered Flask microframework works.
<p>
The various parts of the example application:
<ul>
<li><a href="{{ url_for('hello_user') }}">Hello World</a>
<li><a href="{{ url_for('counter') }}">Counter</a>
</ul>
{% endblock %}

13
examples/apishowcase/templates/hello.html

@ -0,0 +1,13 @@
{% extends "layout.html" %}
{% block body %}
{% if name %}
<h2>Hello {{ name }}!</h2>
{% else %}
<h3>Hello Stranger …</h3>
<form action="{{ url_for('hello_user') }}" method="post">
<p>… What's your name?
<p><input type=text name=name size=30>
<input type=submit value="That's me">
</form>
{% endif %}
{% endblock %}

11
examples/apishowcase/templates/index.html

@ -0,0 +1,11 @@
{% extends "layout.html" %}
{% block body %}
<p>
This is an example application that shows how
the Werkzeug powered Flask microframework works.
<p>
The various parts of the example application:
<ul>
<li><a href="{{ url_for('hello_user') }}">Hello World</a>
</ul>
{% endblock %}

8
examples/apishowcase/templates/layout.html

@ -0,0 +1,8 @@
<!doctype html>
<title>Flask API Showcase</title>
<link rel=stylesheet href="{{ url_for('static', filename='style.css') }}" type=text/css>
<h1>Flask API Showcase</h1>
{% if request.endpoint != 'index' %}
<div class=backlink><a href="{{ url_for('index') }}">&laquo; back to index</a></div>
{% endif %}
{% block body %}{% endblock %}

228
examples/minitwit/minitwit.py

@ -0,0 +1,228 @@
# -*- coding: utf-8 -*-
from __future__ import with_statement
import re
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, generate_password_hash, \
check_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 database 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.lower().encode('utf-8')).hexdigest(), size)
@app.request_init
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 = sqlite3.connect(DATABASE)
if 'user_id' in session:
g.user = query_db('select * from user where user_id = ?',
[session['user_id']], one=True)
@app.request_shutdown
def after_request(request):
"""Closes the database again at the end of the request."""
g.db.close()
return request
@app.route('/')
def timeline():
if not 'user_id' in session:
return redirect(url_for('public_timeline'))
offset = request.args.get('offset', type=int)
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():
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('/<username>')
def user_timeline(username):
profile_user = query_db('select * from user where username = ?',
[username], one=True)
if profile_user is None:
abort(404)
followd = False
if 'user_id' in session:
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('/<username>/follow')
def follow_user(username):
if not 'user_id' in session:
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('/<username>/unfollow')
def unfollow_user(username):
if not 'user_id' in session:
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')
def add_message():
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')
def login():
if 'user_id' in session:
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')
def register():
if 'user_id' in session:
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 to 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():
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()

21
examples/minitwit/schema.sql

@ -0,0 +1,21 @@
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
);

178
examples/minitwit/static/style.css

@ -0,0 +1,178 @@
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;
}

32
examples/minitwit/templates/layout.html

@ -0,0 +1,32 @@
<!doctype html>
<title>{% block title %}Welcome{% endblock %} | MiniTwit</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>MiniTwit</h1>
<div class=navigation>
{% if g.user %}
<a href="{{ url_for('timeline') }}">my timeline</a> |
<a href="{{ url_for('public_timeline') }}">public timeline</a> |
<a href="{{ url_for('logout') }}">sign out [{{ g.user.username }}]</a>
{% else %}
<a href="{{ url_for('public_timeline') }}">public timeline</a> |
<a href="{{ url_for('register') }}">sign up</a> |
<a href="{{ url_for('login') }}">sign in</a>
{% endif %}
</div>
{% with flashes = get_flashed_messages() %}
{% if flashes %}
<ul class=flashes>
{% for message in flashes %}
<li>{{ message }}
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<div class=body>
{% block body %}{% endblock %}
</div>
<div class=footer>
MiniTwit &mdash; A Flask Application
</div>
</div>

16
examples/minitwit/templates/login.html

@ -0,0 +1,16 @@
{% extends "layout.html" %}
{% block title %}Sign In{% endblock %}
{% block body %}
<h2>Sign In</h2>
{% if error %}<div class=error><strong>Error:</strong> {{ error }}</div>{% endif %}
<form action="" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username size=30 value="{{ request.form.username }}">
<dt>Password:
<dd><input type=password name=password size=30>
</dl>
<div class=actions><input type=submit value="Sign In"></div>
</form>
{% endblock %}

19
examples/minitwit/templates/register.html

@ -0,0 +1,19 @@
{% extends "layout.html" %}
{% block title %}Sign Up{% endblock %}
{% block body %}
<h2>Sign Up</h2>
{% if error %}<div class=error><strong>Error:</strong> {{ error }}</div>{% endif %}
<form action="" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username size=30 value="{{ request.form.username }}">
<dt>E-Mail:
<dd><input type=text name=email size=30 value="{{ request.form.email }}">
<dt>Password:
<dd><input type=password name=password size=30>
<dt>Password <small>(repeat)</small>:
<dd><input type=password name=password2 size=30>
</dl>
<div class=actions><input type=submit value="Sign Up"></div>
</form>
{% endblock %}

49
examples/minitwit/templates/timeline.html

@ -0,0 +1,49 @@
{% 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 %}
<h2>{{ self.title() }}</h2>
{% if g.user %}
{% if request.endpoint == 'user_timeline' %}
<div class=followstatus>
{% if g.user.user_id == profile_user.user_id %}
This is you!
{% elif followed %}
You are currently following this user.
<a class=unfollow href="{{ url_for('unfollow_user', username=profile_user.username)
}}">Unfollow user</a>.
{% else %}
You are not yet following this user.
<a class=follow href="{{ url_for('follow_user', username=profile_user.username)
}}">Follow user</a>.
{% endif %}
</div>
{% elif request.endpoint == 'timeline' %}
<div class=twitbox>
<h3>What's on your mind {{ g.user.username }}?</h3>
<form action="{{ url_for('add_message') }}" method=post>
<p><input type=text name=text size=60><!--
--><input type=submit value="Share">
</form>
</div>
{% endif %}
{% endif %}
<ul class=messages>
{% for message in messages %}
<li><img src="{{ message.email|gravatar(size=48) }}"><p>
<strong><a href="{{ url_for('user_timeline', username=message.username)
}}">{{ message.username }}</a></strong>
{{ message.text }}
<small>&mdash; {{ message.pub_date|datetimeformat }}</small>
{% else %}
<li><em>There are no messages so far.</em>
{% endfor %}
</ul>
{% endblock %}

356
flask.py

@ -0,0 +1,356 @@
# -*- 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.
"""
import os
import sys
import pkg_resources
from threading import local
from jinja2 import Environment, PackageLoader
from werkzeug import Request, Response, LocalStack, LocalProxy
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, InternalServerError
from werkzeug.contrib.securecookie import SecureCookie
# try to import the json helpers
try:
from simplejson import loads as load_json, dumps as dump_json
except ImportError:
try:
from json import loads as load_json, dumps as dump_json
except ImportError:
pass
# utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface.
from werkzeug import abort, redirect, secure_filename, cached_property, \
html, import_string, generate_password_hash, check_password_hash
from jinja2 import Markup, escape
class FlaskRequest(Request):
"""The request object used by default in flask. Remembers the
matched endpoint and view arguments.
"""
def __init__(self, environ):
Request.__init__(self, environ)
self.endpoint = None
self.view_args = None
class FlaskResponse(Response):
"""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.
"""
default_mimetype = 'text/html'
class _RequestGlobals(object):
pass
class _RequestContext(object):
"""The request context contains all request relevant information. It is
created at the beginning of the request and pushed to the
`_request_ctx_stack` and removed at the end of it. It will create the
URL adapter and request object for the WSGI environment provided.
"""
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
self.flashes = None
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 jsonified(**values):
"""Returns a json response"""
return current_app.response_class(dump_json(values),
mimetype='application/json')
def flash(message):
"""Flashes a message to the next request. In order to remove the
flashed message from the session and to display it to the user,
the template has to call :func:`get_flashed_messages`.
"""
session['_flashes'] = (session.get('_flashes', [])) + [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 render_template(template_name, **context):
"""Renders a template from the template folder with the given
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.
"""
return current_app.jinja_env.from_string(source).render(context)
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
and optionally a configuration. When it's created it sets up the
template engine and provides ways to register view functions.
"""
#: the class that is used for request objects
request_class = FlaskRequest
#: the class that is used for response objects
response_class = FlaskResponse
#: 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 = dict(
autoescape=True,
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
)
def __init__(self, package_name):
self.debug = False
self.package_name = package_name
self.view_functions = {}
self.error_handlers = {}
self.request_init_funcs = []
self.request_shutdown_funcs = []
self.url_map = Map()
if self.static_path is not None:
self.url_map.add(Rule(self.static_path + '/<filename>',
build_only=True, endpoint='static'))
self.jinja_env = Environment(loader=self.create_jinja_loader(),
**self.jinja_options)
self.jinja_env.globals.update(
url_for=url_for,
request=request,
session=session,
g=g,
get_flashed_messages=get_flashed_messages
)
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.
"""
return PackageLoader(self.package_name)
def run(self, host='localhost', port=5000, **options):
"""Runs the application on a local development server"""
from werkzeug import run_simple
if 'debug' in options:
self.debug = options.pop('debug')
if self.static_path is not None:
options['static_files'] = {
self.static_path: (self.package_name, 'static')
}
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options)
@cached_property
def test(self):
"""A test client for this application"""
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"""
return pkg_resources.resource_stream(self.package_name, resource)
def open_session(self, request):
"""Creates or opens a new session. Default implementation requires
that `securecookie.secret_key` is set.
"""
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."""
if session is not None:
session.save_cookie(response, self.session_cookie_name)
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'
"""
def decorator(f):
if 'endpoint' not in options:
options['endpoint'] = f.__name__
self.url_map.add(Rule(rule, **options))
self.view_functions[options['endpoint']] = f
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
"""
def decorator(f):
self.error_handlers[code] = f
return f
return decorator
def request_init(self, f):
"""Registers a function to run before each request."""
self.request_init_funcs.append(f)
return f
def request_shutdown(self, f):
"""Register a function to be run after each request."""
self.request_shutdown_funcs.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`.
"""
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 :func:`request_init` 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.request_init_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.
"""
session = _request_ctx_stack.top.session
if session is not None:
self.save_session(session, response)
for handler in self.request_shutdown_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:
app.wsgi_app = MyMiddleware(app.wsgi_app)
"""
_request_ctx_stack.push(_RequestContext(self, environ))
try:
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)
finally:
_request_ctx_stack.pop()
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)
Loading…
Cancel
Save