or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

handler-system.mdindex.mdinventory-system.mdlogging-system.mdmarkdown-processing.mdplugin-integration.mdrendering-system.md

handler-system.mddocs/

0

# Handler System

1

2

Extensible handler system for processing different programming languages. Handlers are responsible for collecting documentation data from source code and rendering it into HTML. The system provides base classes for creating custom handlers and a manager for handler lifecycle.

3

4

## Capabilities

5

6

### Base Handler

7

8

Abstract base class that defines the interface for language-specific documentation handlers.

9

10

```python { .api }

11

class BaseHandler:

12

"""Base class for all documentation handlers."""

13

14

# Class attributes

15

name: ClassVar[str] = ""

16

"""Handler identifier name."""

17

18

domain: ClassVar[str] = ""

19

"""Handler domain for inventory organization."""

20

21

enable_inventory: ClassVar[bool] = False

22

"""Whether handler supports inventory generation."""

23

24

fallback_theme: ClassVar[str] = ""

25

"""Default theme when specified theme unavailable."""

26

27

extra_css: str = ""

28

"""Additional CSS content for handler."""

29

30

def __init__(self, *args: Any, **kwargs: Any) -> None:

31

"""Initialize handler."""

32

33

# Abstract methods (must be implemented by subclasses)

34

def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:

35

"""

36

Collect documentation data for the given identifier.

37

38

Args:

39

identifier: The object identifier to collect docs for

40

options: Handler-specific options

41

42

Returns:

43

Collected documentation data

44

45

Raises:

46

CollectionError: If collection fails

47

"""

48

49

def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:

50

"""

51

Render collected data into HTML.

52

53

Args:

54

data: Data returned by collect method

55

options: Handler-specific options

56

locale: Locale for rendering

57

58

Returns:

59

Rendered HTML string

60

"""

61

62

# Concrete methods

63

def get_inventory_urls(self) -> list[tuple[str, dict[str, Any]]]:

64

"""Get inventory URLs for this handler."""

65

66

@classmethod

67

def load_inventory(cls, in_file: BinaryIO, url: str, base_url: str | None = None, **kwargs: Any) -> Iterator[tuple[str, str]]:

68

"""Load external inventory file."""

69

70

def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions:

71

"""Merge global and local handler options."""

72

73

def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str:

74

"""Render cross-reference backlinks."""

75

76

def teardown(self) -> None:

77

"""Cleanup after processing."""

78

79

def get_templates_dir(self, handler: str | None = None) -> Path:

80

"""Get templates directory for this handler."""

81

82

def get_extended_templates_dirs(self, handler: str) -> list[Path]:

83

"""Get all template directories in search order."""

84

85

def get_aliases(self, identifier: str) -> tuple[str, ...]:

86

"""Get alternative identifiers for the given identifier."""

87

88

def do_convert_markdown(self, text: str, heading_level: int, html_id: str = "", *, strip_paragraph: bool = False, autoref_hook: AutorefsHookInterface | None = None) -> Markup:

89

"""Convert Markdown text to HTML."""

90

91

def do_heading(self, content: Markup, heading_level: int, *, role: str | None = None, hidden: bool = False, toc_label: str | None = None, skip_inventory: bool = False, **attributes: str) -> Markup:

92

"""Create a heading element."""

93

94

def get_headings(self) -> Sequence[Element]:

95

"""Get heading elements from last render."""

96

97

def update_env(self, *args: Any, **kwargs: Any) -> None:

98

"""Update template environment."""

99

100

@property

101

def md(self) -> Markdown:

102

"""The Markdown processor instance."""

103

104

@property

105

def outer_layer(self) -> bool:

106

"""Whether in outer Markdown conversion layer."""

107

```

108

109

**Custom Handler Example:**

110

111

```python

112

from mkdocstrings import BaseHandler, CollectionError

113

114

class MyLanguageHandler(BaseHandler):

115

name = "mylang"

116

domain = "mylang"

117

enable_inventory = True

118

119

def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:

120

try:

121

# Parse source code and extract documentation

122

# This would be language-specific logic

123

data = self._parse_source(identifier)

124

return data

125

except Exception as e:

126

raise CollectionError(f"Failed to collect {identifier}: {e}")

127

128

def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:

129

# Render data using templates

130

template = self.env.get_template("mylang.html")

131

return template.render(data=data, options=options)

132

133

def _parse_source(self, identifier: str):

134

# Language-specific parsing logic

135

pass

136

```

137

138

### Handler Manager

139

140

Container and manager for handler instances, providing handler lifecycle management and configuration.

141

142

```python { .api }

143

class Handlers:

144

"""Container and manager for handler instances."""

145

146

def __init__(

147

self,

148

*,

149

theme: str,

150

default: str,

151

inventory_project: str,

152

inventory_version: str = "0.0.0",

153

handlers_config: dict[str, HandlerConfig] | None = None,

154

custom_templates: str | None = None,

155

mdx: Sequence[str | Extension] | None = None,

156

mdx_config: Mapping[str, Any] | None = None,

157

locale: str = "en",

158

tool_config: Any

159

) -> None:

160

"""

161

Initialize handlers manager.

162

163

Args:

164

theme: Theme name for template selection

165

default: Default handler name

166

inventory_project: Project name for inventory

167

inventory_version: Project version for inventory

168

handlers_config: Configuration for each handler

169

custom_templates: Custom templates directory

170

mdx: Markdown extensions to load

171

mdx_config: Configuration for Markdown extensions

172

locale: Default locale

173

tool_config: Tool-specific configuration

174

"""

175

176

def get_handler_name(self, config: dict) -> str:

177

"""Determine handler name from configuration."""

178

179

def get_handler_config(self, name: str) -> dict:

180

"""Get configuration for specific handler."""

181

182

def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHandler:

183

"""

184

Get or create handler instance.

185

186

Args:

187

name: Handler name

188

handler_config: Override configuration

189

190

Returns:

191

Handler instance

192

"""

193

194

def get_anchors(self, identifier: str) -> tuple[str, ...]:

195

"""Get anchors for identifier (deprecated)."""

196

197

def teardown(self) -> None:

198

"""Cleanup all handlers."""

199

200

@property

201

def inventory(self) -> Inventory:

202

"""The objects inventory for cross-referencing."""

203

204

@property

205

def seen_handlers(self) -> Iterable[BaseHandler]:

206

"""Handlers used during current build."""

207

```

208

209

**Usage Example:**

210

211

```python

212

from mkdocstrings import Handlers

213

214

# Create handlers manager

215

handlers = Handlers(

216

theme="material",

217

default="python",

218

inventory_project="my-project",

219

inventory_version="1.0.0",

220

handlers_config={

221

"python": {

222

"paths": ["src"],

223

"options": {

224

"docstring_style": "google",

225

"show_source": False

226

}

227

}

228

},

229

custom_templates="templates/",

230

locale="en"

231

)

232

233

# Get a handler

234

python_handler = handlers.get_handler("python")

235

236

# Use handler to collect and render documentation

237

data = python_handler.collect("my_module.MyClass", options)

238

html = python_handler.render(data, options)

239

240

# Cleanup when done

241

handlers.teardown()

242

```

243

244

## Handler Development

245

246

### Creating Custom Handlers

247

248

To create a custom handler:

249

250

1. **Inherit from BaseHandler**:

251

```python

252

class MyHandler(BaseHandler):

253

name = "mylang"

254

domain = "mylang"

255

enable_inventory = True

256

```

257

258

2. **Implement Abstract Methods**:

259

```python

260

def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:

261

# Parse source code and extract documentation

262

pass

263

264

def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:

265

# Render documentation using templates

266

pass

267

```

268

269

3. **Register Handler**:

270

Handler registration happens automatically when the handler module is imported and the handler class is available in the module's namespace.

271

272

### Handler Options

273

274

Handlers receive options through the `HandlerOptions` type:

275

276

```python

277

# Options are passed from plugin configuration

278

options = {

279

"show_source": False,

280

"docstring_style": "google",

281

"show_signature_annotations": True,

282

"heading_level": 2

283

}

284

285

# Handler processes options in collect/render methods

286

def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:

287

show_source = options.get("show_source", True)

288

# Use options to customize behavior

289

```

290

291

### Template Integration

292

293

Handlers use Jinja2 templates for rendering:

294

295

```python

296

def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:

297

template = self.env.get_template("mylang/class.html")

298

return template.render(

299

data=data,

300

options=options,

301

locale=locale,

302

logger=self.get_template_logger()

303

)

304

```

305

306

Template directories are resolved in this order:

307

1. Custom templates directory (if specified)

308

2. Handler-specific template directory

309

3. Default template directory

310

4. Fallback theme templates

311

312

### Error Handling

313

314

Handlers should raise appropriate exceptions:

315

316

```python

317

from mkdocstrings import CollectionError

318

319

def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:

320

try:

321

return self._do_collection(identifier)

322

except ImportError as e:

323

raise CollectionError(f"Could not import {identifier}: {e}")

324

except Exception as e:

325

raise CollectionError(f"Collection failed for {identifier}: {e}")

326

```

327

328

### Inventory Integration

329

330

Handlers that support inventory should:

331

332

1. Set `enable_inventory = True`

333

2. Set appropriate `domain` value

334

3. Register items during rendering:

335

336

```python

337

def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:

338

# Register in inventory

339

self.handlers.inventory.register(

340

name=data.name,

341

domain=self.domain,

342

role="class",

343

uri=f"#{data.anchor}",

344

dispname=data.display_name

345

)

346

347

# Render normally

348

return template.render(data=data)

349

```