You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
4.5 KiB
150 lines
4.5 KiB
"""Tornado handlers for frontend config storage."""
|
|
|
|
# Copyright (c) Jupyter Development Team.
|
|
# Distributed under the terms of the Modified BSD License.
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
import json
|
|
from threading import Event
|
|
|
|
from notebook.base.handlers import APIHandler
|
|
from tornado import gen, web
|
|
from tornado.concurrent import run_on_executor
|
|
|
|
from ..commands import build, clean, build_check, AppOptions, _ensure_options
|
|
from ..coreconfig import CoreConfig
|
|
|
|
|
|
class Builder(object):
|
|
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
|
|
|
|
@gen.coroutine
|
|
def get_status(self):
|
|
if self.core_mode:
|
|
raise gen.Return(dict(status='stable', message=''))
|
|
if self.building:
|
|
raise gen.Return(dict(status='building', message=''))
|
|
|
|
try:
|
|
messages = yield self._run_build_check(
|
|
self.app_dir, self.log, self.core_config)
|
|
status = 'needed' if messages else 'stable'
|
|
if messages:
|
|
self.log.warn('Build recommended')
|
|
[self.log.warn(m) for m in messages]
|
|
else:
|
|
self.log.info('Build is up to date')
|
|
except ValueError as e:
|
|
self.log.warn(
|
|
'Could not determine jupyterlab build status without nodejs'
|
|
)
|
|
status = 'stable'
|
|
messages = []
|
|
|
|
raise gen.Return(dict(status=status, message='\n'.join(messages)))
|
|
|
|
@gen.coroutine
|
|
def build(self):
|
|
if self._canceling:
|
|
raise ValueError('Cancel in progress')
|
|
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)
|
|
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:
|
|
raise ValueError('No current build')
|
|
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):
|
|
return build_check(app_options=AppOptions(
|
|
app_dir=app_dir, logger=logger, core_config=core_config))
|
|
|
|
@run_on_executor
|
|
def _run_build(self, app_dir, logger, kill_event, core_config):
|
|
app_options = AppOptions(
|
|
app_dir=app_dir, logger=logger, kill_event=kill_event,
|
|
core_config=core_config)
|
|
try:
|
|
return build(command='build', app_options=app_options)
|
|
except Exception as e:
|
|
if self._kill_event.is_set():
|
|
return
|
|
self.log.warn('Build failed, running a clean and rebuild')
|
|
clean(app_options=app_options)
|
|
return build(command='build', app_options=app_options)
|
|
|
|
|
|
class BuildHandler(APIHandler):
|
|
|
|
def initialize(self, builder):
|
|
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))
|
|
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))
|
|
|
|
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"
|