|
from functools import wraps |
|
from copy import deepcopy |
|
from traitlets import TraitError |
|
from traitlets.config.loader import ( |
|
Config, |
|
) |
|
from jupyter_core.application import JupyterApp |
|
from jupyter_server.serverapp import ServerApp |
|
from jupyter_server.extension.application import ExtensionApp |
|
from .traits import NotebookAppTraits |
|
|
|
|
|
def NBAPP_AND_SVAPP_SHIM_MSG(trait_name): return ( |
|
"'{trait_name}' was found in both NotebookApp " |
|
"and ServerApp. This is likely a recent change. " |
|
"This config will only be set in NotebookApp. " |
|
"Please check if you should also config these traits in " |
|
"ServerApp for your purpose.".format( |
|
trait_name=trait_name, |
|
) |
|
) |
|
|
|
|
|
def NBAPP_TO_SVAPP_SHIM_MSG(trait_name): return ( |
|
"'{trait_name}' has moved from NotebookApp to " |
|
"ServerApp. This config will be passed to ServerApp. " |
|
"Be sure to update your config before " |
|
"our next release.".format( |
|
trait_name=trait_name, |
|
) |
|
) |
|
|
|
|
|
def EXTAPP_AND_NBAPP_AND_SVAPP_SHIM_MSG(trait_name, extapp_name): return ( |
|
"'{trait_name}' is found in {extapp_name}, NotebookApp, " |
|
"and ServerApp. This is a recent change. " |
|
"This config will only be set in {extapp_name}. " |
|
"Please check if you should also config these traits in " |
|
"NotebookApp and ServerApp for your purpose.".format( |
|
trait_name=trait_name, |
|
extapp_name=extapp_name |
|
) |
|
) |
|
|
|
|
|
def EXTAPP_AND_SVAPP_SHIM_MSG(trait_name, extapp_name): return ( |
|
"'{trait_name}' is found in both {extapp_name} " |
|
"and ServerApp. This is a recent change. " |
|
"This config will only be set in {extapp_name}. " |
|
"Please check if you should also config these traits in " |
|
"ServerApp for your purpose.".format( |
|
trait_name=trait_name, |
|
extapp_name=extapp_name |
|
) |
|
) |
|
|
|
|
|
def EXTAPP_AND_NBAPP_SHIM_MSG(trait_name, extapp_name): return ( |
|
"'{trait_name}' is found in both {extapp_name} " |
|
"and NotebookApp. This is a recent change. " |
|
"This config will only be set in {extapp_name}. " |
|
"Please check if you should also config these traits in " |
|
"NotebookApp for your purpose.".format( |
|
trait_name=trait_name, |
|
extapp_name=extapp_name |
|
) |
|
) |
|
|
|
|
|
def NOT_EXTAPP_NBAPP_AND_SVAPP_SHIM_MSG(trait_name, extapp_name): return ( |
|
"'{trait_name}' is not found in {extapp_name}, but " |
|
"it was found in both NotebookApp " |
|
"and ServerApp. This is likely a recent change. " |
|
"This config will only be set in ServerApp. " |
|
"Please check if you should also config these traits in " |
|
"NotebookApp for your purpose.".format( |
|
trait_name=trait_name, |
|
extapp_name=extapp_name |
|
) |
|
) |
|
|
|
|
|
def EXTAPP_TO_SVAPP_SHIM_MSG(trait_name, extapp_name): return ( |
|
"'{trait_name}' has moved from {extapp_name} to " |
|
"ServerApp. Be sure to update your config before " |
|
"our next release.".format( |
|
trait_name=trait_name, |
|
extapp_name=extapp_name |
|
) |
|
) |
|
|
|
|
|
def EXTAPP_TO_NBAPP_SHIM_MSG(trait_name, extapp_name): return ( |
|
"'{trait_name}' has moved from {extapp_name} to " |
|
"NotebookApp. Be sure to update your config before " |
|
"our next release.".format( |
|
trait_name=trait_name, |
|
extapp_name=extapp_name |
|
) |
|
) |
|
|
|
|
|
|
|
|
|
IGNORED_TRAITS = ("open_browser", "log_level", "log_format", "default_url", "show_banner") |
|
|
|
|
|
class NotebookConfigShimMixin: |
|
"""A Mixin class for shimming configuration from |
|
NotebookApp to ServerApp. This class handles warnings, errors, |
|
etc. |
|
|
|
This class should be used during a transition period for apps |
|
that are switching from depending on NotebookApp to ServerApp. |
|
|
|
After one release cycle, this class can be safely removed |
|
from the inheriting class. |
|
|
|
TL;DR |
|
|
|
The entry point to shimming is at the `update_config` method. |
|
Once traits are loaded, before updating config across all |
|
configurable objects, this class injects a method to reroute |
|
traits to their *most logical* classes. |
|
|
|
This class raises warnings when: |
|
1. a trait has moved. |
|
2. a trait is redundant across classes. |
|
|
|
Redundant traits across multiple classes now must be |
|
configured separately, *or* removed from their old |
|
location to avoid this warning. |
|
|
|
For a longer description on how individual traits are handled, |
|
read the docstring under `shim_config_from_notebook_to_jupyter_server`. |
|
""" |
|
|
|
@wraps(JupyterApp.update_config) |
|
def update_config(self, config): |
|
|
|
shimmed_config = self.shim_config_from_notebook_to_jupyter_server( |
|
config) |
|
super().update_config(shimmed_config) |
|
|
|
def shim_config_from_notebook_to_jupyter_server(self, config): |
|
"""Reorganizes a config object to reroute traits to their expected destinations |
|
after the transition from NotebookApp to ServerApp. |
|
|
|
A detailed explanation of how traits are handled: |
|
|
|
1. If the argument is prefixed with `ServerApp`, |
|
pass this trait to `ServerApp`. |
|
2. If the argument is prefixed with `NotebookApp`, |
|
* If the argument is a trait of `NotebookApp` *and* `ServerApp`: |
|
1. Raise a warning—**for the extension developers**—that |
|
there's redundant traits. |
|
2. Pass trait to `NotebookApp`. |
|
* If the argument is a trait of just `ServerApp` only |
|
(i.e. the trait moved from `NotebookApp` to `ServerApp`): |
|
1. Raise a "this trait has moved" **for the user**. |
|
3. Pass trait to `ServerApp`. |
|
* If the argument is a trait of `NotebookApp` only, pass trait |
|
to `NotebookApp`. |
|
* If the argument is not found in any object, raise a |
|
`"Trait not found."` error. |
|
3. If the argument is prefixed with `ExtensionApp`: |
|
* If the argument is a trait of `ExtensionApp`, |
|
`NotebookApp`, and `ServerApp`, |
|
1. Raise a warning about redundancy. |
|
2. Pass to the ExtensionApp |
|
* If the argument is a trait of `ExtensionApp` and `NotebookApp`, |
|
1. Raise a warning about redundancy. |
|
2. Pass to ExtensionApp. |
|
* If the argument is a trait of `ExtensionApp` and `ServerApp`, |
|
1. Raise a warning about redundancy. |
|
2. Pass to ExtensionApp. |
|
* If the argument is a trait of `ExtensionApp`. |
|
1. Pass to ExtensionApp. |
|
* If the argument is a trait of `NotebookApp` but not `ExtensionApp`, |
|
1. Raise a warning that trait has likely moved to NotebookApp. |
|
2. Pass to NotebookApp |
|
* If the arguent is a trait of `ServerApp` but not `ExtensionApp`, |
|
1. Raise a warning that the trait has likely moved to ServerApp. |
|
2. Pass to ServerApp. |
|
* else |
|
* Raise a TraitError: "trait not found." |
|
""" |
|
extapp_name = self.__class__.__name__ |
|
|
|
|
|
nbapp_config = config.pop('NotebookApp', {}) |
|
svapp_config = config.pop('ServerApp', {}) |
|
extapp_config = config.pop(extapp_name, {}) |
|
|
|
|
|
|
|
config_shim = deepcopy(config) |
|
svapp_config_shim = {} |
|
nbapp_config_shim = {} |
|
extapp_config_shim = {} |
|
|
|
extapp_traits = ( |
|
self.__class__.class_trait_names() + |
|
ExtensionApp.class_trait_names() |
|
) |
|
svapp_traits = ServerApp.class_trait_names() |
|
nbapp_traits = ( |
|
NotebookAppTraits.class_trait_names() + |
|
ExtensionApp.class_trait_names() |
|
) |
|
|
|
|
|
svapp_config_shim.update(svapp_config) |
|
|
|
|
|
warning_msg = None |
|
for trait_name, trait_value in nbapp_config.items(): |
|
in_svapp = trait_name in svapp_traits |
|
in_nbapp = trait_name in nbapp_traits |
|
if trait_name in IGNORED_TRAITS: |
|
|
|
nbapp_config_shim.update({trait_name: trait_value}) |
|
elif in_svapp and in_nbapp: |
|
warning_msg = NBAPP_AND_SVAPP_SHIM_MSG(trait_name) |
|
nbapp_config_shim.update({trait_name: trait_value}) |
|
elif in_svapp: |
|
warning_msg = NBAPP_TO_SVAPP_SHIM_MSG(trait_name) |
|
svapp_config_shim.update({trait_name: trait_value}) |
|
elif in_nbapp: |
|
nbapp_config_shim.update({trait_name: trait_value}) |
|
else: |
|
raise TraitError("Trait, {}, not found.".format(trait_name)) |
|
|
|
|
|
if warning_msg: |
|
self.log.warning(warning_msg) |
|
|
|
|
|
warning_msg = None |
|
for trait_name, trait_value in extapp_config.items(): |
|
in_extapp = trait_name in extapp_traits |
|
in_svapp = trait_name in svapp_traits |
|
in_nbapp = trait_name in nbapp_traits |
|
if trait_name in IGNORED_TRAITS: |
|
|
|
extapp_config_shim.update({trait_name: trait_value}) |
|
elif all([in_extapp, in_svapp, in_nbapp]): |
|
warning_msg = EXTAPP_AND_NBAPP_AND_SVAPP_SHIM_MSG( |
|
trait_name, |
|
extapp_name |
|
) |
|
extapp_config_shim.update({trait_name: trait_value}) |
|
elif in_extapp and in_svapp: |
|
warning_msg = EXTAPP_AND_SVAPP_SHIM_MSG( |
|
trait_name, |
|
extapp_name |
|
) |
|
extapp_config_shim.update({trait_name: trait_value}) |
|
elif in_extapp and in_nbapp: |
|
warning_msg = EXTAPP_AND_NBAPP_SHIM_MSG( |
|
trait_name, |
|
extapp_name |
|
) |
|
extapp_config_shim.update({trait_name: trait_value}) |
|
elif in_extapp: |
|
extapp_config_shim.update({trait_name: trait_value}) |
|
elif in_svapp and in_nbapp: |
|
warning_msg = NOT_EXTAPP_NBAPP_AND_SVAPP_SHIM_MSG( |
|
trait_name, |
|
extapp_name |
|
) |
|
svapp_config_shim.update({trait_name: trait_value}) |
|
elif in_svapp: |
|
warning_msg = EXTAPP_TO_SVAPP_SHIM_MSG( |
|
trait_name, |
|
extapp_name |
|
) |
|
svapp_config_shim.update({trait_name: trait_value}) |
|
elif in_nbapp: |
|
warning_msg = EXTAPP_TO_NBAPP_SHIM_MSG( |
|
trait_name, |
|
extapp_name |
|
) |
|
nbapp_config_shim.update({trait_name: trait_value}) |
|
else: |
|
raise TraitError("Trait, {}, not found.".format(trait_name)) |
|
|
|
|
|
if warning_msg: |
|
self.log.warning(warning_msg) |
|
|
|
|
|
new_config = Config({ |
|
'NotebookApp': nbapp_config_shim, |
|
'ServerApp': svapp_config_shim, |
|
}) |
|
if extapp_config_shim: |
|
new_config.update(Config({ |
|
self.__class__.__name__: extapp_config_shim |
|
})) |
|
|
|
config_shim.update(new_config) |
|
return config_shim |
|
|