base.py |
|
---|---|
The base extension providing request, response, and core views. |
|
Imports |
from __future__ import unicode_literals
from io import IOBase
try:
IOBase = (IOBase, file)
except:
pass
try:
from collections import Generator
except ImportError:
|
def _tmp(): yield None # pragma: no cover
Generator = type(_tmp())
from os.path import getmtime
from time import mktime, gmtime
from datetime import datetime
from mimetypes import init, add_type, guess_type
from webob import Request, Response
from web.core.compat import str, unicode
from web.core.util import safe_name
|
|
Module Globals |
log = __import__('logging').getLogger(__name__)
|
Extension |
|
Base framework extension. This extension is not meant to be manually constructed or manipulated; use is automatic. |
class BaseExtension(object):
first = True # Must occur as early as possible in callback lists.
always = True # Always enabled.
provides = ["base", "request", "response"] # Export these symbols for use as other extension's dependencies.
|
Application-Level Callbacks |
|
def start(self, context):
if __debug__:
log.debug("Registering core return value handlers.")
|
|
This prepares the mimetypes registry, and adds values typically missing from it. |
init()
add_type('text/x-yaml', 'yml')
add_type('text/x-yaml', 'yaml')
|
Register the core views supported by the base framework. |
register = context.view.register
register(type(None), self.render_none)
register(Response, self.render_response)
register(str, self.render_binary)
register(unicode, self.render_text)
register(IOBase, self.render_file)
register(Generator, self.render_generator)
|
Request-Level Callbacks |
|
Add the usual suspects to the context. This adds |
def prepare(self, context):
if __debug__:
log.debug("Preparing request context.", extra=dict(request=id(context)))
|
Bridge in WebOb |
context.request = Request(context.environ)
context.response = Response(request=context.request)
|
Record the initial path representing the point where a front-end web server bridged to us. |
context.environ['web.base'] = context.request.script_name
|
Track the remaining (unprocessed) path elements. |
context.request.remainder = context.request.path_info.split('/')
if context.request.remainder and not context.request.remainder[0]:
del context.request.remainder[0]
|
Track the "breadcrumb list" of dispatch through distinct controllers. |
context.path = []
|
Called as dispatch descends into a tier. The base extension uses this to maintain the "current url". |
def dispatch(self, context, consumed, handler, is_endpoint):
request = context.request
if __debug__:
log.debug("Handling dispatch event.", extra=dict(
request = id(context),
consumed = consumed,
handler = safe_name(handler),
endpoint = is_endpoint
))
|
The leading path element (leading slash) requires special treatment. |
if not consumed and context.request.path_info_peek() == '':
consumed = ['']
if consumed:
|
Migrate path elements consumed from the |
if not isinstance(consumed, (list, tuple)):
consumed = consumed.split('/')
for element in consumed:
if element == context.request.path_info_peek():
context.request.path_info_pop()
|
Update the breadcrumb list. |
context.path.append((handler, request.script_name))
if consumed: # Lastly, update the remaining path element list.
request.remainder = request.remainder[len(consumed):]
|
Views |
|
Render empty responses. |
def render_none(self, context, result):
context.response.length = 0
context.response.body = b''
return True
|
Allow direct returning of WebOb |
def render_response(self, context, result):
context.response = result
return True
|
Return binary responses unmodified. |
def render_binary(self, context, result):
context.response.app_iter = iter((result, )) # This wraps the binary string in a WSGI body iterable.
return True
|
Return textual responses, encoding as needed. |
def render_text(self, context, result):
context.response.text = result
return True
|
Perform appropriate metadata wrangling for returned open file handles. |
def render_file(self, context, result):
if __debug__:
log.debug("Processing file-like object.", extra=dict(request=id(context), result=repr(result)))
response = context.response = Response(
conditional_response = True,
)
modified = mktime(gmtime(getmtime(result.name)))
response.last_modified = datetime.fromtimestamp(modified)
response.cache_control = 'public'
ct, ce = guess_type(result.name)
if not ct: ct = 'application/octet-stream'
response.content_type, response.content_encoding = ct, ce
response.etag = unicode(modified)
result.seek(0, 2) # Seek to the end of the file.
response.content_length = result.tell()
result.seek(0) # Seek back to the start of the file.
response.body_file = result
return True
|
Attempt to serve generator responses through stream encoding. This allows for direct use of cinje template functions, which are generators, as returned views. |
def render_generator(self, context, result):
context.response.encoding = 'utf8'
context.response.app_iter = (
(i.encode('utf8') if isinstance(i, unicode) else i) # Stream encode unicode chunks.
for i in result if i is not None # Skip None values.
)
return True
|