Skip to content

Instantly share code, notes, and snippets.

@Integralist
Last active March 24, 2022 01:01
Show Gist options
  • Save Integralist/abe4a3e377b4114d08564164e9e8b192 to your computer and use it in GitHub Desktop.
Save Integralist/abe4a3e377b4114d08564164e9e8b192 to your computer and use it in GitHub Desktop.
[Python Tornado Example Application] #python #python3 #tornado #example #basic #simple

There are two examples...

  1. Simple
  2. Advanced
import os
import tornado.autoreload
import tornado.httpclient
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import uvloop
app_settings = {
"env": os.environ.get("ENV", "dev"),
"port": os.environ.get("APP_PORT", "9000"),
}
http_client = tornado.httpclient.AsyncHTTPClient()
class FooHandler(tornado.web.RequestHandler):
async def get(self, *args, **kwargs):
resp = await http_client.fetch("https://httpbin.org/get")
self.finish(resp.body)
class App(tornado.web.Application):
def __init__(self):
super().__init__([
(r"/", FooHandler),
], **app_settings)
if __name__ == "__main__":
uvloop.install()
tornado.options.options.logging = None # configure tornado to leave logging alone
if app_settings["env"] == "dev":
tornado.autoreload.start()
App().listen(app_settings["port"])
else:
server = tornado.httpserver.HTTPServer(App())
server.bind(app_settings["port"])
server.start(0) # multi process mode (one process per cpu)
tornado.ioloop.IOLoop.current().start()
#!/usr/bin/env python
"""This is our complete Tornado application.
Example endpoints...
curl localhost:9000/headers
curl localhost:9000/static/foo.js
curl localhost:9000/
"""
import logging
import os
import structlog
# from tornado import gen
from tornado.httpclient import AsyncHTTPClient
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.options import options
from tornado.process import cpu_count
from tornado.web import Application, RequestHandler, StaticFileHandler
from tornado.web import ErrorHandler as BaseErrorHandler
app_settings = {
"compress_response": True,
"default_handler_args": dict(status_code=404),
"default_handler_class": ErrorHandler,
"env": os.environ.get("ENV", "dev"),
"log_level": os.environ.get("LOG_LEVEL", "INFO"),
"port": os.environ.get("APP_PORT", "9000"),
"static_path": os.path.join(os.path.dirname(__file__), "static"),
"static_handler_class": RobotsHandler, # probably should be more generically named
"template_path": os.path.join(os.path.dirname(__file__), "templates"),
# debug=True (can also use explicit: tornado.autoreload.start())
#
# Example:
#
# if settings.get("cluster") == "dev":
# tornado.autoreload.start()
}
logging.basicConfig(
level=getattr(logging, app_settings["log_level"]),
format="[%(levelname)s %(asctime)s path:%(pathname)s lineno:%(lineno)s] %(message)s", # noqa
datefmt="%Y/%m/%d %I:%M:%S"
)
structlog.configure(logger_factory=structlog.stdlib.LoggerFactory())
log = structlog.get_logger()
class HeadersHandler(RequestHandler):
"""Serves application header information."""
# Following method is here to demonstrate older Tornado syntax
# Pre-async/await support version, where you needed Tornado's gen module
#
# @gen.coroutine
# def get(self, *args, **kwargs):
# """A function that will return the content for the health endpoint.""" # noqa
# log.info("Endpoint hit", handler="health")
# http_client = AsyncHTTPClient()
# response = yield http_client.fetch("https://httpbin.org/headers")
# self.finish(response.body)
def initialize(self, some_data_object):
self.some_data_object = some_data_object
async def get(self, *args, **kwargs):
"""A function that will return the content for the header endpoint."""
log.info("Endpoint hit", handler="health")
http_client = AsyncHTTPClient()
response = await http_client.fetch("https://httpbin.org/headers")
self.finish(response.body)
def data_received(self, chunk):
"""Defined to avoid abstract-method lint issue."""
pass
class HomeHandler(RequestHandler):
"""Serves application home information."""
async def get(self, *args, **kwargs):
"""A function that will return the content for the home endpoint."""
log.info("Endpoint hit", handler="home")
self.render(
"index.html",
header_text="Header goes here",
footer_text="Footer goes here"
)
def data_received(self, chunk):
"""Defined to avoid abstract-method lint issue."""
pass
class RobotsHandler(StaticFileHandler):
async def get(self, path="robots.txt", include_body=True):
return await super().get(path, include_body)
def set_default_headers(self):
self.clear_header("Server")
def set_extra_headers(self, path):
self.set_header("foo", "bar")
class ErrorHandler(BaseErrorHandler):
def write_error(self, status_code, **kwargs):
log = logger.bind(status=status_code)
log.error("incoming request")
# notice these methods...
self.set_default_headers()
self.set_extra_headers()
# ...are no longer automatically called
#
# so I manually call them
#
# i believe this is because tornado.web.ErrorHandler doesn't call the parent RequestHandler __init__
self.write("Error %s" % status_code)
def set_default_headers(self):
self.clear_header("Server")
def set_extra_headers(self, path=None):
for header, value in get_headers(self.get_status(), key=path):
self.set_header(header, value)
def robots_path() -> str:
return os.path.join(os.path.dirname(__file__), "static", "robots.txt")
class App(Application):
"""Base Application class to define the app's routes and settings."""
def __init__(self):
"""Setup the routes and import the application settings."""
app_handlers = [
(r"/", HomeHandler),
(r"/headers", HeadersHandler, {"some_data_object": ["i", "could", "be", "a", "class"]}),
(r"/robots.txt", RobotsHandler, {"path": robots_path()}), # MUST set `static_handler_class` in app_settings
]
super().__init__(app_handlers, **app_settings)
if __name__ == "__main__":
options.logging = None # configure tornado to leave logging alone
log = log.bind(port=app_settings["port"])
if app_settings["env"] == "dev":
log.info("Starting applications", mode="single")
App().listen(app_settings["port"])
else:
log.info("Starting applications", mode="forked", cpu_count=cpu_count())
server = HTTPServer(App())
server.bind(app_settings["port"])
server.start(0) # multi process mode (one process per cpu)
IOLoop.current().start()
{{ static_url("foo.js") }}
<!--
creates a hash based on the content of the file and appends it to the end of the URL
/static/foo.js?v=da9209eb23a61fd3633a1fd140069bae
-->
{% extends layout.html %}
<!--
alternatively we could 'include' another file (path is relevative to the current 'templates' directory):
{% include partials/_head.html %}
...so the file would be located: templates/partials/_head.html
-->
{% block header %}
<h1>{{ header_text }}</h1>
{% end %}
{% block body %}
<p>Hello from the child template!</p>
{% end %}
{% block footer %}
<p>{{ footer_text }}</p>
{% set mailLink = "<a href=\"mailto:[email protected]\">Contact Us</a>" %}
<p>{{ mailLink }}</p>
<p>{% raw mailLink %}</p>
{% end %}
<html>
<body>
<header>
{% block header %}{% end %}
</header>
<content>
{% block body %}{% end %}
</content>
<footer>
{% block footer %}{% end %}
</footer>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment