Source code for ipypublish.postprocessors.reveal_serve

""" serve HTML page
TODO the RevealServer setting should be available at front end
"""

# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

from __future__ import print_function

import os
import signal
import webbrowser

from tornado import web, ioloop, httpserver, log, gen
from tornado.httpclient import AsyncHTTPClient
from traitlets import Bool, Unicode, Int
from ipypublish.postprocessors.base import IPyPostProcessor


[docs]class ProxyHandler(web.RequestHandler): """handler the proxies requests from a local prefix to a CDN"""
[docs] @gen.coroutine def get(self, prefix, url): """proxy a request to a CDN""" proxy_url = "/".join([self.settings["cdn"], url]) client = self.settings["client"] client.fetch(proxy_url) response = yield client.fetch(proxy_url) for header in [ "Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires", ]: if header in response.headers: self.set_header(header, response.headers[header]) self.finish(response.body)
[docs]class RevealServer(IPyPostProcessor): """Post processor designed to serve files Proxies reveal.js requests to a CDN if no local reveal.js is present """ @property def allowed_mimetypes(self): return "text/html" @property def requires_path(self): return True @property def logger_name(self): return "reveal-server" open_in_browser = Bool( True, help="Should the browser be opened automatically?" ).tag(config=True) reveal_cdn = Unicode( "https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.1.0", help="""URL for reveal.js CDN.""", ).tag(config=True) reveal_prefix = Unicode("reveal.js", help="URL prefix for reveal.js").tag( config=True ) ip = Unicode("127.0.0.1", help="The IP address to listen on.").tag(config=True) port = Int(8000, help="port for the server to listen on.").tag(config=True)
[docs] def run_postprocess(self, stream, mimetype, filepath, resources): """Serve the build directory with a webserver.""" if not filepath.exists(): self.handle_error( "the target file path does not exist: {}".format(filepath), IOError ) # TODO rewrite this as pathlib dirname, filename = os.path.split(str(filepath)) handlers = [ (r"/(.+)", web.StaticFileHandler, {"path": dirname}), (r"/", web.RedirectHandler, {"url": "/%s" % filename}), ] if "://" in self.reveal_prefix or self.reveal_prefix.startswith("//"): # reveal specifically from CDN, nothing to do pass elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)): # reveal prefix exists self.logger.info("Serving local %s", self.reveal_prefix) self.logger.info("Serving local %s", self.reveal_prefix) else: self.logger.info( "Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn ) self.logger.info( "Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn ) handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler)) app = web.Application(handlers, cdn=self.reveal_cdn, client=AsyncHTTPClient()) # hook up tornado logging to our self.logger log.app_log = self.logger http_server = httpserver.HTTPServer(app) # find an available port port_attempts = list(range(10)) for port_attempt in port_attempts: try: url = "http://%s:%i/%s" % (self.ip, self.port, filename) self.logger.info("Attempting to serve at %s" % url) http_server.listen(self.port, address=self.ip) break except IOError: self.port += 1 if port_attempt == port_attempts[-1]: self.handle_error( "no port available to launch slides on, " "try closing some slideshows", IOError, ) self.logger.info("Serving your slides at %s" % url) self.logger.info("Use Control-C to stop this server") # don't let people press ctrl-z, which leaves port open def handler(signum, frame): self.logger.info("Control-Z pressed, but ignored, use Control-C!") signal.signal(signal.SIGTSTP, handler) if self.open_in_browser: # 2 opens the url in a new tab webbrowser.open(url, new=2) try: ioloop.IOLoop.instance().start() except KeyboardInterrupt: # dosen't look like line below is necessary ioloop.IOLoop.instance().stop() self.logger.info("\nInterrupted") return stream, filepath, resources