Add a Twisted Web-based renderer and start using it on Python 3

This could be the thing that eventually replaces the Nevow-based testing
renderer on Python 2 as well.
This commit is contained in:
Jean-Paul Calderone 2020-10-18 11:00:57 -04:00
parent 84acf4e50f
commit e710fd883a
3 changed files with 114 additions and 42 deletions

View File

@ -0,0 +1,46 @@
"""
General helpers related to Nevow.
"""
from nevow.context import WebContext
from nevow.testutil import FakeRequest
from nevow.appserver import (
processingFailed,
DefaultExceptionHandler,
)
from nevow.inevow import (
ICanHandleException,
IRequest,
IResource as INevowResource,
IData,
)
def render(resource, query_args):
"""
Render (in the manner of the Nevow appserver) a Nevow ``Page`` or a
Twisted ``Resource`` against a request with the given query arguments .
:param resource: The page or resource to render.
:param query_args: The query arguments to put into the request being
rendered. A mapping from ``bytes`` to ``list`` of ``bytes``.
:return Deferred: A Deferred that fires with the rendered response body as
``bytes``.
"""
ctx = WebContext(tag=resource)
req = FakeRequest(args=query_args)
ctx.remember(DefaultExceptionHandler(), ICanHandleException)
ctx.remember(req, IRequest)
ctx.remember(None, IData)
def maybe_concat(res):
if isinstance(res, bytes):
return req.v + res
return req.v
resource = INevowResource(resource)
d = maybeDeferred(resource.renderHTTP, ctx)
d.addErrback(processingFailed, req, ctx)
d.addCallback(maybe_concat)
return d

View File

@ -0,0 +1,54 @@
from zope.interface import (
classImplements,
)
from twisted.python.reflect import (
fullyQualifiedName,
)
from twisted.internet.defer import (
succeed,
)
from twisted.web.test.requesthelper import (
DummyRequest,
)
from twisted.web.iweb import (
IRequest,
)
from twisted.web.server import (
NOT_DONE_YET,
)
classImplements(DummyRequest, IRequest)
def render(resource, query_args):
"""
Render (in the manner of the Twisted Web Site) a Twisted ``Resource``
against a request with the given query arguments .
:param resource: The page or resource to render.
:param query_args: The query arguments to put into the request being
rendered. A mapping from ``bytes`` to ``list`` of ``bytes``.
:return Deferred: A Deferred that fires with the rendered response body as
``bytes``.
"""
request = DummyRequest([])
request.args = query_args
result = resource.render(request)
if isinstance(result, bytes):
request.write(result)
done = succeed(None)
elif result == NOT_DONE_YET:
if request.finished:
done = succeed(None)
else:
done = request.notifyFinish()
else:
raise ValueError(
"{!r} returned {!r}, required bytes or NOT_DONE_YET.".format(
fullyQualifiedName(resource.render),
result,
),
)
done.addCallback(lambda ignored: b"".join(request.written))
return done

View File

@ -1,3 +1,4 @@
from future.utils import PY2
import treq
from twisted.internet.defer import (
@ -7,19 +8,6 @@ from twisted.internet.defer import (
)
from twisted.web.error import Error
from nevow.context import WebContext
from nevow.testutil import FakeRequest
from nevow.appserver import (
processingFailed,
DefaultExceptionHandler,
)
from nevow.inevow import (
ICanHandleException,
IRequest,
IResource as INevowResource,
IData,
)
@inlineCallbacks
def do_http(method, url, **kwargs):
response = yield treq.request(method, url, persistent=False, **kwargs)
@ -31,32 +19,16 @@ def do_http(method, url, **kwargs):
returnValue(body)
def render(resource, query_args):
"""
Render (in the manner of the Nevow appserver) a Nevow ``Page`` or a
Twisted ``Resource`` against a request with the given query arguments .
:param resource: The page or resource to render.
:param query_args: The query arguments to put into the request being
rendered. A mapping from ``bytes`` to ``list`` of ``bytes``.
:return Deferred: A Deferred that fires with the rendered response body as
``bytes``.
"""
ctx = WebContext(tag=resource)
req = FakeRequest(args=query_args)
ctx.remember(DefaultExceptionHandler(), ICanHandleException)
ctx.remember(req, IRequest)
ctx.remember(None, IData)
def maybe_concat(res):
if isinstance(res, bytes):
return req.v + res
return req.v
resource = INevowResource(resource)
d = maybeDeferred(resource.renderHTTP, ctx)
d.addErrback(processingFailed, req, ctx)
d.addCallback(maybe_concat)
return d
if PY2:
# We can only use Nevow on Python 2 and Tahoe-LAFS still *does* use Nevow
# so prefer the Nevow-based renderer if we can get it.
from .common_nevow import (
render,
)
else:
# However, Tahoe-LAFS *will* use Twisted Web before too much longer so go
# ahead and let some tests run against the Twisted Web-based renderer on
# Python 3. Later this will become the only codepath.
from .common_tweb import (
render,
)