Hello community,

here is the log from the commit of package python-jupyter_jupyterlab_launcher 
for openSUSE:Factory checked in at 2018-08-03 12:39:45
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jupyter_jupyterlab_launcher (Old)
 and      /work/SRC/openSUSE:Factory/.python-jupyter_jupyterlab_launcher.new 
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jupyter_jupyterlab_launcher"

Fri Aug  3 12:39:45 2018 rev:2 rq:627154 version:0.11.2

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-jupyter_jupyterlab_launcher/python-jupyter_jupyterlab_launcher.changes
    2018-05-13 16:03:52.432939315 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-jupyter_jupyterlab_launcher.new/python-jupyter_jupyterlab_launcher.changes
       2018-08-03 12:39:46.399790118 +0200
@@ -1,0 +2,14 @@
+Thu Aug  2 18:34:03 UTC 2018 - [email protected]
+
+- Update to 0.11.2
+  * Update test utils to remove redundancy.
+  * Support the launcher specific endpoints in the barebones app.
+  * Implement workspaces listing.
+  * Implement individual workspace deletion.
+  * Handle requests to /lab/api/workspaces and /lab/api/workspaces/
+  * Improve use of process quiet flag
+  * move process utilities from jupyterlab to here
+  * make workspaces directory if it does not exist.
+  * Include plugin/schema name in error messages.
+
+-------------------------------------------------------------------

Old:
----
  jupyterlab_launcher-0.10.5.tar.gz

New:
----
  jupyterlab_launcher-0.11.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-jupyter_jupyterlab_launcher.spec ++++++
--- /var/tmp/diff_new_pack.uiS9aU/_old  2018-08-03 12:39:46.955790947 +0200
+++ /var/tmp/diff_new_pack.uiS9aU/_new  2018-08-03 12:39:46.955790947 +0200
@@ -18,7 +18,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %bcond_without  test
 Name:           python-jupyter_jupyterlab_launcher
-Version:        0.10.5
+Version:        0.11.2
 Release:        0
 License:        BSD-3-Clause
 Summary:        Jupyter Launcher
@@ -68,6 +68,7 @@
 %files %{python_files}
 %defattr(-,root,root,-)
 %doc LICENSE README.md
-%{python_sitelib}/*
+%{python_sitelib}/jupyterlab_launcher-%{version}-py*.egg-info
+%{python_sitelib}/jupyterlab_launcher/
 
 %changelog

++++++ jupyterlab_launcher-0.10.5.tar.gz -> jupyterlab_launcher-0.11.2.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyterlab_launcher-0.10.5/PKG-INFO 
new/jupyterlab_launcher-0.11.2/PKG-INFO
--- old/jupyterlab_launcher-0.10.5/PKG-INFO     2018-02-20 19:04:12.000000000 
+0100
+++ new/jupyterlab_launcher-0.11.2/PKG-INFO     2018-07-20 13:01:43.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: jupyterlab_launcher
-Version: 0.10.5
+Version: 0.11.2
 Summary: Jupyter Launcher
 Home-page: http://jupyter.org
 Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/_version.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/_version.py
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/_version.py      
2018-02-20 19:03:57.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/_version.py      
2018-07-20 13:01:33.000000000 +0200
@@ -1,2 +1,2 @@
-version_info = (0, 10, 5)
+version_info = (0, 11, 2)
 __version__ = ".".join(map(str, version_info))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/handlers.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/handlers.py
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/handlers.py      
2018-01-25 14:49:44.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/handlers.py      
2018-07-20 12:50:50.000000000 +0200
@@ -222,11 +222,20 @@
 
         # Handle API requests for workspaces.
         config.workspaces_api_url = ujoin(base_url, default_workspaces_api_url)
-        workspaces_api_path = config.workspaces_api_url + '(?P<space_name>.+)'
+
+        # Handle requests for the list of workspaces. Make slash optional.
+        workspaces_api_path = config.workspaces_api_url + '?'
         handlers.append((workspaces_api_path, WorkspacesHandler, {
             'workspaces_url': config.workspaces_url,
             'path': config.workspaces_dir
         }))
+
+        # Handle requests for an individually named workspace.
+        workspace_api_path = config.workspaces_api_url + '(?P<space_name>.+)'
+        handlers.append((workspace_api_path, WorkspacesHandler, {
+            'workspaces_url': config.workspaces_url,
+            'path': config.workspaces_dir
+        }))
 
     # Handle local themes.
     if config.themes_dir:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/process.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/process.py
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/process.py       
1970-01-01 01:00:00.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/process.py       
2018-07-20 13:01:25.000000000 +0200
@@ -0,0 +1,291 @@
+# coding: utf-8
+"""JupyterLab Launcher process handler"""
+
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+from __future__ import print_function
+
+import atexit
+import logging
+import os
+import re
+import signal
+import sys
+import threading
+import time
+import weakref
+
+from tornado import gen
+
+from ipython_genutils.py3compat import which as _which
+
+try:
+    import subprocess32 as subprocess
+except ImportError:
+    import subprocess
+
+try:
+    import pty
+except ImportError:
+    pty = False
+
+if sys.platform == 'win32':
+    list2cmdline = subprocess.list2cmdline
+else:
+    def list2cmdline(cmd_list):
+        import pipes
+        return ' '.join(map(pipes.quote, cmd_list))
+
+logging.basicConfig(format='%(message)s', level=logging.INFO)
+
+
+def which(command, env=None):
+    """Get the full path to a command.
+
+    Parameters
+    ----------
+    command: str
+        The command name or path.
+    env: dict, optional
+        The environment variables, defaults to `os.environ`.
+    """
+    env = env or os.environ
+    path = env.get('PATH') or os.defpath
+    command_with_path = _which(command, path=path)
+
+    # Allow nodejs as an alias to node.
+    if command == 'node' and not command_with_path:
+        command = 'nodejs'
+        command_with_path = _which('nodejs', path=path)
+
+    if not command_with_path:
+        if command in ['nodejs', 'node', 'npm']:
+            msg = 'Please install nodejs 5+ and npm before continuing 
installation. nodejs may be installed using conda or directly from the nodejs 
website.'
+            raise ValueError(msg)
+        raise ValueError('The command was not found or was not ' +
+                'executable: %s.' % command)
+    return command_with_path
+
+
+class Process(object):
+    """A wrapper for a child process.
+    """
+    _procs = weakref.WeakSet()
+    _pool = None
+
+    def __init__(self, cmd, logger=None, cwd=None, kill_event=None,
+                 env=None, quiet=False):
+        """Start a subprocess that can be run asynchronously.
+
+        Parameters
+        ----------
+        cmd: list
+            The command to run.
+        logger: :class:`~logger.Logger`, optional
+            The logger instance.
+        cwd: string, optional
+            The cwd of the process.
+        env: dict, optional
+            The environment for the process.
+        kill_event: :class:`~threading.Event`, optional
+            An event used to kill the process operation.
+        quiet: bool, optional
+            Whether to suppress output.
+        """
+        if not isinstance(cmd, (list, tuple)):
+            raise ValueError('Command must be given as a list')
+
+        if kill_event and kill_event.is_set():
+            raise ValueError('Process aborted')
+
+        self.logger = logger = logger or logging.getLogger('jupyterlab')
+        self._last_line = ''
+        if not quiet:
+            self.logger.info('> ' + list2cmdline(cmd))
+        self.cmd = cmd
+
+        kwargs = {}
+        if quiet:
+            kwargs['stdout'] = subprocess.DEVNULL
+
+        self.proc = self._create_process(cwd=cwd, env=env, **kwargs)
+        self._kill_event = kill_event or threading.Event()
+
+        Process._procs.add(self)
+
+    def terminate(self):
+        """Terminate the process and return the exit code.
+        """
+        proc = self.proc
+
+        # Kill the process.
+        if proc.poll() is None:
+            os.kill(proc.pid, signal.SIGTERM)
+
+        # Wait for the process to close.
+        try:
+            proc.wait()
+        finally:
+            Process._procs.remove(self)
+
+        return proc.returncode
+
+    def wait(self):
+        """Wait for the process to finish.
+
+        Returns
+        -------
+        The process exit code.
+        """
+        proc = self.proc
+        kill_event = self._kill_event
+        while proc.poll() is None:
+            if kill_event.is_set():
+                self.terminate()
+                raise ValueError('Process was aborted')
+            time.sleep(1.)
+        return self.terminate()
+
+    @gen.coroutine
+    def wait_async(self):
+        """Asynchronously wait for the process to finish.
+        """
+        proc = self.proc
+        kill_event = self._kill_event
+        while proc.poll() is None:
+            if kill_event.is_set():
+                self.terminate()
+                raise ValueError('Process was aborted')
+            yield gen.sleep(1.)
+
+        raise gen.Return(self.terminate())
+
+    def _create_process(self, **kwargs):
+        """Create the process.
+        """
+        cmd = self.cmd
+        kwargs.setdefault('stderr', subprocess.STDOUT)
+
+        cmd[0] = which(cmd[0], kwargs.get('env'))
+
+        if os.name == 'nt':
+            kwargs['shell'] = True
+
+        proc = subprocess.Popen(cmd, **kwargs)
+        return proc
+
+    @classmethod
+    def _cleanup(cls):
+        """Clean up the started subprocesses at exit.
+        """
+        for proc in list(cls._procs):
+            proc.terminate()
+
+
+class WatchHelper(Process):
+    """A process helper for a watch process.
+    """
+
+    def __init__(self, cmd, startup_regex, logger=None, cwd=None,
+            kill_event=None, env=None):
+        """Initialize the process helper.
+
+        Parameters
+        ----------
+        cmd: list
+            The command to run.
+        startup_regex: string
+            The regex to wait for at startup.
+        logger: :class:`~logger.Logger`, optional
+            The logger instance.
+        cwd: string, optional
+            The cwd of the process.
+        env: dict, optional
+            The environment for the process.
+        kill_event: callable, optional
+            A function to call to check if we should abort.
+        """
+        super(WatchHelper, self).__init__(cmd, logger=logger,
+            cwd=cwd, kill_event=kill_event, env=env)
+
+        if not pty:
+            self._stdout = self.proc.stdout
+
+        while 1:
+            line = self._stdout.readline().decode('utf-8')
+            if not line:
+                raise RuntimeError('Process ended improperly')
+            print(line.rstrip())
+            if re.match(startup_regex, line):
+                break
+
+        self._read_thread = threading.Thread(target=self._read_incoming)
+        self._read_thread.setDaemon(True)
+        self._read_thread.start()
+
+    def terminate(self):
+        """Terminate the process.
+        """
+        proc = self.proc
+
+        if proc.poll() is None:
+            if os.name != 'nt':
+                # Kill the process group if we started a new session.
+                os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
+            else:
+                os.kill(proc.pid, signal.SIGTERM)
+
+        # Close stdout.
+        try:
+            self._stdout.close()
+        except Exception as e:
+            pass
+
+        # Wait for the process to close.
+        try:
+            proc.wait()
+        finally:
+            Process._procs.remove(self)
+
+        return proc.returncode
+
+    def _read_incoming(self):
+        """Run in a thread to read stdout and print"""
+        fileno = self._stdout.fileno()
+        while 1:
+            try:
+                buf = os.read(fileno, 1024)
+            except OSError as e:
+                self.logger.debug('Read incoming error %s', e)
+                return
+
+            if not buf:
+                return
+
+            print(buf.decode('utf-8'), end='')
+
+    def _create_process(self, **kwargs):
+        """Create the watcher helper process.
+        """
+        kwargs['bufsize'] = 0
+
+        if pty:
+            master, slave = pty.openpty()
+            kwargs['stderr'] = kwargs['stdout'] = slave
+            kwargs['start_new_session'] = True
+            self._stdout = os.fdopen(master, 'rb')
+        else:
+            kwargs['stdout'] = subprocess.PIPE
+
+            if os.name == 'nt':
+                startupinfo = subprocess.STARTUPINFO()
+                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+                kwargs['startupinfo'] = startupinfo
+                kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
+                kwargs['shell'] = True
+
+        return super(WatchHelper, self)._create_process(**kwargs)
+
+
+# Register the cleanup handler.
+atexit.register(Process._cleanup)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/process_app.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/process_app.py
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/process_app.py   
1970-01-01 01:00:00.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/process_app.py   
2018-07-20 12:50:50.000000000 +0200
@@ -0,0 +1,48 @@
+# coding: utf-8
+"""A lab app that runs a sub process for a demo or a test."""
+
+from __future__ import print_function, absolute_import
+
+import sys
+
+from notebook.notebookapp import NotebookApp
+from tornado.ioloop import IOLoop
+from traitlets import Bool
+
+from .handlers import add_handlers, LabConfig
+from .process import Process
+
+
+class ProcessApp(NotebookApp):
+    """A notebook app that runs a separate process and exits on completion."""
+
+    open_browser = Bool(False)
+
+    lab_config = LabConfig()
+
+    def get_command(self):
+        """Get the command and kwargs to run with `Process`.
+        This is intended to be overridden.
+        """
+        return ['python', '--version'], dict()
+
+    def start(self):
+        """Start the application.
+        """
+        add_handlers(self.web_app, self.lab_config)
+        IOLoop.current().add_callback(self._run_command)
+        NotebookApp.start(self)
+
+    def _run_command(self):
+        command, kwargs = self.get_command()
+        kwargs.setdefault('logger', self.log)
+        future = Process(command, **kwargs).wait_async()
+        IOLoop.current().add_future(future, self._process_finished)
+
+    def _process_finished(self, future):
+        try:
+            IOLoop.current().stop()
+            sys.exit(future.result())
+        except Exception as e:
+            self.log.error(str(e))
+            sys.exit(1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/settings_handler.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/settings_handler.py
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/settings_handler.py      
2018-01-05 04:17:45.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/settings_handler.py      
2018-07-20 12:50:50.000000000 +0200
@@ -27,13 +27,11 @@
                 try:
                     self.overrides = json.load(fid)
                 except Exception as e:
-                    self.log.warn(str(e))
+                    self.log.warn('Failed loading overrides {}'.format(str(e)))
 
     @json_errors
     @web.authenticated
     def get(self, section_name):
-        self.set_header('Content-Type', 'application/json')
-
         schema = _get_schema(self.schemas_dir, section_name, self.overrides)
         path = _path(self.settings_dir, section_name, _file_extension)
         raw = '{}'
@@ -46,7 +44,8 @@
                     raw = fid.read() or raw
                     settings = json.loads(json_minify(raw))
                 except Exception as e:
-                    self.log.warn(str(e))
+                    message = 'Failed loading settings ({}): {}'
+                    self.log.warn(message.format(section_name, str(e)))
 
         # Validate the parsed data against the schema.
         if len(settings):
@@ -54,7 +53,8 @@
             try:
                 validator.validate(settings)
             except ValidationError as e:
-                self.log.warn(str(e))
+                message = 'Failed validating settings ({}): {}'
+                self.log.warn(message.format(section_name, str(e)))
                 raw = '{}'
 
         # Send back the raw data to the client.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/test_settings_api.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/test_settings_api.py
--- 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/test_settings_api.py   
    2018-01-05 04:17:45.000000000 +0100
+++ 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/test_settings_api.py   
    2018-07-20 12:50:50.000000000 +0200
@@ -1,5 +1,7 @@
 """Test the kernels service API."""
 import json
+import os
+import shutil
 
 from jupyterlab_launcher.tests.utils import LabTestBase, APITester
 from notebook.tests.launchnotebook import assert_http_error
@@ -21,11 +23,19 @@
     """Test the settings web service API"""
 
     def setUp(self):
+        src = os.path.join(
+            os.path.abspath(os.path.dirname(__file__)),
+            'schemas',
+            '@jupyterlab')
+        dst = os.path.join(self.lab_config.schemas_dir, '@jupyterlab')
+        if not os.path.exists(dst):
+            shutil.copytree(src, dst)
         self.settings_api = SettingsAPI(self.request)
 
     def test_get(self):
         id = '@jupyterlab/apputils-extension:themes'
         data = self.settings_api.get(id).json()
+
         assert data['id'] == id
         assert len(data['schema'])
         assert 'raw' in data
@@ -36,8 +46,8 @@
 
     def test_patch(self):
         id = '@jupyterlab/shortcuts-extension:plugin'
-        resp = self.settings_api.put(id, dict())
-        assert resp.status_code == 204
+
+        assert self.settings_api.put(id, dict()).status_code == 204
 
     def test_patch_wrong_id(self):
         with assert_http_error(404):
@@ -45,6 +55,6 @@
 
     def test_patch_bad_data(self):
         id = '@jupyterlab/codemirror-extension:commands'
-        data = dict(keyMap=10)
+
         with assert_http_error(400):
-            self.settings_api.put(id, data)
+            self.settings_api.put(id, dict(keyMap=10))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/test_workspaces_api.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/test_workspaces_api.py
--- 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/test_workspaces_api.py 
    2018-01-05 04:17:45.000000000 +0100
+++ 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/test_workspaces_api.py 
    2018-07-20 12:50:50.000000000 +0200
@@ -1,5 +1,7 @@
 """Test the kernels service API."""
 import json
+import os
+import shutil
 
 from jupyterlab_launcher.tests.utils import LabTestBase, APITester
 
@@ -9,7 +11,10 @@
 
     url = 'lab/api/workspaces'
 
-    def get(self, section_name):
+    def delete(self, section_name):
+        return self._req('DELETE', section_name)
+
+    def get(self, section_name=''):
         return self._req('GET', section_name)
 
     def put(self, section_name, body):
@@ -20,15 +25,37 @@
     """Test the workspaces web service API"""
 
     def setUp(self):
+        data = os.path.join(
+            os.path.abspath(os.path.dirname(__file__)),
+            'workspaces')
+        for item in os.listdir(data):
+            src = os.path.join(data, item)
+            dst = os.path.join(self.lab_config.workspaces_dir, item)
+            if not os.path.exists(dst):
+                shutil.copy(src, self.lab_config.workspaces_dir)
         self.workspaces_api = WorkspacesAPI(self.request)
 
+    def test_delete(self):
+        orig = 'foo'
+        copy = 'baz'
+        data = self.workspaces_api.get(orig).json()
+        data['metadata']['id'] = copy
+
+        assert self.workspaces_api.put(copy, data).status_code == 204
+        assert self.workspaces_api.delete(copy).status_code == 204
+
     def test_get(self):
         id = 'foo'
-        data = self.workspaces_api.get(id).json()
-        assert data['metadata']['id'] == id
+
+        assert self.workspaces_api.get(id).json()['metadata']['id'] == id
+
+    def test_listing(self):
+        listing = set(['foo', 'bar'])
+
+        assert set(self.workspaces_api.get().json()['workspaces']) == listing
 
     def test_put(self):
         id = 'foo'
         data = self.workspaces_api.get(id).json()
-        resp = self.workspaces_api.put(id, data)
-        assert resp.status_code == 204
+
+        assert self.workspaces_api.put(id, data).status_code == 204
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/utils.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/utils.py
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/utils.py   
2018-01-05 04:17:45.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/utils.py   
2018-07-20 12:50:50.000000000 +0200
@@ -23,6 +23,8 @@
 
 
 class LabTestBase(NotebookTestBase):
+    Application = LabLauncherApp
+    """The application being tested. Sub-classes should change this."""
 
     @classmethod
     def setup_class(cls):
@@ -38,20 +40,30 @@
             return path
 
         cls.home_dir = tmp('home')
-        data_dir = cls.data_dir = tmp('data')
-        config_dir = cls.config_dir = tmp('config')
-        runtime_dir = cls.runtime_dir = tmp('runtime')
-        cls.notebook_dir = tmp('notebooks')
+        cls.data_dir = tmp('data')
+        cls.config_dir = tmp('config')
+        cls.runtime_dir = tmp('runtime')
+        cls.lab_dir = tmp('lab')
+        cls.lab_schemas = tmp('labschemas')
+        cls.lab_settings = tmp('labsettings')
+        cls.lab_workspaces = tmp('labworkspaces')
         cls.env_patch = patch.dict('os.environ', {
             'HOME': cls.home_dir,
             'PYTHONPATH': os.pathsep.join(sys.path),
             'IPYTHONDIR': pjoin(cls.home_dir, '.ipython'),
             'JUPYTER_NO_CONFIG': '1',  # needed in the future
-            'JUPYTER_CONFIG_DIR': config_dir,
-            'JUPYTER_DATA_DIR': data_dir,
-            'JUPYTER_RUNTIME_DIR': runtime_dir,
+            'JUPYTER_CONFIG_DIR': cls.config_dir,
+            'JUPYTER_DATA_DIR': cls.data_dir,
+            'JUPYTER_RUNTIME_DIR': cls.runtime_dir,
+            'JUPYTERLAB_DIR': cls.lab_dir,
+            'JUPYTERLAB_SETTINGS_DIR': cls.lab_settings
         })
         cls.env_patch.start()
+        cls.lab_config = LabConfig(
+            schemas_dir=cls.lab_schemas,
+            user_settings_dir=cls.lab_settings,
+            workspaces_dir=cls.lab_workspaces)
+        cls.notebook_dir = tmp('notebooks')
         cls.path_patch = patch.multiple(
             jupyter_core.paths,
             SYSTEM_JUPYTER_PATH=[tmp('share', 'jupyter')],
@@ -61,19 +73,19 @@
         )
         cls.path_patch.start()
 
-        config = cls.config or Config()
-        config.NotebookNotary.db_file = ':memory:'
+        cls.config = cls.config or Config()
+        cls.config.NotebookNotary.db_file = ':memory:'
 
         cls.token = hexlify(os.urandom(4)).decode('ascii')
 
         started = Event()
 
-        lab_config = LabConfig(schemas_dir=pjoin(here, 'schemas'),
-                               user_settings_dir=tmp('user_settings'),
-                               workspaces_dir=pjoin(here, 'workspaces'))
-
         def start_thread():
-            app = cls.notebook = LabLauncherApp(
+            if 'asyncio' in sys.modules:
+                import asyncio
+                asyncio.set_event_loop(asyncio.new_event_loop())
+            app = cls.notebook = cls.Application(
+                app_dir=cls.lab_dir,
                 port=cls.port,
                 port_retries=0,
                 open_browser=False,
@@ -82,10 +94,10 @@
                 runtime_dir=cls.runtime_dir,
                 notebook_dir=cls.notebook_dir,
                 base_url=cls.url_prefix,
-                config=config,
+                config=cls.config,
                 allow_root=True,
                 token=cls.token,
-                lab_config=lab_config
+                lab_config=cls.lab_config
             )
             # don't register signal handler during tests
             app.init_signal = lambda: None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/workspaces/bar.jupyterlab-workspace
 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/workspaces/bar.jupyterlab-workspace
--- 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/workspaces/bar.jupyterlab-workspace
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/workspaces/bar.jupyterlab-workspace
        2018-07-20 12:50:50.000000000 +0200
@@ -0,0 +1 @@
+{"data": {}, "metadata": {"id": "bar"}}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/workspaces/foo.jupyterlab-workspace
 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/workspaces/foo.jupyterlab-workspace
--- 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/tests/workspaces/foo.jupyterlab-workspace
        2018-01-05 04:17:45.000000000 +0100
+++ 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/tests/workspaces/foo.jupyterlab-workspace
        2018-07-20 12:50:50.000000000 +0200
@@ -1 +1 @@
-{"metadata": {"id": "foo"}, "data": {}}
\ No newline at end of file
+{"data": {}, "metadata": {"id": "foo"}}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/workspaces_handler.py 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/workspaces_handler.py
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher/workspaces_handler.py    
2018-02-13 18:35:18.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher/workspaces_handler.py    
2018-07-20 12:50:50.000000000 +0200
@@ -17,30 +17,63 @@
     def initialize(self, path, default_filename=None, workspaces_url=None):
         self.workspaces_dir = path
 
+    def ensure_directory(self):
+        if not self.workspaces_dir:
+            raise web.HTTPError(500, 'Workspaces directory is not set')
+
+        return self.workspaces_dir
+
+    @json_errors
+    @web.authenticated
+    def delete(self, space_name):
+        directory = self.ensure_directory()
+
+        if not space_name:
+            raise web.HTTPError(400, 'Workspace name is required for DELETE')
+
+        workspace_path = os.path.join(directory, space_name + _file_extension)
+        if not os.path.exists(workspace_path):
+            raise web.HTTPError(404, 'Workspace %r not found' % space_name)
+
+        try:  # to delete the workspace file.
+            os.remove(workspace_path)
+            return self.set_status(204)
+        except Exception as e:
+            raise web.HTTPError(500, str(e))
+
     @json_errors
     @web.authenticated
-    def get(self, space_name):
-        directory = self.workspaces_dir
-        self.set_header('Content-Type', 'application/json')
+    def get(self, space_name=''):
+        directory = self.ensure_directory()
+
+        if not space_name:
+            if not os.path.exists(directory):
+                return self.finish(json.dumps(dict(workspaces=[])))
+
+            try:  # to read the contents of the workspaces directory.
+                items = [item[:-len(_file_extension)]
+                         for item in os.listdir(directory)
+                         if item.endswith(_file_extension)]
+                items.sort()
+
+                return self.finish(json.dumps(dict(workspaces=items)))
+            except Exception as e:
+                raise web.HTTPError(500, str(e))
+
         workspace_path = os.path.join(directory, space_name + _file_extension)
         if os.path.exists(workspace_path):
             with open(workspace_path) as fid:
-                # Attempt to load and parse the workspace file.
-                try:
-                    workspace = json.load(fid)
+                try:  # to load and parse the workspace file.
+                    return self.finish(json.dumps(json.load(fid)))
                 except Exception as e:
                     raise web.HTTPError(500, str(e))
         else:
-            raise web.HTTPError(404, 'Workspace not found: %r' % space_name)
-
-        self.finish(json.dumps(workspace))
+            raise web.HTTPError(404, 'Workspace %r not found' % space_name)
 
     @json_errors
     @web.authenticated
     def put(self, space_name):
-        directory = self.workspaces_dir
-        if not directory:
-            raise web.HTTPError(500, 'No current workspaces directory')
+        directory = self.ensure_directory()
 
         if not os.path.exists(directory):
             try:
@@ -59,8 +92,10 @@
             raise web.HTTPError(400, str(e))
 
         # Make sure metadata ID matches the workspace name.
-        if workspace['metadata']['id'] != space_name:
-            message = 'Workspace metadata ID mismatch: %r' % space_name
+        metadata_id = workspace['metadata']['id']
+        if metadata_id != space_name:
+            message = ('Workspace metadata ID mismatch: expected %r got %r'
+                       % (space_name, metadata_id))
             raise web.HTTPError(400, message)
 
         # Write the workspace data to a file.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher.egg-info/PKG-INFO 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher.egg-info/PKG-INFO
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher.egg-info/PKG-INFO        
2018-02-20 19:04:12.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher.egg-info/PKG-INFO        
2018-07-20 13:01:43.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: jupyterlab-launcher
-Version: 0.10.5
+Version: 0.11.2
 Summary: Jupyter Launcher
 Home-page: http://jupyter.org
 Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyterlab_launcher-0.10.5/jupyterlab_launcher.egg-info/SOURCES.txt 
new/jupyterlab_launcher-0.11.2/jupyterlab_launcher.egg-info/SOURCES.txt
--- old/jupyterlab_launcher-0.10.5/jupyterlab_launcher.egg-info/SOURCES.txt     
2018-02-20 19:04:12.000000000 +0100
+++ new/jupyterlab_launcher-0.11.2/jupyterlab_launcher.egg-info/SOURCES.txt     
2018-07-20 13:01:43.000000000 +0200
@@ -1,6 +1,7 @@
 LICENSE
 MANIFEST.in
 README.md
+setup.cfg
 setup.py
 setupbase.py
 jupyterlab_launcher/__init__.py
@@ -10,6 +11,8 @@
 jupyterlab_launcher/handlers.py
 jupyterlab_launcher/index.html
 jupyterlab_launcher/json_minify.py
+jupyterlab_launcher/process.py
+jupyterlab_launcher/process_app.py
 jupyterlab_launcher/settings_handler.py
 jupyterlab_launcher/themes_handler.py
 jupyterlab_launcher/workspaces_handler.py
@@ -25,4 +28,5 @@
 jupyterlab_launcher/tests/schemas/@jupyterlab/apputils-extension/themes.json
 
jupyterlab_launcher/tests/schemas/@jupyterlab/codemirror-extension/commands.json
 jupyterlab_launcher/tests/schemas/@jupyterlab/shortcuts-extension/plugin.json
+jupyterlab_launcher/tests/workspaces/bar.jupyterlab-workspace
 jupyterlab_launcher/tests/workspaces/foo.jupyterlab-workspace
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyterlab_launcher-0.10.5/setup.cfg 
new/jupyterlab_launcher-0.11.2/setup.cfg
--- old/jupyterlab_launcher-0.10.5/setup.cfg    2018-02-20 19:04:12.000000000 
+0100
+++ new/jupyterlab_launcher-0.11.2/setup.cfg    2018-07-20 13:01:43.000000000 
+0200
@@ -1,3 +1,6 @@
+[metadata]
+license_file = LICENSE
+
 [egg_info]
 tag_build = 
 tag_date = 0


Reply via email to