mbuali's picture
Upload folder using huggingface_hub
d1ceb73 verified
"""Utilities for installing extensions"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
import logging
import os
import sys
import typing as t
from jupyter_core.application import JupyterApp
from jupyter_core.paths import ENV_CONFIG_PATH, SYSTEM_CONFIG_PATH, jupyter_config_dir
from tornado.log import LogFormatter
from traitlets import Bool
from jupyter_server._version import __version__
from jupyter_server.extension.config import ExtensionConfigManager
from jupyter_server.extension.manager import ExtensionManager, ExtensionPackage
def _get_config_dir(user: bool = False, sys_prefix: bool = False) -> str:
"""Get the location of config files for the current context
Returns the string to the environment
Parameters
----------
user : bool [default: False]
Get the user's .jupyter config directory
sys_prefix : bool [default: False]
Get sys.prefix, i.e. ~/.envs/my-env/etc/jupyter
"""
if user and sys_prefix:
sys_prefix = False
if user:
extdir = jupyter_config_dir()
elif sys_prefix:
extdir = ENV_CONFIG_PATH[0]
else:
extdir = SYSTEM_CONFIG_PATH[0]
return extdir
def _get_extmanager_for_context(
write_dir: str = "jupyter_server_config.d", user: bool = False, sys_prefix: bool = False
) -> tuple[str, ExtensionManager]:
"""Get an extension manager pointing at the current context
Returns the path to the current context and an ExtensionManager object.
Parameters
----------
write_dir : str [default: 'jupyter_server_config.d']
Name of config directory to write extension config.
user : bool [default: False]
Get the user's .jupyter config directory
sys_prefix : bool [default: False]
Get sys.prefix, i.e. ~/.envs/my-env/etc/jupyter
"""
config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
config_manager = ExtensionConfigManager(
read_config_path=[config_dir],
write_config_dir=os.path.join(config_dir, write_dir),
)
extension_manager = ExtensionManager(
config_manager=config_manager,
)
return config_dir, extension_manager
class ArgumentConflict(ValueError):
pass
_base_flags: dict[str, t.Any] = {}
_base_flags.update(JupyterApp.flags)
_base_flags.pop("y", None)
_base_flags.pop("generate-config", None)
_base_flags.update(
{
"user": (
{
"BaseExtensionApp": {
"user": True,
}
},
"Apply the operation only for the given user",
),
"system": (
{
"BaseExtensionApp": {
"user": False,
"sys_prefix": False,
}
},
"Apply the operation system-wide",
),
"sys-prefix": (
{
"BaseExtensionApp": {
"sys_prefix": True,
}
},
"Use sys.prefix as the prefix for installing extensions (for environments, packaging)",
),
"py": (
{
"BaseExtensionApp": {
"python": True,
}
},
"Install from a Python package",
),
}
)
_base_flags["python"] = _base_flags["py"]
_base_aliases: dict[str, t.Any] = {}
_base_aliases.update(JupyterApp.aliases)
class BaseExtensionApp(JupyterApp):
"""Base extension installer app"""
_log_formatter_cls = LogFormatter # type:ignore[assignment]
flags = _base_flags
aliases = _base_aliases
version = __version__
user = Bool(False, config=True, help="Whether to do a user install")
sys_prefix = Bool(True, config=True, help="Use the sys.prefix as the prefix")
python = Bool(False, config=True, help="Install from a Python package")
def _log_format_default(self) -> str:
"""A default format for messages"""
return "%(message)s"
@property
def config_dir(self) -> str: # type:ignore[override]
return _get_config_dir(user=self.user, sys_prefix=self.sys_prefix)
# Constants for pretty print extension listing function.
# Window doesn't support coloring in the commandline
GREEN_ENABLED = "\033[32menabled\033[0m" if os.name != "nt" else "enabled"
RED_DISABLED = "\033[31mdisabled\033[0m" if os.name != "nt" else "disabled"
GREEN_OK = "\033[32mOK\033[0m" if os.name != "nt" else "ok"
RED_X = "\033[31m X\033[0m" if os.name != "nt" else " X"
# ------------------------------------------------------------------------------
# Public API
# ------------------------------------------------------------------------------
def toggle_server_extension_python(
import_name: str,
enabled: bool | None = None,
parent: t.Any = None,
user: bool = False,
sys_prefix: bool = True,
) -> None:
"""Toggle the boolean setting for a given server extension
in a Jupyter config file.
"""
sys_prefix = False if user else sys_prefix
config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
manager = ExtensionConfigManager(
read_config_path=[config_dir],
write_config_dir=os.path.join(config_dir, "jupyter_server_config.d"),
)
if enabled:
manager.enable(import_name)
else:
manager.disable(import_name)
# ----------------------------------------------------------------------
# Applications
# ----------------------------------------------------------------------
flags = {}
flags.update(BaseExtensionApp.flags)
flags.pop("y", None)
flags.pop("generate-config", None)
flags.update(
{
"user": (
{
"ToggleServerExtensionApp": {
"user": True,
}
},
"Perform the operation for the current user",
),
"system": (
{
"ToggleServerExtensionApp": {
"user": False,
"sys_prefix": False,
}
},
"Perform the operation system-wide",
),
"sys-prefix": (
{
"ToggleServerExtensionApp": {
"sys_prefix": True,
}
},
"Use sys.prefix as the prefix for installing server extensions",
),
"py": (
{
"ToggleServerExtensionApp": {
"python": True,
}
},
"Install from a Python package",
),
}
)
flags["python"] = flags["py"]
_desc = "Enable/disable a server extension using frontend configuration files."
class ToggleServerExtensionApp(BaseExtensionApp):
"""A base class for enabling/disabling extensions"""
name = "jupyter server extension enable/disable"
description = _desc
flags = flags
_toggle_value = Bool()
_toggle_pre_message = ""
_toggle_post_message = ""
def toggle_server_extension(self, import_name: str) -> None:
"""Change the status of a named server extension.
Uses the value of `self._toggle_value`.
Parameters
---------
import_name : str
Importable Python module (dotted-notation) exposing the magic-named
`load_jupyter_server_extension` function
"""
# Create an extension manager for this instance.
config_dir, extension_manager = _get_extmanager_for_context(
user=self.user, sys_prefix=self.sys_prefix
)
try:
self.log.info(f"{self._toggle_pre_message.capitalize()}: {import_name}")
self.log.info(f"- Writing config: {config_dir}")
# Validate the server extension.
self.log.info(f" - Validating {import_name}...")
# Interface with the Extension Package and validate.
extpkg = ExtensionPackage(name=import_name)
extpkg.validate()
version = extpkg.version
self.log.info(f" {import_name} {version} {GREEN_OK}")
# Toggle extension config.
config = extension_manager.config_manager
if config:
if self._toggle_value is True:
config.enable(import_name)
else:
config.disable(import_name)
# If successful, let's log.
self.log.info(f" - Extension successfully {self._toggle_post_message}.")
except Exception as err:
self.log.info(f" {RED_X} Validation failed: {err}")
def start(self) -> None:
"""Perform the App's actions as configured"""
if not self.extra_args:
sys.exit("Please specify a server extension/package to enable or disable")
for arg in self.extra_args:
self.toggle_server_extension(arg)
class EnableServerExtensionApp(ToggleServerExtensionApp):
"""An App that enables (and validates) Server Extensions"""
name = "jupyter server extension enable"
description = """
Enable a server extension in configuration.
Usage
jupyter server extension enable [--system|--sys-prefix]
"""
_toggle_value = True # type:ignore[assignment]
_toggle_pre_message = "enabling"
_toggle_post_message = "enabled"
class DisableServerExtensionApp(ToggleServerExtensionApp):
"""An App that disables Server Extensions"""
name = "jupyter server extension disable"
description = """
Disable a server extension in configuration.
Usage
jupyter server extension disable [--system|--sys-prefix]
"""
_toggle_value = False # type:ignore[assignment]
_toggle_pre_message = "disabling"
_toggle_post_message = "disabled"
class ListServerExtensionsApp(BaseExtensionApp):
"""An App that lists (and validates) Server Extensions"""
name = "jupyter server extension list"
version = __version__
description = "List all server extensions known by the configuration system"
def list_server_extensions(self) -> None:
"""List all enabled and disabled server extensions, by config path
Enabled extensions are validated, potentially generating warnings.
"""
configurations = (
{"user": True, "sys_prefix": False},
{"user": False, "sys_prefix": True},
{"user": False, "sys_prefix": False},
)
for option in configurations:
config_dir = _get_config_dir(**option)
self.log.info(f"Config dir: {config_dir}")
write_dir = "jupyter_server_config.d"
config_manager = ExtensionConfigManager(
read_config_path=[config_dir],
write_config_dir=os.path.join(config_dir, write_dir),
)
jpserver_extensions = config_manager.get_jpserver_extensions()
for name, enabled in jpserver_extensions.items():
# Attempt to get extension metadata
self.log.info(f" {name} {GREEN_ENABLED if enabled else RED_DISABLED}")
try:
self.log.info(f" - Validating {name}...")
extension = ExtensionPackage(name=name, enabled=enabled)
if not extension.validate():
msg = "validation failed"
raise ValueError(msg)
version = extension.version
self.log.info(f" {name} {version} {GREEN_OK}")
except Exception as err:
exc_info = False
if int(self.log_level) <= logging.DEBUG: # type:ignore[call-overload]
exc_info = True
self.log.warning(f" {RED_X} {err}", exc_info=exc_info)
# Add a blank line between paths.
self.log.info("")
def start(self) -> None:
"""Perform the App's actions as configured"""
self.list_server_extensions()
_examples = """
jupyter server extension list # list all configured server extensions
jupyter server extension enable --py <packagename> # enable all server extensions in a Python package
jupyter server extension disable --py <packagename> # disable all server extensions in a Python package
"""
class ServerExtensionApp(BaseExtensionApp):
"""Root level server extension app"""
name = "jupyter server extension"
version = __version__
description: str = "Work with Jupyter server extensions"
examples = _examples
subcommands: dict[str, t.Any] = {
"enable": (EnableServerExtensionApp, "Enable a server extension"),
"disable": (DisableServerExtensionApp, "Disable a server extension"),
"list": (ListServerExtensionsApp, "List server extensions"),
}
def start(self) -> None:
"""Perform the App's actions as configured"""
super().start()
# The above should have called a subcommand and raised NoStart; if we
# get here, it didn't, so we should self.log.info a message.
subcmds = ", ".join(sorted(self.subcommands))
sys.exit("Please supply at least one subcommand: %s" % subcmds)
main = ServerExtensionApp.launch_instance
if __name__ == "__main__":
main()