File size: 5,157 Bytes
d1ceb73 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
"""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"
|