or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

code-formatters.mdconfiguration-system.mdfile-editor-formatting.mdfrontend-integration.mdhttp-api-client.mdhttp-api-handlers.mdindex.mdnotebook-formatting.md

http-api-handlers.mddocs/

0

# HTTP API Handlers

1

2

Server-side HTTP request handlers for formatter discovery and code formatting operations with authentication, error handling, and caching support.

3

4

## Capabilities

5

6

### Formatters API Handler

7

8

Handles requests for discovering available code formatters.

9

10

```python { .api }

11

class FormattersAPIHandler(APIHandler):

12

@tornado.web.authenticated

13

def get(self) -> None:

14

"""Show what formatters are installed and available."""

15

pass

16

```

17

18

**Endpoint**: `GET /jupyterlab_code_formatter/formatters[?cached]`

19

20

**Query Parameters**:

21

- `cached` (optional) - Use cached formatter availability checks

22

23

**Response Format**:

24

```json

25

{

26

"formatters": {

27

"formatter_name": {

28

"enabled": boolean,

29

"label": "Human-readable name"

30

}

31

}

32

}

33

```

34

35

### Format API Handler

36

37

Handles code formatting requests.

38

39

```python { .api }

40

class FormatAPIHandler(APIHandler):

41

@tornado.web.authenticated

42

def post(self) -> None:

43

"""Format code using specified formatter."""

44

pass

45

```

46

47

**Endpoint**: `POST /jupyterlab_code_formatter/format[?cached]`

48

49

**Request Body**:

50

```json

51

{

52

"code": ["string array of code"],

53

"formatter": "formatter_name",

54

"notebook": boolean,

55

"options": {

56

"formatter_specific_options": "values"

57

}

58

}

59

```

60

61

**Response Format**:

62

```json

63

{

64

"code": [

65

{

66

"code": "formatted_code_string"

67

}

68

]

69

}

70

```

71

72

**Error Response**:

73

```json

74

{

75

"code": [

76

{

77

"error": "error_message"

78

}

79

]

80

}

81

```

82

83

### Handler Setup Function

84

85

Registers API handlers with the Jupyter server.

86

87

```python { .api }

88

def setup_handlers(web_app) -> None:

89

"""Register API handlers with Jupyter server."""

90

pass

91

```

92

93

**Parameters**:

94

- `web_app` - Tornado web application instance

95

96

## Usage Examples

97

98

### Handler Registration

99

100

```python

101

from jupyterlab_code_formatter.handlers import setup_handlers

102

103

def _load_jupyter_server_extension(server_app):

104

"""Register the API handlers to receive HTTP requests."""

105

setup_handlers(server_app.web_app)

106

server_app.log.info("Registered jupyterlab_code_formatter server extension")

107

```

108

109

### Request Processing Flow

110

111

#### Formatters Discovery Request

112

113

```python

114

# Client request: GET /jupyterlab_code_formatter/formatters

115

# Handler processes request:

116

117

def get(self) -> None:

118

use_cache = self.get_query_argument("cached", default=None)

119

120

response_data = {

121

"formatters": {

122

name: {

123

"enabled": formatter.cached_importable if use_cache else formatter.importable,

124

"label": formatter.label,

125

}

126

for name, formatter in SERVER_FORMATTERS.items()

127

}

128

}

129

130

self.finish(json.dumps(response_data))

131

```

132

133

#### Code Formatting Request

134

135

```python

136

# Client request: POST /jupyterlab_code_formatter/format

137

# Handler processes request:

138

139

def post(self) -> None:

140

data = json.loads(self.request.body.decode("utf-8"))

141

formatter_instance = SERVER_FORMATTERS.get(data["formatter"])

142

use_cache = self.get_query_argument("cached", default=None)

143

144

if formatter_instance is None or not (

145

formatter_instance.cached_importable if use_cache else formatter_instance.importable

146

):

147

self.set_status(404, f"Formatter {data['formatter']} not found!")

148

self.finish()

149

return

150

151

notebook = data["notebook"]

152

options = data.get("options", {})

153

formatted_code = []

154

155

for code in data["code"]:

156

try:

157

result = formatter_instance.format_code(code, notebook, **options)

158

formatted_code.append({"code": result})

159

except Exception as e:

160

formatted_code.append({"error": str(e)})

161

162

self.finish(json.dumps({"code": formatted_code}))

163

```

164

165

## API Endpoints

166

167

### Get Available Formatters

168

169

**URL**: `/jupyterlab_code_formatter/formatters`

170

**Method**: `GET`

171

**Authentication**: Required (Jupyter token)

172

173

**Query Parameters**:

174

- `cached` (optional) - Enable cached availability checks

175

176

**Success Response (200)**:

177

```json

178

{

179

"formatters": {

180

"black": {

181

"enabled": true,

182

"label": "Apply Black Formatter"

183

},

184

"isort": {

185

"enabled": true,

186

"label": "Apply Isort Formatter"

187

},

188

"yapf": {

189

"enabled": false,

190

"label": "Apply YAPF Formatter"

191

}

192

}

193

}

194

```

195

196

**Example Requests**:

197

```bash

198

# Get current formatter availability

199

curl -H "Authorization: token YOUR_TOKEN" \

200

http://localhost:8888/jupyterlab_code_formatter/formatters

201

202

# Get cached formatter availability

203

curl -H "Authorization: token YOUR_TOKEN" \

204

http://localhost:8888/jupyterlab_code_formatter/formatters?cached

205

```

206

207

### Format Code

208

209

**URL**: `/jupyterlab_code_formatter/format`

210

**Method**: `POST`

211

**Authentication**: Required (Jupyter token)

212

**Content-Type**: `application/json`

213

214

**Query Parameters**:

215

- `cached` (optional) - Use cached formatter availability

216

217

**Request Body**:

218

```json

219

{

220

"code": [

221

"def hello():\n pass",

222

"import os\nimport sys"

223

],

224

"formatter": "black",

225

"notebook": true,

226

"options": {

227

"line_length": 88,

228

"string_normalization": true

229

}

230

}

231

```

232

233

**Success Response (200)**:

234

```json

235

{

236

"code": [

237

{

238

"code": "def hello():\n pass\n"

239

},

240

{

241

"code": "import os\nimport sys\n"

242

}

243

]

244

}

245

```

246

247

**Error Response (200 with errors)**:

248

```json

249

{

250

"code": [

251

{

252

"error": "cannot use --safe with this Python version"

253

}

254

]

255

}

256

```

257

258

**Formatter Not Found (404)**:

259

```

260

Formatter unknown_formatter not found!

261

```

262

263

**Example Request**:

264

```bash

265

curl -X POST \

266

-H "Authorization: token YOUR_TOKEN" \

267

-H "Content-Type: application/json" \

268

-d '{

269

"code": ["def hello():\n pass"],

270

"formatter": "black",

271

"notebook": true,

272

"options": {"line_length": 88}

273

}' \

274

http://localhost:8888/jupyterlab_code_formatter/format

275

```

276

277

## Authentication and Security

278

279

### Jupyter Authentication

280

281

Both handlers use Tornado's `@tornado.web.authenticated` decorator:

282

283

- **Token-based**: Uses Jupyter server's authentication system

284

- **Automatic**: Authentication handled by base APIHandler class

285

- **Secure**: Prevents unauthorized access to formatting capabilities

286

287

### Input Validation

288

289

Request processing includes validation:

290

291

- **JSON Parsing**: Validates request body is valid JSON

292

- **Required Fields**: Checks for required fields in request data

293

- **Formatter Existence**: Validates requested formatter exists

294

- **Sanitization**: Processes user input safely

295

296

### Error Handling

297

298

Comprehensive error handling:

299

300

- **HTTP Status Codes**: Appropriate status codes for different error types

301

- **Error Messages**: Clear error descriptions in responses

302

- **Exception Handling**: Graceful handling of formatter exceptions

303

- **Logging**: Error logging for debugging and monitoring

304

305

## Handler Configuration

306

307

### URL Pattern Registration

308

309

```python

310

def setup_handlers(web_app):

311

host_pattern = ".*$"

312

base_url = web_app.settings["base_url"]

313

314

# Register formatters endpoint

315

web_app.add_handlers(

316

host_pattern,

317

[

318

(

319

url_path_join(base_url, "jupyterlab_code_formatter/formatters"),

320

FormattersAPIHandler,

321

)

322

],

323

)

324

325

# Register format endpoint

326

web_app.add_handlers(

327

host_pattern,

328

[

329

(

330

url_path_join(base_url, "/jupyterlab_code_formatter/format"),

331

FormatAPIHandler,

332

)

333

],

334

)

335

```

336

337

### Base URL Handling

338

339

Handlers respect JupyterLab's base URL configuration:

340

341

- **Dynamic Base URL**: Uses `web_app.settings["base_url"]`

342

- **URL Construction**: Uses `url_path_join()` for proper URL construction

343

- **Flexible Deployment**: Works with different JupyterLab deployment configurations

344

345

## Performance Considerations

346

347

### Caching Support

348

349

Both handlers support caching for improved performance:

350

351

- **Query Parameter**: `?cached` enables cached checks

352

- **Formatter Availability**: Caches `importable` property checks

353

- **Persistent Cache**: Cache persists for server session duration

354

- **Selective Caching**: Can enable caching per request

355

356

### Asynchronous Processing

357

358

Handlers are designed for async operation:

359

360

- **Non-blocking**: Formatters run without blocking server

361

- **Concurrent Requests**: Multiple formatting requests can be processed

362

- **Resource Management**: Proper cleanup of formatter processes

363

364

### Error Recovery

365

366

Robust error handling ensures server stability:

367

368

- **Isolated Errors**: Formatter errors don't crash server

369

- **Partial Success**: Individual code blocks can fail independently

370

- **Graceful Degradation**: Server continues operating with failed formatters