handlers.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. """API Handlers for nbconvert."""
  2. import asyncio
  3. import json
  4. from anyio.to_thread import run_sync
  5. from tornado import web
  6. from jupyter_server.auth.decorator import authorized
  7. from ...base.handlers import APIHandler
  8. AUTH_RESOURCE = "nbconvert"
  9. class NbconvertRootHandler(APIHandler):
  10. """The nbconvert root API handler."""
  11. auth_resource = AUTH_RESOURCE
  12. _exporter_lock: asyncio.Lock
  13. def initialize(self, **kwargs):
  14. """Initialize an nbconvert root handler."""
  15. super().initialize(**kwargs)
  16. # share lock across instances of this handler class
  17. if not hasattr(self.__class__, "_exporter_lock"):
  18. self.__class__._exporter_lock = asyncio.Lock()
  19. self._exporter_lock = self.__class__._exporter_lock
  20. @web.authenticated
  21. @authorized
  22. async def get(self):
  23. """Get the list of nbconvert exporters."""
  24. try:
  25. from nbconvert.exporters import base
  26. except ImportError as e:
  27. raise web.HTTPError(500, "Could not import nbconvert: %s" % e) from e
  28. res = {}
  29. # Some exporters use the filesystem when instantiating, delegate that
  30. # to a thread so we don't block the event loop for it.
  31. exporters = await run_sync(base.get_export_names)
  32. async with self._exporter_lock:
  33. for exporter_name in exporters:
  34. try:
  35. exporter_class = await run_sync(base.get_exporter, exporter_name)
  36. except ValueError:
  37. # I think the only way this will happen is if the entrypoint
  38. # is uninstalled while this method is running
  39. continue
  40. # XXX: According to the docs, it looks like this should be set to None
  41. # if the exporter shouldn't be exposed to the front-end and a friendly
  42. # name if it should. However, none of the built-in exports have it defined.
  43. # if not exporter_class.export_from_notebook:
  44. # continue
  45. res[exporter_name] = {
  46. "output_mimetype": exporter_class.output_mimetype,
  47. }
  48. self.finish(json.dumps(res))
  49. default_handlers = [
  50. (r"/api/nbconvert", NbconvertRootHandler),
  51. ]