def test_routes(self): """ L{getRoutes} returns all the defined routes and their attributes. """ app = Klein() def f(): pass f.attr = "attr" def g(): pass g.attr = "G" app.route(b"/", methods=[b"GET"])(f) app.route(b"/g", methods=[b"PUT", b"OPTIONS"])(g) routes = sorted(getRoutes(app)) self.assertEqual(routes, [ KleinRoute(methods={b"GET"}, path=b"/", endpoint="f", attributes={'attr': 'attr'}), KleinRoute(methods={b'PUT', b'OPTIONS'}, path=b'/g', endpoint='g', attributes={'attr': 'G'}) ])
def test_private_not_visible(self): """ When an endpoint is decorated with ``@private_api`` it is omitted from result of ``makeRst``. """ app = Klein() @private_api def g(): pass app.route(b"/g", methods=[b"GET"])(g) rest = list(makeRst(b"/", "section", app, None, {})) self.assertEqual(rest, [])
def simpleSessionRouter(): # type: () -> Tuple[sessions, errors, str, str, StubTreq] """ Construct a simple router. """ sessions = [] exceptions = [] mss = MemorySessionStore.fromAuthorizers([memoryAuthorizer]) router = Klein() token = "X-Test-Session-Token" cookie = "X-Test-Session-Cookie" sproc = SessionProcurer( mss, secureTokenHeader=b"X-Test-Session-Token", secureCookie=b"X-Test-Session-Cookie", ) @router.route("/") @inlineCallbacks def route(request): # type: (IRequest) -> Deferred try: sessions.append((yield sproc.procureSession(request))) except NoSuchSession as nss: exceptions.append(nss) returnValue(b"ok") requirer = Requirer() @requirer.prerequisite([ISession]) def procure(request): # type: (IRequest) -> Deferred return sproc.procureSession(request) @requirer.require(router.route("/test"), simple=Authorization(ISimpleTest)) def testRoute(simple): # type: (SimpleTest) -> str return "ok: " + str(simple.doTest() + 4) @requirer.require(router.route("/denied"), nope=Authorization(IDenyMe)) def testDenied(nope): # type: (IDenyMe) -> str return "bad" treq = StubTreq(router.resource()) return sessions, exceptions, token, cookie, treq
def test_routes(self): """ L{getRoutes} returns all the defined routes and their attributes. """ app = Klein() def f(): pass f.attr = "attr" def g(): pass g.attr = "G" app.route(b"/", methods=[b"GET"])(f) app.route(b"/g", methods=[b"PUT", b"OPTIONS"])(g) routes = sorted(getRoutes(app)) self.assertEqual(routes, [ KleinRoute(methods={b"GET"}, path=b"/", endpoint="f", attributes={'attr': 'attr'}), KleinRoute(methods={b'PUT', b'OPTIONS'}, path=b'/g', endpoint='g', attributes={'attr': 'G'})])
def simpleFormRouter() -> Tuple[Klein, List[Tuple[str, int]]]: """ Create a simple router hooked up to a field handler. """ router = Klein() requirer = Requirer() calls = [] @requirer.require( router.route("/getme", methods=["GET"]), name=Field.text(), value=Field.number(), custom=Field(formInputType="number", converter=int, required=False), ) def justGet(name: str, value: int, custom: int) -> bytes: calls.append((name, value or custom)) return b"got" return router, calls
def simpleFormRouter(): # type: () -> Tuple[Klein, List[Tuple[str, int]]] """ Create a simple router hooked up to a field handler. """ router = Klein() requirer = Requirer() calls = [] @requirer.require( router.route("/getme", methods=['GET']), name=Field.text(), value=Field.number(), custom=Field(formInputType='number', converter=int, required=False), ) def justGet(name, value, custom): # type: (str, int, int) -> bytes calls.append((name, value or custom)) return b'got' return router, calls
requirer = Requirer() @requirer.prerequisite([ISession]) def procurer(request): return SessionProcurer(sessions).procureSession(request) style = Plating(tags=tags.html(tags.head(tags.title("yay")), tags.body(tags.div(slot(Plating.CONTENT))))) @requirer.require( style.routed( app.route("/", methods=["POST"]), tags.h1("u did it: ", slot("an-form-arg")), ), foo=Field.number(minimum=3, maximum=10), bar=Field.text(), ) def postHandler(foo, bar): return {"an-form-arg": foo} @requirer.require( style.routed(app.route("/", methods=["GET"]), tags.div(slot("anForm"))), theForm=Form.rendererFor(postHandler, action="/?post=yes"), ) def formRenderer(theForm): return {"anForm": theForm}
api, "execute", capability_version, capability_hash, '') links.append({"href": capability_url, "rel": "capability"}) return links else: return url def transaction_id(request): """ Extract the transaction id from the given request. :param IRequest request: The request we are trying to get the transaction id for. :returns: A string transaction id. """ return request.responseHeaders.getRawHeaders('X-Response-Id')[0] app = Klein() app.route = partial(app.route, strict_slashes=False) root = Resource() root.putChild('v1.0', app.resource()) root.putChild('', Data('', 'text/plain'))
""" def getAllRawHeaders(self): # type: () -> Iterable[Tuple[bytes, List[bytes]]] """ Don't return a host header. """ for key, values in super(BadlyBehavedHeaders, self).getAllRawHeaders(): if key != b"Host": yield (key, values) router = Klein() requirer = Requirer() @requirer.require(router.route("/hello/world", methods=["GET"]), url=RequestURL()) def requiresURL(url): # type: (DecodedURL) -> Text """ This is a route that requires a URL. """ return url.child(u"hello/ world").asText() class ISample(Interface): """ Interface for testing. """
if capability_hash is not None: capability_url = append_segments(get_url_root(), api, "execute", capability_version, capability_hash, '') links.append({"href": capability_url, "rel": "capability"}) return links else: return url def transaction_id(request): """ Extract the transaction id from the given request. :param IRequest request: The request we are trying to get the transaction id for. :returns: A string transaction id. """ return request.responseHeaders.getRawHeaders('X-Response-Id')[0] app = Klein() app.route = partial(app.route, strict_slashes=False) root = Resource() root.putChild('v1.0', app.resource()) root.putChild('', Data('', 'text/plain'))
requirer = Requirer() @requirer.prerequisite([ISession]) def procurer(request): return SessionProcurer(sessions).procureSession(request) style = Plating(tags=tags.html( tags.head(tags.title("yay")), tags.body(tags.div(slot(Plating.CONTENT)))) ) @style.routed( requirer.require( app.route("/", methods=["POST"]), foo=Field.integer(minimum=3, maximum=10), bar=Field.text(), ), tags.h1('u did it: ', slot("an-form-arg")) ) def postHandler(foo, bar): return {"an-form-arg": foo} @requirer.require( style.routed( app.route("/", methods=["GET"]), tags.div(slot("anForm")) ), theForm=Form.rendererFor(postHandler, action=u"/?post=yes") ) def formRenderer(theForm):
class PlatingTests(TestCase): """ Tests for L{Plating}. """ def setUp(self): """ Create an app and a resource wrapping that app for this test. """ self.app = Klein() self.kr = self.app.resource() def get(self, uri): """ Issue a virtual GET request to the given path that is expected to succeed synchronously, and return the generated request object and written bytes. """ request = requestMock(uri) d = _render(self.kr, request) self.successResultOf(d) return request, request.getWrittenData() def test_template_html(self): """ Rendering a L{Plating.routed} decorated route results in templated HTML. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return {"ok": "test-data-present"} request, written = self.get(b"/") self.assertIn(b'<span>test-data-present</span>', written) self.assertIn(b'<title>default title unchanged</title>', written) def test_template_json(self): """ Rendering a L{Plating.routed} decorated route with a query parameter asking for JSON will yield JSON instead. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return {"ok": "an-plating-test"} request, written = self.get(b"/?json=true") self.assertEqual( request.responseHeaders.getRawHeaders(b'content-type')[0], b'text/json; charset=utf-8' ) self.assertEquals({"ok": "an-plating-test", "title": "default title unchanged"}, json.loads(written.decode('utf-8'))) def test_template_numbers(self): """ Data returned from a plated method may include numeric types (integers, floats, and possibly longs), which although they are not normally serializable by twisted.web.template, will be converted by plating into their decimal representation. """ @page.routed(self.app.route("/"), tags.div(tags.span(slot("anInteger")), tags.i(slot("anFloat")), tags.b(slot("anLong")), )) def plateMe(result): return {"anInteger": 7, "anFloat": 3.2, "anLong": 0x10000000000000001} request, written = self.get(b"/") self.assertIn(b"<span>7</span>", written) self.assertIn(b"<i>3.2</i>", written) self.assertIn(b"<b>18446744073709551617</b>", written) def test_render_list(self): """ The C{:list} renderer suffix will render the slot named by the renderer as a list, filling each slot. """ @page.routed(self.app.route("/"), tags.ul(tags.li(slot("item"), render="subplating:list"))) def rsrc(request): return {"subplating": [1, 2, 3]} request, written = self.get(b"/") self.assertIn(b'<ul><li>1</li><li>2</li><li>3</li></ul>', written) self.assertIn(b'<title>default title unchanged</title>', written) def test_widget_html(self): """ When L{Plating.widgeted} is applied as a decorator, it gives the decorated function a C{widget} attribute which is a version of the function with a modified return type that turns it into a renderable HTML sub-element that may fill a slot. """ @page.routed(self.app.route("/"), tags.div(slot("widget"))) def rsrc(request): return {"widget": enwidget.widget(3, 4)} request, written = self.get(b"/") self.assertIn(b"<span>a: 3</span>", written) self.assertIn(b"<span>b: 4</span>", written) def test_widget_json(self): """ When L{Plating.widgeted} is applied as a decorator, and the result is serialized to JSON, it appears the same as the returned value despite the HTML-friendly wrapping described above. """ @page.routed(self.app.route("/"), tags.div(slot("widget"))) def rsrc(request): return {"widget": enwidget.widget(3, 4)} request, written = self.get(b"/?json=1") self.assertEqual(json.loads(written.decode('utf-8')), {"widget": {"a": 3, "b": 4}, "title": "default title unchanged"}) def test_prime_directive_return(self): """ Nothing within these Articles Of Federation shall authorize the United Federation of Planets to alter the return value of a callable by applying a decorator to it... """ exact_result = {"ok": "some nonsense value"} @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return exact_result self.assertIdentical(plateMe(None), exact_result) def test_prime_directive_arguments(self): """ ... or shall require the function to modify its signature under these Articles Of Federation. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request, one, two, three): return (one, two, three) exact_one = {"one": "and"} exact_two = {"two": "and"} exact_three = {"three": "and"} result_one, result_two, result_three = plateMe( None, exact_one, exact_two, three=exact_three ) self.assertIdentical(result_one, exact_one) self.assertIdentical(result_two, exact_two) self.assertIdentical(result_three, exact_three) def test_presentation_only_json(self): """ Slots marked as "presentation only" will not be reflected in the output. """ plating = Plating(tags=tags.span(slot("title")), presentation_slots={"title"}) @plating.routed(self.app.route("/"), tags.span(slot("data"))) def justJson(request): return {"title": "uninteresting", "data": "interesting"} request, written = self.get(b"/?json=1") self.assertEqual(json.loads(written.decode("utf-8")), {"data": "interesting"}) def test_missing_renderer(self): """ Missing renderers will result in an exception during rendering. """ def test(missing): plating = Plating(tags=tags.span(slot(Plating.CONTENT))) @plating.routed(self.app.route("/"), tags.span(tags.span(render=missing))) def no(request): return {} self.get(b"/") [fe] = self.flushLoggedErrors(FlattenerError) self.assertIsInstance(fe.value.args[0], MissingRenderMethod) test("garbage") test("garbage:missing") def test_json_serialize_unknown_type(self): """ The JSON serializer will raise a L{TypeError} when it can't find an appropriate type. """ from klein._plating import json_serialize class reprish(object): def __repr__(self): return '<blub>' te = self.assertRaises(TypeError, json_serialize, {"an": reprish()}) self.assertIn("<blub>", str(te))
sessions = MemorySessionStore() requirer = Requirer() @requirer.prerequisite([ISession]) def procurer(request): return SessionProcurer(sessions).procureSession(request) style = Plating(tags=tags.html(tags.head(tags.title("yay")), tags.body(tags.div(slot(Plating.CONTENT))))) @requirer.require( style.routed(app.route("/", methods=["POST"]), tags.h1('u did it: ', slot("an-form-arg"))), foo=Field.number(minimum=3, maximum=10), bar=Field.text(), ) def postHandler(foo, bar): return {"an-form-arg": foo} @requirer.require(style.routed(app.route("/", methods=["GET"]), tags.div(slot("anForm"))), theForm=Form.rendererFor(postHandler, action=u"/?post=yes")) def formRenderer(theForm): return {"anForm": theForm}
from twisted.web.template import tags, slot from klein import Klein, Plating app = Klein() myStyle = Plating( tags=tags.html( tags.head(tags.title(slot("pageTitle"))), tags.body(tags.h1(slot("pageTitle"), Class="titleHeading"), tags.div(slot(Plating.CONTENT))) ), defaults={"pageTitle": "Places & Foods"} ) @myStyle.routed( app.route("/"), tags.div( tags.h2("Sample Places:"), tags.ul([tags.li(tags.a(href=["/places/", place])(place)) for place in ["new york", "san francisco", "shanghai"]]), tags.h2("Sample Foods:"), tags.ul([tags.li(tags.a(href=["/foods/", food])(food)) for food in ["hamburgers", "cheeseburgers", "hot dogs"]]), )) def root(request): return {} @myStyle.routed(app.route("/foods/<food>"), tags.table(border="2", style="color: blue")( tags.tr(tags.td("Name:"), tags.td(slot("name"))), tags.tr(tags.td("Deliciousness:"),
#!/usr/bin/python3 # -*- coding: utf-8 -*- from klein import run, route from twisted.web.template import tags, slot from klein import Klein, Plating from input_handler import handle_input app = Klein() myStyle = Plating( tags=tags.html( tags.head(tags.title(slot("pageTitle"))), tags.body(tags.h1(slot("pageTitle"), Class="titleHeading"), tags.div(slot(Plating.CONTENT))) ) ) @myStyle.routed( app.route("/input/"), tags.div()) def input(request): return {"r":['Hello, world!']} if __name__ == '__main__': app.run("localhost", 8080)
return Random(unpack("!I", sha256(string.encode("utf-8")).digest()[:4])[0]) from twisted.web.template import tags, slot from klein import Klein, Plating app = Klein() myStyle = Plating(tags=tags.html( tags.head(tags.title(slot("pageTitle"))), tags.body(tags.h1(slot("pageTitle"), Class="titleHeading"), tags.div(slot(Plating.CONTENT)))), defaults={"pageTitle": "Places & Foods"}) @myStyle.routed( app.route("/"), tags.div( tags.h2("Sample Places:"), tags.ul([ tags.li(tags.a(href=["/places/", place])(place)) for place in ["new york", "san francisco", "shanghai"] ]), tags.h2("Sample Foods:"), tags.ul([ tags.li(tags.a(href=["/foods/", food])(food)) for food in ["hamburgers", "cheeseburgers", "hot dogs"] ]), )) def root(request): return {}
class PlatingTests(TestCase): """ Tests for L{Plating}. """ def setUp(self): """ Create an app and a resource wrapping that app for this test. """ self.app = Klein() self.kr = self.app.resource() def get(self, uri): """ Issue a virtual GET request to the given path that is expected to succeed synchronously, and return the generated request object and written bytes. """ request = requestMock(uri) d = _render(self.kr, request) self.successResultOf(d) return request, request.getWrittenData() def test_template_html(self): """ Rendering a L{Plating.routed} decorated route results in templated HTML. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return {"ok": "test-data-present"} request, written = self.get(b"/") self.assertIn(b'<span>test-data-present</span>', written) self.assertIn(b'<title>default title unchanged</title>', written) def test_template_json(self): """ Rendering a L{Plating.routed} decorated route with a query parameter asking for JSON will yield JSON instead. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return {"ok": "an-plating-test"} request, written = self.get(b"/?json=true") self.assertEqual( request.responseHeaders.getRawHeaders(b'content-type')[0], b'text/json; charset=utf-8') self.assertEquals( { "ok": "an-plating-test", "title": "default title unchanged" }, json.loads(written.decode('utf-8'))) def test_template_numbers(self): """ Data returned from a plated method may include numeric types (integers, floats, and possibly longs), which although they are not normally serializable by twisted.web.template, will be converted by plating into their decimal representation. """ @page.routed(self.app.route("/"), tags.div( tags.span(slot("anInteger")), tags.i(slot("anFloat")), tags.b(slot("anLong")), )) def plateMe(result): return { "anInteger": 7, "anFloat": 3.2, "anLong": 0x10000000000000001 } request, written = self.get(b"/") self.assertIn(b"<span>7</span>", written) self.assertIn(b"<i>3.2</i>", written) self.assertIn(b"<b>18446744073709551617</b>", written) def test_render_list(self): """ The C{:list} renderer suffix will render the slot named by the renderer as a list, filling each slot. """ @page.routed(self.app.route("/"), tags.ul(tags.li(slot("item"), render="subplating:list"))) def rsrc(request): return {"subplating": [1, 2, 3]} request, written = self.get(b"/") self.assertIn(b'<ul><li>1</li><li>2</li><li>3</li></ul>', written) self.assertIn(b'<title>default title unchanged</title>', written) def test_widget_html(self): """ When L{Plating.widgeted} is applied as a decorator, it gives the decorated function a C{widget} attribute which is a version of the function with a modified return type that turns it into a renderable HTML sub-element that may fill a slot. """ @page.routed(self.app.route("/"), tags.div(slot("widget"))) def rsrc(request): return {"widget": enwidget.widget(3, 4)} request, written = self.get(b"/") self.assertIn(b"<span>a: 3</span>", written) self.assertIn(b"<span>b: 4</span>", written) def test_widget_json(self): """ When L{Plating.widgeted} is applied as a decorator, and the result is serialized to JSON, it appears the same as the returned value despite the HTML-friendly wrapping described above. """ @page.routed(self.app.route("/"), tags.div(slot("widget"))) def rsrc(request): return {"widget": enwidget.widget(3, 4)} request, written = self.get(b"/?json=1") self.assertEqual(json.loads(written.decode('utf-8')), { "widget": { "a": 3, "b": 4 }, "title": "default title unchanged" }) def test_prime_directive_return(self): """ Nothing within these Articles Of Federation shall authorize the United Federation of Planets to alter the return value of a callable by applying a decorator to it... """ exact_result = {"ok": "some nonsense value"} @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return exact_result self.assertIdentical(plateMe(None), exact_result) def test_prime_directive_arguments(self): """ ... or shall require the function to modify its signature under these Articles Of Federation. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request, one, two, three): return (one, two, three) exact_one = {"one": "and"} exact_two = {"two": "and"} exact_three = {"three": "and"} result_one, result_two, result_three = plateMe(None, exact_one, exact_two, three=exact_three) self.assertIdentical(result_one, exact_one) self.assertIdentical(result_two, exact_two) self.assertIdentical(result_three, exact_three) def test_presentation_only_json(self): """ Slots marked as "presentation only" will not be reflected in the output. """ plating = Plating(tags=tags.span(slot("title")), presentation_slots={"title"}) @plating.routed(self.app.route("/"), tags.span(slot("data"))) def justJson(request): return {"title": "uninteresting", "data": "interesting"} request, written = self.get(b"/?json=1") self.assertEqual(json.loads(written.decode("utf-8")), {"data": "interesting"}) def test_missing_renderer(self): """ Missing renderers will result in an exception during rendering. """ def test(missing): plating = Plating(tags=tags.span(slot(Plating.CONTENT))) @plating.routed(self.app.route("/"), tags.span(tags.span(render=missing))) def no(request): return {} self.get(b"/") [fe] = self.flushLoggedErrors(FlattenerError) self.assertIsInstance(fe.value.args[0], MissingRenderMethod) test("garbage") test("garbage:missing") def test_json_serialize_unknown_type(self): """ The JSON serializer will raise a L{TypeError} when it can't find an appropriate type. """ from klein._plating import json_serialize class reprish(object): def __repr__(self): return '<blub>' te = self.assertRaises(TypeError, json_serialize, {"an": reprish()}) self.assertIn("<blub>", str(te))
app = Klein() myStyle = Plating( tags=tags.html( tags.head(tags.title(slot("pageTitle"))), tags.body( tags.h1(slot("pageTitle"), Class="titleHeading"), tags.div(slot(Plating.CONTENT)), ), ), defaults={"pageTitle": "Places & Foods"}, ) @myStyle.routed( app.route("/"), tags.div( tags.h2("Sample Places:"), tags.ul([ tags.li(tags.a(href=["/places/", place])(place)) for place in ["new york", "san francisco", "shanghai"] ]), tags.h2("Sample Foods:"), tags.ul([ tags.li(tags.a(href=["/foods/", food])(food)) for food in ["hamburgers", "cheeseburgers", "hot dogs"] ]), ), ) def root(request): return {}