123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- # -*- 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)
- 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"]
|