Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-jupyter-server for 
openSUSE:Factory checked in at 2021-02-15 23:20:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jupyter-server (Old)
 and      /work/SRC/openSUSE:Factory/.python-jupyter-server.new.28504 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jupyter-server"

Mon Feb 15 23:20:21 2021 rev:11 rq:872453 version:1.3.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-jupyter-server/python-jupyter-server.changes  
    2021-02-10 21:30:06.502260260 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-jupyter-server.new.28504/python-jupyter-server.changes
   2021-02-15 23:22:02.979911033 +0100
@@ -1,0 +2,15 @@
+Sun Feb 14 18:05:36 UTC 2021 - Arun Persaud <a...@gmx.de>
+
+- update to version 1.3.0:
+  * Special case ExtensionApp that starts the ServerApp #401 (afshin)
+  * only use deprecated notebook_dir config if root_dir is not set
+    #400 (minrk)
+  * Use async kernel manager by default #399 (kevin-bates)
+  * Revert Session.username default value change #398 (mwakaba2)
+  * Re-enable default_url in ExtensionApp #393 (afshin)
+  * Enable notebook ContentsManager in jupyter_server #392 (afshin)
+  * Use jupyter_server_config.json as config file in the update
+    password api #390 (echarles)
+  * Increase culling test idle timeout #388 (kevin-bates)
+
+-------------------------------------------------------------------

Old:
----
  jupyter_server-1.2.3.tar.gz

New:
----
  jupyter_server-1.3.0.tar.gz

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

Other differences:
------------------
++++++ python-jupyter-server.spec ++++++
--- /var/tmp/diff_new_pack.9sfjgF/_old  2021-02-15 23:22:03.719912138 +0100
+++ /var/tmp/diff_new_pack.9sfjgF/_new  2021-02-15 23:22:03.723912144 +0100
@@ -19,15 +19,13 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define         skip_python2 1
 Name:           python-jupyter-server
-Version:        1.2.3
+Version:        1.3.0
 Release:        0
 Summary:        The Jupyter Server
 License:        BSD-3-Clause
 Group:          Development/Languages/Python
 URL:            https://github.com/jupyter/jupyter_server
 Source:         
https://github.com/jupyter/jupyter_server/archive/%{version}.tar.gz#/jupyter_server-%{version}.tar.gz
-# We need the full stdlib
-BuildRequires:  %pythons
 BuildRequires:  %{python_module Jinja2}
 BuildRequires:  %{python_module Send2Trash}
 BuildRequires:  %{python_module anyio >= 2.0.2}
@@ -42,6 +40,8 @@
 BuildRequires:  %{python_module terminado >= 0.8.3}
 BuildRequires:  %{python_module tornado >= 6.1}
 BuildRequires:  %{python_module traitlets >= 4.2.1}
+# We need the full stdlib
+BuildRequires:  %{pythons}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-Jinja2
@@ -59,6 +59,8 @@
 Requires:       python-traitlets >= 4.2.1
 Requires(post): update-alternatives
 Requires(postun): update-alternatives
+Provides:       python-jupyter_server = %{version}-%{release}
+Obsoletes:      python-jupyter_server < %{version}-%{release}
 # SECTION extras_require test
 BuildRequires:  %{python_module ipykernel}
 BuildRequires:  %{python_module pytest-console-scripts}
@@ -66,8 +68,6 @@
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module requests}
 # /SECTION
-Provides:       python-jupyter_server = %{version}-%{release}
-Obsoletes:      python-jupyter_server < %{version}-%{release}
 %if "%{python_flavor}" == "python3" || "%{python_provides}" == "python3"
 Provides:       jupyter-jupyter-server = %{version}-%{release}
 Obsoletes:      jupyter-jupyter-server < %{version}-%{release}

++++++ jupyter_server-1.2.3.tar.gz -> jupyter_server-1.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.2.3/CHANGELOG.md 
new/jupyter_server-1.3.0/CHANGELOG.md
--- old/jupyter_server-1.2.3/CHANGELOG.md       2021-01-30 00:34:24.000000000 
+0100
+++ new/jupyter_server-1.3.0/CHANGELOG.md       2021-02-05 02:40:21.000000000 
+0100
@@ -2,8 +2,22 @@
 
 All notable changes to this project will be documented in this file.
 
+## [1.3.0](https://github.com/jupyter-server/jupyter_server/tree/1.3.0) 
(2021-02-4)
+
+[Full 
Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.2.3...HEAD)
+
+**Merged pull requests:**
+
+- Special case ExtensionApp that starts the ServerApp 
[\#401](https://github.com/jupyter-server/jupyter_server/pull/401) 
([afshin](https://github.com/afshin))
+- only use deprecated notebook\_dir config if root\_dir is not set 
[\#400](https://github.com/jupyter-server/jupyter_server/pull/400) 
([minrk](https://github.com/minrk))
+- Use async kernel manager by default 
[\#399](https://github.com/jupyter-server/jupyter_server/pull/399) 
([kevin-bates](https://github.com/kevin-bates))
+- Revert Session.username default value change 
[\#398](https://github.com/jupyter-server/jupyter_server/pull/398) 
([mwakaba2](https://github.com/mwakaba2))
+
+
 ## [1.2.3](https://github.com/jupyter-server/jupyter_server/tree/1.2.3) 
(2021-01-29)
 
+This was a broken release and was yanked from PyPI.
+
 [Full 
Changelog](https://github.com/jupyter-server/jupyter_server/compare/1.2.2...HEAD)
 
 **Merged pull requests:**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.2.3/docs/source/developers/extensions.rst 
new/jupyter_server-1.3.0/docs/source/developers/extensions.rst
--- old/jupyter_server-1.2.3/docs/source/developers/extensions.rst      
2021-01-30 00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/docs/source/developers/extensions.rst      
2021-02-05 02:40:21.000000000 +0100
@@ -128,7 +128,7 @@
 
         # -------------- Required traits --------------
         name = "myextension"
-        extension_url = "/myextension"
+        default_url = "/myextension"
         load_other_extensions = True
 
         # --- ExtensionApp traits you can configure ---
@@ -167,7 +167,7 @@
 Properties
 
 * ``name``: the name of the extension
-* ``extension_url``: the default url for this extension???i.e. the landing 
page for this extension when launched from the CLI.
+* ``default_url``: the default url for this extension???i.e. the landing page 
for this extension when launched from the CLI.
 * ``load_other_extensions``: a boolean enabling/disabling other extensions 
when launching this extension directly.
 
 ``ExtensionApp`` request handlers
@@ -302,13 +302,13 @@
 ``ExtensionApp`` as a classic Notebook server extension
 -------------------------------------------------------
 
-An extension that extends ``ExtensionApp`` should still work with the old 
Tornado server from the classic Jupyter Notebook. The ``ExtensionApp`` class 
+An extension that extends ``ExtensionApp`` should still work with the old 
Tornado server from the classic Jupyter Notebook. The ``ExtensionApp`` class
 provides a method, ``load_classic_server_extension``, that handles the 
extension initialization. Simply  define a ``load_jupyter_server_extension`` 
reference
-pointing at the ``load_classic_server_extension`` method: 
+pointing at the ``load_classic_server_extension`` method:
 
 .. code-block:: python
 
-    # This is typically defined in the root `__init__.py` 
+    # This is typically defined in the root `__init__.py`
     # file of the extension package.
     load_jupyter_server_extension = 
MyExtensionApp.load_classic_server_extension
 
@@ -483,7 +483,7 @@
     .. code-block:: python
 
         def load_jupyter_server_extension(nb_server_app):
-            
+
             web_app = nb_server_app.web_app
             host_pattern = '.*$'
             base_url = web_app.settings['base_url']
@@ -495,50 +495,50 @@
 
             # Favicon redirects.
             favicon_redirects = [
-                (   
-                    url_path_join(base_url, "/static/favicons/favicon.ico"), 
+                (
+                    url_path_join(base_url, "/static/favicons/favicon.ico"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/favicon.ico")
                 ),
                 (
-                    url_path_join(base_url, 
"/static/favicons/favicon-busy-1.ico"), 
+                    url_path_join(base_url, 
"/static/favicons/favicon-busy-1.ico"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/favicon-busy-1.ico")}
                 ),
                 (
-                    url_path_join(base_url, 
"/static/favicons/favicon-busy-2.ico"), 
+                    url_path_join(base_url, 
"/static/favicons/favicon-busy-2.ico"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/favicon-busy-2.ico")}
                 ),
                 (
-                    url_path_join(base_url, 
"/static/favicons/favicon-busy-3.ico"), 
+                    url_path_join(base_url, 
"/static/favicons/favicon-busy-3.ico"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/favicon-busy-3.ico")}
                 ),
                 (
-                    url_path_join(base_url, 
"/static/favicons/favicon-file.ico"), 
+                    url_path_join(base_url, 
"/static/favicons/favicon-file.ico"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/favicon-file.ico")}
                 ),
                 (
-                    url_path_join(base_url, 
"/static/favicons/favicon-notebook.ico"), 
+                    url_path_join(base_url, 
"/static/favicons/favicon-notebook.ico"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/favicon-notebook.ico")}
                 ),
                 (
-                    url_path_join(base_url, 
"/static/favicons/favicon-terminal.ico"), 
+                    url_path_join(base_url, 
"/static/favicons/favicon-terminal.ico"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/favicon-terminal.ico")}
                 ),
                 (
-                    url_path_join(base_url, "/static/logo/logo.png"), 
+                    url_path_join(base_url, "/static/logo/logo.png"),
                     RedirectHandler,
                     {"url": url_path_join(serverapp.base_url, 
"static/base/images/logo.png")}
                 ),
             ]
 
             web_app.add_handlers(
-                host_pattern, 
+                host_pattern,
                 custom_handlers + favicon_redirects
             )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.2.3/docs/source/other/full-config.rst 
new/jupyter_server-1.3.0/docs/source/other/full-config.rst
--- old/jupyter_server-1.2.3/docs/source/other/full-config.rst  2021-01-30 
00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/docs/source/other/full-config.rst  2021-02-05 
02:40:21.000000000 +0100
@@ -110,9 +110,9 @@
     Default: ``''``
 
     Set the Access-Control-Allow-Origin header
-    
+
     Use '*' to allow any origin to access your server.
-    
+
     Takes precedence over allow_origin_pat.
 
 
@@ -120,13 +120,13 @@
     Default: ``''``
 
     Use a regular expression for the Access-Control-Allow-Origin header
-    
+
     Requests from an origin matching the expression will get replies with:
-    
+
         Access-Control-Allow-Origin: origin
-    
+
     where `origin` is the origin of the request.
-    
+
     Ignored if allow_origin is set.
 
 
@@ -134,11 +134,11 @@
     Default: ``True``
 
     Allow password to be changed at login for the Jupyter server.
-    
+
     While loggin in with a token, the Jupyter server UI will give the 
opportunity to
     the user to enter a new password at the same time that will replace
     the token login mechanism.
-    
+
     This can be set to false to prevent changing password from the UI/API.
 
 
@@ -146,15 +146,15 @@
     Default: ``False``
 
     Allow requests where the Host header doesn't point to a local server
-    
+
     By default, requests get a 403 forbidden response if the 'Host' header
     shows that the browser thinks it's on a non-local domain.
     Setting this option to True disables this check.
-    
+
     This protects against 'DNS rebinding' attacks, where a remote web server
     serves you a page and then changes its DNS to send later requests to a
     local IP, bypassing same-origin checks.
-    
+
     Local IP addresses (such as 127.0.0.1 and ::1) are allowed as local,
     along with hostnames configured in local_hostnames.
 
@@ -173,7 +173,7 @@
     Default: ``'/'``
 
     The base URL for the Jupyter server.
-    
+
     Leading and trailing slashes can be omitted,
     and will automatically be added.
 
@@ -229,7 +229,7 @@
     The random bytes used to secure cookies.
     By default this is a new random number every time you start the server.
     Set it to a value in a config file to enable logins to persist across 
server sessions.
-    
+
     Note: Cookie secrets should be kept private, do not share config files with
     cookie_secret stored in plaintext (you can read the value from a file).
 
@@ -243,12 +243,12 @@
     Default: ``''``
 
     Override URL shown to users.
-    
+
     Replace actual URL, including protocol, address, port and base URL,
     with the given value when displaying URL to the users. Do not change
     the actual connection URL. If authentication token is enabled, the
     token is added to the custom URL automatically.
-    
+
     This option is intended to be used when the URL to display to the user
     cannot be determined reliably by the Jupyter server (proxified
     or containerized setups for example).
@@ -262,13 +262,13 @@
     Default: ``False``
 
     Disable cross-site-request-forgery protection
-    
+
     Jupyter notebook 4.3.1 introduces protection from cross-site request 
forgeries,
     requiring API requests to either:
-    
+
     - originate from pages served by this server (validated with XSRF cookie 
and token), or
     - authenticate with a token
-    
+
     Some anonymous compute resources still desire the ability to run code,
     completely without authentication.
     These services can disable all authentication and security checks,
@@ -284,7 +284,7 @@
     Default: ``[]``
 
     Extra paths to search for serving static files.
-    
+
     This allows adding javascript/css to be available from the Jupyter server 
machine,
     or overriding individual files in the IPython
 
@@ -292,7 +292,7 @@
     Default: ``[]``
 
     Extra paths to search for serving jinja templates.
-    
+
     Can be used to override templates from jupyter_server.templates.
 
 ServerApp.file_to_run : Unicode
@@ -352,10 +352,10 @@
 ServerApp.kernel_spec_manager_class : Type
     Default: ``'jupyter_client.kernelspec.KernelSpecManager'``
 
-    
+
     The kernel spec manager class to use. Should be a subclass
     of `jupyter_client.kernelspec.KernelSpecManager`.
-    
+
     The Api of KernelSpecManager is provisional and might change
     without warning between this version of Jupyter and the next stable one.
 
@@ -369,7 +369,7 @@
     Default: ``['localhost']``
 
     Hostnames to allow as local when allow_remote_access is False.
-    
+
     Local IP addresses (such as 127.0.0.1 and ::1) are automatically accepted
     as local as well.
 
@@ -402,19 +402,19 @@
 ServerApp.max_body_size : Int
     Default: ``536870912``
 
-    
+
     Sets the maximum allowed size of the client request body, specified in
     the Content-Length request header field. If the size in a request
     exceeds the configured value, a malformed HTTP message is returned to
     the client.
-    
+
     Note: max_body_size is applied even in streaming mode.
 
 
 ServerApp.max_buffer_size : Int
     Default: ``536870912``
 
-    
+
     Gets or sets the maximum amount of memory, in bytes, that is allocated
     for use by the buffer manager.
 
@@ -438,11 +438,11 @@
     Default: ``''``
 
     Hashed password to use for web authentication.
-    
+
     To generate, type in a python/IPython shell:
-    
+
       from jupyter_server.auth import passwd; passwd()
-    
+
     The string should be of the form type:salt:hashed-password.
 
 
@@ -452,10 +452,10 @@
     Forces users to use a password for the Jupyter server.
     This is useful in a multi user environment, for instance when
     everybody in the LAN can access each other's machine through ssh.
-    
+
     In such a case, serving on localhost is not secure since
     any user can connect to the Jupyter server via ssh.
-    
+
 
 
 ServerApp.port : Int
@@ -471,7 +471,7 @@
 ServerApp.pylab : Unicode
     Default: ``'disabled'``
 
-    
+
     DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.
 
 
@@ -531,10 +531,10 @@
     Default: ``True``
 
     Set to False to disable terminals.
-    
+
     This does *not* make the server more secure by itself.
     Anything the user can in a terminal, they can also do in a notebook.
-    
+
     Terminals may also be automatically disabled if the terminado package
     is not available.
 
@@ -543,10 +543,10 @@
     Default: ``'<generated>'``
 
     Token used for authenticating first-time connections to the server.
-    
+
     When no password is enabled,
     the default is to generate a new, random token.
-    
+
     Setting to an empty string disables authentication altogether, which is 
NOT RECOMMENDED.
 
 
@@ -567,24 +567,24 @@
     `new` argument passed to the standard library method `webbrowser.open`.
     The behaviour is not guaranteed, but depends on browser support. Valid
     values are:
-    
+
      - 2 opens a new tab,
      - 1 opens a new window,
      - 0 opens in an existing window.
-    
+
     See the `webbrowser.open` documentation for details.
 
 
 ServerApp.websocket_compression_options : Any
     Default: ``None``
 
-    
+
     Set the tornado compression options for websocket connections.
-    
+
     This value will be returned from 
:meth:`WebSocketHandler.get_compression_options`.
     None (default) will disable compression.
     A dict (even an empty one) will enable compression.
-    
+
     See the tornado docs for WebSocketHandler.get_compression_options for 
details.
 
 
@@ -593,7 +593,7 @@
 
     The base URL for websockets,
     if it differs from the HTTP server (hint: it almost certainly doesn't).
-    
+
     Should be in the form of an HTTP origin: ws[s]://hostname[:port]
 
 
@@ -601,7 +601,7 @@
     Default: ``''``
 
     JSON file in which to store connection info [default: kernel-<pid>.json]
-    
+
     This file will contain the IP, ports, and authentication key needed to 
connect
     clients to this kernel. By default, this file will be created in the 
security dir
     of the current profile, but can be specified by absolute path.
@@ -654,7 +654,7 @@
     Default: ``''``
 
     JSON file in which to store connection info [default: kernel-<pid>.json]
-    
+
     This file will contain the IP, ports, and authentication key needed to 
connect
     clients to this kernel. By default, this file will be created in the 
security dir
     of the current profile, but can be specified by absolute path.
@@ -687,7 +687,7 @@
     Default: ``[]``
 
     DEPRECATED: Use kernel_name instead.
-    
+
     The Popen Command to launch the kernel.
     Override this if you have a custom kernel.
     If kernel_cmd is specified in a configuration file,
@@ -727,7 +727,7 @@
     Default: ``True``
 
     Whether to check PID to protect against calls after fork.
-    
+
     This check can be disabled if fork-safety is handled elsewhere.
 
 
@@ -745,7 +745,7 @@
     Default: ``65536``
 
     The maximum number of digests to remember.
-    
+
     The digest history will be culled when it exceeds this value.
 
 
@@ -796,7 +796,7 @@
     Only used with custom functions for `packer`.
 
 Session.username : Unicode
-    Default: ``'mwakabayashi'``
+    Default: ``'username'``
 
     Username for the Session. Default is your system username.
 
@@ -833,10 +833,10 @@
     Default: ``True``
 
     Whether messages from kernels whose frontends have disconnected should be 
buffered in-memory.
-    
+
     When True (default), messages are buffered and replayed on reconnect,
     avoiding lost messages due to interrupted connectivity.
-    
+
     Disable if long-running kernels will produce too much output while
     no frontends are connected.
 
@@ -874,7 +874,7 @@
     Default: ``60``
 
     Timeout for giving up on a kernel (in seconds).
-    
+
     On starting and restarting kernels, we check whether the
     kernel is running and responsive by sending kernel_info_requests.
     This sets the timeout in seconds for how long the kernel can take
@@ -923,7 +923,7 @@
     Default: ``set()``
 
     Whitelist of allowed kernel names.
-    
+
     By default, all installed kernels are allowed.
 
 
@@ -951,13 +951,13 @@
     Default: ``'jupyter_server.files.handlers.FilesHandler'``
 
     handler class to use when serving raw file requests.
-    
+
     Default is a fallback that talks to the ContentsManager API,
     which may be inefficient, especially for large files.
-    
+
     Local files-based ContentsManagers can use a StaticFileHandler subclass,
     which will be much more efficient.
-    
+
     Access to these files should be Authenticated.
 
 
@@ -965,7 +965,7 @@
     Default: ``{}``
 
     Extra parameters to pass to files_handler_class.
-    
+
     For example, StaticFileHandlers generally expect a `path` argument
     specifying the root directory from which to serve files.
 
@@ -973,7 +973,7 @@
 ContentsManager.hide_globs : List
     Default: ``['__pycache__', '*.pyc', '*.pyo', '.DS_Store', '*.so', 
'*.dyl...``
 
-    
+
     Glob patterns to hide in file and directory listings.
 
 
@@ -981,17 +981,17 @@
     Default: ``None``
 
     Python callable or importstring thereof
-    
+
     To be called on a contents model prior to save.
-    
+
     This can be used to process the structure,
     such as removing notebook outputs or other side effects that
     should not be saved.
-    
+
     It will be called as (all arguments passed by keyword)::
-    
+
         hook(path=path, model=model, contents_manager=self)
-    
+
     - model: the model to be saved. Includes file contents.
       Modifying this dict will affect the file that is stored.
     - path: the API path of the save destination
@@ -1056,13 +1056,13 @@
     Default: ``'jupyter_server.files.handlers.FilesHandler'``
 
     handler class to use when serving raw file requests.
-    
+
     Default is a fallback that talks to the ContentsManager API,
     which may be inefficient, especially for large files.
-    
+
     Local files-based ContentsManagers can use a StaticFileHandler subclass,
     which will be much more efficient.
-    
+
     Access to these files should be Authenticated.
 
 
@@ -1070,7 +1070,7 @@
     Default: ``{}``
 
     Extra parameters to pass to files_handler_class.
-    
+
     For example, StaticFileHandlers generally expect a `path` argument
     specifying the root directory from which to serve files.
 
@@ -1078,7 +1078,7 @@
 FileContentsManager.hide_globs : List
     Default: ``['__pycache__', '*.pyc', '*.pyo', '.DS_Store', '*.so', 
'*.dyl...``
 
-    
+
     Glob patterns to hide in file and directory listings.
 
 
@@ -1086,16 +1086,16 @@
     Default: ``None``
 
     Python callable or importstring thereof
-    
+
     to be called on the path of a file just saved.
-    
+
     This can be used to process the file on disk,
     such as converting the notebook to a script or HTML via nbconvert.
-    
+
     It will be called as (all arguments passed by keyword)::
-    
+
         hook(os_path=os_path, model=model, contents_manager=instance)
-    
+
     - path: the filesystem path to the file just written
     - model: the model representing the file
     - contents_manager: this ContentsManager instance
@@ -1105,17 +1105,17 @@
     Default: ``None``
 
     Python callable or importstring thereof
-    
+
     To be called on a contents model prior to save.
-    
+
     This can be used to process the structure,
     such as removing notebook outputs or other side effects that
     should not be saved.
-    
+
     It will be called as (all arguments passed by keyword)::
-    
+
         hook(path=path, model=model, contents_manager=self)
-    
+
     - model: the model to be saved. Includes file contents.
       Modifying this dict will affect the file that is stored.
     - path: the API path of the save destination
@@ -1194,10 +1194,10 @@
     Default: ``True``
 
     Whether messages from kernels whose frontends have disconnected should be 
buffered in-memory.
-    
+
     When True (default), messages are buffered and replayed on reconnect,
     avoiding lost messages due to interrupted connectivity.
-    
+
     Disable if long-running kernels will produce too much output while
     no frontends are connected.
 
@@ -1235,7 +1235,7 @@
     Default: ``60``
 
     Timeout for giving up on a kernel (in seconds).
-    
+
     On starting and restarting kernels, we check whether the
     kernel is running and responsive by sending kernel_info_requests.
     This sets the timeout in seconds for how long the kernel can take
@@ -1284,7 +1284,7 @@
     Default: ``set()``
 
     Whitelist of allowed kernel names.
-    
+
     By default, all installed kernels are allowed.
 
 
@@ -1388,4 +1388,3 @@
 
     The websocket url of the Kernel or Enterprise Gateway server.  If not 
provided, this value
     will correspond to the value of the Gateway url with 'ws' in place of 
'http'.  (JUPYTER_GATEWAY_WS_URL env var)
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.2.3/jupyter_server/_version.py 
new/jupyter_server-1.3.0/jupyter_server/_version.py
--- old/jupyter_server-1.2.3/jupyter_server/_version.py 2021-01-30 
00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/jupyter_server/_version.py 2021-02-05 
02:40:21.000000000 +0100
@@ -11,5 +11,5 @@
 # However, be sure to INCLUDE a dot prefix when adding the dev release: 
X.Y.Z.devN
 # See: https://www.python.org/dev/peps/pep-0440/#public-version-identifiers
 
-version_info = (1, 2, 3, '')
+version_info = (1, 3, 0, '')
 __version__ = '.'.join(map(str, version_info[:3])) + ''.join(version_info[3:])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.2.3/jupyter_server/extension/application.py 
new/jupyter_server-1.3.0/jupyter_server/extension/application.py
--- old/jupyter_server-1.2.3/jupyter_server/extension/application.py    
2021-01-30 00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/jupyter_server/extension/application.py    
2021-02-05 02:40:21.000000000 +0100
@@ -4,6 +4,7 @@
 
 from jinja2 import Environment, FileSystemLoader
 
+from traitlets.config import Config
 from traitlets import (
     HasTraits,
     Unicode,
@@ -12,7 +13,6 @@
     Bool,
     default
 )
-from traitlets.config import Config
 from tornado.log import LogFormatter
 from tornado.web import RedirectHandler
 
@@ -186,6 +186,9 @@
     def _default_url(self):
         return self.extension_url
 
+    # Is this linked to a serverapp yet?
+    _linked = Bool(False)
+
     # Extension can configure the ServerApp from the command-line
     classes = [
         ServerApp,
@@ -196,9 +199,6 @@
 
     _log_formatter_cls = LogFormatter
 
-    # Whether this app is the starter app
-    _is_starter_app = False
-
     @default('log_level')
     def _default_log_level(self):
         return logging.INFO
@@ -333,14 +333,14 @@
             })
         self.initialize_templates()
 
-    @classmethod
-    def _jupyter_server_config(cls):
+    def _jupyter_server_config(self):
         base_config = {
             "ServerApp": {
-                "jpserver_extensions": {cls.get_extension_package(): True},
+                "default_url": self.default_url,
+                "open_browser": self.open_browser
             }
         }
-        base_config["ServerApp"].update(cls.serverapp_config)
+        base_config["ServerApp"].update(self.serverapp_config)
         return base_config
 
     def _link_jupyter_server_extension(self, serverapp):
@@ -351,6 +351,10 @@
         the command line contains traits for the ExtensionApp
         or the ExtensionApp's config files have server
         settings.
+
+        Note, the ServerApp has not initialized the Tornado
+        Web Application yet, so do not try to affect the
+        `web_app` attribute.
         """
         self.serverapp = serverapp
         # Load config from an ExtensionApp's config files.
@@ -370,23 +374,8 @@
         # ServerApp, do it here.
         # i.e. ServerApp traits <--- ExtensionApp config
         self.serverapp.update_config(self.config)
-
-    @classmethod
-    def initialize_server(cls, argv=[], load_other_extensions=True, **kwargs):
-        """Creates an instance of ServerApp where this extension is enabled
-        (superceding disabling found in other config from files).
-
-        This is necessary when launching the ExtensionApp directly from
-        the `launch_instance` classmethod.
-        """
-        # The ExtensionApp needs to add itself as enabled extension
-        # to the jpserver_extensions trait, so that the ServerApp
-        # initializes it.
-        config = Config(cls._jupyter_server_config())
-        serverapp = ServerApp.instance(**kwargs, argv=[], config=config)
-        cls._is_starter_app = True
-        serverapp.initialize(argv=argv, find_extensions=load_other_extensions)
-        return serverapp
+        # Acknowledge that this extension has been linked.
+        self._linked = True
 
     def initialize(self):
         """Initialize the extension app. The
@@ -440,12 +429,7 @@
         except KeyError:
             extension = cls()
             extension._link_jupyter_server_extension(serverapp)
-        if cls._is_starter_app:
-            serverapp._starter_app = extension
         extension.initialize()
-        # Set the serverapp's default url to the extension's url.
-        if cls._is_starter_app:
-            serverapp.default_url = extension.default_url
         return extension
 
     @classmethod
@@ -479,6 +463,24 @@
         extension.initialize()
 
     @classmethod
+    def initialize_server(cls, argv=[], load_other_extensions=True, **kwargs):
+        """Creates an instance of ServerApp and explicitly sets
+        this extension to enabled=True (i.e. superceding disabling
+        found in other config from files).
+
+        The `launch_instance` method uses this method to initialize
+        and start a server.
+        """
+        serverapp = ServerApp.instance(
+            jpserver_extensions={cls.get_extension_package(): True}, **kwargs)
+        serverapp.initialize(
+            argv=argv,
+            starter_extension=cls.name,
+            find_extensions=cls.load_other_extensions,
+        )
+        return serverapp
+
+    @classmethod
     def launch_instance(cls, argv=None, **kwargs):
         """Launch the extension like an application. Initializes+configs a 
stock server
         and appends the extension to the server. Then starts the server and 
routes to
@@ -489,27 +491,29 @@
             args = sys.argv[1:]  # slice out extension config.
         else:
             args = argv
-        # Check for subcommands
+
+        # Handle all "stops" that could happen before
+        # continuing to launch a server+extension.
         subapp = _preparse_for_subcommand(cls, args)
         if subapp:
             subapp.start()
-        else:
-            # Check for help, version, and generate-config arguments
-            # before initializing server to make sure these
-            # arguments trigger actions from the extension not the server.
-            _preparse_for_stopping_flags(cls, args)
-            # Get a jupyter server instance.
-            serverapp = cls.initialize_server(
-                argv=args,
-                load_other_extensions=cls.load_other_extensions
+            return
+
+        # Check for help, version, and generate-config arguments
+        # before initializing server to make sure these
+        # arguments trigger actions from the extension not the server.
+        _preparse_for_stopping_flags(cls, args)
+
+        serverapp = cls.initialize_server(argv=args)
+
+        # Log if extension is blocking other extensions from loading.
+        if not cls.load_other_extensions:
+            serverapp.log.info(
+                "{ext_name} is running without loading "
+                "other extensions.".format(ext_name=cls.name)
             )
-            # Log if extension is blocking other extensions from loading.
-            if not cls.load_other_extensions:
-                serverapp.log.info(
-                    "{ext_name} is running without loading "
-                    "other extensions.".format(ext_name=cls.name)
-                )
-            try:
-                serverapp.start()
-            except NoStart:
-                pass
+        # Start the server.
+        try:
+            serverapp.start()
+        except NoStart:
+            pass
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.2.3/jupyter_server/extension/manager.py 
new/jupyter_server-1.3.0/jupyter_server/extension/manager.py
--- old/jupyter_server-1.2.3/jupyter_server/extension/manager.py        
2021-01-30 00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/jupyter_server/extension/manager.py        
2021-02-05 02:40:21.000000000 +0100
@@ -1,11 +1,13 @@
 import importlib
 
-from traitlets.config import LoggingConfigurable
+from traitlets.config import LoggingConfigurable, Config
+
 from traitlets import (
     HasTraits,
     Dict,
     Unicode,
     Bool,
+    Any,
     validate
 )
 
@@ -21,12 +23,10 @@
     """A simple API for connecting to a Jupyter Server extension
     point defined by metadata and importable from a Python package.
     """
-    metadata = Dict()
+    _linked = Bool(False)
+    _app = Any(None, allow_none=True)
 
-    def __init__(self, *args, **kwargs):
-        # Store extension points that have been linked.
-        self._app = None
-        super().__init__(*args, **kwargs)
+    metadata = Dict()
 
     @validate('metadata')
     def _valid_metadata(self, proposed):
@@ -54,6 +54,13 @@
 
     @property
     def linked(self):
+        """Has this extension point been linked to the server.
+
+        Will pull from ExtensionApp's trait, if this point
+        is an instance of ExtensionApp.
+        """
+        if self.app:
+            return self.app._linked
         return self._linked
 
     @property
@@ -62,6 +69,16 @@
         return self._app
 
     @property
+    def config(self):
+        """Return any configuration provided by this extension point."""
+        if self.app:
+            return self.app._jupyter_server_config()
+        # At some point, we might want to add logic to load config from
+        # disk when extensions don't use ExtensionApp.
+        else:
+            return {}
+
+    @property
     def module_name(self):
         """Name of the Python package module where the extension's
         _load_jupyter_server_extension can be found.
@@ -119,8 +136,11 @@
         This looks for a `_link_jupyter_server_extension` function
         in the extension's module or ExtensionApp class.
         """
-        linker = self._get_linker()
-        return linker(serverapp)
+        if not self.linked:
+            linker = self._get_linker()
+            linker(serverapp)
+            # Store this extension as already linked.
+            self._linked = True
 
     def load(self, serverapp):
         """Load the extension in a Jupyter ServerApp object.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.2.3/jupyter_server/gateway/managers.py 
new/jupyter_server-1.3.0/jupyter_server/gateway/managers.py
--- old/jupyter_server-1.2.3/jupyter_server/gateway/managers.py 2021-01-30 
00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/jupyter_server/gateway/managers.py 2021-02-05 
02:40:21.000000000 +0100
@@ -9,7 +9,7 @@
 from tornado.escape import json_encode, json_decode, url_escape
 from tornado.httpclient import HTTPClient, AsyncHTTPClient, HTTPError
 
-from ..services.kernels.kernelmanager import MappingKernelManager
+from ..services.kernels.kernelmanager import AsyncMappingKernelManager
 from ..services.sessions.sessionmanager import SessionManager
 
 from jupyter_client.kernelspec import KernelSpecManager
@@ -329,7 +329,7 @@
     return response
 
 
-class GatewayKernelManager(MappingKernelManager):
+class GatewayKernelManager(AsyncMappingKernelManager):
     """Kernel manager that supports remote kernels hosted by Jupyter Kernel or 
Enterprise Gateway."""
 
     # We'll maintain our own set of kernel ids
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.2.3/jupyter_server/serverapp.py 
new/jupyter_server-1.3.0/jupyter_server/serverapp.py
--- old/jupyter_server-1.2.3/jupyter_server/serverapp.py        2021-01-30 
00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/jupyter_server/serverapp.py        2021-02-05 
02:40:21.000000000 +0100
@@ -956,25 +956,6 @@
        """
     )
 
-    # The name of the app that started this server (if not started directly).
-    # It is sometimes important to know if + which another app (say a server 
extension)
-    # started the serverapp to properly configure some traits.
-    # This trait should not be configured by users. It will likely be set by 
ExtensionApp.
-    _starter_app = Instance(JupyterApp, allow_none=True)
-
-    @validate('_starter_app')
-    def _validate_starter_app(self, proposal):
-        # Check that a previous server extension isn't named yet
-        value = proposal["value"]
-        if self._starter_app != None:
-            raise TraitError("Another extension was already named as the 
starter_server_extension.")
-        return value
-
-    @property
-    def starter_app(self):
-        """Get the Extension that started this server."""
-        return self._starter_app
-
     open_browser = Bool(False, config=True,
                         help="""Whether to open in a browser after starting.
                         The specific browser used is platform dependent and
@@ -983,31 +964,6 @@
                         (ServerApp.browser) configuration option.
                         """)
 
-
-    def _handle_browser_opening(self):
-        """This method handles whether a browser should be opened.
-        By default, Jupyter Server doesn't try to open an browser. However,
-        it's many server extensions might want to open the browser by default.
-        This essentially toggles the default value for open_browser.
-
-        From a UX perspective, this needs to be surfaced to the user. The 
default
-        behavior of Jupyter Server switches, which can be confusing.
-        """
-        # If the server was started by another application, use that 
applications
-        # trait for the open_browser trait. If that trait is not given, ignore
-        if self.starter_app:
-            try:
-                if self.starter_app.open_browser:
-                    self.launch_browser()
-            # If the starter_app doesn't have an open_browser trait, ignore
-            # move on and don't start a browser.
-            except AttributeError:
-                pass
-        else:
-            if self.open_browser:
-                self.launch_browser()
-
-
     browser = Unicode(u'', config=True,
                       help="""Specify what command to use to invoke a web
                       browser when starting the server. If not specified, the
@@ -1174,7 +1130,8 @@
         self.root_dir = change['new']
 
     kernel_manager_class = Type(
-        default_value=MappingKernelManager,
+        default_value=AsyncMappingKernelManager,
+        klass=MappingKernelManager,
         config=True,
         help=_('The kernel manager class to use.')
     )
@@ -1264,16 +1221,22 @@
 
     @observe('notebook_dir')
     def _update_notebook_dir(self, change):
+        if self._root_dir_set:
+            # only use deprecated config if new config is not set
+            return
         self.log.warning(_("notebook_dir is deprecated, use root_dir"))
         self.root_dir = change['new']
 
-    root_dir = Unicode(config=True,
+    root_dir = Unicode(
+        config=True,
         help=_("The directory to use for notebooks and kernels.")
     )
+    _root_dir_set = False
 
     @default('root_dir')
     def _default_root_dir(self):
         if self.file_to_run:
+            self._root_dir_set = True
             return os.path.dirname(os.path.abspath(self.file_to_run))
         else:
             return py3compat.getcwd()
@@ -1294,6 +1257,10 @@
             raise TraitError(trans.gettext("No such notebook dir: '%r'") % 
value)
         return value
 
+    @observe('root_dir')
+    def _root_dir_changed(self, change):
+        self._root_dir_set = True
+
     @observe('server_extensions')
     def _update_server_extensions(self, change):
         self.log.warning(_("server_extensions is deprecated, use 
jpserver_extensions"))
@@ -1351,6 +1318,17 @@
         config=True
     )
 
+    _starter_app = Instance(
+        default_value=None,
+        allow_none=True,
+        klass='jupyter_server.extension.application.ExtensionApp'
+    )
+
+    @property
+    def starter_app(self):
+        """Get the Extension that started this server."""
+        return self._starter_app
+
     def parse_command_line(self, argv=None):
 
         super(ServerApp, self).parse_command_line(argv)
@@ -1781,7 +1759,7 @@
         )
 
     @catch_config_error
-    def initialize(self, argv=None, find_extensions=True, new_httpserver=True):
+    def initialize(self, argv=None, find_extensions=True, new_httpserver=True, 
starter_extension=None):
         """Initialize the Server application class, configurables, web 
application, and http server.
 
         Parameters
@@ -1797,11 +1775,14 @@
         new_httpserver: bool
             If True, a tornado HTTPServer instance will be created and 
configured for the Server Web
             Application. This will set the http_server attribute of this class.
+
+        starter_extension: str
+            If given, it references the name of an extension point that 
started the Server.
+            We will try to load configuration from extension point
         """
         # Parse command line, load ServerApp config files,
         # and update ServerApp config.
-        super(ServerApp, self).initialize(argv)
-        # Initialize all components of the ServerApp.
+        super(ServerApp, self).initialize(argv=argv)
         if self._dispatching:
             return
         # Then, use extensions' config loading mechanism to
@@ -1810,6 +1791,19 @@
             self.find_server_extensions()
         self.init_logging()
         self.init_server_extensions()
+
+        # Special case the starter extension and load
+        # any server configuration is provides.
+        if starter_extension:
+            # Configure ServerApp based on named extension.
+            point = self.extension_manager.extension_points[starter_extension]
+            # Set starter_app property.
+            if point.app:
+                self._starter_app = point.app
+            # Load any configuration that comes from the Extension point.
+            self.update_config(Config(point.config))
+
+        # Initialize other pieces of the server.
         self.init_resources()
         self.init_configurables()
         self.init_components()
@@ -1979,7 +1973,8 @@
         self.write_browser_open_file()
 
         # Handle the browser opening.
-        self._handle_browser_opening()
+        if self.open_browser:
+            self.launch_browser()
 
         if self.token and self._token_generated:
             # log full URL with generated token, so there's a copy/pasteable 
link
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.2.3/jupyter_server/utils.py 
new/jupyter_server-1.3.0/jupyter_server/utils.py
--- old/jupyter_server-1.2.3/jupyter_server/utils.py    2021-01-30 
00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/jupyter_server/utils.py    2021-02-05 
02:40:21.000000000 +0100
@@ -225,4 +225,4 @@
                 # just return a Future, hoping that it will be awaited
                 result = asyncio.ensure_future(maybe_async)
         return result
-    return wrapped()
+    return wrapped()
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.2.3/tests/extension/mockextensions/app.py 
new/jupyter_server-1.3.0/tests/extension/mockextensions/app.py
--- old/jupyter_server-1.2.3/tests/extension/mockextensions/app.py      
2021-01-30 00:34:24.000000000 +0100
+++ new/jupyter_server-1.3.0/tests/extension/mockextensions/app.py      
2021-02-05 02:40:21.000000000 +0100
@@ -13,6 +13,15 @@
 
 STATIC_PATH = os.path.join(os.path.dirname(__file__), "static")
 
+# Function that makes these extensions discoverable
+# by the test functions.
+def _jupyter_server_extension_points():
+    return [
+        {
+            'module': __name__,
+            'app': MockExtensionApp
+        }
+    ]
 
 class MockExtensionHandler(ExtensionHandlerMixin, JupyterHandler):
 
@@ -41,4 +50,8 @@
     def initialize_handlers(self):
         self.handlers.append(('/mock', MockExtensionHandler))
         self.handlers.append(('/mock_template', MockExtensionTemplateHandler))
-        self.loaded = True
\ No newline at end of file
+        self.loaded = True
+
+
+if __name__ == "__main__":
+    MockExtensionApp.launch_instance()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.2.3/tests/extension/test_launch.py 
new/jupyter_server-1.3.0/tests/extension/test_launch.py
--- old/jupyter_server-1.2.3/tests/extension/test_launch.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/jupyter_server-1.3.0/tests/extension/test_launch.py     2021-02-05 
02:40:21.000000000 +0100
@@ -0,0 +1,95 @@
+"""Test launching Jupyter Server Applications
+through as ExtensionApp launch_instance.
+"""
+import os
+import sys
+import time
+import pytest
+import subprocess
+import requests
+from binascii import hexlify
+
+
+HERE = os.path.dirname(os.path.abspath(__file__))
+
+
+@pytest.fixture
+def port():
+    return 9999
+
+
+@pytest.fixture
+def token():
+    return hexlify(os.urandom(4)).decode("ascii")
+
+
+@pytest.fixture
+def auth_header(token):
+    return {
+        'Authorization': 'token %s' % token
+    }
+
+
+def wait_up(url, interval=0.1, check=None):
+    while True:
+        try:
+            r = requests.get(url)
+        except Exception:
+            if check:
+                assert check()
+            #print("waiting for %s" % url)
+            time.sleep(interval)
+        else:
+            break
+
+
+@pytest.fixture
+def launch_instance(request, port, token):
+    def _run_in_subprocess(argv=[]):
+
+        def _kill_extension_app():
+            try:
+                process.terminate()
+            except OSError:
+                # Already dead.
+                pass
+            process.wait(10)
+
+        process = subprocess.Popen([
+            sys.executable, '-m',
+            'mockextensions.app',
+            f'--port={port}',
+            '--ip=127.0.0.1',
+            '--no-browser',
+            f'--ServerApp.token={token}',
+            *argv,
+        ], cwd=HERE)
+
+        request.addfinalizer(_kill_extension_app)
+        url = f'http://127.0.0.1:{port}'
+        wait_up(url, check=lambda: process.poll() is None)
+        return process
+
+    return _run_in_subprocess
+
+
+@pytest.fixture
+def fetch(port, auth_header):
+    def _get(endpoint):
+        url = f"http://127.0.0.1:{port}"; + endpoint
+        return requests.get(url, headers=auth_header)
+    return _get
+
+
+def test_launch_instance(launch_instance, fetch):
+    launch_instance()
+    r = fetch('/mock')
+    assert r.status_code == 200
+
+
+def test_base_url(launch_instance, fetch):
+    launch_instance(['--ServerApp.base_url=/foo'])
+    r = fetch("/foo/mock")
+    assert r.status_code == 200
+
+

Reply via email to