The backend infrastructure for Jupyter web applications providing core services, APIs, and REST endpoints.
The Jupyter Server extension system provides a powerful framework for building pluggable server applications and adding functionality to existing servers.
Base class for creating server extensions that can run standalone or integrate with existing servers.
from jupyter_server.extension.application import ExtensionAppfrom jupyter_server.extension.application import ExtensionApp
from jupyter_server.base.handlers import JupyterHandler
class HelloHandler(JupyterHandler):
def get(self):
self.finish({"message": "Hello from my extension!"})
class MyExtension(ExtensionApp):
name = "my_extension"
description = "My custom Jupyter extension"
version = "1.0.0"
# Extension metadata
extension_url = "/my_extension"
load_other_extensions = True
def initialize_handlers(self):
"""Register URL handlers for this extension."""
handlers = [
(r"/my_extension/hello", HelloHandler),
]
self.handlers.extend(handlers)
def initialize_settings(self):
"""Initialize custom settings."""
self.settings.update({
"my_extension_config": self.config,
})from jupyter_server.extension.application import ExtensionAppJinjaMixin, ExtensionApp
from jupyter_server.base.handlers import JupyterHandler
class MyTemplateHandler(JupyterHandler):
def get(self):
# Render template with context
html = self.render_template(
"my_template.html",
title="My Extension",
data={"key": "value"}
)
self.finish(html)
class MyExtensionWithTemplates(ExtensionAppJinjaMixin, ExtensionApp):
name = "my_templated_extension"
def initialize_templates(self):
"""Setup Jinja2 template environment."""
self.initialize_jinja2_settings()
# Add custom template paths
template_paths = [
os.path.join(os.path.dirname(__file__), "templates"),
]
self.settings["jinja2_env"].loader.searchpath.extend(template_paths)
def initialize_handlers(self):
handlers = [
(r"/my_extension/page", MyTemplateHandler),
]
self.handlers.extend(handlers)# In your extension package's __init__.py or main module
def _load_jupyter_server_extension(serverapp):
"""Load the extension into a Jupyter server."""
extension = MyExtension()
extension.serverapp = serverapp
extension.load_config_file()
extension.initialize()
return extension
# Optional: Define extension metadata
def _jupyter_server_extension_paths():
"""Return list of extension paths."""
return [
{
"module": "my_extension",
"app": MyExtension,
}
]# In setup.py or pyproject.toml
setup(
name="my-extension",
entry_points={
"jupyter_server.extension": [
"my_extension = my_extension:_load_jupyter_server_extension",
],
},
)Manages the lifecycle of extensions in a server instance.
from jupyter_server.extension.manager import ExtensionManager, ExtensionPackage
# Create extension manager
manager = ExtensionManager()
# Enable extension
manager.enable_extension("my_extension")
# Disable extension
manager.disable_extension("my_extension")
# List enabled extensions
enabled = manager.enabled_extensions
print(enabled) # {"my_extension": True}
# Check if extension is enabled
is_enabled = manager.is_extension_enabled("my_extension")Represents metadata about an extension package.
from jupyter_server.extension.manager import ExtensionPackage
# Create extension package info
package = ExtensionPackage(
name="my_extension",
enabled=True,
module="my_extension",
app=MyExtension,
)
# Access metadata
print(package.name) # "my_extension"
print(package.version) # Extension version
print(package.description) # Extension description
print(package.enabled) # True/FalseCommand-line interface for managing extensions.
from jupyter_server.extension.serverextension import (
ServerExtensionApp,
EnableServerExtensionApp,
DisableServerExtensionApp,
ListServerExtensionsApp,
)
# Enable extension via CLI
enable_app = EnableServerExtensionApp()
enable_app.initialize(["my_extension"])
enable_app.start()
# Disable extension via CLI
disable_app = DisableServerExtensionApp()
disable_app.initialize(["my_extension"])
disable_app.start()
# List extensions via CLI
list_app = ListServerExtensionsApp()
list_app.initialize()
list_app.start()# Enable extension system-wide
jupyter server extension enable my_extension
# Enable for current user only
jupyter server extension enable my_extension --user
# Enable for specific Python environment
jupyter server extension enable my_extension --sys-prefix
# Disable extension
jupyter server extension disable my_extension
# List all extensions
jupyter server extension listfrom jupyter_server.extension.utils import (
get_loader,
get_metadata,
validate_extension,
)
# Get extension loader function
loader = get_loader("my_extension")
if loader:
extension = loader(serverapp)
# Extract extension metadata
metadata = get_metadata("my_extension")
print(metadata) # Extension info dict
# Validate extension structure
try:
validate_extension("my_extension")
print("Extension is valid")
except Exception as e:
print(f"Extension validation failed: {e}")from jupyter_server.extension.config import ExtensionConfigManager
# Create config manager for extension
config_manager = ExtensionConfigManager(extension_name="my_extension")
# Get extension-specific config
config = config_manager.get("my_section")
# Set configuration values
config_manager.set("my_section", "setting", "value")
# Update configuration
config_manager.update("my_section", {
"setting1": "value1",
"setting2": "value2",
})Base mixin for extension handlers with common functionality.
from jupyter_server.extension.handler import ExtensionHandlerMixin
from jupyter_server.base.handlers import JupyterHandler
class MyExtensionHandler(ExtensionHandlerMixin, JupyterHandler):
"""Handler for my extension endpoints."""
def get(self):
# Access extension-specific settings
extension_name = self.extension_name
config = self.extension_config
self.finish({
"extension": extension_name,
"config": config,
})from jupyter_server.extension.handler import ExtensionHandlerJinjaMixin
from jupyter_server.base.handlers import JupyterHandler
class MyTemplatedHandler(ExtensionHandlerJinjaMixin, JupyterHandler):
"""Handler with Jinja2 template support."""
def get(self):
# Render extension template
html = self.render_template(
"my_extension/page.html",
title="My Extension Page",
data=self.get_extension_data(),
)
self.finish(html)
def get_extension_data(self):
"""Get data to pass to template."""
return {
"version": self.extension_version,
"config": self.extension_config,
}from jupyter_server.extension.application import ExtensionApp
from jupyter_server.base.handlers import APIHandler
from traitlets import Instance, Unicode
class MyServiceManager:
"""Custom service for the extension."""
def __init__(self, config=None):
self.config = config or {}
async def get_data(self):
"""Get service data."""
return {"status": "active", "data": "service_data"}
class MyServiceHandler(APIHandler):
"""API handler for the custom service."""
def initialize(self, service_manager):
self.service_manager = service_manager
async def get(self):
data = await self.service_manager.get_data()
self.finish(data)
class MyServiceExtension(ExtensionApp):
name = "my_service_extension"
# Custom service as a trait
service_manager = Instance(MyServiceManager)
def initialize_services(self):
"""Initialize custom services."""
self.service_manager = MyServiceManager(config=self.config)
def initialize_handlers(self):
handlers = [
(
r"/my_service/api/data",
MyServiceHandler,
{"service_manager": self.service_manager}
),
]
self.handlers.extend(handlers)from jupyter_server.extension.application import ExtensionApp
from jupyter_server.base.zmqhandlers import WebSocketMixin
from tornado import websocket
import json
class MyWebSocketHandler(WebSocketMixin, websocket.WebSocketHandler):
"""WebSocket handler for real-time communication."""
def open(self):
"""Handle WebSocket connection."""
self.log.info("WebSocket connection opened")
def on_message(self, message):
"""Handle incoming WebSocket message."""
try:
data = json.loads(message)
# Process message
response = {"type": "response", "data": data}
self.write_message(json.dumps(response))
except json.JSONDecodeError:
self.write_message(json.dumps({
"type": "error",
"message": "Invalid JSON"
}))
def on_close(self):
"""Handle WebSocket disconnection."""
self.log.info("WebSocket connection closed")
class MyWebSocketExtension(ExtensionApp):
name = "my_websocket_extension"
def initialize_handlers(self):
handlers = [
(r"/my_extension/ws", MyWebSocketHandler),
]
self.handlers.extend(handlers)from jupyter_server.extension.application import ExtensionApp
class BaseExtension(ExtensionApp):
"""Base extension with shared functionality."""
def get_common_settings(self):
return {
"base_url": self.base_url,
"common_config": self.config.get("common", {}),
}
class ExtensionA(BaseExtension):
name = "extension_a"
def initialize_handlers(self):
# Extension A specific handlers
pass
class ExtensionB(BaseExtension):
name = "extension_b"
def initialize_handlers(self):
# Extension B specific handlers
pass
# Registration functions
def _load_jupyter_server_extension(serverapp):
"""Load all extensions in this package."""
extensions = []
for ext_class in [ExtensionA, ExtensionB]:
ext = ext_class()
ext.serverapp = serverapp
ext.load_config_file()
ext.initialize()
extensions.append(ext)
return extensions
def _jupyter_server_extension_paths():
"""Return extension paths for discovery."""
return [
{"module": "my_multi_extension.extension_a", "app": ExtensionA},
{"module": "my_multi_extension.extension_b", "app": ExtensionB},
]import pytest
from jupyter_server.extension.application import ExtensionApp
from jupyter_server.serverapp import ServerApp
@pytest.fixture
def extension_app():
"""Create extension app for testing."""
app = MyExtension()
app.serverapp = ServerApp()
app.initialize()
return app
def test_extension_handlers(extension_app):
"""Test extension handler registration."""
assert len(extension_app.handlers) > 0
# Check specific handler exists
handler_patterns = [handler[0] for handler in extension_app.handlers]
assert "/my_extension/api" in handler_patterns
def test_extension_config(extension_app):
"""Test extension configuration."""
assert extension_app.name == "my_extension"
assert hasattr(extension_app, "config")from jupyter_server.extension.utils import (
ExtensionLoadingError,
ExtensionMetadataError,
ExtensionModuleNotFound,
NotAnExtensionApp,
)
try:
loader = get_loader("nonexistent_extension")
except ExtensionModuleNotFound as e:
print(f"Extension module not found: {e}")
try:
validate_extension("invalid_extension")
except ExtensionMetadataError as e:
print(f"Extension metadata error: {e}")
try:
# Load extension that isn't properly structured
extension = loader(serverapp)
except NotAnExtensionApp as e:
print(f"Not a valid extension app: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-jupyter-server