[tipfy] push by rodrigo.moraes - Added registry dict to Request. And named this 1.0b1. on 2011-04-03 14:25 GMT

0 views
Skip to first unread message

ti...@googlecode.com

unread,
Apr 3, 2011, 10:25:53 AM4/3/11
to tipfy-...@googlegroups.com
Revision: 6a9c0be616ea
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Sun Apr 3 07:25:05 2011
Log: Added registry dict to Request. And named this 1.0b1.
http://code.google.com/p/tipfy/source/detail?r=6a9c0be616ea

Modified:
/CHANGES.txt
/tipfy/app.py

=======================================
--- /CHANGES.txt Thu Nov 25 12:59:56 2010
+++ /CHANGES.txt Sun Apr 3 07:25:05 2011
@@ -1,4 +1,4 @@
-Version 1.0 - Not yet released
+Version 1.0b1 - April 03, 2011
==============================
*This change log is a work in progress*

@@ -30,7 +30,7 @@
- APPENGINE: True is the app is running on App Engine, False otherwise.
- APPLICATION_ID: the application ID as defined in *app.yaml*.
- CURRENT_VERSION_ID: the deployed version ID. Always '1' when using
- the dev server.
+ the dev server.

- NEW: auth, debugger, i18n and sessions are now part of tipfy core. They
are
all optional, lazily loaded, default and overridable implementations.
@@ -41,9 +41,9 @@
- NEW: Auth is now part of tipfy core. The auth store is configured in
tipfy
module and uses an App Engine store by default::

- config['tipfy'] = {
- 'auth_store_class': 'tipfy.appengine.auth.AuthStore',
- }
+ config['tipfy'] = {
+ 'auth_store_class': 'tipfy.appengine.auth.AuthStore',
+ }

Then it is available through lazy loading in the RequestHandler:

@@ -51,13 +51,13 @@

Example::

- class MyHandler(RequestHandler):
- def get(self, **kwargs):
- # Get the current user, if any.
- current_user = self.auth.user
-
- # Generate a login URL.
- login_url = self.auth.login_url()
+ class MyHandler(RequestHandler):
+ def get(self, **kwargs):
+ # Get the current user, if any.
+ current_user = self.auth.user
+
+ # Generate a login URL.
+ login_url = self.auth.login_url()

- NEW: AppEngineMixedAuthStore, which allows login using App Engine Auth
across subdomains. It mixes App Engine auth with own sessions to achieve
@@ -73,9 +73,9 @@
- NEW: i18n is now part of tipfy core. The i18n store is configured in
tipfy module and uses the shipped store by default::

- config['tipfy'] = {
- 'i18n_store_class': 'tipfy.i18n.I18nStore',
- }
+ config['tipfy'] = {
+ 'i18n_store_class': 'tipfy.i18n.I18nStore',
+ }

Then it is available through lazy loading in the RequestHandler:

@@ -83,10 +83,10 @@

Example::

- class MyHandler(RequestHandler):
- def get(self, **kwargs):
- # Get a translated string.
- message = self.i18n.gettext('Translate me')
+ class MyHandler(RequestHandler):
+ def get(self, **kwargs):
+ # Get a translated string.
+ message = self.i18n.gettext('Translate me')

Global i18n functions are still available in tipfy.i18n.

@@ -96,34 +96,34 @@
- NEW: Sessions are now part of tipfy core. The auth store is configured in
tipfy module and uses the shipped store by default::

- config['tipfy'] = {
- 'session_store_class': 'tipfy.sessions.SessionStore',
- }
+ config['tipfy'] = {
+ 'session_store_class': 'tipfy.sessions.SessionStore',
+ }

Then it is available through lazy loading in the RequestHandler:

- RequestHandler.session provides access to the session using the
configured
- backend and session key.
+ backend and session key.
- RequestHandler.session_store provides session related utilities such as
- getting sessions from different backends and setting/deleting cookies.
+ getting sessions from different backends and setting/deleting cookies.

Example::

- class MyHandler(RequestHandler):
- middleware = [SessionMiddleware()]
-
- def get(self, **kwargs):
- # Set a value in the current session.
- self.session['foo'] = 'bar'
-
- # Get a list of flash messages.
- flashes = self.session.get_flashes()
-
- # Add a flash message.
- self.session.flash('Hello, world!')
-
- # Get a session from a different backend.
- session = self.session_store.get_session(backend='memcache')
+ class MyHandler(RequestHandler):
+ middleware = [SessionMiddleware()]
+
+ def get(self, **kwargs):
+ # Set a value in the current session.
+ self.session['foo'] = 'bar'
+
+ # Get a list of flash messages.
+ flashes = self.session.get_flashes()
+
+ # Add a flash message.
+ self.session.flash('Hello, world!')
+
+ # Get a session from a different backend.
+ session = self.session_store.get_session(backend='memcache')

- NEW: secure cookie has a faster and easier to use implementation. See
tipfy.sessions.SecureCookieStore.
@@ -138,9 +138,9 @@
template engine. The debugger is enabled by default when in development
mode,
and can be disabled with a configuration:

- config['tipfy'] = {
- 'enable_debugger': False,
- }
+ config['tipfy'] = {
+ 'enable_debugger': False,
+ }

- IMPROVED: the debugger now works even if the libraries are imported using
zipimport.
@@ -152,17 +152,17 @@
method is defined using a 'Handler:method' notation in the Rule
definition. Example:

- # handlers.py
-
- class MyHandler(RequestHandler):
- def my_method(self, **kwargs):
- return Response('I am not coming from a get() method!')
-
- # main.py
-
- app = Tipfy(rules=[
- Rule('/', name='home', handler='handlers.MyHandler:my_method')
- ])
+ # handlers.py
+
+ class MyHandler(RequestHandler):
+ def my_method(self, **kwargs):
+ return Response('I am not coming from a get() method!')
+
+ # main.py
+
+ app = Tipfy(rules=[
+ Rule('/', name='home', handler='handlers.MyHandler:my_method')
+ ])

This opens a whole new set of possibilities for routing and handler
definitions, as you can match the HTTP method scheme and custom method
@@ -170,43 +170,43 @@
a single class can handle listing, viewing, inserting, editting and
deleting::

- rules = [
- HandlerPrefix('handlers.ImageHandler', [
- # List images is handled by ImageHandler.index()
- Rule('/images/', name='images/index', handler=':index'),
- # Adding an image is handled by ImageHandler.new()
- Rule('/images/new', name='images/edit', handler=':new'),
- # Displaying an image is handled by ImageHandler.get()
- Rule('/image/<key>', name='images/view', handler=''),
- # Editting an image is handled by ImageHandler.edit()
- Rule('/images/<key>/edit', name='images/edit',
handler=':edit'),
- # Deleting an image is handled by ImageHandler.delete()
- Rule('/images/<key>/delete', name='images/delete',
handler=':delete'),
- ],
- ]
+ rules = [
+ HandlerPrefix('handlers.ImageHandler', [
+ # List images is handled by ImageHandler.index()
+ Rule('/images/', name='images/index', handler=':index'),
+ # Adding an image is handled by ImageHandler.new()
+ Rule('/images/new', name='images/edit', handler=':new'),
+ # Displaying an image is handled by ImageHandler.get()
+ Rule('/image/<key>', name='images/view', handler=''),
+ # Editting an image is handled by ImageHandler.edit()
+ Rule('/images/<key>/edit', name='images/edit', handler=':edit'),
+ # Deleting an image is handled by ImageHandler.delete()
+ Rule('/images/<key>/delete', name='images/delete', handler=':delete'),
+ ],
+ ]

- NEW: added a router object that centralizes URL matching, dispatching and
building URLs. It makes it more flexible to implement alternative routing
schemes. Some implications:

- Moved methods from Tipfy that are now part of the router functionality:
- get_url_map(), add_url_rule(), match_url(), pre_dispatch(), dispatch()
and
- post_dispatch(). If you extended them in some way, you must now extend
- the router.
+ get_url_map(), add_url_rule(), match_url(), pre_dispatch(), dispatch() and
+ post_dispatch(). If you extended them in some way, you must now extend
+ the router.
- URL building is centralized in the router, so url_for() methods
- (from RequestHandler or Tipfy) call router.build().
+ (from RequestHandler or Tipfy) call router.build().

- NEW: added a new rule converter, RegexConverter, which can be usefull
specially to match subdomains excluding some default ones (e.g., 'www').
For example, here we define a rule for the root URL of all non-www
subdomains, and a different one for www::

- rules = [
- Rule('/', endpoint='index', handler='IndexHandler'),
- Subdomain(r'<regex("(?!www\\b)\\w+"):namespace>', [
- Rule('/', endpoint='index', handler='SubdomainIndexHandler'),
- ]),
- ]
+ rules = [
+ Rule('/', endpoint='index', handler='IndexHandler'),
+ Subdomain(r'<regex("(?!www\\b)\\w+"):namespace>', [
+ Rule('/', endpoint='index', handler='SubdomainIndexHandler'),
+ ]),
+ ]

In this example, the request method is ignored since the Rule defines
which
handler method will handle all requests.
@@ -216,88 +216,88 @@

- **_full**: If True, builds an absolute URL.
- **_method**: Uses a rule defined to handle specific request
- methods, if any are defined.
+ methods, if any are defined.
- **_scheme**: URL scheme, e.g., `http` or `https`. If defined,
- an absolute URL is always returned.
+ an absolute URL is always returned.
- **_netloc**: Network location, e.g., `www.google.com`. If
- defined, an absolute URL is always returned.
+ defined, an absolute URL is always returned.
- **_anchor**: If set, appends an anchor to generated URL.

- IMPROVED: Rule now accepts 'name' as keyword argument. It is an alias to
'endpoint'. This is just for semantic correctness in tipfy context and
'endpoint' will still work::

- Rule('/', name='home', handler='handlers.HomeHandler')
- # is the same as...
- Rule('/', endpoint='home', handler='handlers.HomeHandler')
+ Rule('/', name='home', handler='handlers.HomeHandler')
+ # is the same as...
+ Rule('/', endpoint='home', handler='handlers.HomeHandler')

- CHANGED: URL rules are not automatically loaded from urls.py. Instead
tipfy
is instantiated passing the URL rules, like webapp. Later other rules can
be added to the router. For example:

- app = Tipfy(rules=[
- Rule('/', name='home', handler='handlers.HomeHandler'),
- Rule('/about', name='about', handler='handlers.AboutHandler'),
- ])
-
- # Add an extra rule.
- app.router.add(Rule('/contact', name='contact',
handler='handlers.ContactHandler'))
-
- # Or add a list of rules.
- app.router.add([
- Rule('/products', name='products',
handler='handlers.ProductsHandler'),
- Rule('/history', name='history',
handler='handlers.HistorytHandler'),
- ])
+ app = Tipfy(rules=[
+ Rule('/', name='home', handler='handlers.HomeHandler'),
+ Rule('/about', name='about', handler='handlers.AboutHandler'),
+ ])
+
+ # Add an extra rule.
+ app.router.add(Rule('/contact', name='contact',
handler='handlers.ContactHandler'))
+
+ # Or add a list of rules.
+ app.router.add([
+ Rule('/products', name='products', handler='handlers.ProductsHandler'),
+ Rule('/history', name='history', handler='handlers.HistorytHandler'),
+ ])

The previous behavior using urls.py is easy to reproduce. For example,
given a urls.py like the following:

- from tipfy import Rule
-
- def get_rules(app):
- rules = [
- Rule('/', name='home', handler='handlers.HomeHandler'),
- Rule('/about', name='about',
handler='handlers.AboutHandler'),
- Rule('/contact', name='contact',
handler='handlers.ContactHandler'),
- ]
-
- return rules
+ from tipfy import Rule
+
+ def get_rules(app):
+ rules = [
+ Rule('/', name='home', handler='handlers.HomeHandler'),
+ Rule('/about', name='about', handler='handlers.AboutHandler'),
+ Rule('/contact', name='contact', handler='handlers.ContactHandler'),
+ ]
+
+ return rules

You can still import and use it in main.py:

- from urls import get_rules
-
- app = Tipfy()
-
- # Add a list of rules.
- app.router.add(get_rules(app))
+ from urls import get_rules
+
+ app = Tipfy()
+
+ # Add a list of rules.
+ app.router.add(get_rules(app))

Or you can simply have a list of rules defined in urls.py, outside of a
function, and import it:

- from urls import rules
-
- app = Tipfy(rules=rules)
+ from urls import rules
+
+ app = Tipfy(rules=rules)

- IMPROVED: redirect() (or self.redirect() in handlers) now accepts
relative
URLs. If a relative URL is passed, it'll be joined to the current request
URL. This is mostly useful to redirect to the result of url_for() without
needing to build a full URL::

- # current URL is http://localhost/foo/bar
-
- # redirects to http://localhost/baz
- return redirect('/baz')
+ # current URL is http://localhost/foo/bar
+
+ # redirects to http://localhost/baz
+ return redirect('/baz')

...but it also allows curious results for relative paths:

- # current URL is http://localhost/foo/bar
-
- # redirects to http://localhost/foo/baz
- return redirect('./baz')
-
- # redirects to http://localhost/baz
- return redirect('../baz')
+ # current URL is http://localhost/foo/bar
+
+ # redirects to http://localhost/foo/baz
+ return redirect('./baz')
+
+ # redirects to http://localhost/baz
+ return redirect('../baz')


Config
@@ -306,15 +306,15 @@
auto-loading configuration values when needed and honoring required
configs.
For example, we always used this::

- secret_key = self.app.get_config('tipfy.sessions', 'secret_key')
+ secret_key = self.app.get_config('tipfy.sessions', 'secret_key')

Now it is also possible to use direct access and dict methods::

- secret_key = self.app.config['tipfy.sessions']['secret_key']
- # or...
- secret_key = self.app.config['tipfy.sessions'].get('secret_key')
- # or...
- secret_key = self.app.config.get('tipfy.sessions').get('secret_key')
+ secret_key = self.app.config['tipfy.sessions']['secret_key']
+ # or...
+ secret_key = self.app.config['tipfy.sessions'].get('secret_key')
+ # or...
+ secret_key = self.app.config.get('tipfy.sessions').get('secret_key')

The previous get_config() method works as always.

@@ -327,32 +327,32 @@
build a response. HTTPExceptions are also converted to a proper response
when returned. Example:

- class MyHandler(RequestHandler):
- def get(self, **kwargs):
- # Returns a simple string.
- return 'Hello, World'
-
- # Returns a tuple of values to create a response.
- return 'Not Found', 404
-
- # Return an HTTPException which will be converted to a
response.
- return MethodNotAllowed()
+ class MyHandler(RequestHandler):
+ def get(self, **kwargs):
+ # Returns a simple string.
+ return 'Hello, World'
+
+ # Returns a tuple of values to create a response.
+ return 'Not Found', 404
+
+ # Return an HTTPException which will be converted to a response.
+ return MethodNotAllowed()

- NEW: added Tipfy.get_test_handler(). This provides a context to test a
handler, matching it from a custom request if needed. This is intended
to be
used with a `with` statement::

- from __future__ import with_statement
-
- from tipfy import Tipfy, Rule
-
- app = Tipfy(rules=[
- Rule('/about', name='home', handler='handlers.AboutHandler'),
- ])
-
- with app.get_test_handler('/about') as handler:
- self.assertEqual(handler.url_for('/', _full=True),
- 'http://localhost/about')
+ from __future__ import with_statement
+
+ from tipfy import Tipfy, Rule
+
+ app = Tipfy(rules=[
+ Rule('/about', name='home', handler='handlers.AboutHandler'),
+ ])
+
+ with app.get_test_handler('/about') as handler:
+ self.assertEqual(handler.url_for('/', _full=True),
+ 'http://localhost/about')

- NEW: app has now a debug attribute, set when it is initialized with
debug=True.
@@ -419,19 +419,19 @@
- Added HandlerPrefix, a rule factory to prefix handler definition in
rules.
For example, take these rules::

- rules = [
- HandlerPrefix('my_app.handlers.', [
- Rule('/', endpoint='index', handler='IndexHandler'),
- Rule('/entry/<entry_slug>', endpoint='show',
handler='ShowHandler'),
- ]),
- ]
+ rules = [
+ HandlerPrefix('my_app.handlers.', [
+ Rule('/', endpoint='index', handler='IndexHandler'),
+ Rule('/entry/<entry_slug>', endpoint='show', handler='ShowHandler'),
+ ]),
+ ]

These are the same as::

- rules = [
- Rule('/', endpoint='index',
handler='my_app.handlers.IndexHandler'),
- Rule('/entry/<entry_slug>', endpoint='show',
handler='my_app.handlers.ShowHandler'),
- ]
+ rules = [
+ Rule('/', endpoint='index', handler='my_app.handlers.IndexHandler'),
+ Rule('/entry/<entry_slug>', endpoint='show',
handler='my_app.handlers.ShowHandler'),
+ ]

- If the requested method is not in ALLOWED_METHODS, a NotImplemented
exception
("501 Not Implemented") is raised, instead of MethodNotAllowed ("405
Method
@@ -458,56 +458,56 @@
defined in a single file mapped in app.yaml, without the need for a
separate
urls.py::

- import sys
- sys.path.insert(0, 'distlib')
-
- from tipfy import RequestHandler, Rule, make_wsgi_app, run_wsgi_app
-
- class HelloWorldHandler(RequestHandler):
- def get(self, **kwargs):
- return 'Hello, World!'
-
- class ByeWorldHandler(RequestHandler):
- def get(self, **kwargs):
- return 'Bye, World!'
-
- app = make_wsgi_app(rules=[
- Rule('/', endpoint='hello', handler=HelloWorldHandler),
- Rule('/bye', endpoint='bye', handler=ByeWorldHandler),
- ])
-
- def main():
- run_wsgi_app(app)
-
- if __name__ == '__main__':
- main()
+ import sys
+ sys.path.insert(0, 'distlib')
+
+ from tipfy import RequestHandler, Rule, make_wsgi_app, run_wsgi_app
+
+ class HelloWorldHandler(RequestHandler):
+ def get(self, **kwargs):
+ return 'Hello, World!'
+
+ class ByeWorldHandler(RequestHandler):
+ def get(self, **kwargs):
+ return 'Bye, World!'
+
+ app = make_wsgi_app(rules=[
+ Rule('/', endpoint='hello', handler=HelloWorldHandler),
+ Rule('/bye', endpoint='bye', handler=ByeWorldHandler),
+ ])
+
+ def main():
+ run_wsgi_app(app)
+
+ if __name__ == '__main__':
+ main()

This also helps to test handlers defined in a test file::

- import unittest
-
- from tipfy import RequestHandler, Rule, Tipfy, redirect
-
- class TestHandler(unittest.TestCase):
- def test_redirect(self):
- class Handler1(RequestHandler):
- def get(self, **kwargs):
- return redirect_to('test2')
-
- class Handler2(RequestHandler):
- def get(self, **kwargs):
- return 'Hello, World!'
-
- rules = [
- Rule('/1', endpoint='test1', handler=Handler1),
- Rule('/2', endpoint='test2', handler=Handler2),
- ]
-
- app = Tipfy(rules=rules)
- client = app.get_test_client()
- response = client.get('/1', follow_redirects=True)
-
- assert response.data == 'Hello, World!'
+ import unittest
+
+ from tipfy import RequestHandler, Rule, Tipfy, redirect
+
+ class TestHandler(unittest.TestCase):
+ def test_redirect(self):
+ class Handler1(RequestHandler):
+ def get(self, **kwargs):
+ return redirect_to('test2')
+
+ class Handler2(RequestHandler):
+ def get(self, **kwargs):
+ return 'Hello, World!'
+
+ rules = [
+ Rule('/1', endpoint='test1', handler=Handler1),
+ Rule('/2', endpoint='test2', handler=Handler2),
+ ]
+
+ app = Tipfy(rules=rules)
+ client = app.get_test_client()
+ response = client.get('/1', follow_redirects=True)
+
+ assert response.data == 'Hello, World!'

- Added get_url_map() and add_url_rule() methods to the WSGI app.

@@ -515,11 +515,11 @@
object if needed. So a simple string can also be returned, among other
options::

- from tipfy import RequestHandler, Response
-
- class HelloWorldHandler(RequestHandler):
- def get(self):
- return 'Hello, World!'
+ from tipfy import RequestHandler, Response
+
+ class HelloWorldHandler(RequestHandler):
+ def get(self):
+ return 'Hello, World!'


Backwards compatibility warnings
@@ -532,7 +532,7 @@
* request, local.request: Tipfy.request, a class attribute in the WSGI
app.
* app, local.app: Tipfy.app, a class attribute in the WSGI app.
* local: request.context and request.registry, dictionaries set for a
- request.
+ request.

- REMOVED: old aliases: WSGIApplication (Tipfy) and REQUIRED_CONFIG
(REQUIRED_VALUE)
@@ -580,16 +580,16 @@
my_middleware = MyMiddleware(foo='bar')

class MyRequestHandler(RequestHandler):
- middleware = [my_middleware]
-
- # ...
+ middleware = [my_middleware]
+
+ # ...

my_middleware2 = MyMiddleware(foo='zing')

class MyRequestHandler2(RequestHandler):
- middleware = [my_middleware2]
-
- # ...
+ middleware = [my_middleware2]
+
+ # ...

- Fixed using tipfy with zipimport. For whatever reason py_zipimport fails
to
load werkzeug modules if werkzeug package is not imported first. This is
@@ -609,11 +609,11 @@

- REMOVED 'pre_run_app' hook. Use post_make_app instead:

- def post_make_app(self, app):
- # Wrap the callable, so we keep a reference to the app...
- app.wsgi_app = my_middleware(app.wsgi_app)
- # ...and return the original app.
- return app
+ def post_make_app(self, app):
+ # Wrap the callable, so we keep a reference to the app...
+ app.wsgi_app = my_middleware(app.wsgi_app)
+ # ...and return the original app.
+ return app

- IMPROVED tipfy.ext.appstatts and tipfy.ext.debugger: middleware is set on
'post_make_app'.
@@ -629,10 +629,10 @@
WSGIApplication and Request in the handler. If you implemented
__init__() on
RequestHandler, please update it to accept these arguments:

- class MyRequestHandler(RequestHandler):
- def __init__(self, app, request):
- super(MyRequestHandler, self).__init__(app, request)
- ...
+ class MyRequestHandler(RequestHandler):
+ def __init__(self, app, request):
+ super(MyRequestHandler, self).__init__(app, request)
+ ...

We now have access to the URL rule and rule arguments that matched on
__init__(), and can read app configuration without relying on local.
@@ -660,24 +660,24 @@
So an application using Tipfy outside of App Engine needs to setup a
proxy on
these attributes pointing to a thread local. This monkeypatch covers it
all:

- from werkzeug import Local
-
- local = Local()
-
- def set_wsgi_app(self):
- local.app = self
-
- def set_request(self, request):
- local.request = request
-
- def cleanup(self):
- local.__release_local__()
-
- Tipfy.set_wsgi_app = set_wsgi_app
- Tipfy.set_request = set_request
- Tipfy.cleanup = cleanup
- Tipfy.app = local('app')
- Tipfy.request = local('request')
+ from werkzeug import Local
+
+ local = Local()
+
+ def set_wsgi_app(self):
+ local.app = self
+
+ def set_request(self, request):
+ local.request = request
+
+ def cleanup(self):
+ local.__release_local__()
+
+ Tipfy.set_wsgi_app = set_wsgi_app
+ Tipfy.set_request = set_request
+ Tipfy.cleanup = cleanup
+ Tipfy.app = local('app')
+ Tipfy.request = local('request')

This makes Tipfy.app and Tipfy.request thread-safe outside of App Engine.

@@ -688,8 +688,8 @@
append an anchor to the generated URL. Also keyword arguments in these
two
functions are now prefixed with a '_':

- url_for(endpoint, _full=False, _method=None, _anchor=None, **kwargs)
- redirect_to(endpoint, _method=None, _anchor=None, _code=302, **kwargs)
+ url_for(endpoint, _full=False, _method=None, _anchor=None, **kwargs)
+ redirect_to(endpoint, _method=None, _anchor=None, _code=302, **kwargs)

This is just a sanity measure to avoid collisions with variables set in
rules. The old, non-prefixed keywords are still accepted, but they should
@@ -727,22 +727,22 @@

- ADDED some ideas from Flask:
- Local variables are not cleaned when in development and an exception is
- raised. This allows the debugger to still access request and other
- variables from local in the interactive shell.
+ raised. This allows the debugger to still access request and other
+ variables from local in the interactive shell.
- Added a wsgi_app() function to WSGIApplication, wrapping __call__().
- Added a make_response() method to WSGIApplication, so that handlers
- can return not only a Response object.
+ can return not only a Response object.
- Refactoring: split pre_dispatch/dispatch/post_dispatch into separate
- functions. Makes it easier to override these methods in a middleware.
+ functions. Makes it easier to override these methods in a middleware.
- Added function get_test_client() to return a werkzeug.Client for the
wsgi
- app.
+ app.
- Added function ext.jinja2.get_template_attribute().
- ADDED: a Response object that extends werkzeug's Response, with default
- mimetype set to 'text/html'.
+ mimetype set to 'text/html'.

- REMOVED:
- tipfy.response and 'extensions' configuration key. They were deprecated
- since 0.5 and not useful at all since then.
+ since 0.5 and not useful at all since then.
- tipfy.local_manager. No longer needed since latest Werkzeug.


=======================================
--- /tipfy/app.py Sun Apr 3 07:14:04 2011
+++ /tipfy/app.py Sun Apr 3 07:25:05 2011
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
"""
- tipfy.app
- ~~~~~~~~~
-
- WSGI Application and RequestHandler.
-
- :copyright: 2011 by tipfy.org.
- :license: BSD, see LICENSE.txt for more details.
+ tipfy.app
+ ~~~~~~~~~
+
+ WSGI Application and RequestHandler.
+
+ :copyright: 2011 by tipfy.org.
+ :license: BSD, see LICENSE.txt for more details.
"""
from __future__ import with_statement

@@ -34,463 +34,469 @@

#: TODO: remove from here.
from tipfy.appengine import (APPENGINE, APPLICATION_ID, CURRENT_VERSION_ID,
- DEV_APPSERVER)
+ DEV_APPSERVER)


class Request(werkzeug.wrappers.Request):
- """Provides all environment variables for the current request: GET,
POST,
- FILES, cookies and headers.
- """
- #: The WSGI app.
- app = None
- #: Exception caught by the WSGI app during dispatch().
- exception = None
- #: URL adapter.
- rule_adapter = None
- #: Matched :class:`tipfy.routing.Rule`.
- rule = None
- #: Keyword arguments from the matched rule.
- rule_args = None
-
- @werkzeug.utils.cached_property
- def auth(self):
- """The auth store which provides access to the authenticated user
and
- auth related functions.
-
- :returns:
- An auth store instance.
- """
- return self.app.auth_store_class(self)
-
- @werkzeug.utils.cached_property
- def json(self):
- """If the mimetype is `application/json` this will contain the
- parsed JSON data.
-
- This function is borrowed from `Flask`_.
-
- :returns:
- The decoded JSON request data.
- """
- if self.mimetype == 'application/json':
- from tipfy.json import json_decode
- return json_decode(self.data)
-
- @werkzeug.utils.cached_property
- def i18n(self):
- """The internationalization store which provides access to several
- translation and localization utilities.
-
- :returns:
- An i18n store instance.
- """
- return self.app.i18n_store_class(self)
-
- @werkzeug.utils.cached_property
- def session(self):
- """A session dictionary using the default session configuration.
-
- :returns:
- A dictionary-like object with the current session data.
- """
- return self.session_store.get_session()
-
- @werkzeug.utils.cached_property
- def session_store(self):
- """The session store, responsible for managing sessions and
flashes.
-
- :returns:
- A session store instance.
- """
- return self.app.session_store_class(self)
-
- def _get_rule_adapter(self):
- from warnings import warn
- warn(DeprecationWarning("Request.url_adapter: this attribute "
- "is deprecated. Use Request.rule_adapter instead."))
- return self.rule_adapter
-
- def _set_rule_adapter(self, adapter):
- from warnings import warn
- warn(DeprecationWarning("Request.url_adapter: this attribute "
- "is deprecated. Use Request.rule_adapter instead."))
- self.rule_adapter = adapter
-
- # Old name
- url_adapter = property(_get_rule_adapter, _set_rule_adapter)
+ """Provides all environment variables for the current request: GET, POST,
+ FILES, cookies and headers.
+ """
+ #: The WSGI app.
+ app = None
+ #: Exception caught by the WSGI app during dispatch().
+ exception = None
+ #: URL adapter.
+ rule_adapter = None
+ #: Matched :class:`tipfy.routing.Rule`.
+ rule = None
+ #: Keyword arguments from the matched rule.
+ rule_args = None
+ #: A dictionary for request variables.
+ registry = None
+
+ def __init__(self, *args, **kwargs):
+ super(Request, self).__init__(*args, **kwargs)
+ self.registry = {}
+
+ @werkzeug.utils.cached_property
+ def auth(self):
+ """The auth store which provides access to the authenticated user and
+ auth related functions.
+
+ :returns:
+ An auth store instance.
+ """
+ return self.app.auth_store_class(self)
+
+ @werkzeug.utils.cached_property
+ def json(self):
+ """If the mimetype is `application/json` this will contain the
+ parsed JSON data.
+
+ This function is borrowed from `Flask`_.
+
+ :returns:
+ The decoded JSON request data.
+ """
+ if self.mimetype == 'application/json':
+ from tipfy.json import json_decode
+ return json_decode(self.data)
+
+ @werkzeug.utils.cached_property
+ def i18n(self):
+ """The internationalization store which provides access to several
+ translation and localization utilities.
+
+ :returns:
+ An i18n store instance.
+ """
+ return self.app.i18n_store_class(self)
+
+ @werkzeug.utils.cached_property
+ def session(self):
+ """A session dictionary using the default session configuration.
+
+ :returns:
+ A dictionary-like object with the current session data.
+ """
+ return self.session_store.get_session()
+
+ @werkzeug.utils.cached_property
+ def session_store(self):
+ """The session store, responsible for managing sessions and flashes.
+
+ :returns:
+ A session store instance.
+ """
+ return self.app.session_store_class(self)
+
+ def _get_rule_adapter(self):
+ from warnings import warn
+ warn(DeprecationWarning("Request.url_adapter: this attribute "
+ "is deprecated. Use Request.rule_adapter instead."))
+ return self.rule_adapter
+
+ def _set_rule_adapter(self, adapter):
+ from warnings import warn
+ warn(DeprecationWarning("Request.url_adapter: this attribute "
+ "is deprecated. Use Request.rule_adapter instead."))
+ self.rule_adapter = adapter
+
+ # Old name
+ url_adapter = property(_get_rule_adapter, _set_rule_adapter)


class Response(werkzeug.wrappers.Response):
- """A response object with default mimetype set to ``text/html``."""
- default_mimetype = 'text/html'
+ """A response object with default mimetype set to ``text/html``."""
+ default_mimetype = 'text/html'


class RequestContext(object):
- """Sets and releases the context locals used during a request.
-
- User meth:`App.get_test_context` to build a `RequestContext` for
- testing purposes.
- """
- def __init__(self, app, environ):
- """Initializes the request context.
-
- :param app:
- An :class:`App` instance.
- :param environ:
- A WSGI environment.
- """
- self.app = app
- self.environ = environ
-
- def __enter__(self):
- """Enters the request context.
-
- :returns:
- A :class:`Request` instance.
- """
- local.request = request = self.app.request_class(self.environ)
- local.app = request.app = self.app
- return request
-
- def __exit__(self, exc_type, exc_value, traceback):
- """Exits the request context.
-
- This will release the context locals except if an exception is
caught
- in debug mode. In this case the locals are kept to be inspected.
- """
- if exc_type is None or not self.app.debug:
- local.__release_local__()
+ """Sets and releases the context locals used during a request.
+
+ User meth:`App.get_test_context` to build a `RequestContext` for
+ testing purposes.
+ """
+ def __init__(self, app, environ):
+ """Initializes the request context.
+
+ :param app:
+ An :class:`App` instance.
+ :param environ:
+ A WSGI environment.
+ """
+ self.app = app
+ self.environ = environ
+
+ def __enter__(self):
+ """Enters the request context.
+
+ :returns:
+ A :class:`Request` instance.
+ """
+ local.request = request = self.app.request_class(self.environ)
+ local.app = request.app = self.app
+ return request
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ """Exits the request context.
+
+ This will release the context locals except if an exception is caught
+ in debug mode. In this case the locals are kept to be inspected.
+ """
+ if exc_type is None or not self.app.debug:
+ local.__release_local__()


class App(object):
- """The WSGI application."""
- # Allowed request methods.
- allowed_methods =
frozenset(['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST',
- 'PUT', 'TRACE'])
- #: Default class for requests.
- request_class = Request
- #: Default class for responses.
- response_class = Response
- #: Default class for the configuration object.
- config_class = Config
- #: Default class for the configuration object.
- router_class = Router
- #: Context class used when a request comes in.
- request_context_class = RequestContext
-
- def __init__(self, rules=None, config=None, debug=False):
- """Initializes the application.
-
- :param rules:
- URL rules definitions for the application.
- :param config:
- Dictionary with configuration for the application modules.
- :param debug:
- True if this is debug mode, False otherwise.
- """
- local.current_app = self
- self.debug = debug
- self.registry = {}
- self.error_handlers = {}
- self.config = self.config_class(config, {'tipfy': default_config})
- self.router = self.router_class(self, rules)
-
- if debug:
- logging.getLogger().setLevel(logging.DEBUG)
-
- def __call__(self, environ, start_response):
- """Called when a request comes in."""
- if self.debug and self.config['tipfy']['enable_debugger']:
- return self._debugged_wsgi_app(environ, start_response)
-
- return self.dispatch(environ, start_response)
-
- def dispatch(self, environ, start_response):
- """This is the actual WSGI application. This is not implemented in
- :meth:`__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.dispatch = MyMiddleware(app.dispatch)
-
- Then you still have the original application object around and
- can continue to call methods on it.
-
- This idea comes from `Flask`_.
-
- :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_class(self, environ) as request:
- try:
- if request.method not in self.allowed_methods:
- abort(501)
-
- rv = self.router.dispatch(request)
- response = self.make_response(request, rv)
- except Exception, e:
- try:
- rv = self.handle_exception(request, e)
- response = self.make_response(request, rv)
- except HTTPException, e:
- response = self.make_response(request, e)
- except Exception, e:
- if self.debug:
- raise
-
- logging.exception(e)
- rv = werkzeug.exceptions.InternalServerError()
- response = self.make_response(request, rv)
-
- return response(environ, start_response)
-
- def handle_exception(self, request, exception):
- """Handles an exception. To set app-wide error handlers, define
them
- using the corresponent HTTP status code in the ``error_handlers``
- dictionary of :class:`App`. For example, to set a custom
- `Not Found` page::
-
- class Handle404(RequestHandler):
- def __call__(self):
- logging.exception(self.request.exception)
- return Response('Oops! I could swear this page was
here!',
- status=404)
-
- app = App([
- Rule('/', handler=MyHandler, name='home'),
- ])
- app.error_handlers[404] = Handle404
-
- When an ``HTTPException`` is raised using :func:`abort` or because
the
- app could not fulfill the request, the error handler defined for
the
- exception HTTP status code will be called. If it is not set, the
- exception is reraised.
-
- .. note::
- Although being a :class:`RequestHandler`, the error handler will
- execute the ``handle_exception`` method after instantiation,
instead
- of the method corresponding to the current request.
-
- Also, the error handler is responsible for setting the response
- status code and logging the exception, as shown in the example
- above.
-
- :param request:
- A :attr:`request_class` instance.
- :param exception:
- The raised exception.
- """
- if isinstance(exception, HTTPException):
- code = exception.code
- else:
- code = 500
-
- handler = self.error_handlers.get(code)
- if not handler:
- raise
-
- request.exception = exception
- rv = handler(request)
- if not isinstance(rv, werkzeug.wrappers.BaseResponse):
- if hasattr(rv, '__call__'):
- # If it is a callable but not a response, we call it again.
- rv = rv()
-
- return rv
-
- def make_response(self, request, *rv):
- """Converts the returned value from a :class:`RequestHandler` to a
- response object that is an instance of :attr:`response_class`.
-
- This function is borrowed from `Flask`_.
-
- :param rv:
- - If no arguments are passed, returns an empty response.
- - If a single argument is passed, the returned value varies
- according to its type:
-
- - :attr:`response_class`: the response is returned unchanged.
- - :class:`str`: a response is created with the string as
body.
- - :class:`unicode`: a response is created with the string
- encoded to utf-8 as body.
- - a WSGI function: the function is called as WSGI application
- and buffered as response object.
- - None: a ValueError exception is raised.
-
- - If multiple arguments are passed, a response is created using
- the arguments.
-
- :returns:
- A :attr:`response_class` instance.
- """
- if not rv:
- return self.response_class()
-
- if len(rv) == 1:
- rv = rv[0]
-
- if isinstance(rv, self.response_class):
- return rv
-
- if isinstance(rv, basestring):
- return self.response_class(rv)
-
- if rv is None:
- raise ValueError('RequestHandler did not return a
response.')
-
- return self.response_class.force_type(rv, request.environ)
-
- return self.response_class(*rv)
-
- def get_config(self, module, key=None, default=REQUIRED_VALUE):
- """Returns a configuration value for a module.
-
- .. seealso:: :meth:`Config.get_config`.
- """
- from warnings import warn
- warn(DeprecationWarning("App.get_config(): this method "
- "is deprecated. Use App.config['module']['key'] instead."))
- return self.config.get_config(module, key=key, default=default)
-
- def get_test_client(self):
- """Creates a test client for this application.
-
- :returns:
- A ``werkzeug.Client`` with the WSGI application wrapped for
tests.
- """
- from werkzeug.test import Client
- return Client(self, self.response_class, use_cookies=True)
-
- def get_test_context(self, *args, **kwargs):
- """Creates a test client for this application.
-
- :param args:
- Positional arguments to construct a
`werkzeug.test.EnvironBuilder`.
- :param kwargs:
- Keyword arguments to construct a
`werkzeug.test.EnvironBuilder`.
- :returns:
- A :class:``RequestContext`` instance.
- """
- from werkzeug.test import EnvironBuilder
- builder = EnvironBuilder(*args, **kwargs)
- return self.request_context_class(self, builder.get_environ())
-
- def get_test_handler(self, *args, **kwargs):
- """Returns a handler set as a current handler for testing purposes.
-
- .. seealso:: :class:`tipfy.testing.CurrentHandlerContext`.
-
- :returns:
- A :class:`tipfy.testing.CurrentHandlerContext` instance.
- """
- from tipfy.testing import CurrentHandlerContext
- return CurrentHandlerContext(self, *args, **kwargs)
-
- def run(self):
- """Runs the app using ``CGIHandler``. This must be called inside a
- ``main()`` function in the file defined in *app.yaml* to run the
- application::
-
- # ...
-
- app = App(rules=[
- Rule('/', name='home', handler=HelloWorldHandler),
- ])
-
- def main():
- app.run()
-
- if __name__ == '__main__':
- main()
-
- """
- wsgiref.handlers.CGIHandler().run(self)
-
- @werkzeug.utils.cached_property
- def _debugged_wsgi_app(self):
- """Returns the WSGI app wrapped by an interactive debugger."""
- from tipfy.debugger import DebuggedApplication
- return DebuggedApplication(self.dispatch, evalex=True)
-
- @werkzeug.utils.cached_property
- def auth_store_class(self):
- """Returns the configured auth store class.
-
- :returns:
- An auth store class.
- """
- cls = self.config['tipfy']['auth_store_class']
- return werkzeug.utils.import_string(cls)
-
- @werkzeug.utils.cached_property
- def i18n_store_class(self):
- """Returns the configured i18n store class.
-
- :returns:
- An i18n store class.
- """
- cls = self.config['tipfy']['i18n_store_class']
- return werkzeug.utils.import_string(cls)
-
- @werkzeug.utils.cached_property
- def session_store_class(self):
- """Returns the configured session store class.
-
- :returns:
- A session store class.
- """
- cls = self.config['tipfy']['session_store_class']
- return werkzeug.utils.import_string(cls)
-
- # Old names
- wsgi_app = dispatch
+ """The WSGI application."""
+ # Allowed request methods.
+ allowed_methods = frozenset(['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST',
+ 'PUT', 'TRACE'])
+ #: Default class for requests.
+ request_class = Request
+ #: Default class for responses.
+ response_class = Response
+ #: Default class for the configuration object.
+ config_class = Config
+ #: Default class for the configuration object.
+ router_class = Router
+ #: Context class used when a request comes in.
+ request_context_class = RequestContext
+
+ def __init__(self, rules=None, config=None, debug=False):
+ """Initializes the application.
+
+ :param rules:
+ URL rules definitions for the application.
+ :param config:
+ Dictionary with configuration for the application modules.
+ :param debug:
+ True if this is debug mode, False otherwise.
+ """
+ local.current_app = self
+ self.debug = debug
+ self.registry = {}
+ self.error_handlers = {}
+ self.config = self.config_class(config, {'tipfy': default_config})
+ self.router = self.router_class(self, rules)
+
+ if debug:
+ logging.getLogger().setLevel(logging.DEBUG)
+
+ def __call__(self, environ, start_response):
+ """Called when a request comes in."""
+ if self.debug and self.config['tipfy']['enable_debugger']:
+ return self._debugged_wsgi_app(environ, start_response)
+
+ return self.dispatch(environ, start_response)
+
+ def dispatch(self, environ, start_response):
+ """This is the actual WSGI application. This is not implemented in
+ :meth:`__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.dispatch = MyMiddleware(app.dispatch)
+
+ Then you still have the original application object around and
+ can continue to call methods on it.
+
+ This idea comes from `Flask`_.
+
+ :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_class(self, environ) as request:
+ try:
+ if request.method not in self.allowed_methods:
+ abort(501)
+
+ rv = self.router.dispatch(request)
+ response = self.make_response(request, rv)
+ except Exception, e:
+ try:
+ rv = self.handle_exception(request, e)
+ response = self.make_response(request, rv)
+ except HTTPException, e:
+ response = self.make_response(request, e)
+ except Exception, e:
+ if self.debug:
+ raise
+
+ logging.exception(e)
+ rv = werkzeug.exceptions.InternalServerError()
+ response = self.make_response(request, rv)
+
+ return response(environ, start_response)
+
+ def handle_exception(self, request, exception):
+ """Handles an exception. To set app-wide error handlers, define them
+ using the corresponent HTTP status code in the ``error_handlers``
+ dictionary of :class:`App`. For example, to set a custom
+ `Not Found` page::
+
+ class Handle404(RequestHandler):
+ def __call__(self):
+ logging.exception(self.request.exception)
+ return Response('Oops! I could swear this page was here!',
+ status=404)
+
+ app = App([
+ Rule('/', handler=MyHandler, name='home'),
+ ])
+ app.error_handlers[404] = Handle404
+
+ When an ``HTTPException`` is raised using :func:`abort` or because the
+ app could not fulfill the request, the error handler defined for the
+ exception HTTP status code will be called. If it is not set, the
+ exception is reraised.
+
+ .. note::
+ Although being a :class:`RequestHandler`, the error handler will
+ execute the ``handle_exception`` method after instantiation, instead
+ of the method corresponding to the current request.
+
+ Also, the error handler is responsible for setting the response
+ status code and logging the exception, as shown in the example
+ above.
+
+ :param request:
+ A :attr:`request_class` instance.
+ :param exception:
+ The raised exception.
+ """
+ if isinstance(exception, HTTPException):
+ code = exception.code
+ else:
+ code = 500
+
+ handler = self.error_handlers.get(code)
+ if not handler:
+ raise
+
+ request.exception = exception
+ rv = handler(request)
+ if not isinstance(rv, werkzeug.wrappers.BaseResponse):
+ if hasattr(rv, '__call__'):
+ # If it is a callable but not a response, we call it again.
+ rv = rv()
+
+ return rv
+
+ def make_response(self, request, *rv):
+ """Converts the returned value from a :class:`RequestHandler` to a
+ response object that is an instance of :attr:`response_class`.
+
+ This function is borrowed from `Flask`_.
+
+ :param rv:
+ - If no arguments are passed, returns an empty response.
+ - If a single argument is passed, the returned value varies
+ according to its type:
+
+ - :attr:`response_class`: the response is returned unchanged.
+ - :class:`str`: a response is created with the string as body.
+ - :class:`unicode`: a response is created with the string
+ encoded to utf-8 as body.
+ - a WSGI function: the function is called as WSGI application
+ and buffered as response object.
+ - None: a ValueError exception is raised.
+
+ - If multiple arguments are passed, a response is created using
+ the arguments.
+
+ :returns:
+ A :attr:`response_class` instance.
+ """
+ if not rv:
+ return self.response_class()
+
+ if len(rv) == 1:
+ rv = rv[0]
+
+ if isinstance(rv, self.response_class):
+ return rv
+
+ if isinstance(rv, basestring):
+ return self.response_class(rv)
+
+ if rv is None:
+ raise ValueError('RequestHandler did not return a response.')
+
+ return self.response_class.force_type(rv, request.environ)
+
+ return self.response_class(*rv)
+
+ def get_config(self, module, key=None, default=REQUIRED_VALUE):
+ """Returns a configuration value for a module.
+
+ .. seealso:: :meth:`Config.get_config`.
+ """
+ from warnings import warn
+ warn(DeprecationWarning("App.get_config(): this method "
+ "is deprecated. Use App.config['module']['key'] instead."))
+ return self.config.get_config(module, key=key, default=default)
+
+ def get_test_client(self):
+ """Creates a test client for this application.
+
+ :returns:
+ A ``werkzeug.Client`` with the WSGI application wrapped for tests.
+ """
+ from werkzeug.test import Client
+ return Client(self, self.response_class, use_cookies=True)
+
+ def get_test_context(self, *args, **kwargs):
+ """Creates a test client for this application.
+
+ :param args:
+ Positional arguments to construct a `werkzeug.test.EnvironBuilder`.
+ :param kwargs:
+ Keyword arguments to construct a `werkzeug.test.EnvironBuilder`.
+ :returns:
+ A :class:``RequestContext`` instance.
+ """
+ from werkzeug.test import EnvironBuilder
+ builder = EnvironBuilder(*args, **kwargs)
+ return self.request_context_class(self, builder.get_environ())
+
+ def get_test_handler(self, *args, **kwargs):
+ """Returns a handler set as a current handler for testing purposes.
+
+ .. seealso:: :class:`tipfy.testing.CurrentHandlerContext`.
+
+ :returns:
+ A :class:`tipfy.testing.CurrentHandlerContext` instance.
+ """
+ from tipfy.testing import CurrentHandlerContext
+ return CurrentHandlerContext(self, *args, **kwargs)
+
+ def run(self):
+ """Runs the app using ``CGIHandler``. This must be called inside a
+ ``main()`` function in the file defined in *app.yaml* to run the
+ application::
+
+ # ...
+
+ app = App(rules=[
+ Rule('/', name='home', handler=HelloWorldHandler),
+ ])
+
+ def main():
+ app.run()
+
+ if __name__ == '__main__':
+ main()
+
+ """
+ wsgiref.handlers.CGIHandler().run(self)
+
+ @werkzeug.utils.cached_property
+ def _debugged_wsgi_app(self):
+ """Returns the WSGI app wrapped by an interactive debugger."""
+ from tipfy.debugger import DebuggedApplication
+ return DebuggedApplication(self.dispatch, evalex=True)
+
+ @werkzeug.utils.cached_property
+ def auth_store_class(self):
+ """Returns the configured auth store class.
+
+ :returns:
+ An auth store class.
+ """
+ cls = self.config['tipfy']['auth_store_class']
+ return werkzeug.utils.import_string(cls)
+
+ @werkzeug.utils.cached_property
+ def i18n_store_class(self):
+ """Returns the configured i18n store class.
+
+ :returns:
+ An i18n store class.
+ """
+ cls = self.config['tipfy']['i18n_store_class']
+ return werkzeug.utils.import_string(cls)
+
+ @werkzeug.utils.cached_property
+ def session_store_class(self):
+ """Returns the configured session store class.
+
+ :returns:
+ A session store class.
+ """
+ cls = self.config['tipfy']['session_store_class']
+ return werkzeug.utils.import_string(cls)
+
+ # Old names
+ wsgi_app = dispatch


def redirect(location, code=302, response_class=Response, body=None):
- """Returns a response object that redirects to the given location.
-
- Supported codes are 301, 302, 303, 305, and 307. 300 is not supported
- because it's not a real redirect and 304 because it's the answer for a
- request with a request with defined If-Modified-Since headers.
-
- :param location:
- A relative or absolute URI (e.g., '/contact'). If relative, it
- will be merged to the current request URL to form an absolute URL.
- :param code:
- The HTTP status code for the redirect. Default is 302.
- :param response_class:
- The class used to build the response. Default is :class:`Response`.
- :body:
- The response body. If not set uses a body with a standard message.
- :returns:
- A :class:`Response` object with headers set for redirection.
- """
- assert code in (301, 302, 303, 305, 307), 'invalid code'
-
- if location.startswith(('.', '/')):
- # Make it absolute.
- location = urlparse.urljoin(get_request().url, location)
-
- display_location = location
- if isinstance(location, unicode):
- location = werkzeug.urls.iri_to_uri(location)
-
- if body is None:
- body = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
\
- '<title>Redirecting...</title>\n<h1>Redirecting...</h1>\n' \
- '<p>You should be redirected automatically to target URL: ' \
- '<a href="%s">%s</a>. If not click the link.' % \
- (location, display_location)
-
- response = response_class(body, code, mimetype='text/html')
- response.headers['Location'] = location
- return response
+ """Returns a response object that redirects to the given location.
+
+ Supported codes are 301, 302, 303, 305, and 307. 300 is not supported
+ because it's not a real redirect and 304 because it's the answer for a
+ request with a request with defined If-Modified-Since headers.
+
+ :param location:
+ A relative or absolute URI (e.g., '/contact'). If relative, it
+ will be merged to the current request URL to form an absolute URL.
+ :param code:
+ The HTTP status code for the redirect. Default is 302.
+ :param response_class:
+ The class used to build the response. Default is :class:`Response`.
+ :body:
+ The response body. If not set uses a body with a standard message.
+ :returns:
+ A :class:`Response` object with headers set for redirection.
+ """
+ assert code in (301, 302, 303, 305, 307), 'invalid code'
+
+ if location.startswith(('.', '/')):
+ # Make it absolute.
+ location = urlparse.urljoin(get_request().url, location)
+
+ display_location = location
+ if isinstance(location, unicode):
+ location = werkzeug.urls.iri_to_uri(location)
+
+ if body is None:
+ body = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n' \
+ '<title>Redirecting...</title>\n<h1>Redirecting...</h1>\n' \
+ '<p>You should be redirected automatically to target URL: ' \
+ '<a href="%s">%s</a>. If not click the link.' % \
+ (location, display_location)
+
+ response = response_class(body, code, mimetype='text/html')
+ response.headers['Location'] = location
+ return response


# Old names.

Reply all
Reply to author
Forward
0 new messages