Package: gh3
gh3 Python WSGI nanoframeworkif you are not already there, read this doc with API links working
in all their glory on the
[website](https://gh3-website.web.app/README.html)First Appimportgh3defsay_hello(ctx:gh3.Context):"""A view handler that returns some text."""ctx.reply_text('hello, world')# Create the app, add a route, and start the debug server.app=gh3.App()app.add_route('/',say_hello)app.debug()Here, we create an instance ofgh3.Appand we add a simple route to
it, and start the debug server.You can immediately see some features of the request handler:You modify the response in place from thegh3.Context.It is simple to make a simple textual response, usingreply_textand there are analogs forreply_htmlandreply_json. Thesereply_methods are shortcuts to
setting the response data, the response content-type, and the response status
code.TestingWe use Werkzeug's testing infrastructure, by callinggh3.App.testerto get awerkzeug test
clientand use it to make requests against your app. For
many examples, check out the gh3 test suite.You can test the request context in your views, e.g.deftext_view(ctx):assert'/'==ctx.req.pathctx.reply_text('ok')app=gh3.App()app.add_route('/',text_view)resp=app.tester().get('/')assert200==resp.status_codeWe have made 2 assertions above:That the response is a 200That the request path at the view was/You can use the pattern above to test anything in the context.Installationvirtualenv-ppython3.9env
./env/bin/pipinstallgh3Route handler argumentsArguments from the route are available inctx.endpoint_args. They are not passed to the
handler functions as all handler functions take only the request context as an
argument.importgh3defsay_hello(ctx:gh3.Context):"""A view handler that returns some text."""user=ctx.endpoint_args['user']ctx.reply_text(f'hello,{user}')# Create the app, add a route, and start the debug server.app=gh3.App()app.add_route('/<user>',say_hello)app.debug()Routes, targets, and endpointsAs in the simple example above, there is a route'/'and a target, thesay_hellofunction. What is not described there is that the endpoint is
inferred from the function name, but can be set explicitly:app.add_route('/',say_hello,endpoint='home')The endpoint is used for reverse lookup of URLs.More complex routingThe entire range of Werkzeug's routing is available by usingadd_ruleandadd_target.
For example, to use Werkzeug'sSubmountrule
factory, you should create the rule and add the target handlers manually.rule=gh3.wz_routing.Submount(Rule('/',endpoint='blog/index'),Rule('/entry/<entry_slug>',endpoint='blog/show'))app.add_rule(rule)app.add_target('blog/index',my_blog_index_handler)app.add_target('blog/show',my_blog_show_handler)Why?I love Werkzeug, but I really don't love Flask (sorry, Armin). I swear I have
used Flask a lot, in big production applications that serve billions of pages a
year. There are patterns in Flask that have made it extremely hard for me to
develop as part of a large (30+) engineering team, and these are the things
intentionally left out of gh3. gh3 will never be as featured as Flask, and
that's OK too. You'll forgive me these few 100 lines of well-tested code. And if
you are still wondering why, please feel free to move on.