Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/nbconvert/postprocessors/serve.py: 38%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

61 statements  

1"""PostProcessor for serving reveal.js HTML slideshows.""" 

2 

3# Copyright (c) Jupyter Development Team. 

4# Distributed under the terms of the Modified BSD License. 

5from __future__ import annotations 

6 

7import os 

8import threading 

9import typing as t 

10import webbrowser 

11 

12from tornado import gen, httpserver, ioloop, log, web 

13from tornado.httpclient import AsyncHTTPClient 

14from traitlets import Bool, Int, Unicode 

15 

16from .base import PostProcessorBase 

17 

18 

19class ProxyHandler(web.RequestHandler): 

20 """handler the proxies requests from a local prefix to a CDN""" 

21 

22 @gen.coroutine 

23 def get(self, prefix, url): 

24 """proxy a request to a CDN""" 

25 proxy_url = "/".join([self.settings["cdn"], url]) 

26 client = self.settings["client"] 

27 response = yield client.fetch(proxy_url) 

28 

29 for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]: 

30 if header in response.headers: 

31 self.set_header(header, response.headers[header]) 

32 self.finish(response.body) 

33 

34 

35class ServePostProcessor(PostProcessorBase): 

36 """Post processor designed to serve files 

37 

38 Proxies reveal.js requests to a CDN if no local reveal.js is present 

39 """ 

40 

41 open_in_browser = Bool(True, help="""Should the browser be opened automatically?""").tag( 

42 config=True 

43 ) 

44 

45 browser = Unicode( 

46 "", 

47 help="""Specify what browser should be used to open slides. See 

48 https://docs.python.org/3/library/webbrowser.html#webbrowser.register 

49 to see how keys are mapped to browser executables. If 

50 not specified, the default browser will be determined 

51 by the `webbrowser` 

52 standard library module, which allows setting of the BROWSER 

53 environment variable to override it. 

54 """, 

55 ).tag(config=True) 

56 

57 reveal_cdn = Unicode( 

58 "https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0", help="""URL for reveal.js CDN.""" 

59 ).tag(config=True) 

60 reveal_prefix = Unicode("reveal.js", help="URL prefix for reveal.js").tag(config=True) 

61 ip = Unicode("127.0.0.1", help="The IP address to listen on.").tag(config=True) 

62 port = Int(8000, help="port for the server to listen on.").tag(config=True) 

63 

64 def postprocess(self, input): 

65 """Serve the build directory with a webserver.""" 

66 dirname, filename = os.path.split(input) 

67 handlers: list[tuple[t.Any, ...]] = [ 

68 (r"/(.+)", web.StaticFileHandler, {"path": dirname}), 

69 (r"/", web.RedirectHandler, {"url": "/%s" % filename}), 

70 ] 

71 

72 if "://" in self.reveal_prefix or self.reveal_prefix.startswith("//"): 

73 # reveal specifically from CDN, nothing to do 

74 pass 

75 elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)): 

76 # reveal prefix exists 

77 self.log.info("Serving local %s", self.reveal_prefix) 

78 else: 

79 self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn) 

80 handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler)) 

81 

82 app = web.Application( 

83 handlers, 

84 cdn=self.reveal_cdn, 

85 client=AsyncHTTPClient(), 

86 ) 

87 

88 # hook up tornado logging to our logger 

89 log.app_log = self.log 

90 

91 http_server = httpserver.HTTPServer(app) 

92 http_server.listen(self.port, address=self.ip) 

93 url = "http://%s:%i/%s" % (self.ip, self.port, filename) 

94 print("Serving your slides at %s" % url) 

95 print("Use Control-C to stop this server") 

96 if self.open_in_browser: 

97 try: 

98 browser = webbrowser.get(self.browser or None) 

99 b = lambda: browser.open(url, new=2) # noqa: E731 

100 threading.Thread(target=b).start() 

101 except webbrowser.Error as e: 

102 self.log.warning("No web browser found: %s.", e) 

103 browser = None 

104 

105 try: 

106 ioloop.IOLoop.instance().start() 

107 except KeyboardInterrupt: 

108 print("\nInterrupted") 

109 

110 

111def main(path): 

112 """allow running this module to serve the slides""" 

113 server = ServePostProcessor() 

114 server(path) 

115 

116 

117if __name__ == "__main__": 

118 import sys 

119 

120 main(sys.argv[1])