0
# Agents
1
2
Analysis agents for extracting API information from Python code. Griffe provides two complementary approaches: static analysis through AST parsing (`visit`) and dynamic analysis through runtime inspection (`inspect`). These agents form the foundation of Griffe's code analysis capabilities.
3
4
## Capabilities
5
6
### Static Analysis (Visitor)
7
8
Parse and analyze Python source code using Abstract Syntax Tree (AST) parsing.
9
10
```python { .api }
11
def visit(
12
module: Module,
13
filepath: str | Path,
14
code: str | None = None,
15
extensions: Extensions | None = None,
16
parent: Module | None = None,
17
in_try_block: bool = False,
18
) -> Module:
19
"""
20
Parse and visit a module file using static analysis.
21
22
Creates Griffe objects from AST nodes by parsing Python source code.
23
This approach analyzes code structure without executing it, making it
24
safe for analyzing any Python code.
25
26
Args:
27
module: Module object to populate with visited data
28
filepath: Path to the Python file to analyze
29
code: Source code string (if None, reads from filepath)
30
extensions: Extensions to apply during visiting
31
parent: Parent module for submodule analysis
32
in_try_block: Whether this module is in a try block
33
34
Returns:
35
Module: The populated module with analyzed contents
36
37
Raises:
38
LoadingError: If file cannot be read or parsed
39
40
Examples:
41
Visit a module file:
42
>>> module = griffe.Module("mymodule")
43
>>> visited = griffe.visit(module, "path/to/mymodule.py")
44
45
Visit with custom code:
46
>>> code = "def hello(): return 'world'"
47
>>> visited = griffe.visit(module, "virtual.py", code=code)
48
"""
49
50
class Visitor:
51
"""
52
AST visitor class that traverses Python source code.
53
54
Creates Griffe objects from AST nodes by walking through
55
the abstract syntax tree and extracting relevant information.
56
"""
57
58
def __init__(
59
self,
60
module: Module,
61
filepath: str | Path,
62
code: str,
63
extensions: Extensions | None = None,
64
parent: Module | None = None,
65
in_try_block: bool = False,
66
) -> None:
67
"""
68
Initialize the visitor.
69
70
Args:
71
module: Module to populate
72
filepath: Path to source file
73
code: Source code to parse
74
extensions: Extensions to apply
75
parent: Parent module
76
in_try_block: Whether in try block context
77
"""
78
79
def visit(self) -> None:
80
"""
81
Visit the module and populate it with objects.
82
83
Parses the source code and creates appropriate Griffe objects
84
for all discovered Python constructs.
85
"""
86
87
def visit_module(self, node: ast.Module) -> None:
88
"""Visit a module AST node."""
89
90
def visit_classdef(self, node: ast.ClassDef) -> None:
91
"""Visit a class definition AST node."""
92
93
def visit_functiondef(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
94
"""Visit a function definition AST node."""
95
96
def visit_assign(self, node: ast.Assign) -> None:
97
"""Visit an assignment AST node."""
98
99
def visit_annassign(self, node: ast.AnnAssign) -> None:
100
"""Visit an annotated assignment AST node."""
101
```
102
103
### Dynamic Analysis (Inspector)
104
105
Inspect live Python objects at runtime to extract API information.
106
107
```python { .api }
108
def inspect(
109
module_name: str,
110
filepath: str | Path | None = None,
111
parent: Module | None = None,
112
) -> Module:
113
"""
114
Inspect a module dynamically at runtime.
115
116
Creates Griffe objects from live Python objects by importing
117
and examining modules at runtime. This approach can access
118
runtime-generated attributes and dynamic behavior.
119
120
Args:
121
module_name: Name of module to inspect
122
filepath: Path to module file (optional)
123
parent: Parent module for submodules
124
125
Returns:
126
Module: Module populated with inspected data
127
128
Raises:
129
UnimportableModuleError: If module cannot be imported
130
131
Examples:
132
Inspect an installed package:
133
>>> module = griffe.inspect("requests")
134
135
Inspect with specific filepath:
136
>>> module = griffe.inspect("mymodule", filepath="path/to/mymodule.py")
137
"""
138
139
class Inspector:
140
"""
141
Runtime inspector class that examines live Python objects.
142
143
Creates Griffe representations by importing modules and
144
using Python's introspection capabilities to extract information.
145
"""
146
147
def __init__(
148
self,
149
module_name: str,
150
filepath: str | Path | None = None,
151
parent: Module | None = None,
152
) -> None:
153
"""
154
Initialize the inspector.
155
156
Args:
157
module_name: Name of module to inspect
158
filepath: Path to module file
159
parent: Parent module
160
"""
161
162
def inspect(self) -> Module:
163
"""
164
Inspect the module and return populated Module object.
165
166
Returns:
167
Module: Module with inspected contents
168
"""
169
170
def inspect_module(self, module: types.ModuleType) -> Module:
171
"""Inspect a module object."""
172
173
def inspect_class(self, class_obj: type, parent: Module | Class) -> Class:
174
"""Inspect a class object."""
175
176
def inspect_function(
177
self,
178
func_obj: types.FunctionType | types.MethodType,
179
parent: Module | Class
180
) -> Function:
181
"""Inspect a function or method object."""
182
183
def inspect_attribute(
184
self,
185
name: str,
186
value: Any,
187
parent: Module | Class
188
) -> Attribute:
189
"""Inspect an attribute."""
190
```
191
192
## Decorator Recognition
193
194
Built-in decorator sets that Griffe recognizes and handles specially.
195
196
```python { .api }
197
builtin_decorators: set[str]
198
"""Set of built-in Python decorators recognized by Griffe."""
199
200
stdlib_decorators: set[str]
201
"""Set of standard library decorators recognized by Griffe."""
202
203
typing_overload: str
204
"""Reference to typing.overload decorator for function overloads."""
205
```
206
207
## AST Utilities
208
209
Helper functions for working with Abstract Syntax Tree nodes during static analysis.
210
211
```python { .api }
212
def ast_children(node: ast.AST) -> Iterator[ast.AST]:
213
"""
214
Get child nodes of an AST node.
215
216
Args:
217
node: AST node to examine
218
219
Yields:
220
ast.AST: Child nodes
221
"""
222
223
def ast_first_child(node: ast.AST) -> ast.AST | None:
224
"""
225
Get the first child node of an AST node.
226
227
Args:
228
node: AST node to examine
229
230
Returns:
231
ast.AST | None: First child or None if no children
232
"""
233
234
def ast_last_child(node: ast.AST) -> ast.AST | None:
235
"""
236
Get the last child node of an AST node.
237
238
Args:
239
node: AST node to examine
240
241
Returns:
242
ast.AST | None: Last child or None if no children
243
"""
244
245
def ast_next(node: ast.AST) -> ast.AST | None:
246
"""
247
Get the next sibling of an AST node.
248
249
Args:
250
node: AST node to examine
251
252
Returns:
253
ast.AST | None: Next sibling or None
254
"""
255
256
def ast_previous(node: ast.AST) -> ast.AST | None:
257
"""
258
Get the previous sibling of an AST node.
259
260
Args:
261
node: AST node to examine
262
263
Returns:
264
ast.AST | None: Previous sibling or None
265
"""
266
267
def ast_siblings(node: ast.AST) -> Iterator[ast.AST]:
268
"""
269
Get all siblings of an AST node.
270
271
Args:
272
node: AST node to examine
273
274
Yields:
275
ast.AST: Sibling nodes
276
"""
277
```
278
279
## Node Processing Functions
280
281
Functions for extracting specific information from AST nodes.
282
283
```python { .api }
284
def get_docstring(node: ast.AST) -> str | None:
285
"""
286
Extract docstring from an AST node.
287
288
Args:
289
node: AST node (function, class, or module)
290
291
Returns:
292
str | None: Docstring text or None if not found
293
"""
294
295
def get_parameters(node: ast.FunctionDef | ast.AsyncFunctionDef) -> Parameters:
296
"""
297
Extract function parameters from an AST node.
298
299
Args:
300
node: Function definition AST node
301
302
Returns:
303
Parameters: Extracted parameters
304
"""
305
306
def get_name(node: ast.AST) -> str | None:
307
"""
308
Get name from an assignment AST node.
309
310
Args:
311
node: Assignment AST node
312
313
Returns:
314
str | None: Variable name or None
315
"""
316
317
def get_names(node: ast.AST) -> list[str]:
318
"""
319
Get multiple names from an assignment AST node.
320
321
Args:
322
node: Assignment AST node
323
324
Returns:
325
list[str]: List of variable names
326
"""
327
328
def get_value(node: ast.AST) -> Expr | None:
329
"""
330
Extract value from an AST node as a Griffe expression.
331
332
Args:
333
node: AST node with value
334
335
Returns:
336
Expr | None: Griffe expression or None
337
"""
338
339
def safe_get_value(node: ast.AST) -> Expr | None:
340
"""
341
Safely extract value from AST node, returns None on error.
342
343
Args:
344
node: AST node with value
345
346
Returns:
347
Expr | None: Griffe expression or None if extraction fails
348
"""
349
```
350
351
## Usage Examples
352
353
### Static Analysis with Visit
354
355
```python
356
import griffe
357
358
# Create a module and visit source code
359
module = griffe.Module("mymodule")
360
361
# Visit from file
362
visited_module = griffe.visit(
363
module=module,
364
filepath="path/to/mymodule.py"
365
)
366
367
# Visit from code string
368
code = '''
369
def hello(name: str) -> str:
370
"""Say hello to someone."""
371
return f"Hello, {name}!"
372
373
class Greeter:
374
"""A class for greeting."""
375
376
def __init__(self, greeting: str = "Hello"):
377
self.greeting = greeting
378
379
def greet(self, name: str) -> str:
380
return f"{self.greeting}, {name}!"
381
'''
382
383
visited_from_code = griffe.visit(
384
module=griffe.Module("example"),
385
filepath="example.py",
386
code=code
387
)
388
389
print("Functions:", list(visited_from_code.functions.keys()))
390
print("Classes:", list(visited_from_code.classes.keys()))
391
```
392
393
### Dynamic Analysis with Inspect
394
395
```python
396
import griffe
397
398
# Inspect installed packages
399
requests_module = griffe.inspect("requests")
400
print("Requests classes:", list(requests_module.classes.keys()))
401
402
# Inspect with error handling
403
try:
404
custom_module = griffe.inspect("my_custom_module")
405
except griffe.UnimportableModuleError as e:
406
print(f"Cannot import module: {e}")
407
408
# Compare static vs dynamic analysis
409
static_module = griffe.visit(
410
griffe.Module("example"),
411
"example.py"
412
)
413
414
dynamic_module = griffe.inspect("example")
415
416
print("Static analysis found:", len(static_module.functions))
417
print("Dynamic analysis found:", len(dynamic_module.functions))
418
```
419
420
### Using Both Agents Together
421
422
```python
423
import griffe
424
425
# Load using both agents (this is what load() does internally)
426
loader = griffe.GriffeLoader(allow_inspection=True)
427
428
# This will try static analysis first, fall back to inspection
429
module = loader.load("some_package")
430
431
# Manual combination
432
try:
433
# Try static analysis first
434
static_result = griffe.visit(
435
griffe.Module("mypackage"),
436
"path/to/mypackage/__init__.py"
437
)
438
except Exception:
439
# Fall back to dynamic analysis
440
static_result = griffe.inspect("mypackage")
441
```
442
443
### Working with Extensions
444
445
```python
446
import griffe
447
448
# Load extensions for enhanced analysis
449
extensions = griffe.load_extensions(["dataclasses"])
450
451
# Use with visitor
452
module = griffe.Module("mymodule")
453
visited = griffe.visit(
454
module=module,
455
filepath="mymodule.py",
456
extensions=extensions
457
)
458
459
# Custom visitor with extensions
460
visitor = griffe.Visitor(
461
module=module,
462
filepath="mymodule.py",
463
code=source_code,
464
extensions=extensions
465
)
466
visitor.visit()
467
```
468
469
### AST Analysis Utilities
470
471
```python
472
import ast
473
import griffe
474
475
# Parse Python code
476
source = "def hello(): return 'world'"
477
tree = ast.parse(source)
478
479
# Use AST utilities
480
for node in griffe.ast_children(tree):
481
if isinstance(node, ast.FunctionDef):
482
print(f"Function: {node.name}")
483
484
# Get function details
485
docstring = griffe.get_docstring(node)
486
parameters = griffe.get_parameters(node)
487
488
print(f" Docstring: {docstring}")
489
print(f" Parameters: {[p.name for p in parameters]}")
490
```
491
492
## Types
493
494
```python { .api }
495
import ast
496
import types
497
from pathlib import Path
498
from typing import Iterator, Any
499
500
# AST node types from Python's ast module
501
AST = ast.AST
502
Module = ast.Module
503
ClassDef = ast.ClassDef
504
FunctionDef = ast.FunctionDef
505
AsyncFunctionDef = ast.AsyncFunctionDef
506
507
# Runtime types from Python's types module
508
ModuleType = types.ModuleType
509
FunctionType = types.FunctionType
510
MethodType = types.MethodType
511
512
# Griffe types
513
from griffe import Module, Class, Function, Attribute, Extensions, Parameters, Expr
514
515
# Parameter extraction type
516
ParametersType = Parameters
517
```