Latest posts for tag tornado
Starting tornado on a random free port
One of the software I maintain for work is a GUI data browser that uses Tornado as a backend and a web browser as a front-end.
It is quite convenient to start the command and have the browser open automatically on the right URL. It's quite annoying to start the command and be told that the default port is already in use.
I've needed this trick quite often, also when writing unit tests, and it's time I note it down somewhere, so it's easier to find than going through Tornado's unittest code where I found it the first time.
This is how to start Tornado on a free random port:
from tornado.options import define, options
import tornado.netutil
import tornado.httpserver
define("web_port", type=int, default=None, help="listening port for web interface")
application = Application(self.db_url)
if options.web_port is None:
sockets = tornado.netutil.bind_sockets(0, '127.0.0.1')
self.web_port = sockets[0].getsockname()[:2][1]
server = tornado.httpserver.HTTPServer(application)
server.add_sockets(sockets)
else:
server = tornado.httpserver.HTTPServer(application)
server.listen(options.web_port)
Serving debian-distributed javascript libraries in Tornado
Debian conveniently distribute JavaScript libraries, and expects packaged software to use them rather than embedding their own copy.
Here is a convenient custom StaticFileHandler for Tornado that looks for the Debian-distributed versions of JavaScript libraries, and falls back to the vendored versions if they are not found:
from tornado import web
import pathlib
class StaticFileHandler(web.StaticFileHandler):
"""
StaticFileHandler that allows overriding paths in the static directory with
system provided versions
"""
SYSTEM_ASSET_PATH = pathlib.Path("/usr/share/javascript")
@classmethod
def get_absolute_path(self, root, path):
path = pathlib.PurePath(path)
if not path.parts:
return super().get_absolute_path(root, path)
system_dir = self.SYSTEM_ASSET_PATH.joinpath(path.parts[0])
if system_dir.is_dir():
# If that asset directory exists in the system, look for things in
# there
return self.SYSTEM_ASSET_PATH.joinpath(path)
else:
# Else go ahead with the default static dir
return super().get_absolute_path(root, path)
def validate_absolute_path(self, root, absolute_path):
"""
Rewrite of tornado's validate_absolute_path not to raise an error for
paths in /usr/share/javascript/
"""
root = pathlib.Path(root)
absolute_path = pathlib.Path(absolute_path)
is_system_root = absolute_path.parts[:len(self.SYSTEM_ASSET_PATH.parts)] == self.SYSTEM_ASSET_PATH.parts
is_static_root = absolute_path.parts[:len(root.parts)] == root.parts
if not is_system_root and not is_static_root:
raise web.HTTPError(403, "%s is not in root static directory or system assets path",
self.path)
if absolute_path.is_dir() and self.default_filename is not None:
# need to look at the request.path here for when path is empty
# but there is some prefix to the path that was already
# trimmed by the routing
if not self.request.path.endswith("/"):
self.redirect(self.request.path + "/", permanent=True)
return
absolute_path = absolute_path.joinpath(self.default_filename)
if not absolute_path.exists():
raise web.HTTPError(404)
if not absolute_path.is_file():
raise web.HTTPError(403, "%s is not a file", self.path)
return str(absolute_path)
This is how to use it:
class DebianApplication(tornado.web.Application):
def __init__(self, *args, **settings):
from .static import StaticFileHandler
settings.setdefault("static_handler_class", StaticFileHandler)
super().__init__(*args, **settings)
And from HTML it's simply a matter of matching the first path component to what
is used by Debian's packages under /usr/share/javascript
:
<link rel="stylesheet" href="{{static_url('bootstrap4/css/bootstrap.min.css')}}">
<script src="{{static_url('jquery/jquery.min.js')}}"></script>
<script src="{{static_url('popper.js/umd/popper.min.js')}}"></script>
<script src="{{static_url('bootstrap4/js/bootstrap.min.js')}}"></script>
I find it quite convenient: this way I can start writing prototype code without worrying about fetching javascript libraries to bundle.
I only need to start worrying about it if I need to deploy outside of Debian,
or to old stable versions of Debian that don't contain the required JavaScript
dependencies. In that case, I just cp -r
from a working
/usr/share/javascript
into Tornado's static directory, and I'm done.