or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-interface.mdconfiguration.mdindex.mdprogrammatic-api.mdtemplates.md

templates.mddocs/

0

# Template System

1

2

Extensible Jinja2-based template system for customizing generated code structure, formatting, and content. The template system provides complete control over how OpenAPI specifications are transformed into Python client code.

3

4

## Capabilities

5

6

### Template Environment

7

8

The core template system built on Jinja2 with custom filters and globals.

9

10

```python { .api }

11

class Project:

12

env: Environment # Jinja2 environment with custom configuration

13

14

def __init__(

15

self,

16

*,

17

openapi: GeneratorData,

18

config: Config,

19

custom_template_path: Optional[Path] = None,

20

) -> None:

21

"""

22

Initialize project with template environment.

23

24

The environment includes:

25

- Custom template filters (snakecase, kebabcase, pascalcase, any)

26

- Global variables (config, utils, openapi data, etc.)

27

- Template loader with optional custom template override

28

29

Parameters:

30

- openapi: Parsed OpenAPI specification data

31

- config: Generation configuration

32

- custom_template_path: Optional directory containing custom templates

33

"""

34

```

35

36

### Template Filters

37

38

Built-in filters for string transformation and formatting.

39

40

```python { .api }

41

# Internal constant used by the template system

42

TEMPLATE_FILTERS = {

43

"snakecase": utils.snake_case, # Convert to snake_case

44

"kebabcase": utils.kebab_case, # Convert to kebab-case

45

"pascalcase": utils.pascal_case, # Convert to PascalCase

46

"any": any, # Python any() function

47

}

48

```

49

50

### Template Globals

51

52

Global variables and functions available in all templates.

53

54

```python { .api }

55

# Template globals include:

56

config: Config # Generation configuration

57

utils: module # Utility functions module

58

python_identifier: Callable # Create valid Python identifiers

59

class_name: Callable # Create valid class names

60

package_name: str # Generated package name

61

package_dir: Path # Package directory path

62

package_description: str # Package description

63

package_version: str # Package version

64

project_name: str # Project name

65

project_dir: Path # Project directory path

66

openapi: GeneratorData # Parsed OpenAPI data

67

endpoint_collections_by_tag: dict # Endpoints grouped by tag

68

```

69

70

### Code Generation Pipeline

71

72

The template system generates code through a structured pipeline.

73

74

```python { .api }

75

class Project:

76

def build(self) -> Sequence[GeneratorError]:

77

"""

78

Create the project from templates using this pipeline:

79

80

1. _create_package() - Generate package structure and __init__.py

81

2. _build_metadata() - Generate project metadata files

82

3. _build_models() - Generate data model classes

83

4. _build_api() - Generate API endpoint functions and client classes

84

5. _run_post_hooks() - Execute post-generation commands

85

86

Returns:

87

List of errors encountered during generation

88

"""

89

```

90

91

## Template Structure

92

93

### Default Templates

94

95

The built-in templates generate a complete Python client structure:

96

97

```

98

templates/

99

├── package_init.py.jinja # Package __init__.py

100

├── types.py.jinja # Type definitions

101

├── client.py.jinja # HTTP client classes

102

├── errors.py.jinja # Error definitions

103

├── model.py.jinja # Individual model classes

104

├── str_enum.py.jinja # String enum classes

105

├── int_enum.py.jinja # Integer enum classes

106

├── literal_enum.py.jinja # Literal enum types

107

├── models_init.py.jinja # Models package __init__.py

108

├── api_init.py.jinja # API package __init__.py

109

├── endpoint_module.py.jinja # API endpoint functions

110

├── endpoint_init.py.jinja # Tag-specific API __init__.py

111

├── pyproject.toml.jinja # Project metadata

112

├── setup.py.jinja # Setup.py metadata

113

├── README.md.jinja # Generated documentation

114

└── .gitignore.jinja # Git ignore rules

115

```

116

117

### Custom Templates

118

119

Override any default template by creating a custom template directory:

120

121

```

122

custom-templates/

123

├── client.py.jinja # Custom client implementation

124

├── model.py.jinja # Custom model generation

125

└── README.md.jinja # Custom documentation

126

```

127

128

## Template Variables

129

130

### OpenAPI Data Access

131

132

Templates have access to the complete parsed OpenAPI specification:

133

134

```jinja2

135

{# Access OpenAPI metadata #}

136

{{ openapi.title }}

137

{{ openapi.version }}

138

{{ openapi.description }}

139

140

{# Access models and enums #}

141

{% for model in openapi.models %}

142

{{ model.class_info.name }}

143

{{ model.class_info.module_name }}

144

{% endfor %}

145

146

{% for enum in openapi.enums %}

147

{{ enum.class_info.name }}

148

{{ enum.value_type }}

149

{% endfor %}

150

151

{# Access API endpoints #}

152

{% for tag, collection in endpoint_collections_by_tag.items() %}

153

{% for endpoint in collection.endpoints %}

154

{{ endpoint.name }}

155

{{ endpoint.method }}

156

{{ endpoint.path }}

157

{% endfor %}

158

{% endfor %}

159

```

160

161

### Configuration Access

162

163

All configuration options are available in templates:

164

165

```jinja2

166

{# Project configuration #}

167

{{ config.project_name_override }}

168

{{ config.package_name_override }}

169

{{ config.field_prefix }}

170

171

{# Generation options #}

172

{% if config.docstrings_on_attributes %}

173

# Generate attribute docstrings

174

{% endif %}

175

176

{% if config.literal_enums %}

177

# Use literal types

178

{% endif %}

179

```

180

181

### Utility Functions

182

183

Access utility functions for string manipulation:

184

185

```jinja2

186

{# String transformations #}

187

{{ "MyClassName" | snakecase }} {# -> my_class_name #}

188

{{ "my_function" | pascalcase }} {# -> MyFunction #}

189

{{ "some-value" | kebabcase }} {# -> some-value #}

190

191

{# Python identifier creation #}

192

{{ python_identifier("field name", config.field_prefix) }}

193

{{ class_name("model-name", config.field_prefix) }}

194

```

195

196

## Custom Template Usage

197

198

### Creating Custom Templates

199

200

1. Create a directory for your custom templates

201

2. Copy default templates you want to modify

202

3. Customize the templates using Jinja2 syntax

203

4. Use the custom template path in generation

204

205

```bash

206

# Copy default templates

207

cp -r /path/to/openapi_python_client/templates ./my-templates

208

209

# Modify templates as needed

210

vim ./my-templates/client.py.jinja

211

212

# Use custom templates

213

openapi-python-client generate \

214

--url https://api.example.com/openapi.json \

215

--custom-template-path ./my-templates

216

```

217

218

### Example Custom Client Template

219

220

```jinja2

221

{# custom-templates/client.py.jinja #}

222

"""Custom HTTP client implementation."""

223

224

from typing import Dict, Optional, Union

225

import httpx

226

227

class CustomClient:

228

"""Custom client with enhanced features."""

229

230

def __init__(

231

self,

232

base_url: str,

233

*,

234

cookies: Optional[Dict[str, str]] = None,

235

headers: Optional[Dict[str, str]] = None,

236

timeout: float = 30.0,

237

verify_ssl: bool = True,

238

follow_redirects: bool = False,

239

custom_auth: Optional[str] = None, # Custom authentication

240

) -> None:

241

self._base_url = base_url

242

self._cookies = cookies

243

self._headers = headers or {}

244

245

# Add custom authentication header

246

if custom_auth:

247

self._headers["X-Custom-Auth"] = custom_auth

248

249

self._client = httpx.Client(

250

base_url=base_url,

251

cookies=cookies,

252

headers=self._headers,

253

timeout=timeout,

254

verify=verify_ssl,

255

follow_redirects=follow_redirects,

256

)

257

258

def get_headers(self) -> Dict[str, str]:

259

"""Get current headers."""

260

return self._client.headers.copy()

261

262

def close(self) -> None:

263

"""Close the underlying client."""

264

self._client.close()

265

266

def __enter__(self) -> "CustomClient":

267

return self

268

269

def __exit__(self, *args) -> None:

270

self.close()

271

```

272

273

### Template Development Tips

274

275

1. **Use template inheritance** for common patterns:

276

```jinja2

277

{# base_model.jinja #}

278

{% macro model_docstring(model) %}

279

"""{{ model.description | default("Generated model class.") }}"""

280

{% endmacro %}

281

282

{# model.py.jinja #}

283

{% from "base_model.jinja" import model_docstring %}

284

285

class {{ model.class_info.name }}:

286

{{ model_docstring(model) }}

287

# ... rest of model

288

```

289

290

2. **Add custom filters** programmatically:

291

```python

292

from openapi_python_client import Project

293

294

def custom_filter(value):

295

return value.upper().replace(" ", "_")

296

297

project = Project(openapi=data, config=config)

298

project.env.filters["custom"] = custom_filter

299

```

300

301

3. **Test templates** with sample data:

302

```python

303

from jinja2 import Environment, FileSystemLoader

304

305

env = Environment(loader=FileSystemLoader("./my-templates"))

306

template = env.get_template("model.py.jinja")

307

result = template.render(model=sample_model, config=sample_config)

308

```

309

310

## Template Best Practices

311

312

### Code Quality

313

314

- **Format generated code**: Use post-hooks for consistent formatting

315

- **Type annotations**: Always include complete type annotations

316

- **Documentation**: Generate comprehensive docstrings

317

- **Error handling**: Include proper error handling patterns

318

319

### Maintainability

320

321

- **Modular templates**: Break complex templates into smaller, reusable components

322

- **Version compatibility**: Test templates with different OpenAPI versions

323

- **Configuration driven**: Use configuration options rather than hardcoding values

324

- **Default fallbacks**: Provide sensible defaults for missing data

325

326

### Performance

327

328

- **Minimize template complexity**: Keep template logic simple

329

- **Cache static content**: Avoid regenerating static template parts

330

- **Efficient imports**: Generate only necessary imports

331

- **Lazy loading**: Use lazy imports where appropriate