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.