or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdconfiguration-utilities.mdcore-application.mddatabase-models.mdindex.mdmonitoring-metrics.mdrbac-permissions.mdrest-api.mdservices-oauth.mdsingleuser-integration.mdspawners.md

configuration-utilities.mddocs/

0

# Configuration and Utilities

1

2

JupyterHub provides a comprehensive configuration system using traitlets along with utility functions for common operations including token management, URL handling, SSL setup, and asynchronous programming patterns.

3

4

## Capabilities

5

6

### Configuration System (Traitlets)

7

8

Custom traitlet types for JupyterHub configuration validation and processing.

9

10

```python { .api }

11

class Command(TraitType):

12

"""

13

Traitlet for command-line commands with shell and string list support.

14

15

Validates and processes command specifications for spawners and services.

16

"""

17

18

def validate(self, obj, value):

19

"""

20

Validate command specification.

21

22

Args:

23

obj: Object being configured

24

value: Command value (string, list, or callable)

25

26

Returns:

27

Validated command as list of strings

28

29

Raises:

30

TraitError: If command format is invalid

31

"""

32

33

class URLPrefix(TraitType):

34

"""

35

Traitlet for URL prefix validation and normalization.

36

37

Ensures URL prefixes are properly formatted with leading/trailing slashes.

38

"""

39

40

def validate(self, obj, value):

41

"""

42

Validate and normalize URL prefix.

43

44

Args:

45

obj: Object being configured

46

value: URL prefix string

47

48

Returns:

49

Normalized URL prefix with proper slash handling

50

"""

51

52

class ByteSpecification(TraitType):

53

"""

54

Traitlet for memory/storage size specifications with unit support.

55

56

Accepts values like '1G', '512M', '2048K' and converts to bytes.

57

"""

58

59

UNIT_MAP = {

60

'K': 1024,

61

'M': 1024**2,

62

'G': 1024**3,

63

'T': 1024**4

64

}

65

66

def validate(self, obj, value):

67

"""

68

Validate and convert byte specification to integer bytes.

69

70

Args:

71

obj: Object being configured

72

value: Size specification (int, string with units)

73

74

Returns:

75

Size in bytes as integer

76

"""

77

78

class Callable(TraitType):

79

"""

80

Traitlet for callable objects with import string support.

81

82

Accepts callable objects or import strings that resolve to callables.

83

"""

84

85

def validate(self, obj, value):

86

"""

87

Validate and resolve callable specification.

88

89

Args:

90

obj: Object being configured

91

value: Callable or import string

92

93

Returns:

94

Resolved callable object

95

"""

96

97

class EntryPointType(TraitType):

98

"""

99

Traitlet for loading Python entry points for plugin systems.

100

101

Supports authenticators, spawners, proxies, and other plugin types.

102

"""

103

104

def __init__(self, entry_point_group, **kwargs):

105

"""

106

Initialize entry point loader.

107

108

Args:

109

entry_point_group: Entry point group name (e.g., 'jupyterhub.authenticators')

110

**kwargs: Additional traitlet options

111

"""

112

self.entry_point_group = entry_point_group

113

super().__init__(**kwargs)

114

115

def validate(self, obj, value):

116

"""

117

Load plugin from entry point specification.

118

119

Args:

120

obj: Object being configured

121

value: Entry point name or class

122

123

Returns:

124

Loaded plugin class

125

"""

126

```

127

128

### Token Management Utilities

129

130

Secure token generation, hashing, and validation functions.

131

132

```python { .api }

133

def new_token(length: int = 32, entropy: int = None) -> str:

134

"""

135

Generate a new random API token.

136

137

Args:

138

length: Token length in characters (default: 32)

139

entropy: Additional entropy source (optional)

140

141

Returns:

142

Random token string (URL-safe base64)

143

"""

144

145

def hash_token(token: str, salt: bytes = None, rounds: int = 16384) -> str:

146

"""

147

Hash a token for secure storage using PBKDF2.

148

149

Args:

150

token: Token string to hash

151

salt: Salt bytes (generated if None)

152

rounds: PBKDF2 iteration count

153

154

Returns:

155

Base64-encoded hash suitable for database storage

156

"""

157

158

def compare_token(token: str, hashed: str) -> bool:

159

"""

160

Compare a token against its stored hash.

161

162

Args:

163

token: Plain token string

164

hashed: Stored hash from hash_token()

165

166

Returns:

167

True if token matches hash

168

"""

169

170

def token_authenticated(method):

171

"""

172

Decorator for methods that require token authentication.

173

174

Args:

175

method: Method to protect with token auth

176

177

Returns:

178

Decorated method that checks for valid token

179

"""

180

```

181

182

### URL and Path Utilities

183

184

Functions for safe URL and path manipulation in web contexts.

185

186

```python { .api }

187

def url_path_join(*pieces) -> str:

188

"""

189

Join URL path components with proper slash handling.

190

191

Args:

192

*pieces: URL path components to join

193

194

Returns:

195

Properly joined URL path with normalized slashes

196

"""

197

198

def url_escape_path(path: str, safe: str = '@!:$&\'()*+,;=') -> str:

199

"""

200

URL-encode path components while preserving safe characters.

201

202

Args:

203

path: Path string to encode

204

safe: Characters to leave unencoded

205

206

Returns:

207

URL-encoded path string

208

"""

209

210

def url_unescape_path(path: str) -> str:

211

"""

212

URL-decode path components.

213

214

Args:

215

path: URL-encoded path string

216

217

Returns:

218

Decoded path string

219

"""

220

221

def guess_base_url(url: str) -> str:

222

"""

223

Guess the base URL from a full URL.

224

225

Args:

226

url: Full URL string

227

228

Returns:

229

Base URL (protocol + host + port)

230

"""

231

```

232

233

### Async and Concurrency Utilities

234

235

Helper functions for asynchronous programming patterns in JupyterHub.

236

237

```python { .api }

238

def maybe_future(value):

239

"""

240

Convert value to Future if it's not already awaitable.

241

242

Args:

243

value: Value that may or may not be awaitable

244

245

Returns:

246

Future/coroutine that can be awaited

247

"""

248

249

def exponential_backoff(func,

250

max_retries: int = 5,

251

initial_delay: float = 1.0,

252

max_delay: float = 60.0,

253

backoff_factor: float = 2.0):

254

"""

255

Decorator for exponential backoff retry logic.

256

257

Args:

258

func: Function to wrap with retry logic

259

max_retries: Maximum number of retry attempts

260

initial_delay: Initial delay between retries (seconds)

261

max_delay: Maximum delay between retries (seconds)

262

backoff_factor: Multiplier for delay on each retry

263

264

Returns:

265

Decorated function with retry logic

266

"""

267

268

async def cancel_tasks(tasks):

269

"""

270

Cancel a collection of asyncio tasks gracefully.

271

272

Args:

273

tasks: Iterable of asyncio tasks to cancel

274

"""

275

276

def run_sync(async_func):

277

"""

278

Run an async function synchronously.

279

280

Args:

281

async_func: Async function to run

282

283

Returns:

284

Result of the async function

285

"""

286

```

287

288

### Network and SSL Utilities

289

290

Functions for network operations and SSL context management.

291

292

```python { .api }

293

def make_ssl_context(keyfile: str = None,

294

certfile: str = None,

295

cafile: str = None,

296

verify_mode: ssl.VerifyMode = None,

297

check_hostname: bool = None,

298

**kwargs) -> ssl.SSLContext:

299

"""

300

Create SSL context with common JupyterHub defaults.

301

302

Args:

303

keyfile: Path to SSL private key file

304

certfile: Path to SSL certificate file

305

cafile: Path to CA certificate file

306

verify_mode: SSL verification mode

307

check_hostname: Whether to verify hostname

308

**kwargs: Additional SSL context options

309

310

Returns:

311

Configured SSL context

312

"""

313

314

def random_port() -> int:

315

"""

316

Find a random available port.

317

318

Returns:

319

Available port number

320

"""

321

322

def is_valid_ip(ip: str) -> bool:

323

"""

324

Check if string is a valid IP address.

325

326

Args:

327

ip: IP address string to validate

328

329

Returns:

330

True if valid IPv4 or IPv6 address

331

"""

332

333

def get_server_info(url: str) -> Dict[str, Any]:

334

"""

335

Get server information from URL.

336

337

Args:

338

url: Server URL to analyze

339

340

Returns:

341

Dictionary with host, port, protocol info

342

"""

343

```

344

345

### Database and ORM Utilities

346

347

Utilities for database operations and schema management.

348

349

```python { .api }

350

def mysql_large_prefix_check():

351

"""

352

Check MySQL configuration for large index prefix support.

353

354

Raises:

355

DatabaseError: If MySQL doesn't support required index sizes

356

"""

357

358

def upgrade_if_needed(db_url: str, log=None):

359

"""

360

Upgrade database schema if needed.

361

362

Args:

363

db_url: Database connection URL

364

log: Logger instance for output

365

"""

366

367

def get_schema_version(db_url: str) -> str:

368

"""

369

Get current database schema version.

370

371

Args:

372

db_url: Database connection URL

373

374

Returns:

375

Schema version string

376

"""

377

```

378

379

## Usage Examples

380

381

### Configuration with Custom Traitlets

382

383

```python

384

# jupyterhub_config.py using custom traitlets

385

c = get_config()

386

387

# Memory limits using ByteSpecification

388

c.Spawner.mem_limit = '2G' # Converted to 2147483648 bytes

389

c.Spawner.mem_guarantee = '512M' # Converted to 536870912 bytes

390

391

# URL prefix configuration

392

c.JupyterHub.base_url = '/hub/' # Normalized to '/hub/'

393

394

# Command configuration

395

c.Spawner.cmd = ['jupyterhub-singleuser'] # Validated as command list

396

c.LocalProcessSpawner.shell_cmd = ['/bin/bash', '-l', '-c']

397

398

# Plugin loading via entry points

399

c.JupyterHub.authenticator_class = 'pam' # Loads PAMAuthenticator

400

c.JupyterHub.spawner_class = 'localprocess' # Loads LocalProcessSpawner

401

```

402

403

### Token Management

404

405

```python

406

from jupyterhub.utils import new_token, hash_token, compare_token

407

from jupyterhub.orm import APIToken

408

409

# Generate API token for user

410

user = db.query(User).filter(User.name == 'alice').first()

411

token = new_token(length=32)

412

hashed = hash_token(token)

413

414

# Store in database

415

api_token = APIToken(

416

user=user,

417

hashed=hashed,

418

prefix=token[:4], # Store prefix for identification

419

note='CLI access token'

420

)

421

db.add(api_token)

422

db.commit()

423

424

# Validate token later

425

def validate_api_token(provided_token):

426

"""Validate API token against database"""

427

prefix = provided_token[:4]

428

token_record = db.query(APIToken).filter(

429

APIToken.prefix == prefix

430

).first()

431

432

if token_record and compare_token(provided_token, token_record.hashed):

433

return token_record.user

434

return None

435

```

436

437

### URL Handling

438

439

```python

440

from jupyterhub.utils import url_path_join, url_escape_path

441

442

# Safe URL path joining

443

base_url = '/hub'

444

user_name = 'alice@example.com'

445

server_name = 'my-server'

446

447

# Build user server URL safely

448

server_url = url_path_join(

449

base_url,

450

'user',

451

url_escape_path(user_name),

452

url_escape_path(server_name)

453

)

454

# Result: '/hub/user/alice%40example.com/my-server'

455

456

# Build API endpoint URLs

457

api_url = url_path_join(base_url, 'api', 'users', url_escape_path(user_name))

458

# Result: '/hub/api/users/alice%40example.com'

459

```

460

461

### Async Utilities

462

463

```python

464

from jupyterhub.utils import maybe_future, exponential_backoff

465

import asyncio

466

467

# Convert sync/async values consistently

468

async def handle_result(result):

469

"""Handle potentially async result"""

470

# maybe_future ensures we can always await

471

final_result = await maybe_future(result)

472

return final_result

473

474

# Retry with exponential backoff

475

@exponential_backoff(max_retries=3, initial_delay=1.0)

476

async def unreliable_operation():

477

"""Operation that might fail temporarily"""

478

# Simulate operation that might fail

479

if random.random() < 0.5:

480

raise Exception("Temporary failure")

481

return "Success"

482

483

# Usage

484

try:

485

result = await unreliable_operation()

486

print(f"Operation succeeded: {result}")

487

except Exception as e:

488

print(f"Operation failed after retries: {e}")

489

```

490

491

### SSL Configuration

492

493

```python

494

from jupyterhub.utils import make_ssl_context

495

496

# Create SSL context for HTTPS

497

ssl_context = make_ssl_context(

498

keyfile='/path/to/private.key',

499

certfile='/path/to/certificate.crt',

500

cafile='/path/to/ca-bundle.crt'

501

)

502

503

# Use in JupyterHub configuration

504

c.JupyterHub.ssl_key = '/path/to/private.key'

505

c.JupyterHub.ssl_cert = '/path/to/certificate.crt'

506

507

# Or programmatically

508

app = JupyterHub()

509

app.ssl_context = ssl_context

510

```

511

512

### Custom Configuration Classes

513

514

```python

515

from traitlets import Unicode, Integer, Bool

516

from traitlets.config import Configurable

517

from jupyterhub.traitlets import ByteSpecification, Command

518

519

class CustomSpawner(Spawner):

520

"""Custom spawner with additional configuration"""

521

522

# Memory configuration using ByteSpecification

523

memory_limit = ByteSpecification(

524

config=True,

525

help="""

526

Memory limit for user servers.

527

Specify with units like '1G', '512M', etc.

528

"""

529

)

530

531

# Custom command configuration

532

setup_command = Command(

533

config=True,

534

help="""

535

Command to run before starting notebook server.

536

Can be string or list of strings.

537

"""

538

)

539

540

# Container image with validation

541

image = Unicode(

542

'jupyter/base-notebook',

543

config=True,

544

help="Docker image to use for user servers"

545

)

546

547

# Advanced configuration

548

privileged = Bool(

549

False,

550

config=True,

551

help="Whether to run containers in privileged mode"

552

)

553

554

async def start(self):

555

"""Start server with custom configuration"""

556

# Use configured values

557

print(f"Memory limit: {self.memory_limit} bytes")

558

print(f"Setup command: {self.setup_command}")

559

print(f"Image: {self.image}")

560

561

# Custom startup logic here

562

return await super().start()

563

564

# Usage in config

565

c.JupyterHub.spawner_class = CustomSpawner

566

c.CustomSpawner.memory_limit = '4G'

567

c.CustomSpawner.setup_command = ['conda', 'activate', 'myenv']

568

c.CustomSpawner.image = 'myregistry/custom-notebook:latest'

569

```

570

571

### Database Utilities

572

573

```python

574

from jupyterhub.dbutil import upgrade_if_needed

575

576

# Automatic database upgrades

577

def initialize_database(db_url):

578

"""Initialize database with schema upgrades"""

579

try:

580

upgrade_if_needed(db_url, log=app.log)

581

app.log.info("Database schema is up to date")

582

except Exception as e:

583

app.log.error(f"Database upgrade failed: {e}")

584

raise

585

586

# Use in application startup

587

app = JupyterHub()

588

initialize_database(app.db_url)

589

app.start()

590

```

591

592

## Advanced Configuration Patterns

593

594

### Environment-Based Configuration

595

596

```python

597

import os

598

from jupyterhub.utils import url_path_join

599

600

# Environment-aware configuration

601

def get_config():

602

"""Get configuration based on environment"""

603

c = super().get_config()

604

605

# Database URL from environment

606

c.JupyterHub.db_url = os.environ.get(

607

'JUPYTERHUB_DB_URL',

608

'sqlite:///jupyterhub.sqlite'

609

)

610

611

# Base URL handling

612

base_url = os.environ.get('JUPYTERHUB_BASE_URL', '/')

613

c.JupyterHub.base_url = base_url

614

615

# SSL in production

616

if os.environ.get('JUPYTERHUB_ENV') == 'production':

617

c.JupyterHub.ssl_key = os.environ['SSL_KEY_PATH']

618

c.JupyterHub.ssl_cert = os.environ['SSL_CERT_PATH']

619

c.JupyterHub.port = 443

620

else:

621

c.JupyterHub.port = 8000

622

623

return c

624

```

625

626

### Dynamic Configuration Updates

627

628

```python

629

class DynamicConfig(Configurable):

630

"""Configuration that can be updated at runtime"""

631

632

def __init__(self, **kwargs):

633

super().__init__(**kwargs)

634

self.config_watchers = []

635

636

def watch_config_changes(self, callback):

637

"""Register callback for configuration changes"""

638

self.config_watchers.append(callback)

639

640

def update_config(self, **kwargs):

641

"""Update configuration and notify watchers"""

642

for key, value in kwargs.items():

643

if hasattr(self, key):

644

setattr(self, key, value)

645

646

# Notify watchers

647

for callback in self.config_watchers:

648

callback(kwargs)

649

650

# Usage

651

config = DynamicConfig()

652

config.watch_config_changes(lambda changes: print(f"Config updated: {changes}"))

653

config.update_config(memory_limit='8G', image='new-image:latest')

654

```