Viewing file: util.py (12.14 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- test-case-name: twisted.web.test.test_util -*- # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details.
""" An assortment of web server-related utilities. """
from __future__ import division, absolute_import
import linecache
from twisted.python import urlpath from twisted.python.compat import _PY3, unicode, nativeString, escape from twisted.python.reflect import fullyQualifiedName
from twisted.web import resource
from twisted.web.template import TagLoader, XMLString, Element, renderer from twisted.web.template import flattenString
def _PRE(text): """ Wraps <pre> tags around some text and HTML-escape it.
This is here since once twisted.web.html was deprecated it was hard to migrate the html.PRE from current code to twisted.web.template.
For new code consider using twisted.web.template.
@return: Escaped text wrapped in <pre> tags. @rtype: C{str} """ return '<pre>%s</pre>' % (escape(text),)
def redirectTo(URL, request): """ Generate a redirect to the given location.
@param URL: A L{bytes} giving the location to which to redirect. @type URL: L{bytes}
@param request: The request object to use to generate the redirect. @type request: L{IRequest<twisted.web.iweb.IRequest>} provider
@raise TypeError: If the type of C{URL} a L{unicode} instead of L{bytes}.
@return: A C{bytes} containing HTML which tries to convince the client agent to visit the new location even if it doesn't respect the I{FOUND} response code. This is intended to be returned from a render method, eg::
def render_GET(self, request): return redirectTo(b"http://example.com/", request) """ if isinstance(URL, unicode) : raise TypeError("Unicode object not allowed as URL") request.setHeader(b"Content-Type", b"text/html; charset=utf-8") request.redirect(URL) content = """ <html> <head> <meta http-equiv=\"refresh\" content=\"0;URL=%(url)s\"> </head> <body bgcolor=\"#FFFFFF\" text=\"#000000\"> <a href=\"%(url)s\">click here</a> </body> </html> """ % {'url': nativeString(URL)} if _PY3: content = content.encode("utf8") return content
class Redirect(resource.Resource): isLeaf = True
def __init__(self, url): resource.Resource.__init__(self) self.url = url
def render(self, request): return redirectTo(self.url, request)
def getChild(self, name, request): return self
class ChildRedirector(Redirect): isLeaf = 0 def __init__(self, url): # XXX is this enough? if ((url.find('://') == -1) and (not url.startswith('..')) and (not url.startswith('/'))): raise ValueError("It seems you've given me a redirect (%s) that is a child of myself! That's not good, it'll cause an infinite redirect." % url) Redirect.__init__(self, url)
def getChild(self, name, request): newUrl = self.url if not newUrl.endswith('/'): newUrl += '/' newUrl += name return ChildRedirector(newUrl)
class ParentRedirect(resource.Resource): """ I redirect to URLPath.here(). """ isLeaf = 1 def render(self, request): return redirectTo(urlpath.URLPath.fromRequest(request).here(), request)
def getChild(self, request): return self
class DeferredResource(resource.Resource): """ I wrap up a Deferred that will eventually result in a Resource object. """ isLeaf = 1
def __init__(self, d): resource.Resource.__init__(self) self.d = d
def getChild(self, name, request): return self
def render(self, request): self.d.addCallback(self._cbChild, request).addErrback( self._ebChild,request) from twisted.web.server import NOT_DONE_YET return NOT_DONE_YET
def _cbChild(self, child, request): request.render(resource.getChildForRequest(child, request))
def _ebChild(self, reason, request): request.processingFailed(reason) return reason
class _SourceLineElement(Element): """ L{_SourceLineElement} is an L{IRenderable} which can render a single line of source code.
@ivar number: A C{int} giving the line number of the source code to be rendered. @ivar source: A C{str} giving the source code to be rendered. """ def __init__(self, loader, number, source): Element.__init__(self, loader) self.number = number self.source = source
@renderer def sourceLine(self, request, tag): """ Render the line of source as a child of C{tag}. """ return tag(self.source.replace(' ', u' \N{NO-BREAK SPACE}'))
@renderer def lineNumber(self, request, tag): """ Render the line number as a child of C{tag}. """ return tag(str(self.number))
class _SourceFragmentElement(Element): """ L{_SourceFragmentElement} is an L{IRenderable} which can render several lines of source code near the line number of a particular frame object.
@ivar frame: A L{Failure<twisted.python.failure.Failure>}-style frame object for which to load a source line to render. This is really a tuple holding some information from a frame object. See L{Failure.frames<twisted.python.failure.Failure>} for specifics. """ def __init__(self, loader, frame): Element.__init__(self, loader) self.frame = frame
def _getSourceLines(self): """ Find the source line references by C{self.frame} and yield, in source line order, it and the previous and following lines.
@return: A generator which yields two-tuples. Each tuple gives a source line number and the contents of that source line. """ filename = self.frame[1] lineNumber = self.frame[2] for snipLineNumber in range(lineNumber - 1, lineNumber + 2): yield (snipLineNumber, linecache.getline(filename, snipLineNumber).rstrip())
@renderer def sourceLines(self, request, tag): """ Render the source line indicated by C{self.frame} and several surrounding lines. The active line will be given a I{class} of C{"snippetHighlightLine"}. Other lines will be given a I{class} of C{"snippetLine"}. """ for (lineNumber, sourceLine) in self._getSourceLines(): newTag = tag.clone() if lineNumber == self.frame[2]: cssClass = "snippetHighlightLine" else: cssClass = "snippetLine" loader = TagLoader(newTag(**{"class": cssClass})) yield _SourceLineElement(loader, lineNumber, sourceLine)
class _FrameElement(Element): """ L{_FrameElement} is an L{IRenderable} which can render details about one frame from a L{Failure<twisted.python.failure.Failure>}.
@ivar frame: A L{Failure<twisted.python.failure.Failure>}-style frame object for which to load a source line to render. This is really a tuple holding some information from a frame object. See L{Failure.frames<twisted.python.failure.Failure>} for specifics. """ def __init__(self, loader, frame): Element.__init__(self, loader) self.frame = frame
@renderer def filename(self, request, tag): """ Render the name of the file this frame references as a child of C{tag}. """ return tag(self.frame[1])
@renderer def lineNumber(self, request, tag): """ Render the source line number this frame references as a child of C{tag}. """ return tag(str(self.frame[2]))
@renderer def function(self, request, tag): """ Render the function name this frame references as a child of C{tag}. """ return tag(self.frame[0])
@renderer def source(self, request, tag): """ Render the source code surrounding the line this frame references, replacing C{tag}. """ return _SourceFragmentElement(TagLoader(tag), self.frame)
class _StackElement(Element): """ L{_StackElement} renders an L{IRenderable} which can render a list of frames. """ def __init__(self, loader, stackFrames): Element.__init__(self, loader) self.stackFrames = stackFrames
@renderer def frames(self, request, tag): """ Render the list of frames in this L{_StackElement}, replacing C{tag}. """ return [ _FrameElement(TagLoader(tag.clone()), frame) for frame in self.stackFrames]
class FailureElement(Element): """ L{FailureElement} is an L{IRenderable} which can render detailed information about a L{Failure<twisted.python.failure.Failure>}.
@ivar failure: The L{Failure<twisted.python.failure.Failure>} instance which will be rendered.
@since: 12.1 """ loader = XMLString(""" <div xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1"> <style type="text/css"> div.error { color: red; font-family: Verdana, Arial, helvetica, sans-serif; font-weight: bold; }
div { font-family: Verdana, Arial, helvetica, sans-serif; }
div.stackTrace { }
div.frame { padding: 1em; background: white; border-bottom: thin black dashed; }
div.frame:first-child { padding: 1em; background: white; border-top: thin black dashed; border-bottom: thin black dashed; }
div.location { }
span.function { font-weight: bold; font-family: "Courier New", courier, monospace; }
div.snippet { margin-bottom: 0.5em; margin-left: 1em; background: #FFFFDD; }
div.snippetHighlightLine { color: red; }
span.code { font-family: "Courier New", courier, monospace; } </style>
<div class="error"> <span t:render="type" />: <span t:render="value" /> </div> <div class="stackTrace" t:render="traceback"> <div class="frame" t:render="frames"> <div class="location"> <span t:render="filename" />:<span t:render="lineNumber" /> in <span class="function" t:render="function" /> </div> <div class="snippet" t:render="source"> <div t:render="sourceLines"> <span class="lineno" t:render="lineNumber" /> <code class="code" t:render="sourceLine" /> </div> </div> </div> </div> <div class="error"> <span t:render="type" />: <span t:render="value" /> </div> </div> """)
def __init__(self, failure, loader=None): Element.__init__(self, loader) self.failure = failure
@renderer def type(self, request, tag): """ Render the exception type as a child of C{tag}. """ return tag(fullyQualifiedName(self.failure.type))
@renderer def value(self, request, tag): """ Render the exception value as a child of C{tag}. """ return tag(unicode(self.failure.value).encode('utf8'))
@renderer def traceback(self, request, tag): """ Render all the frames in the wrapped L{Failure<twisted.python.failure.Failure>}'s traceback stack, replacing C{tag}. """ return _StackElement(TagLoader(tag), self.failure.frames)
def formatFailure(myFailure): """ Construct an HTML representation of the given failure.
Consider using L{FailureElement} instead.
@type myFailure: L{Failure<twisted.python.failure.Failure>}
@rtype: C{bytes} @return: A string containing the HTML representation of the given failure. """ result = [] flattenString(None, FailureElement(myFailure)).addBoth(result.append) if isinstance(result[0], bytes): # Ensure the result string is all ASCII, for compatibility with the # default encoding expected by browsers. return result[0].decode('utf-8').encode('ascii', 'xmlcharrefreplace') result[0].raiseException()
__all__ = [ "redirectTo", "Redirect", "ChildRedirector", "ParentRedirect", "DeferredResource", "FailureElement", "formatFailure"]
|