or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

auth.mdconfiguration.mdcore-application.mdextensions.mdhandlers.mdindex.mdservices.md

extensions.mddocs/

0

# Extensions

1

2

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

3

4

## Extension Development

5

6

### ExtensionApp

7

8

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

9

10

```python

11

from jupyter_server.extension.application import ExtensionApp

12

```

13

14

#### Basic Extension

15

16

```python{ .api }

17

from jupyter_server.extension.application import ExtensionApp

18

from jupyter_server.base.handlers import JupyterHandler

19

20

class HelloHandler(JupyterHandler):

21

def get(self):

22

self.finish({"message": "Hello from my extension!"})

23

24

class MyExtension(ExtensionApp):

25

name = "my_extension"

26

description = "My custom Jupyter extension"

27

version = "1.0.0"

28

29

# Extension metadata

30

extension_url = "/my_extension"

31

load_other_extensions = True

32

33

def initialize_handlers(self):

34

"""Register URL handlers for this extension."""

35

handlers = [

36

(r"/my_extension/hello", HelloHandler),

37

]

38

self.handlers.extend(handlers)

39

40

def initialize_settings(self):

41

"""Initialize custom settings."""

42

self.settings.update({

43

"my_extension_config": self.config,

44

})

45

```

46

47

#### Advanced Extension with Templates

48

49

```python{ .api }

50

from jupyter_server.extension.application import ExtensionAppJinjaMixin, ExtensionApp

51

from jupyter_server.base.handlers import JupyterHandler

52

53

class MyTemplateHandler(JupyterHandler):

54

def get(self):

55

# Render template with context

56

html = self.render_template(

57

"my_template.html",

58

title="My Extension",

59

data={"key": "value"}

60

)

61

self.finish(html)

62

63

class MyExtensionWithTemplates(ExtensionAppJinjaMixin, ExtensionApp):

64

name = "my_templated_extension"

65

66

def initialize_templates(self):

67

"""Setup Jinja2 template environment."""

68

self.initialize_jinja2_settings()

69

70

# Add custom template paths

71

template_paths = [

72

os.path.join(os.path.dirname(__file__), "templates"),

73

]

74

self.settings["jinja2_env"].loader.searchpath.extend(template_paths)

75

76

def initialize_handlers(self):

77

handlers = [

78

(r"/my_extension/page", MyTemplateHandler),

79

]

80

self.handlers.extend(handlers)

81

```

82

83

### Extension Registration

84

85

#### Setup Function

86

87

```python{ .api }

88

# In your extension package's __init__.py or main module

89

90

def _load_jupyter_server_extension(serverapp):

91

"""Load the extension into a Jupyter server."""

92

extension = MyExtension()

93

extension.serverapp = serverapp

94

extension.load_config_file()

95

extension.initialize()

96

97

return extension

98

99

# Optional: Define extension metadata

100

def _jupyter_server_extension_paths():

101

"""Return list of extension paths."""

102

return [

103

{

104

"module": "my_extension",

105

"app": MyExtension,

106

}

107

]

108

```

109

110

#### Package Metadata

111

112

```python

113

# In setup.py or pyproject.toml

114

setup(

115

name="my-extension",

116

entry_points={

117

"jupyter_server.extension": [

118

"my_extension = my_extension:_load_jupyter_server_extension",

119

],

120

},

121

)

122

```

123

124

## Extension Management

125

126

### ExtensionManager

127

128

Manages the lifecycle of extensions in a server instance.

129

130

```python{ .api }

131

from jupyter_server.extension.manager import ExtensionManager, ExtensionPackage

132

133

# Create extension manager

134

manager = ExtensionManager()

135

136

# Enable extension

137

manager.enable_extension("my_extension")

138

139

# Disable extension

140

manager.disable_extension("my_extension")

141

142

# List enabled extensions

143

enabled = manager.enabled_extensions

144

print(enabled) # {"my_extension": True}

145

146

# Check if extension is enabled

147

is_enabled = manager.is_extension_enabled("my_extension")

148

```

149

150

### ExtensionPackage

151

152

Represents metadata about an extension package.

153

154

```python{ .api }

155

from jupyter_server.extension.manager import ExtensionPackage

156

157

# Create extension package info

158

package = ExtensionPackage(

159

name="my_extension",

160

enabled=True,

161

module="my_extension",

162

app=MyExtension,

163

)

164

165

# Access metadata

166

print(package.name) # "my_extension"

167

print(package.version) # Extension version

168

print(package.description) # Extension description

169

print(package.enabled) # True/False

170

```

171

172

## CLI Management

173

174

### ServerExtensionApp

175

176

Command-line interface for managing extensions.

177

178

```python{ .api }

179

from jupyter_server.extension.serverextension import (

180

ServerExtensionApp,

181

EnableServerExtensionApp,

182

DisableServerExtensionApp,

183

ListServerExtensionsApp,

184

)

185

186

# Enable extension via CLI

187

enable_app = EnableServerExtensionApp()

188

enable_app.initialize(["my_extension"])

189

enable_app.start()

190

191

# Disable extension via CLI

192

disable_app = DisableServerExtensionApp()

193

disable_app.initialize(["my_extension"])

194

disable_app.start()

195

196

# List extensions via CLI

197

list_app = ListServerExtensionsApp()

198

list_app.initialize()

199

list_app.start()

200

```

201

202

### Command Line Usage

203

204

```bash

205

# Enable extension system-wide

206

jupyter server extension enable my_extension

207

208

# Enable for current user only

209

jupyter server extension enable my_extension --user

210

211

# Enable for specific Python environment

212

jupyter server extension enable my_extension --sys-prefix

213

214

# Disable extension

215

jupyter server extension disable my_extension

216

217

# List all extensions

218

jupyter server extension list

219

```

220

221

## Extension Utilities

222

223

### Extension Discovery

224

225

```python{ .api }

226

from jupyter_server.extension.utils import (

227

get_loader,

228

get_metadata,

229

validate_extension,

230

)

231

232

# Get extension loader function

233

loader = get_loader("my_extension")

234

if loader:

235

extension = loader(serverapp)

236

237

# Extract extension metadata

238

metadata = get_metadata("my_extension")

239

print(metadata) # Extension info dict

240

241

# Validate extension structure

242

try:

243

validate_extension("my_extension")

244

print("Extension is valid")

245

except Exception as e:

246

print(f"Extension validation failed: {e}")

247

```

248

249

### Extension Configuration

250

251

```python{ .api }

252

from jupyter_server.extension.config import ExtensionConfigManager

253

254

# Create config manager for extension

255

config_manager = ExtensionConfigManager(extension_name="my_extension")

256

257

# Get extension-specific config

258

config = config_manager.get("my_section")

259

260

# Set configuration values

261

config_manager.set("my_section", "setting", "value")

262

263

# Update configuration

264

config_manager.update("my_section", {

265

"setting1": "value1",

266

"setting2": "value2",

267

})

268

```

269

270

## Extension Handlers

271

272

### ExtensionHandlerMixin

273

274

Base mixin for extension handlers with common functionality.

275

276

```python{ .api }

277

from jupyter_server.extension.handler import ExtensionHandlerMixin

278

from jupyter_server.base.handlers import JupyterHandler

279

280

class MyExtensionHandler(ExtensionHandlerMixin, JupyterHandler):

281

"""Handler for my extension endpoints."""

282

283

def get(self):

284

# Access extension-specific settings

285

extension_name = self.extension_name

286

config = self.extension_config

287

288

self.finish({

289

"extension": extension_name,

290

"config": config,

291

})

292

```

293

294

### Template Support

295

296

```python{ .api }

297

from jupyter_server.extension.handler import ExtensionHandlerJinjaMixin

298

from jupyter_server.base.handlers import JupyterHandler

299

300

class MyTemplatedHandler(ExtensionHandlerJinjaMixin, JupyterHandler):

301

"""Handler with Jinja2 template support."""

302

303

def get(self):

304

# Render extension template

305

html = self.render_template(

306

"my_extension/page.html",

307

title="My Extension Page",

308

data=self.get_extension_data(),

309

)

310

self.finish(html)

311

312

def get_extension_data(self):

313

"""Get data to pass to template."""

314

return {

315

"version": self.extension_version,

316

"config": self.extension_config,

317

}

318

```

319

320

## Advanced Extension Patterns

321

322

### Extension with Custom Services

323

324

```python{ .api }

325

from jupyter_server.extension.application import ExtensionApp

326

from jupyter_server.base.handlers import APIHandler

327

from traitlets import Instance, Unicode

328

329

class MyServiceManager:

330

"""Custom service for the extension."""

331

332

def __init__(self, config=None):

333

self.config = config or {}

334

335

async def get_data(self):

336

"""Get service data."""

337

return {"status": "active", "data": "service_data"}

338

339

class MyServiceHandler(APIHandler):

340

"""API handler for the custom service."""

341

342

def initialize(self, service_manager):

343

self.service_manager = service_manager

344

345

async def get(self):

346

data = await self.service_manager.get_data()

347

self.finish(data)

348

349

class MyServiceExtension(ExtensionApp):

350

name = "my_service_extension"

351

352

# Custom service as a trait

353

service_manager = Instance(MyServiceManager)

354

355

def initialize_services(self):

356

"""Initialize custom services."""

357

self.service_manager = MyServiceManager(config=self.config)

358

359

def initialize_handlers(self):

360

handlers = [

361

(

362

r"/my_service/api/data",

363

MyServiceHandler,

364

{"service_manager": self.service_manager}

365

),

366

]

367

self.handlers.extend(handlers)

368

```

369

370

### Extension with WebSocket Support

371

372

```python{ .api }

373

from jupyter_server.extension.application import ExtensionApp

374

from jupyter_server.base.zmqhandlers import WebSocketMixin

375

from tornado import websocket

376

import json

377

378

class MyWebSocketHandler(WebSocketMixin, websocket.WebSocketHandler):

379

"""WebSocket handler for real-time communication."""

380

381

def open(self):

382

"""Handle WebSocket connection."""

383

self.log.info("WebSocket connection opened")

384

385

def on_message(self, message):

386

"""Handle incoming WebSocket message."""

387

try:

388

data = json.loads(message)

389

# Process message

390

response = {"type": "response", "data": data}

391

self.write_message(json.dumps(response))

392

except json.JSONDecodeError:

393

self.write_message(json.dumps({

394

"type": "error",

395

"message": "Invalid JSON"

396

}))

397

398

def on_close(self):

399

"""Handle WebSocket disconnection."""

400

self.log.info("WebSocket connection closed")

401

402

class MyWebSocketExtension(ExtensionApp):

403

name = "my_websocket_extension"

404

405

def initialize_handlers(self):

406

handlers = [

407

(r"/my_extension/ws", MyWebSocketHandler),

408

]

409

self.handlers.extend(handlers)

410

```

411

412

### Multi-Extension Package

413

414

```python{ .api }

415

from jupyter_server.extension.application import ExtensionApp

416

417

class BaseExtension(ExtensionApp):

418

"""Base extension with shared functionality."""

419

420

def get_common_settings(self):

421

return {

422

"base_url": self.base_url,

423

"common_config": self.config.get("common", {}),

424

}

425

426

class ExtensionA(BaseExtension):

427

name = "extension_a"

428

429

def initialize_handlers(self):

430

# Extension A specific handlers

431

pass

432

433

class ExtensionB(BaseExtension):

434

name = "extension_b"

435

436

def initialize_handlers(self):

437

# Extension B specific handlers

438

pass

439

440

# Registration functions

441

def _load_jupyter_server_extension(serverapp):

442

"""Load all extensions in this package."""

443

extensions = []

444

445

for ext_class in [ExtensionA, ExtensionB]:

446

ext = ext_class()

447

ext.serverapp = serverapp

448

ext.load_config_file()

449

ext.initialize()

450

extensions.append(ext)

451

452

return extensions

453

454

def _jupyter_server_extension_paths():

455

"""Return extension paths for discovery."""

456

return [

457

{"module": "my_multi_extension.extension_a", "app": ExtensionA},

458

{"module": "my_multi_extension.extension_b", "app": ExtensionB},

459

]

460

```

461

462

## Testing Extensions

463

464

### Extension Test Utilities

465

466

```python{ .api }

467

import pytest

468

from jupyter_server.extension.application import ExtensionApp

469

from jupyter_server.serverapp import ServerApp

470

471

@pytest.fixture

472

def extension_app():

473

"""Create extension app for testing."""

474

app = MyExtension()

475

app.serverapp = ServerApp()

476

app.initialize()

477

return app

478

479

def test_extension_handlers(extension_app):

480

"""Test extension handler registration."""

481

assert len(extension_app.handlers) > 0

482

483

# Check specific handler exists

484

handler_patterns = [handler[0] for handler in extension_app.handlers]

485

assert "/my_extension/api" in handler_patterns

486

487

def test_extension_config(extension_app):

488

"""Test extension configuration."""

489

assert extension_app.name == "my_extension"

490

assert hasattr(extension_app, "config")

491

```

492

493

## Extension Exceptions

494

495

```python{ .api }

496

from jupyter_server.extension.utils import (

497

ExtensionLoadingError,

498

ExtensionMetadataError,

499

ExtensionModuleNotFound,

500

NotAnExtensionApp,

501

)

502

503

try:

504

loader = get_loader("nonexistent_extension")

505

except ExtensionModuleNotFound as e:

506

print(f"Extension module not found: {e}")

507

508

try:

509

validate_extension("invalid_extension")

510

except ExtensionMetadataError as e:

511

print(f"Extension metadata error: {e}")

512

513

try:

514

# Load extension that isn't properly structured

515

extension = loader(serverapp)

516

except NotAnExtensionApp as e:

517

print(f"Not a valid extension app: {e}")

518

```