CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-jupyter-server

The backend infrastructure for Jupyter web applications providing core services, APIs, and REST endpoints.

Overview
Eval results
Files

extensions.mddocs/

Extensions

The Jupyter Server extension system provides a powerful framework for building pluggable server applications and adding functionality to existing servers.

Extension Development

ExtensionApp

Base class for creating server extensions that can run standalone or integrate with existing servers.

from jupyter_server.extension.application import ExtensionApp

Basic Extension

from 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,
        })

Advanced Extension with Templates

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)

Extension Registration

Setup Function

# 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,
        }
    ]

Package Metadata

# In setup.py or pyproject.toml
setup(
    name="my-extension",
    entry_points={
        "jupyter_server.extension": [
            "my_extension = my_extension:_load_jupyter_server_extension",
        ],
    },
)

Extension Management

ExtensionManager

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")

ExtensionPackage

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/False

CLI Management

ServerExtensionApp

Command-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()

Command Line Usage

# 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 list

Extension Utilities

Extension Discovery

from 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}")

Extension Configuration

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",
})

Extension Handlers

ExtensionHandlerMixin

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,
        })

Template Support

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,
        }

Advanced Extension Patterns

Extension with Custom Services

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)

Extension with WebSocket Support

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)

Multi-Extension Package

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},
    ]

Testing Extensions

Extension Test Utilities

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")

Extension Exceptions

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

docs

auth.md

configuration.md

core-application.md

extensions.md

handlers.md

index.md

services.md

tile.json