Revision: a3a7e6f8ec42
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Wed Mar 30 04:39:47 2011
Log: Added missing mako_templates in tests.
http://code.google.com/p/tipfy/source/detail?r=a3a7e6f8ec42
Revision: 4e176df7045b
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Wed Mar 30 04:47:00 2011
Log: Cleaner logic in Rule constructor. Router's .build renamed
to .url_for...
http://code.google.com/p/tipfy/source/detail?r=4e176df7045b
Revision: 31667b68c57b
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Wed Mar 30 05:46:53 2011
Log: Several refactorings: added tipfy.local and tipty.handler modules.
http://code.google.com/p/tipfy/source/detail?r=31667b68c57b
==============================================================================
Revision: a3a7e6f8ec42
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Wed Mar 30 04:39:47 2011
Log: Added missing mako_templates in tests.
http://code.google.com/p/tipfy/source/detail?r=a3a7e6f8ec42
Added:
/tests/resources/mako_templates/template1.html
Modified:
/.hgignore
=======================================
--- /dev/null
+++ /tests/resources/mako_templates/template1.html Wed Mar 30 04:39:47 2011
@@ -0,0 +1,1 @@
+${message}
=======================================
--- /.hgignore Thu Mar 3 12:14:53 2011
+++ /.hgignore Wed Mar 30 04:39:47 2011
@@ -5,7 +5,6 @@
\.bak
\.swp
dist
-temp
docs/build
docs/project
tests/project
==============================================================================
Revision: 4e176df7045b
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Wed Mar 30 04:47:00 2011
Log: Cleaner logic in Rule constructor. Router's .build renamed
to .url_for, and .build remains as an alias.
http://code.google.com/p/tipfy/source/detail?r=4e176df7045b
Modified:
/tipfy/app.py
/tipfy/routing.py
/tipfy/utils.py
=======================================
--- /tipfy/app.py Sun Mar 20 13:58:27 2011
+++ /tipfy/app.py Wed Mar 30 04:47:00 2011
@@ -260,9 +260,9 @@
def url_for(self, _name, **kwargs):
"""Returns a URL for a named :class:`Rule`.
- .. seealso:: :meth:`Router.build`.
+ .. seealso:: :meth:`Router.url_for`.
"""
- return self.app.router.build(self.request, _name, kwargs)
+ return self.app.router.url_for(self.request, _name, kwargs)
class RequestHandler(BaseRequestHandler):
=======================================
--- /tipfy/routing.py Wed Mar 23 11:36:51 2011
+++ /tipfy/routing.py Wed Mar 30 04:47:00 2011
@@ -101,7 +101,7 @@
return rv
- def build(self, request, name, kwargs):
+ def url_for(self, request, name, kwargs):
"""Returns a URL for a named :class:`Rule`. This is the central
place
to build URLs for an app. It is used
by :meth:`RequestHandler.url_for`,
which conveniently pass the request object so you don't have to.
@@ -178,6 +178,9 @@
"""
return self.app.config['tipfy']['server_name']
+ # Old name.
+ build = url_for
+
class Rule(BaseRule):
"""A Rule represents one URL pattern. Tipfy extends Werkzeug's Rule
@@ -284,19 +287,28 @@
the script so don't use a leading slash on the target URL
unless
you really mean root of that domain.
"""
+ # In werkzeug.routing, 'endpoint' defines the name or the callable
+ # depending on the implementation, and an extra map is needed to
map
+ # named rules to their callables. We support werkzeug.routing's
+ # 'endpoint' but favor a less ambiguous 'name' keyword, and accept
an
+ # extra 'handler' keyword that defines the callable to be executed.
+ # This way a rule always carries both a name and a callable
definition,
+ # unambiguously, and no extra map is needed.
self.name = kwargs.pop('endpoint', name)
- self.handler = handler or self.name
+ self.handler = handler = handler or self.name
+ # If a handler string has a colon, we take it as the method from a
+ # handler class, e.g., 'my_module.MyClass:my_method', and store it
+ # in the rule as 'handler_method'. Not every rule mapping to a
class
+ # must define a method (the request method is used by default),
and for
+ # functions 'handler_method' is of course always None.
self.handler_method = handler_method
- if isinstance(self.handler, basestring):
- if handler_method and self.handler.find(':') != -1:
+ if isinstance(handler, basestring) and handler.rfind(':') != -1:
+ if handler_method:
raise ValueError(
"If handler_method is defined in a Rule, handler "
- "can't have a colon (got %r)." % self.handler)
+ "can't have a colon (got %r)." % handler)
else:
- parts = self.handler.rsplit(':', 1)
- self.handler = parts[0]
- if len(parts) > 1:
- self.handler_method = parts[1]
+ self.handler, self.handler_method = handler.rsplit(':', 1)
super(Rule, self).__init__(path, endpoint=self.name, **kwargs)
=======================================
--- /tipfy/utils.py Sun Mar 20 05:01:53 2011
+++ /tipfy/utils.py Wed Mar 30 04:47:00 2011
@@ -128,7 +128,7 @@
def url_for(_name, **kwargs):
"""A proxy to :meth:`RequestHandler.url_for`.
- .. seealso:: :meth:`Router.build`.
+ .. seealso:: :meth:`Router.url_for`.
"""
return current_handler.url_for(_name, **kwargs)
==============================================================================
Revision: 31667b68c57b
Author: Rodrigo Moraes <rodrigo...@gmail.com>
Date: Wed Mar 30 05:46:53 2011
Log: Several refactorings: added tipfy.local and tipty.handler modules.
http://code.google.com/p/tipfy/source/detail?r=31667b68c57b
Added:
/tipfy/handler.py
/tipfy/local.py
Modified:
/tipfy/__init__.py
/tipfy/app.py
/tipfy/appengine/__init__.py
/tipfy/routing.py
=======================================
--- /dev/null
+++ /tipfy/handler.py Wed Mar 30 05:46:53 2011
@@ -0,0 +1,291 @@
+# -*- coding: utf-8 -*-
+"""
+ tipfy.handler
+ ~~~~~~~~~~~~~
+
+ Base request handler classes.
+
+ :copyright: 2010 by tipfy.org.
+ :license: BSD, see LICENSE.txt for more details.
+"""
+import urlparse
+
+import werkzeug.utils
+
+from .app import abort, redirect
+from .config import REQUIRED_VALUE
+
+
+class BaseRequestHandler(object):
+ """Base class to handle requests. This is the central piece for an
+ application and provides access to the current WSGI app and request.
+ Additionally it provides lazy access to auth, i18n and session stores,
+ and several utilities to handle a request.
+
+ Although it is convenient to extend this class
(or :class:`RequestHandler`)
+ and some extended functionality like sessions is implemented on top of
it,
+ the only required interface by the WSGI app is the following:
+
+ class RequestHandler(object):
+ def __init__(self, app, request):
+ pass
+
+ def __call__(self):
+ return Response()
+
+ A Tipfy-compatible handler can be implemented using only these two
methods.
+ """
+ def __init__(self, app, request):
+ """Initializes the handler.
+
+ :param app:
+ A :class:`Tipfy` instance.
+ :param request:
+ A :class:`Request` instance.
+ """
+ self.app = app
+ self.request = request
+ # A context for shared data, e.g., template variables.
+ self.context = {}
+
+ def __call__(self):
+ """Executes a handler method. This is called by :class:`Tipfy` and
+ must return a :attr:`response_class` object. If :attr:`middleware`
are
+ defined, use their hooks to process the request or handle
exceptions.
+
+ :returns:
+ A :attr:`response_class` instance.
+ """
+ return self.dispatch()
+
+ def dispatch(self):
+ try:
+ request = self.request
+ method_name = request.rule and request.rule.handler_method
+ if not method_name:
+ method_name = request.method.lower()
+
+ method = getattr(self, method_name, None)
+ if not method:
+ # 405 Method Not Allowed.
+ # The response MUST include an Allow header containing a
+ # list of valid methods for the requested resource.
+ #
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6
+ self.abort(405, valid_methods=self.get_valid_methods())
+
+ return self.make_response(method(**request.rule_args))
+ except Exception, e:
+ return self.handle_exception(exception=e)
+
+ @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 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 abort(self, code, *args, **kwargs):
+ """Raises an :class:`HTTPException`. This stops code execution,
+ leaving the HTTP exception to be handled by an exception handler.
+
+ :param code:
+ HTTP status error code (e.g., 404).
+ :param args:
+ Positional arguments to be passed to the exception class.
+ :param kwargs:
+ Keyword arguments to be passed to the exception class.
+ """
+ abort(code, *args, **kwargs)
+
+ def get_config(self, module, key=None, default=REQUIRED_VALUE):
+ """Returns a configuration value for a module.
+
+ .. seealso:: :meth:`Config.get_config`.
+ """
+ return self.app.config.get_config(module, key=key, default=default)
+
+ def get_valid_methods(self):
+ """Returns a list of methods supported by this handler. By default
it
+ will look for HTTP methods this handler implements. For different
+ routing schemes, override this.
+
+ :returns:
+ A list of methods supported by this handler.
+ """
+ return [method for method in self.app.allowed_methods if
+ getattr(self, method.lower().replace('-', '_'), None)]
+
+ def handle_exception(self, exception=None):
+ """Handles an exception. The default behavior is to reraise the
+ exception (no exception handling is implemented).
+
+ :param exception:
+ The exception that was raised.
+ """
+ raise
+
+ def make_response(self, *rv):
+ """Converts the returned value from a :class:`RequestHandler` to a
+ response object that is an instance
of :attr:`Tipfy.response_class`.
+
+ .. seealso:: :meth:`Tipfy.make_response`.
+ """
+ return self.app.make_response(self.request, *rv)
+
+ def redirect(self, location, code=302, response_class=None, body=None,
+ empty=False):
+ """Returns a response object that redirects to the given location.
+
+ This won't stop code execution, so you must return when calling
it::
+
+ return self.redirect('/some-path')
+
+ :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:`tipfy.app.Response`.
+ :param body:
+ The response body. If not set uses a body with a standard
message.
+ :param empty:
+ If True, returns a response with empty body.
+
+ .. warning: Deprecated. Use `body=''` instead.
+ :returns:
+ A :class:`tipfy.app.Response` object with headers set for
+ redirection.
+
+ ..sealso:: :func:`tipfy.app.redirect`.
+ """
+ response_class = response_class or self.app.response_class
+
+ if location.startswith(('.', '/')):
+ # Make it absolute.
+ location = urlparse.urljoin(self.request.url, location)
+
+ if empty:
+ body = ''
+
+ return redirect(location, code=code, response_class=response_class,
+ body=body)
+
+ def redirect_to(self, _name, _code=302, _body=None, _empty=False,
+ **kwargs):
+ """Returns a redirection response to a named URL rule.
+
+ This is a convenience method that combines meth:`redirect` with
+ meth:`url_for`.
+
+ :param _name:
+ The name of the :class:`tipfy.routing.Rule` to build a URL for.
+ :param _code:
+ The HTTP status code for the redirect. Default is 302.
+ :param _body:
+ The response body. If not set uses a body with a standard
message.
+ :param empty:
+ If True, returns a response with empty body.
+
+ .. warning: Deprecated. Use `body=''` instead.
+ :param kwargs:
+ Keyword arguments to build the URL.
+ :returns:
+ A :class:`tipfy.app.Response` object with headers set for
+ redirection.
+ """
+ url = self.url_for(_name, _full=kwargs.pop('_full', True),
**kwargs)
+ return self.redirect(url, code=_code, body=_body, empty=_empty)
+
+ def url_for(self, _name, **kwargs):
+ """Returns a URL for a named :class:`Rule`.
+
+ .. seealso:: :meth:`Router.url_for`.
+ """
+ return self.app.router.url_for(self.request, _name, kwargs)
+
+
+class RequestHandler(BaseRequestHandler):
+ #: A list of middleware instances. A middleware can implement three
+ #: methods that are called before and after the current request method
+ #: is executed, or if an exception occurs:
+ #:
+ #: before_dispatch(handler)
+ #: Called before the requested method is executed. If returns a
+ #: response, stops the middleware chain and uses that response, not
+ #: calling the requested method.
+ #:
+ #: after_dispatch(handler, response)
+ #: Called after the requested method is executed. Must always
return
+ #: a response. These are executed in reverse order.
+ #:
+ #: handle_exception(handler, exception)
+ #: Called if an exception occurs while executing the requested
method.
+ #: These are executed in reverse order.
+ middleware = None
+
+ def __call__(self):
+ middleware = self.middleware or []
+
+ # Execute before_dispatch middleware.
+ for obj in middleware:
+ func = getattr(obj, 'before_dispatch', None)
+ if func:
+ response = func(self)
+ if response is not None:
+ break
+ else:
+ try:
+ response = self.dispatch()
+ except Exception, e:
+ # Execute handle_exception middleware.
+ for obj in reversed(middleware):
+ func = getattr(obj, 'handle_exception', None)
+ if func:
+ response = func(self, e)
+ if response is not None:
+ break
+ else:
+ # If a middleware didn't return a response, reraise.
+ raise
+
+ # Execute after_dispatch middleware.
+ for obj in reversed(middleware):
+ func = getattr(obj, 'after_dispatch', None)
+ if func:
+ response = func(self, response)
+
+ # Done!
+ return response
=======================================
--- /dev/null
+++ /tipfy/local.py Wed Mar 30 05:46:53 2011
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""
+ tipfy.local
+ ~~~~~~~~~~~
+
+ Context-local utilities.
+
+ :copyright: 2010 by tipfy.org.
+ :license: BSD, see LICENSE.txt for more details.
+"""
+import werkzeug.local
+
+#: Context-local.
+local = werkzeug.local.Local()
+#: A proxy to the active handler for a request. This is intended to be
used by
+#: functions called out of a handler context. Usage is generally
discouraged:
+#: it is preferable to pass the handler as argument when possible and only
use
+#: this as last alternative -- when a proxy is really needed.
+#:
+#: For example, the :func:`tipfy.utils.url_for` function requires the
current
+#: request to generate a URL. As its purpose is to be assigned to a
template
+#: context or other objects shared between requests, we use
`current_handler`
+#: there to dynamically get the currently active handler.
+current_handler = local('current_handler')
+#: Same as current_handler, only for the active WSGI app.
+current_app = local('current_app')
=======================================
--- /tipfy/__init__.py Tue Feb 15 00:10:07 2011
+++ /tipfy/__init__.py Wed Mar 30 05:46:53 2011
@@ -45,8 +45,10 @@
'enable_debugger': True,
}
-from tipfy.app import (HTTPException, Request, RequestHandler, Response,
Tipfy,
- abort, current_app, current_handler, APPENGINE, APPLICATION_ID,
- CURRENT_VERSION_ID, DEV_APPSERVER)
+from tipfy.app import (HTTPException, Request, Response, Tipfy, abort,
+ current_app, current_handler)
+from tipfy.handler import RequestHandler
+from tipfy.appengine import (APPENGINE, APPLICATION_ID, CURRENT_VERSION_ID,
+ DEV_APPSERVER)
from tipfy.config import DEFAULT_VALUE, REQUIRED_VALUE
from tipfy.routing import HandlerPrefix, NamePrefix, Rule, Subdomain,
Submount
=======================================
--- /tipfy/app.py Wed Mar 30 04:47:00 2011
+++ /tipfy/app.py Wed Mar 30 05:46:53 2011
@@ -11,315 +11,32 @@
import logging
import os
import urlparse
-from wsgiref.handlers import CGIHandler
+import wsgiref.handlers
# Werkzeug Swiss knife.
# Need to import werkzeug first otherwise py_zipimport fails.
import werkzeug
-from werkzeug import (Local, cached_property, import_string,
- redirect as base_redirect)
-from werkzeug.exceptions import HTTPException, InternalServerError, abort
-from werkzeug.wrappers import (BaseResponse, Request as WerkzeugRequest,
- Response as WerkzeugResponse)
-
-#: Context-local.
-local = Local()
-#: A proxy to the active handler for a request. This is intended to be
used by
-#: functions called out of a handler context. Usage is generally
discouraged:
-#: it is preferable to pass the handler as argument when possible and only
use
-#: this as last alternative -- when a proxy is really needed.
-#:
-#: For example, the :func:`tipfy.utils.url_for` function requires the
current
-#: request to generate a URL. As its purpose is to be assigned to a
template
-#: context or other objects shared between requests, we use
`current_handler`
-#: there to dynamically get the currently active handler.
-current_handler = local('current_handler')
-#: Same as current_handler, only for the active WSGI app.
-current_app = local('current_app')
-
-from tipfy import default_config
-from tipfy.config import Config, REQUIRED_VALUE
-from tipfy.routing import Router, Rule
-from tipfy.utils import json_decode
-
-__all__ = [
- 'HTTPException', 'Request', 'RequestHandler', 'Response', 'Tipfy',
- 'current_handler', 'APPENGINE', 'APPLICATION_ID', 'CURRENT_VERSION_ID',
- 'DEV_APPSERVER',
-]
-
-# App Engine flags.
-SERVER_SOFTWARE = os.environ.get('SERVER_SOFTWARE', '')
-#: The application ID as defined in *app.yaml*.
-APPLICATION_ID = os.environ.get('APPLICATION_ID')
-#: The deployed version ID. Always '1' when using the dev server.
-CURRENT_VERSION_ID = os.environ.get('CURRENT_VERSION_ID', '1')
-#: True if the app is using App Engine dev server, False otherwise.
-DEV_APPSERVER = SERVER_SOFTWARE.startswith('Development')
-#: True if the app is running on App Engine, False otherwise.
-APPENGINE = (APPLICATION_ID is not None and (DEV_APPSERVER or
- SERVER_SOFTWARE.startswith('Google App Engine')))
-
-
-class BaseRequestHandler(object):
- """Base class to handle requests. This is the central piece for an
- application and provides access to the current WSGI app and request.
- Additionally it provides lazy access to auth, i18n and session stores,
- and several utilities to handle a request.
-
- Although it is convenient to extend this class
(or :class:`RequestHandler`)
- and some extended functionality like sessions is implemented on top of
it,
- the only required interface by the WSGI app is the following:
-
- class RequestHandler(object):
- def __init__(self, app, request):
- pass
-
- def __call__(self):
- return Response()
-
- A Tipfy-compatible handler can be implemented using only these two
methods.
- """
- def __init__(self, app, request):
- """Initializes the handler.
-
- :param app:
- A :class:`Tipfy` instance.
- :param request:
- A :class:`Request` instance.
- """
- self.app = app
- self.request = request
- # A context for shared data, e.g., template variables.
- self.context = {}
-
- def __call__(self):
- """Executes a handler method. This is called by :class:`Tipfy` and
- must return a :attr:`response_class` object. If :attr:`middleware`
are
- defined, use their hooks to process the request or handle
exceptions.
-
- :returns:
- A :attr:`response_class` instance.
- """
- return self.dispatch()
-
- def dispatch(self):
- try:
- request = self.request
- method_name = request.rule and request.rule.handler_method
- if not method_name:
- method_name = request.method.lower()
-
- method = getattr(self, method_name, None)
- if not method:
- # 405 Method Not Allowed.
- # The response MUST include an Allow header containing a
- # list of valid methods for the requested resource.
- #
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6
- self.abort(405, valid_methods=self.get_valid_methods())
-
- return self.make_response(method(**request.rule_args))
- except Exception, e:
- return self.handle_exception(exception=e)
-
- @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)
-
- @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)
-
- @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()
-
- @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 abort(self, code, *args, **kwargs):
- """Raises an :class:`HTTPException`. This stops code execution,
- leaving the HTTP exception to be handled by an exception handler.
-
- :param code:
- HTTP status error code (e.g., 404).
- :param args:
- Positional arguments to be passed to the exception class.
- :param kwargs:
- Keyword arguments to be passed to the exception class.
- """
- abort(code, *args, **kwargs)
-
- def get_config(self, module, key=None, default=REQUIRED_VALUE):
- """Returns a configuration value for a module.
-
- .. seealso:: :meth:`Config.get_config`.
- """
- return self.app.config.get_config(module, key=key, default=default)
-
- def get_valid_methods(self):
- """Returns a list of methods supported by this handler. By default
it
- will look for HTTP methods this handler implements. For different
- routing schemes, override this.
-
- :returns:
- A list of methods supported by this handler.
- """
- return [method for method in self.app.allowed_methods if
- getattr(self, method.lower().replace('-', '_'), None)]
-
- def handle_exception(self, exception=None):
- """Handles an exception. The default behavior is to reraise the
- exception (no exception handling is implemented).
-
- :param exception:
- The exception that was raised.
- """
- raise
-
- def make_response(self, *rv):
- """Converts the returned value from a :class:`RequestHandler` to a
- response object that is an instance
of :attr:`Tipfy.response_class`.
-
- .. seealso:: :meth:`Tipfy.make_response`.
- """
- return self.app.make_response(self.request, *rv)
-
- def redirect(self, location, code=302, empty=False):
- """Returns a response object with headers set for redirection to
the
- given URI. This won't stop code execution, so you must return when
- calling this method::
-
- return self.redirect('/some-path')
-
- :param location:
- A relative or absolute URI (e.g., '../contacts'). If relative,
it
- will be joined to the current request URL.
- :param code:
- The HTTP status code for the redirect.
- :param empty:
- If True, returns a response without body. By default Werkzeug
sets
- a standard message in the body.
- :returns:
- A :class:`Response` object with headers set for redirection.
- """
- if not location.startswith('http'):
- # Make it absolute.
- location = urlparse.urljoin(self.request.url, location)
-
- response = base_redirect(location, code)
-
- if empty:
- response.data = ''
-
- return response
-
- def redirect_to(self, _name, _code=302, _empty=False, **kwargs):
- """Convenience method mixing ``werkzeug.redirect``
and :func:`url_for`:
- returns a response object with headers set for redirection to a URL
- built using a named :class:`Rule`.
-
- :param _name:
- The rule name.
- :param _code:
- The HTTP status code for the redirect.
- :param _empty:
- If True, returns a response without body. By default Werkzeug
sets
- a standard message in the body.
- :param kwargs:
- Keyword arguments to build the URL.
- :returns:
- A :class:`Response` object with headers set for redirection.
- """
- return self.redirect(self.url_for(_name, _full=kwargs.pop('_full',
- True), **kwargs), code=_code, empty=_empty)
-
- def url_for(self, _name, **kwargs):
- """Returns a URL for a named :class:`Rule`.
-
- .. seealso:: :meth:`Router.url_for`.
- """
- return self.app.router.url_for(self.request, _name, kwargs)
-
-
-class RequestHandler(BaseRequestHandler):
- #: A list of middleware instances. A middleware can implement three
- #: methods that are called before and after the current request method
- #: is executed, or if an exception occurs:
- #:
- #: before_dispatch(handler)
- #: Called before the requested method is executed. If returns a
- #: response, stops the middleware chain and uses that response, not
- #: calling the requested method.
- #:
- #: after_dispatch(handler, response)
- #: Called after the requested method is executed. Must always
return
- #: a response. These are executed in reverse order.
- #:
- #: handle_exception(handler, exception)
- #: Called if an exception occurs while executing the requested
method.
- #: These are executed in reverse order.
- middleware = None
-
- def __call__(self):
- middleware = self.middleware or []
-
- # Execute before_dispatch middleware.
- for obj in middleware:
- func = getattr(obj, 'before_dispatch', None)
- if func:
- response = func(self)
- if response is not None:
- break
- else:
- try:
- response = self.dispatch()
- except Exception, e:
- # Execute handle_exception middleware.
- for obj in reversed(middleware):
- func = getattr(obj, 'handle_exception', None)
- if func:
- response = func(self, e)
- if response is not None:
- break
- else:
- # If a middleware didn't return a response, reraise.
- raise
-
- # Execute after_dispatch middleware.
- for obj in reversed(middleware):
- func = getattr(obj, 'after_dispatch', None)
- if func:
- response = func(self, response)
-
- # Done!
- return response
-
-
-class Request(WerkzeugRequest):
+import werkzeug.exceptions
+import werkzeug.urls
+import werkzeug.utils
+import werkzeug.wrappers
+
+from . import default_config
+from .config import Config, REQUIRED_VALUE
+from .local import current_app, current_handler, local
+from .json import json_decode
+from .routing import Router, Rule
+
+#: Public interface.
+HTTPException = werkzeug.exceptions.HTTPException
+abort = werkzeug.exceptions.abort
+
+#: TODO: remove from here.
+from tipfy.appengine import (APPENGINE, APPLICATION_ID, CURRENT_VERSION_ID,
+ DEV_APPSERVER)
+
+
+class Request(werkzeug.wrappers.Request):
"""Provides all environment variables for the current request: GET,
POST,
FILES, cookies and headers.
"""
@@ -332,7 +49,7 @@
#: Keyword arguments from the matched rule.
rule_args = None
- @cached_property
+ @werkzeug.utils.cached_property
def json(self):
"""If the mimetype is `application/json` this will contain the
parsed JSON data.
@@ -346,7 +63,7 @@
return json_decode(self.data)
-class Response(WerkzeugResponse):
+class Response(werkzeug.wrappers.Response):
"""A response object with default mimetype set to ``text/html``."""
default_mimetype = 'text/html'
@@ -437,7 +154,8 @@
# We only log unhandled non-HTTP exceptions. Users should
# take care of logging in custom error handlers.
logging.exception(e)
- response = self.make_response(request,
InternalServerError())
+ rv = werkzeug.exceptions.InternalServerError()
+ response = self.make_response(request, rv)
finally:
if cleanup:
local.__release_local__()
@@ -490,9 +208,10 @@
raise
rv = handler(request.app, request)
- if not isinstance(rv, BaseResponse) and hasattr(rv, '__call__'):
- # If it is a callable but not a response, we call it again.
- rv = rv()
+ 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
@@ -585,37 +304,80 @@
main()
"""
- CGIHandler().run(self)
-
- @cached_property
+ 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.wsgi_app, evalex=True)
- @cached_property
+ @werkzeug.utils.cached_property
def auth_store_class(self):
"""Returns the configured auth store class.
:returns:
An auth store class.
"""
- return import_string(self.config['tipfy']['auth_store_class'])
-
- @cached_property
+ 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.
"""
- return import_string(self.config['tipfy']['i18n_store_class'])
-
- @cached_property
+ 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.
"""
- return import_string(self.config['tipfy']['session_store_class'])
+ cls = self.config['tipfy']['session_store_class']
+ return werkzeug.utils.import_string(cls)
+
+
+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'
+ # not yet.
+ #if location.startswith(('.', '/')):
+ # 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
=======================================
--- /tipfy/appengine/__init__.py Thu Oct 7 10:52:28 2010
+++ /tipfy/appengine/__init__.py Wed Mar 30 05:46:53 2011
@@ -1,1 +1,13 @@
-
+import os
+
+# App Engine flags.
+SERVER_SOFTWARE = os.environ.get('SERVER_SOFTWARE', '')
+#: The application ID as defined in *app.yaml*.
+APPLICATION_ID = os.environ.get('APPLICATION_ID')
+#: The deployed version ID. Always '1' when using the dev server.
+CURRENT_VERSION_ID = os.environ.get('CURRENT_VERSION_ID', '1')
+#: True if the app is using App Engine dev server, False otherwise.
+DEV_APPSERVER = SERVER_SOFTWARE.startswith('Development')
+#: True if the app is running on App Engine, False otherwise.
+APPENGINE = (APPLICATION_ID is not None and (DEV_APPSERVER or
+ SERVER_SOFTWARE.startswith('Google App Engine')))
=======================================
--- /tipfy/routing.py Wed Mar 30 04:47:00 2011
+++ /tipfy/routing.py Wed Mar 30 05:46:53 2011
@@ -13,11 +13,7 @@
Rule as BaseRule, RuleFactory, Subdomain, Submount)
from werkzeug.wrappers import BaseResponse
-from tipfy.app import local
-
-__all__ = [
- 'HandlerPrefix', 'NamePrefix', 'Rule', 'Subdomain', 'Submount',
-]
+from .local import local
class Router(object):