or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

agents.mdbreaking-changes.mdcli.mddocstrings.mdextensions.mdindex.mdloaders.mdmodels.mdserialization.md

serialization.mddocs/

0

# Serialization

1

2

JSON encoding and decoding capabilities for Griffe objects. This system enables serializing complete API structures to JSON format for storage, transmission, or external processing, and deserializing them back to full Griffe objects.

3

4

## Capabilities

5

6

### JSON Encoding

7

8

Custom JSON encoder for serializing Griffe objects to JSON format.

9

10

```python { .api }

11

import json

12

13

class JSONEncoder(json.JSONEncoder):

14

"""

15

JSON encoder for Griffe objects.

16

17

Enables serialization of Griffe data structures to JSON format.

18

Handles all Griffe object types including modules, classes, functions,

19

attributes, aliases, and their associated metadata.

20

"""

21

22

def default(self, obj: Any) -> Any:

23

"""

24

Convert Griffe objects to JSON-serializable formats.

25

26

Args:

27

obj: Object to serialize (Griffe object or standard type)

28

29

Returns:

30

Any: JSON-serializable representation

31

32

Raises:

33

TypeError: If object cannot be serialized

34

35

Examples:

36

Basic usage:

37

>>> encoder = griffe.JSONEncoder()

38

>>> json_str = json.dumps(module, cls=griffe.JSONEncoder)

39

40

With custom options:

41

>>> encoder = griffe.JSONEncoder(indent=2, sort_keys=True)

42

>>> json_str = encoder.encode(module)

43

"""

44

```

45

46

### JSON Decoding

47

48

Function for deserializing JSON data back to Griffe objects.

49

50

```python { .api }

51

def json_decoder(dct: dict[str, Any]) -> dict[str, Any] | Object:

52

"""

53

Decode Griffe objects from JSON.

54

55

JSON decoder that reconstructs Griffe objects from their serialized

56

representations. Works with JSON data produced by JSONEncoder.

57

58

Args:

59

dct: Dictionary from JSON decoder containing object data

60

61

Returns:

62

dict[str, Any] | Object: Decoded Griffe object or plain dictionary

63

64

Examples:

65

Decode from JSON string:

66

>>> json_data = '{"kind": "module", "name": "example", ...}'

67

>>> obj = json.loads(json_data, object_hook=griffe.json_decoder)

68

69

Decode from file:

70

>>> with open("api.json") as f:

71

... data = json.load(f, object_hook=griffe.json_decoder)

72

"""

73

```

74

75

### Object Serialization Methods

76

77

Built-in serialization methods available on all Griffe objects.

78

79

```python { .api }

80

# Available on all Object subclasses (Module, Class, Function, etc.)

81

82

def serialize(self, **kwargs: Any) -> dict[str, Any]:

83

"""

84

Serialize the object to a dictionary.

85

86

Converts the Griffe object to a dictionary representation

87

suitable for JSON encoding or other serialization formats.

88

89

Args:

90

**kwargs: Serialization options including:

91

- full: Include all details (default: False)

92

- docstring_parser: Parser for docstrings

93

- docstring_options: Options for docstring parsing

94

95

Returns:

96

dict[str, Any]: Dictionary representation of the object

97

98

Examples:

99

Basic serialization:

100

>>> data = module.serialize()

101

102

Full serialization with all details:

103

>>> data = module.serialize(full=True)

104

105

With docstring parsing:

106

>>> data = module.serialize(

107

... docstring_parser="google",

108

... docstring_options={"style": "google"}

109

... )

110

"""

111

112

def as_json(self, **kwargs: Any) -> str:

113

"""

114

Serialize the object to JSON string.

115

116

Convenience method that combines serialize() and JSON encoding

117

in a single call.

118

119

Args:

120

**kwargs: Arguments passed to serialize() and json.dumps()

121

122

Returns:

123

str: JSON string representation

124

125

Examples:

126

Basic JSON export:

127

>>> json_str = module.as_json()

128

129

Pretty-printed JSON:

130

>>> json_str = module.as_json(indent=2)

131

132

Full serialization to JSON:

133

>>> json_str = module.as_json(full=True, indent=2)

134

"""

135

136

@classmethod

137

def from_json(cls, json_str: str, **kwargs: Any) -> Object:

138

"""

139

Deserialize an object from JSON string.

140

141

Class method that reconstructs a Griffe object from its

142

JSON representation.

143

144

Args:

145

json_str: JSON string to deserialize

146

**kwargs: Deserialization options

147

148

Returns:

149

Object: Reconstructed Griffe object

150

151

Examples:

152

Deserialize module:

153

>>> json_data = module.as_json()

154

>>> restored = griffe.Module.from_json(json_data)

155

156

Deserialize any object type:

157

>>> restored = griffe.Object.from_json(json_data)

158

"""

159

```

160

161

## Usage Examples

162

163

### Basic Serialization

164

165

```python

166

import griffe

167

import json

168

169

# Load a package

170

package = griffe.load("requests")

171

172

# Serialize to dictionary

173

data = package.serialize()

174

print(f"Package data keys: {list(data.keys())}")

175

176

# Serialize to JSON string

177

json_str = package.as_json(indent=2)

178

print("JSON representation:")

179

print(json_str[:200] + "...")

180

181

# Use custom JSON encoder

182

encoder = griffe.JSONEncoder(indent=2, sort_keys=True)

183

custom_json = encoder.encode(package)

184

```

185

186

### Full API Serialization

187

188

```python

189

import griffe

190

191

# Load package with full details

192

package = griffe.load("mypackage")

193

194

# Serialize with complete information

195

full_data = package.serialize(

196

full=True,

197

docstring_parser="google",

198

docstring_options={"style": "google"}

199

)

200

201

# Export to JSON file

202

with open("api_documentation.json", "w") as f:

203

json.dump(full_data, f, cls=griffe.JSONEncoder, indent=2)

204

205

print(f"Exported API documentation to api_documentation.json")

206

```

207

208

### Deserialization

209

210

```python

211

import griffe

212

import json

213

214

# Load from JSON file

215

with open("api_documentation.json") as f:

216

restored_package = json.load(f, object_hook=griffe.json_decoder)

217

218

print(f"Restored package: {restored_package.name}")

219

print(f"Modules: {list(restored_package.modules.keys())}")

220

221

# Alternative: use from_json class method

222

json_str = open("api_documentation.json").read()

223

restored = griffe.Module.from_json(json_str)

224

```

225

226

### Roundtrip Serialization

227

228

```python

229

import griffe

230

231

# Original package

232

original = griffe.load("requests")

233

234

# Serialize and deserialize

235

json_data = original.as_json(full=True)

236

restored = griffe.Module.from_json(json_data)

237

238

# Compare

239

print(f"Original: {original.name}")

240

print(f"Restored: {restored.name}")

241

print(f"Same classes: {set(original.classes.keys()) == set(restored.classes.keys())}")

242

243

# Detailed comparison

244

def compare_objects(obj1, obj2, path=""):

245

"""Compare two Griffe objects recursively."""

246

if obj1.name != obj2.name:

247

print(f"Name mismatch at {path}: {obj1.name} != {obj2.name}")

248

249

if type(obj1) != type(obj2):

250

print(f"Type mismatch at {path}: {type(obj1)} != {type(obj2)}")

251

252

# Compare specific attributes based on object type

253

if hasattr(obj1, 'functions') and hasattr(obj2, 'functions'):

254

if set(obj1.functions.keys()) != set(obj2.functions.keys()):

255

print(f"Function mismatch at {path}")

256

257

compare_objects(original, restored)

258

```

259

260

### CLI Integration

261

262

```python

263

import griffe

264

import sys

265

266

def export_api_json(package_name: str, output_file: str, full: bool = False):

267

"""Export package API to JSON file."""

268

try:

269

# Load package

270

package = griffe.load(package_name)

271

272

# Serialize

273

if full:

274

data = package.serialize(full=True, docstring_parser="auto")

275

else:

276

data = package.serialize()

277

278

# Export

279

with open(output_file, "w") as f:

280

json.dump(data, f, cls=griffe.JSONEncoder, indent=2)

281

282

print(f"✅ Exported {package_name} API to {output_file}")

283

return 0

284

285

except Exception as e:

286

print(f"❌ Error exporting API: {e}")

287

return 1

288

289

# Command-line usage

290

if __name__ == "__main__":

291

if len(sys.argv) < 3:

292

print("Usage: python script.py <package_name> <output_file> [--full]")

293

sys.exit(1)

294

295

package_name = sys.argv[1]

296

output_file = sys.argv[2]

297

full = "--full" in sys.argv

298

299

exit_code = export_api_json(package_name, output_file, full)

300

sys.exit(exit_code)

301

```

302

303

### Custom Serialization

304

305

```python

306

import griffe

307

import json

308

from typing import Any

309

310

class CustomAPIExporter:

311

"""Custom API exporter with filtering and transformation."""

312

313

def __init__(

314

self,

315

include_private: bool = False,

316

include_docstrings: bool = True,

317

transform_names: bool = False

318

):

319

self.include_private = include_private

320

self.include_docstrings = include_docstrings

321

self.transform_names = transform_names

322

323

def export_module(self, module: griffe.Module) -> dict[str, Any]:

324

"""Export module with custom filtering."""

325

data = {

326

"name": module.name,

327

"type": "module",

328

"path": module.path,

329

}

330

331

if self.include_docstrings and module.docstring:

332

data["docstring"] = module.docstring.value

333

334

# Filter and export functions

335

functions = {}

336

for name, func in module.functions.items():

337

if not self.include_private and name.startswith("_"):

338

continue

339

functions[name] = self.export_function(func)

340

data["functions"] = functions

341

342

# Filter and export classes

343

classes = {}

344

for name, cls in module.classes.items():

345

if not self.include_private and name.startswith("_"):

346

continue

347

classes[name] = self.export_class(cls)

348

data["classes"] = classes

349

350

return data

351

352

def export_function(self, func: griffe.Function) -> dict[str, Any]:

353

"""Export function with custom format."""

354

data = {

355

"name": func.name,

356

"type": "function",

357

"signature": func.signature,

358

}

359

360

if self.include_docstrings and func.docstring:

361

data["docstring"] = func.docstring.value

362

363

# Transform parameter names if requested

364

params = []

365

for param in func.parameters:

366

param_data = {"name": param.name, "kind": param.kind.name}

367

if param.annotation:

368

param_data["type"] = str(param.annotation)

369

if param.default:

370

param_data["default"] = str(param.default)

371

params.append(param_data)

372

373

data["parameters"] = params

374

return data

375

376

def export_class(self, cls: griffe.Class) -> dict[str, Any]:

377

"""Export class with custom format."""

378

data = {

379

"name": cls.name,

380

"type": "class",

381

"bases": [str(base) for base in cls.bases],

382

}

383

384

if self.include_docstrings and cls.docstring:

385

data["docstring"] = cls.docstring.value

386

387

# Export methods

388

methods = {}

389

for name, method in cls.methods.items():

390

if not self.include_private and name.startswith("_"):

391

continue

392

methods[name] = self.export_function(method)

393

data["methods"] = methods

394

395

return data

396

397

# Use custom exporter

398

exporter = CustomAPIExporter(

399

include_private=False,

400

include_docstrings=True,

401

transform_names=True

402

)

403

404

package = griffe.load("requests")

405

custom_data = exporter.export_module(package)

406

407

with open("custom_api.json", "w") as f:

408

json.dump(custom_data, f, indent=2)

409

```

410

411

### Incremental Serialization

412

413

```python

414

import griffe

415

import json

416

import hashlib

417

from pathlib import Path

418

419

class IncrementalAPISerializer:

420

"""Serialize only changed parts of API."""

421

422

def __init__(self, cache_dir: str = ".griffe_cache"):

423

self.cache_dir = Path(cache_dir)

424

self.cache_dir.mkdir(exist_ok=True)

425

426

def get_object_hash(self, obj: griffe.Object) -> str:

427

"""Get hash of object for change detection."""

428

data = obj.serialize()

429

json_str = json.dumps(data, sort_keys=True, cls=griffe.JSONEncoder)

430

return hashlib.sha256(json_str.encode()).hexdigest()

431

432

def serialize_if_changed(self, obj: griffe.Object, force: bool = False) -> bool:

433

"""Serialize object only if it has changed."""

434

obj_hash = self.get_object_hash(obj)

435

cache_file = self.cache_dir / f"{obj.path}.json"

436

hash_file = self.cache_dir / f"{obj.path}.hash"

437

438

# Check if already cached and unchanged

439

if not force and hash_file.exists():

440

with open(hash_file) as f:

441

cached_hash = f.read().strip()

442

if cached_hash == obj_hash:

443

return False # No changes

444

445

# Serialize the object

446

data = obj.serialize(full=True)

447

with open(cache_file, "w") as f:

448

json.dump(data, f, cls=griffe.JSONEncoder, indent=2)

449

450

# Update hash

451

with open(hash_file, "w") as f:

452

f.write(obj_hash)

453

454

return True # Changes detected and serialized

455

456

# Use incremental serializer

457

serializer = IncrementalAPISerializer()

458

459

package = griffe.load("mypackage")

460

for module_name, module in package.modules.items():

461

if serializer.serialize_if_changed(module):

462

print(f"Serialized changed module: {module_name}")

463

else:

464

print(f"No changes in module: {module_name}")

465

```

466

467

## Types

468

469

```python { .api }

470

import json

471

from typing import Any, dict

472

473

# JSON encoder/decoder types

474

JSONEncoder = json.JSONEncoder

475

476

# Serialization function signatures

477

def serialize(**kwargs: Any) -> dict[str, Any]: ...

478

def as_json(**kwargs: Any) -> str: ...

479

def from_json(json_str: str, **kwargs: Any) -> Object: ...

480

def json_decoder(dct: dict[str, Any]) -> dict[str, Any] | Object: ...

481

482

# Core object type

483

from griffe import Object

484

```