| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- """Tornado handlers for frontend config storage."""
- # Copyright (c) Jupyter Development Team.
- # Distributed under the terms of the Modified BSD License.
- import json
- from concurrent.futures import ThreadPoolExecutor
- from threading import Event
- from jupyter_server.base.handlers import APIHandler
- from jupyter_server.extension.handler import ExtensionHandlerMixin
- from tornado import gen, web
- from tornado.concurrent import run_on_executor
- from jupyterlab.commands import AppOptions, _ensure_options, build, build_check, clean
- class Builder:
- building = False
- executor = ThreadPoolExecutor(max_workers=5)
- canceled = False
- _canceling = False
- _kill_event = None
- _future = None
- def __init__(self, core_mode, app_options=None):
- app_options = _ensure_options(app_options)
- self.log = app_options.logger
- self.core_mode = core_mode
- self.app_dir = app_options.app_dir
- self.core_config = app_options.core_config
- self.labextensions_path = app_options.labextensions_path
- @gen.coroutine
- def get_status(self):
- if self.core_mode:
- raise gen.Return({"status": "stable", "message": ""})
- if self.building:
- raise gen.Return({"status": "building", "message": ""})
- try:
- messages = yield self._run_build_check(
- self.app_dir, self.log, self.core_config, self.labextensions_path
- )
- status = "needed" if messages else "stable"
- if messages:
- self.log.warning("Build recommended")
- [self.log.warning(m) for m in messages]
- else:
- self.log.info("Build is up to date")
- except ValueError:
- self.log.warning("Could not determine jupyterlab build status without nodejs")
- status = "stable"
- messages = []
- raise gen.Return({"status": status, "message": "\n".join(messages)})
- @gen.coroutine
- def build(self):
- if self._canceling:
- msg = "Cancel in progress"
- raise ValueError(msg)
- if not self.building:
- self.canceled = False
- self._future = future = gen.Future()
- self.building = True
- self._kill_event = evt = Event()
- try:
- yield self._run_build(
- self.app_dir, self.log, evt, self.core_config, self.labextensions_path
- )
- future.set_result(True)
- except Exception as e:
- if str(e) == "Aborted":
- future.set_result(False)
- else:
- future.set_exception(e)
- finally:
- self.building = False
- try:
- yield self._future
- except Exception as e:
- raise e
- @gen.coroutine
- def cancel(self):
- if not self.building:
- msg = "No current build"
- raise ValueError(msg)
- self._canceling = True
- yield self._future
- self._canceling = False
- self.canceled = True
- @run_on_executor
- def _run_build_check(self, app_dir, logger, core_config, labextensions_path):
- return build_check(
- app_options=AppOptions(
- app_dir=app_dir,
- logger=logger,
- core_config=core_config,
- labextensions_path=labextensions_path,
- )
- )
- @run_on_executor
- def _run_build(self, app_dir, logger, kill_event, core_config, labextensions_path):
- app_options = AppOptions(
- app_dir=app_dir,
- logger=logger,
- kill_event=kill_event,
- core_config=core_config,
- labextensions_path=labextensions_path,
- )
- try:
- return build(app_options=app_options)
- except Exception:
- if self._kill_event.is_set():
- return
- self.log.warning("Build failed, running a clean and rebuild")
- clean(app_options=app_options)
- return build(app_options=app_options)
- class BuildHandler(ExtensionHandlerMixin, APIHandler):
- def initialize(self, builder=None, name=None):
- super().initialize(name=name)
- self.builder = builder
- @web.authenticated
- @gen.coroutine
- def get(self):
- data = yield self.builder.get_status()
- self.finish(json.dumps(data))
- @web.authenticated
- @gen.coroutine
- def delete(self):
- self.log.warning("Canceling build")
- try:
- yield self.builder.cancel()
- except Exception as e:
- raise web.HTTPError(500, str(e)) from None
- self.set_status(204)
- @web.authenticated
- @gen.coroutine
- def post(self):
- self.log.debug("Starting build")
- try:
- yield self.builder.build()
- except Exception as e:
- raise web.HTTPError(500, str(e)) from None
- if self.builder.canceled:
- raise web.HTTPError(400, "Build canceled")
- self.log.debug("Build succeeded")
- self.set_status(200)
- # The path for lab build.
- build_path = r"/lab/api/build"
|