Llama-3.1-8B-DALv0.1
/
venv
/lib
/python3.12
/site-packages
/jupyter_server
/extension
/application.py
"""An extension application.""" | |
from __future__ import annotations | |
import logging | |
import re | |
import sys | |
import typing as t | |
from jinja2 import Environment, FileSystemLoader | |
from jupyter_core.application import JupyterApp, NoStart | |
from tornado.log import LogFormatter | |
from tornado.web import RedirectHandler | |
from traitlets import Any, Bool, Dict, HasTraits, List, Unicode, default | |
from traitlets.config import Config | |
from jupyter_server.serverapp import ServerApp | |
from jupyter_server.transutils import _i18n | |
from jupyter_server.utils import is_namespace_package, url_path_join | |
from .handler import ExtensionHandlerMixin | |
# ----------------------------------------------------------------------------- | |
# Util functions and classes. | |
# ----------------------------------------------------------------------------- | |
def _preparse_for_subcommand(application_klass, argv): | |
"""Preparse command line to look for subcommands.""" | |
# Read in arguments from command line. | |
if len(argv) == 0: | |
return None | |
# Find any subcommands. | |
if application_klass.subcommands and len(argv) > 0: | |
# we have subcommands, and one may have been specified | |
subc, subargv = argv[0], argv[1:] | |
if re.match(r"^\w(\-?\w)*$", subc) and subc in application_klass.subcommands: | |
# it's a subcommand, and *not* a flag or class parameter | |
app = application_klass() | |
app.initialize_subcommand(subc, subargv) | |
return app.subapp | |
def _preparse_for_stopping_flags(application_klass, argv): | |
"""Looks for 'help', 'version', and 'generate-config; commands | |
in command line. If found, raises the help and version of | |
current Application. | |
This is useful for traitlets applications that have to parse | |
the command line multiple times, but want to control when | |
when 'help' and 'version' is raised. | |
""" | |
# Arguments after a '--' argument are for the script IPython may be | |
# about to run, not IPython iteslf. For arguments parsed here (help and | |
# version), we want to only search the arguments up to the first | |
# occurrence of '--', which we're calling interpreted_argv. | |
try: | |
interpreted_argv = argv[: argv.index("--")] | |
except ValueError: | |
interpreted_argv = argv | |
# Catch any help calls. | |
if any(x in interpreted_argv for x in ("-h", "--help-all", "--help")): | |
app = application_klass() | |
app.print_help("--help-all" in interpreted_argv) | |
app.exit(0) | |
# Catch version commands | |
if "--version" in interpreted_argv or "-V" in interpreted_argv: | |
app = application_klass() | |
app.print_version() | |
app.exit(0) | |
# Catch generate-config commands. | |
if "--generate-config" in interpreted_argv: | |
app = application_klass() | |
app.write_default_config() | |
app.exit(0) | |
class ExtensionAppJinjaMixin(HasTraits): | |
"""Use Jinja templates for HTML templates on top of an ExtensionApp.""" | |
jinja2_options = Dict( | |
help=_i18n( | |
"""Options to pass to the jinja2 environment for this | |
""" | |
) | |
).tag(config=True) | |
def _prepare_templates(self): | |
"""Get templates defined in a subclass.""" | |
self.initialize_templates() | |
# Add templates to web app settings if extension has templates. | |
if len(self.template_paths) > 0: | |
self.settings.update({f"{self.name}_template_paths": self.template_paths}) | |
# Create a jinja environment for logging html templates. | |
self.jinja2_env = Environment( | |
loader=FileSystemLoader(self.template_paths), | |
extensions=["jinja2.ext.i18n"], | |
autoescape=True, | |
**self.jinja2_options, | |
) | |
# Add the jinja2 environment for this extension to the tornado settings. | |
self.settings.update({f"{self.name}_jinja2_env": self.jinja2_env}) | |
# ----------------------------------------------------------------------------- | |
# ExtensionApp | |
# ----------------------------------------------------------------------------- | |
class JupyterServerExtensionException(Exception): | |
"""Exception class for raising for Server extensions errors.""" | |
# ----------------------------------------------------------------------------- | |
# ExtensionApp | |
# ----------------------------------------------------------------------------- | |
class ExtensionApp(JupyterApp): | |
"""Base class for configurable Jupyter Server Extension Applications. | |
ExtensionApp subclasses can be initialized two ways: | |
- Extension is listed as a jpserver_extension, and ServerApp calls | |
its load_jupyter_server_extension classmethod. This is the | |
classic way of loading a server extension. | |
- Extension is launched directly by calling its `launch_instance` | |
class method. This method can be set as a entry_point in | |
the extensions setup.py. | |
""" | |
# Subclasses should override this trait. Tells the server if | |
# this extension allows other other extensions to be loaded | |
# side-by-side when launched directly. | |
load_other_extensions = True | |
# A useful class property that subclasses can override to | |
# configure the underlying Jupyter Server when this extension | |
# is launched directly (using its `launch_instance` method). | |
serverapp_config: dict[str, t.Any] = {} | |
# Some subclasses will likely override this trait to flip | |
# the default value to False if they don't offer a browser | |
# based frontend. | |
open_browser = Bool( | |
help="""Whether to open in a browser after starting. | |
The specific browser used is platform dependent and | |
determined by the python standard library `webbrowser` | |
module, unless it is overridden using the --browser | |
(ServerApp.browser) configuration option. | |
""" | |
).tag(config=True) | |
def _default_open_browser(self): | |
assert self.serverapp is not None | |
return self.serverapp.config["ServerApp"].get("open_browser", True) | |
def config_file_paths(self): | |
"""Look on the same path as our parent for config files""" | |
# rely on parent serverapp, which should control all config loading | |
assert self.serverapp is not None | |
return self.serverapp.config_file_paths | |
# The extension name used to name the jupyter config | |
# file, jupyter_{name}_config. | |
# This should also match the jupyter subcommand used to launch | |
# this extension from the CLI, e.g. `jupyter {name}`. | |
name: str | Unicode[str, str] = "ExtensionApp" # type:ignore[assignment] | |
def get_extension_package(cls): | |
"""Get an extension package.""" | |
parts = cls.__module__.split(".") | |
if is_namespace_package(parts[0]): | |
# in this case the package name is `<namespace>.<package>`. | |
return ".".join(parts[0:2]) | |
return parts[0] | |
def get_extension_point(cls): | |
"""Get an extension point.""" | |
return cls.__module__ | |
# Extension URL sets the default landing page for this extension. | |
extension_url = "/" | |
default_url = Unicode().tag(config=True) | |
def _default_url(self): | |
return self.extension_url | |
file_url_prefix = Unicode("notebooks") | |
# Is this linked to a serverapp yet? | |
_linked = Bool(False) | |
# Extension can configure the ServerApp from the command-line | |
classes = [ | |
ServerApp, | |
] | |
# A ServerApp is not defined yet, but will be initialized below. | |
serverapp: ServerApp | None = Any() # type:ignore[assignment] | |
def _default_serverapp(self): | |
# load the current global instance, if any | |
if ServerApp.initialized(): | |
try: | |
return ServerApp.instance() | |
except Exception: | |
# error retrieving instance, e.g. MultipleInstanceError | |
pass | |
# serverapp accessed before it was defined, | |
# declare an empty one | |
return ServerApp() | |
_log_formatter_cls = LogFormatter # type:ignore[assignment] | |
def _default_log_level(self): | |
return logging.INFO | |
def _default_log_format(self): | |
"""override default log format to include date & time""" | |
return ( | |
"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s]%(end_color)s %(message)s" | |
) | |
static_url_prefix = Unicode( | |
help="""Url where the static assets for the extension are served.""" | |
).tag(config=True) | |
def _default_static_url_prefix(self): | |
static_url = f"static/{self.name}/" | |
assert self.serverapp is not None | |
return url_path_join(self.serverapp.base_url, static_url) | |
static_paths = List( | |
Unicode(), | |
help="""paths to search for serving static files. | |
This allows adding javascript/css to be available from the notebook server machine, | |
or overriding individual files in the IPython | |
""", | |
).tag(config=True) | |
template_paths = List( | |
Unicode(), | |
help=_i18n( | |
"""Paths to search for serving jinja templates. | |
Can be used to override templates from notebook.templates.""" | |
), | |
).tag(config=True) | |
settings = Dict(help=_i18n("""Settings that will passed to the server.""")).tag(config=True) | |
handlers: List[tuple[t.Any, ...]] = List( | |
help=_i18n("""Handlers appended to the server.""") | |
).tag(config=True) | |
def _config_file_name_default(self): | |
"""The default config file name.""" | |
if not self.name: | |
return "" | |
return "jupyter_{}_config".format(self.name.replace("-", "_")) | |
def initialize_settings(self): | |
"""Override this method to add handling of settings.""" | |
def initialize_handlers(self): | |
"""Override this method to append handlers to a Jupyter Server.""" | |
def initialize_templates(self): | |
"""Override this method to add handling of template files.""" | |
def _prepare_config(self): | |
"""Builds a Config object from the extension's traits and passes | |
the object to the webapp's settings as `<name>_config`. | |
""" | |
traits = self.class_own_traits().keys() | |
self.extension_config = Config({t: getattr(self, t) for t in traits}) | |
self.settings[f"{self.name}_config"] = self.extension_config | |
def _prepare_settings(self): | |
"""Prepare the settings.""" | |
# Make webapp settings accessible to initialize_settings method | |
assert self.serverapp is not None | |
webapp = self.serverapp.web_app | |
self.settings.update(**webapp.settings) | |
# Add static and template paths to settings. | |
self.settings.update( | |
{ | |
f"{self.name}_static_paths": self.static_paths, | |
f"{self.name}": self, | |
} | |
) | |
# Get setting defined by subclass using initialize_settings method. | |
self.initialize_settings() | |
# Update server settings with extension settings. | |
webapp.settings.update(**self.settings) | |
def _prepare_handlers(self): | |
"""Prepare the handlers.""" | |
assert self.serverapp is not None | |
webapp = self.serverapp.web_app | |
# Get handlers defined by extension subclass. | |
self.initialize_handlers() | |
# prepend base_url onto the patterns that we match | |
new_handlers = [] | |
for handler_items in self.handlers: | |
# Build url pattern including base_url | |
pattern = url_path_join(webapp.settings["base_url"], handler_items[0]) | |
handler = handler_items[1] | |
# Get handler kwargs, if given | |
kwargs: dict[str, t.Any] = {} | |
if issubclass(handler, ExtensionHandlerMixin): | |
kwargs["name"] = self.name | |
try: | |
kwargs.update(handler_items[2]) | |
except IndexError: | |
pass | |
new_handler = (pattern, handler, kwargs) | |
new_handlers.append(new_handler) | |
# Add static endpoint for this extension, if static paths are given. | |
if len(self.static_paths) > 0: | |
# Append the extension's static directory to server handlers. | |
static_url = url_path_join(self.static_url_prefix, "(.*)") | |
# Construct handler. | |
handler = ( | |
static_url, | |
webapp.settings["static_handler_class"], | |
{"path": self.static_paths}, | |
) | |
new_handlers.append(handler) | |
webapp.add_handlers(".*$", new_handlers) | |
def _prepare_templates(self): | |
"""Add templates to web app settings if extension has templates.""" | |
if len(self.template_paths) > 0: | |
self.settings.update({f"{self.name}_template_paths": self.template_paths}) | |
self.initialize_templates() | |
def _jupyter_server_config(self): | |
"""The jupyter server config.""" | |
base_config = { | |
"ServerApp": { | |
"default_url": self.default_url, | |
"open_browser": self.open_browser, | |
"file_url_prefix": self.file_url_prefix, | |
} | |
} | |
base_config["ServerApp"].update(self.serverapp_config) | |
return base_config | |
def _link_jupyter_server_extension(self, serverapp: ServerApp) -> None: | |
"""Link the ExtensionApp to an initialized ServerApp. | |
The ServerApp is stored as an attribute and config | |
is exchanged between ServerApp and `self` in case | |
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. | |
self.load_config_file() | |
# ServerApp's config might have picked up | |
# config for the ExtensionApp. We call | |
# update_config to update ExtensionApp's | |
# traits with these values found in ServerApp's | |
# config. | |
# ServerApp config ---> ExtensionApp traits | |
self.update_config(self.serverapp.config) | |
# Use ExtensionApp's CLI parser to find any extra | |
# args that passed through ServerApp and | |
# now belong to ExtensionApp. | |
self.parse_command_line(self.serverapp.extra_args) | |
# If any config should be passed upstream to the | |
# ServerApp, do it here. | |
# i.e. ServerApp traits <--- ExtensionApp config | |
self.serverapp.update_config(self.config) | |
# Acknowledge that this extension has been linked. | |
self._linked = True | |
def initialize(self): | |
"""Initialize the extension app. The | |
corresponding server app and webapp should already | |
be initialized by this step. | |
- Appends Handlers to the ServerApp, | |
- Passes config and settings from ExtensionApp | |
to the Tornado web application | |
- Points Tornado Webapp to templates and static assets. | |
""" | |
if not self.serverapp: | |
msg = ( | |
"This extension has no attribute `serverapp`. " | |
"Try calling `.link_to_serverapp()` before calling " | |
"`.initialize()`." | |
) | |
raise JupyterServerExtensionException(msg) | |
self._prepare_config() | |
self._prepare_templates() | |
self._prepare_settings() | |
self._prepare_handlers() | |
def start(self): | |
"""Start the underlying Jupyter server. | |
Server should be started after extension is initialized. | |
""" | |
super().start() | |
# Start the server. | |
assert self.serverapp is not None | |
self.serverapp.start() | |
def current_activity(self): | |
"""Return a list of activity happening in this extension.""" | |
return | |
async def stop_extension(self): | |
"""Cleanup any resources managed by this extension.""" | |
def stop(self): | |
"""Stop the underlying Jupyter server.""" | |
assert self.serverapp is not None | |
self.serverapp.stop() | |
self.serverapp.clear_instance() | |
def _load_jupyter_server_extension(cls, serverapp): | |
"""Initialize and configure this extension, then add the extension's | |
settings and handlers to the server's web application. | |
""" | |
extension_manager = serverapp.extension_manager | |
try: | |
# Get loaded extension from serverapp. | |
point = extension_manager.extension_points[cls.name] | |
extension = point.app | |
except KeyError: | |
extension = cls() | |
extension._link_jupyter_server_extension(serverapp) | |
extension.initialize() | |
return extension | |
def load_classic_server_extension(cls, serverapp): | |
"""Enables extension to be loaded as classic Notebook (jupyter/notebook) extension.""" | |
extension = cls() | |
extension.serverapp = serverapp | |
extension.load_config_file() | |
extension.update_config(serverapp.config) | |
extension.parse_command_line(serverapp.extra_args) | |
# Add redirects to get favicons from old locations in the classic notebook server | |
extension.handlers.extend( | |
[ | |
( | |
r"/static/favicons/favicon.ico", | |
RedirectHandler, | |
{"url": url_path_join(serverapp.base_url, "static/base/images/favicon.ico")}, | |
), | |
( | |
r"/static/favicons/favicon-busy-1.ico", | |
RedirectHandler, | |
{ | |
"url": url_path_join( | |
serverapp.base_url, "static/base/images/favicon-busy-1.ico" | |
) | |
}, | |
), | |
( | |
r"/static/favicons/favicon-busy-2.ico", | |
RedirectHandler, | |
{ | |
"url": url_path_join( | |
serverapp.base_url, "static/base/images/favicon-busy-2.ico" | |
) | |
}, | |
), | |
( | |
r"/static/favicons/favicon-busy-3.ico", | |
RedirectHandler, | |
{ | |
"url": url_path_join( | |
serverapp.base_url, "static/base/images/favicon-busy-3.ico" | |
) | |
}, | |
), | |
( | |
r"/static/favicons/favicon-file.ico", | |
RedirectHandler, | |
{ | |
"url": url_path_join( | |
serverapp.base_url, "static/base/images/favicon-file.ico" | |
) | |
}, | |
), | |
( | |
r"/static/favicons/favicon-notebook.ico", | |
RedirectHandler, | |
{ | |
"url": url_path_join( | |
serverapp.base_url, | |
"static/base/images/favicon-notebook.ico", | |
) | |
}, | |
), | |
( | |
r"/static/favicons/favicon-terminal.ico", | |
RedirectHandler, | |
{ | |
"url": url_path_join( | |
serverapp.base_url, | |
"static/base/images/favicon-terminal.ico", | |
) | |
}, | |
), | |
( | |
r"/static/logo/logo.png", | |
RedirectHandler, | |
{"url": url_path_join(serverapp.base_url, "static/base/images/logo.png")}, | |
), | |
] | |
) | |
extension.initialize() | |
serverapp_class = ServerApp | |
def make_serverapp(cls, **kwargs: t.Any) -> ServerApp: | |
"""Instantiate the ServerApp | |
Override to customize the ServerApp before it loads any configuration | |
""" | |
return cls.serverapp_class.instance(**kwargs) | |
def initialize_server(cls, argv=None, load_other_extensions=True, **kwargs): | |
"""Creates an instance of ServerApp and explicitly sets | |
this extension to enabled=True (i.e. superseding disabling | |
found in other config from files). | |
The `launch_instance` method uses this method to initialize | |
and start a server. | |
""" | |
jpserver_extensions = {cls.get_extension_package(): True} | |
find_extensions = cls.load_other_extensions | |
if "jpserver_extensions" in cls.serverapp_config: | |
jpserver_extensions.update(cls.serverapp_config["jpserver_extensions"]) | |
cls.serverapp_config["jpserver_extensions"] = jpserver_extensions | |
find_extensions = False | |
serverapp = cls.make_serverapp(jpserver_extensions=jpserver_extensions, **kwargs) | |
serverapp.aliases.update(cls.aliases) # type:ignore[has-type] | |
serverapp.initialize( | |
argv=argv or [], | |
starter_extension=cls.name, | |
find_extensions=find_extensions, | |
) | |
return serverapp | |
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 | |
extension's landing page. | |
""" | |
# Handle arguments. | |
if argv is None: # noqa: SIM108 | |
args = sys.argv[1:] # slice out extension config. | |
else: | |
args = argv | |
# Handle all "stops" that could happen before | |
# continuing to launch a server+extension. | |
subapp = _preparse_for_subcommand(cls, args) | |
if subapp: | |
subapp.start() | |
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(f"{cls.name} is running without loading other extensions.") | |
# Start the server. | |
try: | |
serverapp.start() | |
except NoStart: | |
pass | |