Revision: 3583e813832d
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Sat Apr 2 14:29:27 2011
Log: Added set_sys_path.py. Helps when you have multiple bootstraps.
http://code.google.com/p/tipfy/source/detail?r=3583e813832d
Revision: fee5ee6de061
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Sat Apr 2 14:59:44 2011
Log: Added some deprecation warnings and fixed some class names in
docblock...
http://code.google.com/p/tipfy/source/detail?r=fee5ee6de061
Revision: fed2e6836e85
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Sat Apr 2 15:00:06 2011
Log: Added RequestHandlerMiddleware base class.
http://code.google.com/p/tipfy/source/detail?r=fed2e6836e85
==============================================================================
Revision: 3583e813832d
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Sat Apr 2 14:29:27 2011
Log: Added set_sys_path.py. Helps when you have multiple bootstraps.
http://code.google.com/p/tipfy/source/detail?r=3583e813832d
Modified:
/project/app/hello_world/handlers.py
/project/app/main.py
/project/app/urls.py
=======================================
--- /project/app/hello_world/handlers.py Sun Feb 27 09:03:33 2011
+++ /project/app/hello_world/handlers.py Sat Apr 2 14:29:27 2011
@@ -8,7 +8,8 @@
:copyright: 2009 by tipfy.org.
:license: BSD, see LICENSE for more details.
"""
-from tipfy import RequestHandler, Response
+from tipfy.app import Response
+from tipfy.handler import RequestHandler
from tipfyext.jinja2 import Jinja2Mixin
=======================================
--- /project/app/main.py Sun Feb 27 09:03:33 2011
+++ /project/app/main.py Sat Apr 2 14:29:27 2011
@@ -1,48 +1,36 @@
# -*- coding: utf-8 -*-
"""WSGI app setup."""
import os
-import sys
-
-if 'lib' not in sys.path:
- # Add lib as primary libraries directory, with fallback to lib/dist
- # and optionally to lib/dist.zip, loaded using zipimport.
- sys.path[0:0] = ['lib', 'lib/dist', 'lib/dist.zip']
-
-from tipfy import Tipfy
+
+import set_sys_path
+
+from tipfy.app import App
from config import config
from urls import rules
-
def enable_appstats(app):
"""Enables appstats middleware."""
from google.appengine.ext.appstats.recording import \
appstats_wsgi_middleware
- app.wsgi_app = appstats_wsgi_middleware(app.wsgi_app)
-
+ app.dispatch = appstats_wsgi_middleware(app.dispatch)
def enable_jinja2_debugging():
"""Enables blacklisted modules that help Jinja2 debugging."""
if not debug:
return
-
- # This enables better debugging info for errors in Jinja2 templates.
from google.appengine.tools.dev_appserver import HardenedModulesHook
HardenedModulesHook._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt']
-
# Is this the development server?
debug = os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')
# Instantiate the application.
-app = Tipfy(rules=rules, config=config, debug=debug)
+app = App(rules=rules, config=config, debug=debug)
enable_appstats(app)
enable_jinja2_debugging()
-
def main():
- # Run the app.
app.run()
-
if __name__ == '__main__':
main()
=======================================
--- /project/app/urls.py Sun Feb 27 09:03:33 2011
+++ /project/app/urls.py Sat Apr 2 14:29:27 2011
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""URL definitions."""
-from tipfy import Rule
+from tipfy.routing import Rule
rules = [
Rule('/', name='hello-world',
handler='hello_world.handlers.HelloWorldHandler'),
==============================================================================
Revision: fee5ee6de061
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Sat Apr 2 14:59:44 2011
Log: Added some deprecation warnings and fixed some class names in
docblocks.
http://code.google.com/p/tipfy/source/detail?r=fee5ee6de061
Modified:
/Makefile
/project/buildout.cfg
/setup.py
/tipfy/__init__.py
/tipfy/app.py
/tipfy/appengine/blobstore.py
/tipfy/config.py
/tipfy/handler.py
/tipfy/i18n.py
/tipfy/routing.py
/tipfy/sessions.py
/tipfy/testing.py
/tipfyext/wtforms/form.py
=======================================
--- /Makefile Sat Apr 2 13:52:07 2011
+++ /Makefile Sat Apr 2 14:59:44 2011
@@ -8,7 +8,7 @@
GAE= /usr/local/google_appengine
GAEPATH=$(GAE):$(GAE)/lib/django_0_96:$(GAE)/lib/webob:$(GAE)/lib/yaml/lib:tests
TESTS= `find tests -name [a-z]\*_test.py`
-NONTESTS=`find tipfy -name [a-z]\*.py
+NONTESTS=`find tipfy -name [a-z]\*.py`
PORT= 8080
ADDRESS=localhost
PYTHON= python -Wignore
=======================================
--- /project/buildout.cfg Fri Mar 11 09:35:40 2011
+++ /project/buildout.cfg Sat Apr 2 14:59:44 2011
@@ -50,11 +50,11 @@
# Define the packages to download. Only tipfy is included, but you can add
# others or uncomment the extra lines to add those common packages.
eggs =
- tipfy-dev
-# babel
-# gaepytz
-# jinja2
-# wtforms
+ tipfy
+ jinja2
+ babel
+ gaepytz
+ wtforms
# Don't copy files that match these glob patterns.
ignore-globs =
=======================================
--- /setup.py Sat Mar 12 17:09:10 2011
+++ /setup.py Sat Apr 2 14:59:44 2011
@@ -35,7 +35,7 @@
setup(
name = 'tipfy',
- version = '1.0b',
+ version = '1.0b1',
license = 'BSD',
url = 'http://www.tipfy.org/',
download_url = 'http://www.tipfy.org/tipfy.tar.gz',
=======================================
--- /tipfy/__init__.py Wed Mar 30 05:57:11 2011
+++ /tipfy/__init__.py Sat Apr 2 14:59:44 2011
@@ -14,7 +14,7 @@
#: Default configuration values for this module. Keys are:
#:
#: auth_store_class
-#: The default auth store class to use in :class:`tipfy.Request`.
+#: The default auth store class to use in :class:`tipfy.app.Request`.
#: Default is `tipfy.appengine.auth.AuthStore`.
#:
#: i18n_store_class
@@ -22,7 +22,7 @@
#: Default is `tipfy.i18n.I18nStore`.
#:
#: session_store_class
-#: The default session store class to use in :class:`tipfy.Request`.
+#: The default session store class to use
in :class:`tipfy.app.Request`.
#: Default is `tipfy.sessions.SessionStore`.
#:
#: server_name
=======================================
--- /tipfy/app.py Sat Apr 2 13:52:07 2011
+++ /tipfy/app.py Sat Apr 2 14:59:44 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,416 +34,425 @@
#: 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.
- 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 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)
-
- def _get_rule_adapter(self):
- # TODO: deprecation warning.
- return self.rule_adapter
-
- def _set_rule_adapter(self, adapter):
- # TODO: deprecation warning.
- 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
+
+ @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)
+
+ 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
-
- 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 RequestContext(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 = Tipfy([
- 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`.
- """
- 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 RequestContext(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 = Tipfy(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.
=======================================
--- /tipfy/appengine/blobstore.py Wed Mar 30 05:57:11 2011
+++ /tipfy/appengine/blobstore.py Sat Apr 2 14:59:44 2011
@@ -133,7 +133,7 @@
to save-as. If string is provided, use string as filename. If
None or False, do not send as attachment.
:returns:
- A :class:`tipfy.Response` object.
+ A :class:`tipfy.app.Response` object.
:raises:
``ValueError`` on invalid save_as parameter.
"""
=======================================
--- /tipfy/config.py Wed Mar 30 06:54:43 2011
+++ /tipfy/config.py Sat Apr 2 14:59:44 2011
@@ -36,7 +36,7 @@
'foo': 'bar',
}
- app = Tipfy(rules=[Rule('/', name='home', handler=MyHandler)],
+ app = App(rules=[Rule('/', name='home', handler=MyHandler)],
config=config)
Then to read configuration values,
use :meth:`RequestHandler.get_config`::
=======================================
--- /tipfy/handler.py Thu Mar 31 07:24:20 2011
+++ /tipfy/handler.py Sat Apr 2 14:59:44 2011
@@ -40,14 +40,16 @@
:param request:
A :class:`Request` instance.
- :param app:
- A :class:`tipfy.app.App` instance.
"""
if app:
# App argument is kept for backwards compatibility. Previously
we
# called passing (app, request) but because view functions are
now
# supported only request is passed and app is an attribute of
the
# request object.
+ from warnings import warn
+ warn(DeprecationWarning("BaseRequestHandler.__init__(): the "
+ "'app' argument is deprecated. The constructor must
receive "
+ "only the Request object."))
self.app = request
self.request = app
else:
@@ -149,8 +151,13 @@
def get_config(self, module, key=None, default=REQUIRED_VALUE):
"""Returns a configuration value for a module.
+ .. warning: Deprecated. Use `self.app.config['module']['key']`
instead.
+
.. seealso:: :meth:`Config.get_config`.
"""
+ from warnings import warn
+ warn(DeprecationWarning("BaseRequestHandler.get_config(): this
method "
+ "is deprecated. Use self.app.config['module']['key']
instead."))
return self.app.config.get_config(module, key=key, default=default)
def get_valid_methods(self):
@@ -213,6 +220,10 @@
response_class = response_class or self.app.response_class
if empty:
+ from warnings import warn
+ warn(DeprecationWarning("BaseRequestHandler.redirect(): the "
+ "'empty' keyword argument is deprecated. Use body='' "
+ "instead."))
body = ''
return redirect(location, code=code, response_class=response_class,
=======================================
--- /tipfy/i18n.py Wed Mar 30 05:57:11 2011
+++ /tipfy/i18n.py Sat Apr 2 14:59:44 2011
@@ -802,7 +802,7 @@
found, returns the default value.
:param request:
- A :class:`tipfy.Request` instance.
+ A :class:`tipfy.app.Request` instance.
:param lookup_list:
A list of `(attribute, key)` tuples to search in request, e.g.,
``[('args', 'lang'), ('session', 'locale')]``.
=======================================
--- /tipfy/routing.py Thu Mar 31 07:24:20 2011
+++ /tipfy/routing.py Sat Apr 2 14:59:44 2011
@@ -21,7 +21,7 @@
"""Initializes the router.
:param app:
- A :class:`tipfy.Tipfy` instance.
+ A :class:`tipfy.app.App` instance.
:param rules:
A list of initial :class:`Rule` instances.
"""
@@ -47,14 +47,14 @@
request and returns the matched rule and rule arguments.
The URL adapter, matched rule and rule arguments will be set in the
- :class:`tipfy.Request` instance.
+ :class:`tipfy.app.Request` instance.
Three exceptions can occur when matching the rules: ``NotFound``,
``MethodNotAllowed`` or ``RequestRedirect``. The WSGI app will
handle
raised exceptions.
:param request:
- A :class:`tipfy.Request` instance.
+ A :class:`tipfy.app.Request` instance.
:returns:
A tuple ``(rule, rule_args)`` with the matched rule and rule
arguments.
@@ -73,14 +73,14 @@
:class:`tipfy.RequestHandler` based on the matched :class:`Rule`.
:param request:
- A :class:`tipfy.Request` instance.
+ A :class:`tipfy.app.Request` instance.
:param match:
A tuple ``(rule, rule_args)`` with the matched rule and rule
arguments.
:param method:
A method to be used instead of using the request or handler
method.
:returns:
- A :class:`tipfy.Response` instance.
+ A :class:`tipfy.app.Response` instance.
"""
rule, rule_args = self.match(request)
handler = rule.handler
@@ -168,7 +168,7 @@
from multiple domains).
:param request:
- A :class:`tipfy.Request` instance.
+ A :class:`tipfy.app.Request` instance.
:returns:
The server name used to build the URL adapter.
"""
=======================================
--- /tipfy/sessions.py Wed Mar 30 05:57:11 2011
+++ /tipfy/sessions.py Sat Apr 2 14:59:44 2011
@@ -148,7 +148,7 @@
"""Returns the given signed cookie if it validates, or None.
:param request:
- A :class:`tipfy.Request` object.
+ A :class:`tipfy.app.Request` object.
:param name:
Cookie name.
:param max_age:
@@ -186,7 +186,7 @@
To read a cookie set with this method, use get_cookie().
:param response:
- A :class:`tipfy.Response` instance.
+ A :class:`tipfy.app.Response` instance.
:param name:
Cookie name.
:param value:
@@ -348,7 +348,7 @@
"""Sets a secure cookie in the response.
:param response:
- A :class:`tipfy.Response` object.
+ A :class:`tipfy.app.Response` object.
:param name:
Cookie name.
:param value:
=======================================
--- /tipfy/testing.py Thu Mar 31 07:24:20 2011
+++ /tipfy/testing.py Sat Apr 2 14:59:44 2011
@@ -22,9 +22,9 @@
from __future__ import with_statement
- from tipfy import Tipfy, Rule
-
- app = Tipfy(rules=[
+ from tipfy import App, Rule
+
+ app = App(rules=[
Rule('/about', name='home', handler='handlers.AboutHandler'),
])
@@ -39,10 +39,10 @@
"""Initializes the handler context.
:param app:
- A :class:`tipfy.Tipfy` instance.
+ A :class:`tipfy.app.App` instance.
:param args:
- Arguments to build a :class:`tipfy.Request` instance if a
request
- is not passed explicitly.
+ Arguments to build a :class:`tipfy.app.Request` instance if a
+ request is not passed explicitly.
:param kwargs:
Keyword arguments to build a :class:`Request` instance if a
request
is not passed explicitly. A few keys have special meaning:
@@ -56,6 +56,9 @@
- `handler`: a handler instance. If passed, the handler is
simply
set and reset as current_handler during the context
execution.
"""
+ from warnings import warn
+ warn(DeprecationWarning("CurrentHandlerContext: this class "
+ "is deprecated. Use tipfy.app.RequestContext instead."))
self.app = app
self.handler = kwargs.pop('handler', None)
self.handler_class = kwargs.pop('handler_class', None)
=======================================
--- /tipfyext/wtforms/form.py Wed Mar 30 05:57:11 2011
+++ /tipfyext/wtforms/form.py Sat Apr 2 14:59:44 2011
@@ -39,7 +39,7 @@
process them.
:param formdata:
- A :class:`tipfy.Request` object or a multidict of form data
coming
+ A :class:`tipfy.app.Request` object or a multidict of form
data coming
from the enduser, usually `request.form` or equivalent.
:param obj:
If `formdata` has no data for a field, the form will try to
get it
==============================================================================
Revision: fed2e6836e85
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Sat Apr 2 15:00:06 2011
Log: Added RequestHandlerMiddleware base class.
http://code.google.com/p/tipfy/source/detail?r=fed2e6836e85
Modified:
/tipfy/handler.py
=======================================
--- /tipfy/handler.py Sat Apr 2 14:59:44 2011
+++ /tipfy/handler.py Sat Apr 2 15:00:06 2011
@@ -315,3 +315,44 @@
# Done!
return response
+
+
+class RequestHandlerMiddleware(object):
+ """Base class for :class:`RequestHandler` middleware."""
+ def before_dispatch(self, handler):
+ """Called before the handler method is executed.
+
+ If the returned value is not None, stops the middleware chain and
uses
+ that value to create a response, and doesn't call the handler
method.
+
+ :param handler:
+ A :class:`RequestHandler` instance.
+ """
+
+ def after_dispatch(self, handler, response):
+ """Called after the handler method is executed.
+
+ Must always return a response object.
+
+ These are executed in reverse order.
+
+ :param handler:
+ A :class:`RequestHandler` instance.
+ :param response:
+ A :class:`tipfy.app.Response` instance.
+ """
+ return response
+
+ def handle_exception(self, handler, exception):
+ """Called if an exception occurs while executing the handler
method.
+
+ If the returned value is not None, stops the middleware chain and
uses
+ that value to create a response.
+
+ These are executed in reverse order.
+
+ :param handler:
+ A :class:`RequestHandler` instance.
+ :param exception:
+ An exception.
+ """