or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-structures.mddev-server.mdexceptions.mdhttp-utilities.mdindex.mdmiddleware.mdrequest-response.mdrouting.mdsecurity.mdtesting.mdurl-wsgi-utils.md

dev-server.mddocs/

0

# Development Server

1

2

Feature-rich WSGI development server with automatic reloading, interactive debugging, SSL support, and multi-threading capabilities. The development server provides a convenient way to run and test applications during development with extensive debugging and monitoring features.

3

4

**⚠️ Warning: This server is designed for development only. Never use it in production environments as it is not designed to be stable, secure, or efficient.**

5

6

## Capabilities

7

8

### Main Server Function

9

10

The primary function for starting the development server with comprehensive configuration options.

11

12

```python { .api }

13

def run_simple(hostname, port, application, use_reloader=False, use_debugger=False, use_evalex=True, extra_files=None, exclude_patterns=None, reloader_interval=1, reloader_type="auto", threaded=False, processes=1, request_handler=None, static_files=None, passthrough_errors=False, ssl_context=None):

14

"""

15

Start a development server for a WSGI application.

16

17

Parameters:

18

- hostname: Host to bind to (e.g., 'localhost', '0.0.0.0')

19

- port: Port number to bind to (use 0 for random free port)

20

- application: WSGI application callable to serve

21

- use_reloader: Enable automatic reloading when files change

22

- use_debugger: Enable interactive debugging on exceptions

23

- use_evalex: Allow interactive Python console in debugger

24

- extra_files: Additional files to watch for reloading

25

- exclude_patterns: File patterns to ignore for reloading (fnmatch format)

26

- reloader_interval: How often to check for file changes (seconds)

27

- reloader_type: Type of reloader ('auto', 'stat', 'watchdog')

28

- threaded: Enable multi-threading for concurrent requests

29

- processes: Number of processes for handling requests (alternative to threading)

30

- request_handler: Custom request handler class

31

- static_files: Dict mapping URL prefixes to directory paths for static files

32

- passthrough_errors: Don't catch unhandled exceptions at server level

33

- ssl_context: SSL configuration for HTTPS ('adhoc', SSLContext, or (cert, key) tuple)

34

"""

35

```

36

37

### Server Factory Function

38

39

Lower-level function for creating server instances with more control.

40

41

```python { .api }

42

def make_server(host, port, app, threaded=False, processes=1, request_handler=None, passthrough_errors=False, ssl_context=None, fd=None):

43

"""

44

Create a WSGI server instance.

45

46

This provides more control than run_simple() and returns the server object

47

for advanced usage like running in separate threads.

48

49

Parameters:

50

- host: Host address to bind to

51

- port: Port number to bind to

52

- app: WSGI application to serve

53

- threaded: Enable threading for concurrent requests

54

- processes: Number of worker processes

55

- request_handler: Custom request handler class

56

- passthrough_errors: Don't catch exceptions at server level

57

- ssl_context: SSL configuration

58

- fd: Existing file descriptor to use

59

60

Returns:

61

BaseWSGIServer instance (or subclass based on threading/process options)

62

"""

63

```

64

65

### Server Classes

66

67

The server class hierarchy provides different concurrency models.

68

69

```python { .api }

70

class BaseWSGIServer(HTTPServer):

71

def __init__(self, host, port, app, handler=None, passthrough_errors=False, ssl_context=None, fd=None):

72

"""

73

Single-threaded WSGI server.

74

75

Parameters:

76

- host: Host address

77

- port: Port number

78

- app: WSGI application

79

- handler: Request handler class

80

- passthrough_errors: Error handling mode

81

- ssl_context: SSL configuration

82

- fd: File descriptor for socket

83

"""

84

85

# Properties

86

multithread: bool = False # Server supports threading

87

multiprocess: bool = False # Server supports multiple processes

88

89

def serve_forever(self, poll_interval=0.5):

90

"""

91

Handle requests until shutdown.

92

93

Parameters:

94

- poll_interval: Time between polling for shutdown

95

"""

96

97

def shutdown_request(self, request):

98

"""Clean up after handling a request."""

99

100

def handle_error(self, request, client_address):

101

"""Handle errors during request processing."""

102

103

class ThreadedWSGIServer(socketserver.ThreadingMixIn, BaseWSGIServer):

104

def __init__(self, host, port, app, handler=None, passthrough_errors=False, ssl_context=None, fd=None):

105

"""

106

Multi-threaded WSGI server for concurrent request handling.

107

108

Uses ThreadingMixIn to spawn a new thread for each request.

109

"""

110

111

multithread: bool = True

112

daemon_threads: bool = True # Threads don't prevent server shutdown

113

114

class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):

115

def __init__(self, host, port, app, processes, handler=None, passthrough_errors=False, ssl_context=None, fd=None):

116

"""

117

Multi-process WSGI server for concurrent request handling.

118

119

Forks worker processes to handle requests. Only available on Unix-like systems.

120

121

Parameters:

122

- processes: Maximum number of worker processes

123

"""

124

125

multiprocess: bool = True

126

max_children: int # Maximum number of child processes

127

```

128

129

### Request Handler

130

131

Custom request handler for WSGI applications with enhanced features.

132

133

```python { .api }

134

class WSGIRequestHandler(BaseHTTPRequestHandler):

135

def __init__(self, request, client_address, server):

136

"""

137

WSGI-compatible HTTP request handler.

138

139

Handles the conversion between HTTP and WSGI protocols.

140

"""

141

142

# Properties

143

server: BaseWSGIServer

144

environ: dict # WSGI environment

145

146

def handle_one_request(self):

147

"""Handle a single HTTP request and convert to WSGI."""

148

149

def run_wsgi(self):

150

"""Execute the WSGI application and return response."""

151

152

def log_request(self, code='-', size='-'):

153

"""Log successful requests."""

154

155

def log_error(self, format, *args):

156

"""Log error messages."""

157

```

158

159

### SSL Functions

160

161

Functions for SSL/TLS configuration and certificate management.

162

163

```python { .api }

164

def generate_adhoc_ssl_context():

165

"""

166

Generate a self-signed SSL context for development.

167

168

Creates temporary certificates that are valid for localhost.

169

Requires the cryptography library.

170

171

Returns:

172

ssl.SSLContext configured with self-signed certificate

173

"""

174

175

def load_ssl_context(cert_file, key_file=None, protocol=None):

176

"""

177

Load SSL context from certificate files.

178

179

Parameters:

180

- cert_file: Path to certificate file (.pem or .crt)

181

- key_file: Path to private key file (optional if cert_file contains both)

182

- protocol: SSL protocol version to use

183

184

Returns:

185

ssl.SSLContext configured with the provided certificates

186

"""

187

188

def make_ssl_devcert(base_path, host="localhost", cn=None):

189

"""

190

Create a self-signed development certificate.

191

192

Parameters:

193

- base_path: Base path for certificate files (will create .crt and .key files)

194

- host: Hostname for certificate

195

- cn: Common name (defaults to host)

196

197

Returns:

198

Tuple of (cert_file_path, key_file_path)

199

"""

200

201

def generate_adhoc_ssl_pair(cn=None):

202

"""

203

Generate a self-signed certificate pair.

204

205

Parameters:

206

- cn: Common name for certificate

207

208

Returns:

209

Tuple of (Certificate, RSAPrivateKey) objects

210

"""

211

```

212

213

### Utility Functions

214

215

Helper functions for server management and detection.

216

217

```python { .api }

218

def is_running_from_reloader():

219

"""

220

Check if the current process is running from the Werkzeug reloader.

221

222

Returns:

223

True if running as a reloader subprocess

224

"""

225

226

def select_address_family(host, port):

227

"""

228

Automatically select appropriate address family (IPv4/IPv6) for host.

229

230

Parameters:

231

- host: Host address

232

- port: Port number

233

234

Returns:

235

socket.AddressFamily (AF_INET or AF_INET6)

236

"""

237

238

def get_interface_ip(family):

239

"""

240

Get the IP address of the network interface.

241

242

Parameters:

243

- family: Address family (AF_INET or AF_INET6)

244

245

Returns:

246

IP address string

247

"""

248

```

249

250

## Usage Examples

251

252

### Basic Development Server

253

254

```python

255

from werkzeug.serving import run_simple

256

from werkzeug.wrappers import Request, Response

257

258

def application(environ, start_response):

259

request = Request(environ)

260

response = Response(f'Hello from {request.path}!')

261

return response(environ, start_response)

262

263

if __name__ == '__main__':

264

# Basic server

265

run_simple('localhost', 8000, application)

266

```

267

268

### Server with Debugging and Reloading

269

270

```python

271

from werkzeug.serving import run_simple

272

from werkzeug.debug import DebuggedApplication

273

274

def create_app():

275

def app(environ, start_response):

276

request = Request(environ)

277

278

if request.path == '/error':

279

# Trigger an error for debugging

280

raise ValueError("This is a test error!")

281

282

response = Response(f'Path: {request.path}')

283

return response(environ, start_response)

284

285

return app

286

287

if __name__ == '__main__':

288

app = create_app()

289

290

# Development server with debugging and auto-reload

291

run_simple(

292

'localhost',

293

8000,

294

app,

295

use_debugger=True, # Interactive debugger on errors

296

use_reloader=True, # Auto-reload on file changes

297

use_evalex=True # Interactive Python console in debugger

298

)

299

```

300

301

### Multi-threaded Server

302

303

```python

304

from werkzeug.serving import run_simple

305

import time

306

import threading

307

308

def slow_application(environ, start_response):

309

request = Request(environ)

310

311

# Simulate slow processing

312

time.sleep(2)

313

314

thread_id = threading.get_ident()

315

response = Response(f'Processed by thread {thread_id}')

316

return response(environ, start_response)

317

318

if __name__ == '__main__':

319

# Multi-threaded server for concurrent requests

320

run_simple(

321

'localhost',

322

8000,

323

slow_application,

324

threaded=True # Handle multiple requests concurrently

325

)

326

```

327

328

### HTTPS Development Server

329

330

```python

331

from werkzeug.serving import run_simple, generate_adhoc_ssl_context

332

333

def secure_app(environ, start_response):

334

request = Request(environ)

335

336

# Check if request is secure

337

is_secure = request.environ.get('wsgi.url_scheme') == 'https'

338

339

response = Response(f'Secure: {is_secure}')

340

return response(environ, start_response)

341

342

if __name__ == '__main__':

343

# Option 1: Self-signed certificate

344

run_simple(

345

'localhost',

346

8000,

347

secure_app,

348

ssl_context='adhoc' # Auto-generate self-signed cert

349

)

350

351

# Option 2: Custom certificate files

352

# run_simple(

353

# 'localhost',

354

# 8000,

355

# secure_app,

356

# ssl_context=('cert.pem', 'key.pem')

357

# )

358

```

359

360

### Server with Static File Serving

361

362

```python

363

from werkzeug.serving import run_simple

364

365

def api_app(environ, start_response):

366

request = Request(environ)

367

368

if request.path.startswith('/api/'):

369

# Handle API requests

370

response = Response('{"status": "ok"}', mimetype='application/json')

371

else:

372

# Let static file middleware handle other requests

373

response = Response('Not Found', status=404)

374

375

return response(environ, start_response)

376

377

if __name__ == '__main__':

378

run_simple(

379

'localhost',

380

8000,

381

api_app,

382

static_files={

383

'/': '/path/to/static/files', # Serve static files from root

384

'/uploads': '/path/to/upload/dir', # Serve uploads from /uploads

385

'/assets': ('/path/to/assets', 'text/css') # With custom MIME type

386

}

387

)

388

```

389

390

### Advanced Server Configuration

391

392

```python

393

from werkzeug.serving import run_simple, make_server

394

import os

395

import threading

396

397

def advanced_app(environ, start_response):

398

request = Request(environ)

399

response = Response(f'Hello from PID {os.getpid()}')

400

return response(environ, start_response)

401

402

def run_with_custom_config():

403

# Advanced reloader configuration

404

run_simple(

405

'0.0.0.0', # Bind to all interfaces

406

8000,

407

advanced_app,

408

use_reloader=True,

409

reloader_type='watchdog', # Efficient file watching

410

reloader_interval=0.5, # Check every 0.5 seconds

411

extra_files=[ # Watch additional files

412

'config.yaml',

413

'templates/base.html'

414

],

415

exclude_patterns=[ # Ignore certain files

416

'*.pyc',

417

'*/__pycache__/*',

418

'*.log'

419

]

420

)

421

422

def run_with_server_object():

423

# Using make_server for more control

424

server = make_server(

425

'localhost',

426

8000,

427

advanced_app,

428

threaded=True

429

)

430

431

print(f"Server running on {server.server_address}")

432

433

# Run in separate thread

434

server_thread = threading.Thread(target=server.serve_forever)

435

server_thread.daemon = True

436

server_thread.start()

437

438

# Do other work...

439

input("Press Enter to stop server...")

440

server.shutdown()

441

server.server_close()

442

443

if __name__ == '__main__':

444

# Choose configuration

445

run_with_custom_config()

446

# run_with_server_object()

447

```

448

449

### Development Server with Custom Request Handler

450

451

```python

452

from werkzeug.serving import run_simple, WSGIRequestHandler

453

from werkzeug.wrappers import Request, Response

454

import logging

455

456

class CustomRequestHandler(WSGIRequestHandler):

457

def log_request(self, code='-', size='-'):

458

# Custom request logging

459

logging.info(f'{self.client_address[0]} - {self.command} {self.path} - {code}')

460

461

def log_error(self, format, *args):

462

# Custom error logging

463

logging.error(f'{self.client_address[0]} - ERROR: {format % args}')

464

465

def logged_app(environ, start_response):

466

request = Request(environ)

467

468

# Log request details

469

logging.info(f'Processing {request.method} {request.path}')

470

471

response = Response('Logged request')

472

return response(environ, start_response)

473

474

if __name__ == '__main__':

475

# Setup logging

476

logging.basicConfig(level=logging.INFO)

477

478

run_simple(

479

'localhost',

480

8000,

481

logged_app,

482

request_handler=CustomRequestHandler

483

)

484

```

485

486

### SSL Certificate Generation

487

488

```python

489

from werkzeug.serving import make_ssl_devcert, load_ssl_context, run_simple

490

491

def setup_ssl_server():

492

# Generate development certificates

493

cert_path, key_path = make_ssl_devcert('dev-cert', host='localhost')

494

495

# Load SSL context

496

ssl_context = load_ssl_context(cert_path, key_path)

497

498

# Or use the files directly

499

run_simple(

500

'localhost',

501

8000,

502

app,

503

ssl_context=(cert_path, key_path)

504

)

505

506

def app(environ, start_response):

507

request = Request(environ)

508

509

# SSL information available in environ

510

ssl_info = {

511

'scheme': request.scheme,

512

'is_secure': request.is_secure,

513

'url': request.url

514

}

515

516

response = Response(f'SSL Info: {ssl_info}')

517

return response(environ, start_response)

518

519

if __name__ == '__main__':

520

setup_ssl_server()

521

```

522

523

### Error Handling and Debugging

524

525

```python

526

from werkzeug.serving import run_simple

527

from werkzeug.debug import DebuggedApplication

528

from werkzeug.exceptions import HTTPException, NotFound

529

530

def debug_app(environ, start_response):

531

request = Request(environ)

532

533

if request.path == '/error':

534

# Unhandled exception - will show in debugger

535

raise ValueError("This is an unhandled error")

536

537

elif request.path == '/http-error':

538

# HTTP exception - proper error response

539

raise NotFound("Page not found")

540

541

elif request.path == '/debug-info':

542

# Show debug information

543

debug_info = {

544

'path': request.path,

545

'method': request.method,

546

'headers': dict(request.headers),

547

'args': dict(request.args)

548

}

549

response = Response(str(debug_info))

550

551

else:

552

response = Response('Hello! Try /error, /http-error, or /debug-info')

553

554

return response(environ, start_response)

555

556

if __name__ == '__main__':

557

# Wrap with debugger for development

558

debugged_app = DebuggedApplication(debug_app, evalex=True)

559

560

run_simple(

561

'localhost',

562

8000,

563

debugged_app,

564

use_debugger=True, # Enable Werkzeug debugger

565

passthrough_errors=False # Let debugger catch errors

566

)

567

```

568

569

### Production Transition

570

571

```python

572

from werkzeug.serving import run_simple, is_running_from_reloader

573

import os

574

575

def production_ready_app(environ, start_response):

576

request = Request(environ)

577

response = Response('Production app')

578

return response(environ, start_response)

579

580

def main():

581

# Check environment

582

is_development = os.getenv('FLASK_ENV') == 'development'

583

584

if is_development:

585

# Development configuration

586

print("Running in development mode")

587

run_simple(

588

'localhost',

589

8000,

590

production_ready_app,

591

use_debugger=True,

592

use_reloader=True,

593

use_evalex=True

594

)

595

else:

596

# Production reminder

597

print("⚠️ Don't use run_simple in production!")

598

print("Use a proper WSGI server like Gunicorn, uWSGI, or mod_wsgi")

599

600

# For demonstration only - don't do this in real production

601

run_simple(

602

'127.0.0.1', # Only local access

603

8000,

604

production_ready_app,

605

threaded=True,

606

use_debugger=False,

607

use_reloader=False

608

)

609

610

if __name__ == '__main__':

611

main()

612

```