0
# Python Syntax Elements
1
2
Python-specific node types that represent all language constructs including modules, functions, classes, statements, expressions, and literals. These specialized classes provide Python-aware methods for code analysis and manipulation.
3
4
## Capabilities
5
6
### Base Python Classes
7
8
Foundation classes that provide Python-specific functionality to all syntax tree elements.
9
10
```python { .api }
11
class PythonMixin:
12
"""
13
Mixin providing Python-specific functionality to nodes and leaves.
14
"""
15
16
class PythonLeaf(PythonMixin, Leaf):
17
"""
18
Base class for Python leaf nodes (tokens).
19
20
Inherits all functionality from Leaf with Python-specific enhancements.
21
"""
22
23
class PythonBaseNode(PythonMixin, BaseNode):
24
"""
25
Base class for Python nodes with children.
26
27
Inherits all functionality from BaseNode with Python-specific enhancements.
28
"""
29
30
class PythonNode(PythonMixin, Node):
31
"""
32
Standard Python node implementation.
33
34
Used for most Python syntax constructs.
35
"""
36
37
class PythonErrorNode(PythonMixin, ErrorNode):
38
"""
39
Error node for invalid Python syntax.
40
41
Contains partial or invalid syntax with error recovery.
42
"""
43
44
class PythonErrorLeaf(ErrorLeaf, PythonLeaf):
45
"""
46
Error leaf for invalid Python tokens.
47
48
Contains partial or invalid tokens with error recovery.
49
"""
50
```
51
52
### Module and Scope Classes
53
54
Top-level containers and scoped constructs that organize Python code.
55
56
```python { .api }
57
class Module(Scope):
58
"""
59
The top-level module node representing a Python file.
60
61
Attributes:
62
type (str): Always 'file_input'
63
"""
64
65
def get_used_names(self):
66
"""
67
Get all name usages in this module.
68
69
Returns:
70
UsedNamesMapping: Immutable mapping from name strings to lists of Name nodes
71
"""
72
73
def iter_imports(self):
74
"""
75
Get all import statements in this module.
76
77
Yields:
78
Import: ImportName and ImportFrom nodes
79
"""
80
81
def iter_funcdefs(self):
82
"""
83
Get all function definitions in this module.
84
85
Yields:
86
Function: Function definition nodes
87
"""
88
89
def iter_classdefs(self):
90
"""
91
Get all class definitions in this module.
92
93
Yields:
94
Class: Class definition nodes
95
"""
96
```
97
98
```python { .api }
99
class Scope(PythonBaseNode):
100
"""
101
Base class for scoped constructs (functions, classes, modules).
102
"""
103
104
def iter_funcdefs(self):
105
"""Get function definitions within this scope."""
106
107
def iter_classdefs(self):
108
"""Get class definitions within this scope."""
109
110
def iter_imports(self):
111
"""Get import statements within this scope."""
112
113
def get_suite(self):
114
"""
115
Get the executable body of this scope.
116
117
Returns:
118
BaseNode: Suite node containing the body statements
119
"""
120
```
121
122
#### Usage Examples
123
124
```python
125
import parso
126
127
code = '''
128
import os
129
import sys
130
from pathlib import Path
131
132
def function1():
133
pass
134
135
class MyClass:
136
def method(self):
137
pass
138
139
def function2():
140
return 42
141
'''
142
143
module = parso.parse(code)
144
145
# Get all imports
146
imports = list(module.iter_imports())
147
print(f"Found {len(imports)} import statements")
148
for imp in imports:
149
if imp.type == 'import_name':
150
names = [path[-1].value for path in imp.get_paths()]
151
print(f"Import: {', '.join(names)}")
152
elif imp.type == 'import_from':
153
from_names = [n.value for n in imp.get_from_names()]
154
defined = [n.value for n in imp.get_defined_names()]
155
print(f"From {'.'.join(from_names)} import {', '.join(defined)}")
156
157
# Get all functions
158
functions = list(module.iter_funcdefs())
159
print(f"Found {len(functions)} functions:")
160
for func in functions:
161
print(f" {func.name.value}() at line {func.start_pos[0]}")
162
163
# Get all classes
164
classes = list(module.iter_classdefs())
165
print(f"Found {len(classes)} classes:")
166
for cls in classes:
167
print(f" {cls.name.value} at line {cls.start_pos[0]}")
168
169
# Analyze name usage
170
used_names = module.get_used_names()
171
print(f"Name 'os' used {len(used_names.get('os', []))} times")
172
```
173
174
### Function and Class Definitions
175
176
Definitions for functions, methods, and classes with specialized analysis methods.
177
178
```python { .api }
179
class Function(ClassOrFunc):
180
"""
181
Function definition node.
182
183
Attributes:
184
type (str): 'funcdef'
185
name (Name): Function name node
186
"""
187
188
def get_params(self):
189
"""
190
Get function parameters.
191
192
Returns:
193
list[Param]: List of parameter objects
194
"""
195
196
def iter_yield_exprs(self):
197
"""
198
Find yield expressions in function body.
199
200
Yields:
201
YieldExpr: Yield expression nodes
202
"""
203
204
def iter_return_stmts(self):
205
"""
206
Find return statements in function body.
207
208
Yields:
209
ReturnStmt: Return statement nodes
210
"""
211
212
def iter_raise_stmts(self):
213
"""
214
Find raise statements in function body.
215
216
Yields:
217
KeywordStatement: Raise statement nodes
218
"""
219
220
def is_generator(self):
221
"""
222
Check if function is a generator.
223
224
Returns:
225
bool: True if function contains yield expressions
226
"""
227
228
@property
229
def annotation(self):
230
"""
231
Get return type annotation.
232
233
Returns:
234
NodeOrLeaf | None: Return annotation or None
235
"""
236
```
237
238
```python { .api }
239
class Class(ClassOrFunc):
240
"""
241
Class definition node.
242
243
Attributes:
244
type (str): 'classdef'
245
name (Name): Class name node
246
"""
247
248
def get_super_arglist(self):
249
"""
250
Get superclass argument list.
251
252
Returns:
253
BaseNode | None: Arglist node with superclasses or None
254
"""
255
```
256
257
```python { .api }
258
class Lambda(Function):
259
"""
260
Lambda expression node.
261
262
Attributes:
263
type (str): 'lambdef'
264
"""
265
266
@property
267
def name(self):
268
"""
269
Lambda name property.
270
271
Raises:
272
AttributeError: Lambdas don't have names
273
"""
274
275
@property
276
def annotation(self):
277
"""
278
Lambda annotation property.
279
280
Returns:
281
None: Lambdas don't have return annotations
282
"""
283
```
284
285
#### Usage Examples
286
287
```python
288
import parso
289
290
code = '''
291
def regular_func(a: int, b: str = "default") -> str:
292
"""A regular function."""
293
return f"{a}: {b}"
294
295
def generator_func():
296
yield 1
297
yield 2
298
return "done"
299
300
class Parent:
301
pass
302
303
class Child(Parent, dict):
304
def method(self, x, y=None):
305
if x:
306
return x
307
raise ValueError("Invalid input")
308
309
lambda_expr = lambda x, y: x + y
310
'''
311
312
module = parso.parse(code)
313
314
# Analyze function definitions
315
for func in module.iter_funcdefs():
316
print(f"\nFunction: {func.name.value}")
317
318
# Check if it's a generator
319
if func.is_generator():
320
print(" Type: Generator")
321
yields = list(func.iter_yield_exprs())
322
print(f" Yield expressions: {len(yields)}")
323
else:
324
print(" Type: Regular function")
325
326
# Get parameters
327
params = func.get_params()
328
print(f" Parameters: {len(params)}")
329
for param in params:
330
param_info = [param.name.value]
331
if param.annotation:
332
param_info.append(f"annotation={param.annotation.get_code()}")
333
if param.default:
334
param_info.append(f"default={param.default.get_code()}")
335
if param.star_count:
336
param_info.append(f"star_count={param.star_count}")
337
print(f" {', '.join(param_info)}")
338
339
# Check return annotation
340
if func.annotation:
341
print(f" Return annotation: {func.annotation.get_code()}")
342
343
# Count return statements
344
returns = list(func.iter_return_stmts())
345
print(f" Return statements: {len(returns)}")
346
347
# Analyze class definitions
348
for cls in module.iter_classdefs():
349
print(f"\nClass: {cls.name.value}")
350
351
# Get superclasses
352
super_arglist = cls.get_super_arglist()
353
if super_arglist:
354
# Extract superclass names
355
superclasses = []
356
for child in super_arglist.children:
357
if hasattr(child, 'value') and child.value not in (',', ):
358
superclasses.append(child.value)
359
if superclasses:
360
print(f" Inherits from: {', '.join(superclasses)}")
361
else:
362
print(" No explicit superclasses")
363
364
# Get methods in class
365
methods = list(cls.iter_funcdefs())
366
print(f" Methods: {len(methods)}")
367
for method in methods:
368
print(f" {method.name.value}()")
369
370
# Find lambda expressions
371
def find_lambdas(node):
372
"""Find all lambda expressions in a tree."""
373
lambdas = []
374
if hasattr(node, 'type') and node.type == 'lambdef':
375
lambdas.append(node)
376
if hasattr(node, 'children'):
377
for child in node.children:
378
lambdas.extend(find_lambdas(child))
379
return lambdas
380
381
lambdas = find_lambdas(module)
382
print(f"\nFound {len(lambdas)} lambda expressions")
383
for lam in lambdas:
384
params = lam.get_params()
385
param_names = [p.name.value for p in params]
386
print(f" lambda {', '.join(param_names)}: ...")
387
```
388
389
### Parameter Handling
390
391
Specialized parameter objects for function analysis.
392
393
```python { .api }
394
class Param(PythonBaseNode):
395
"""
396
Function parameter node.
397
398
Attributes:
399
type (str): 'param'
400
name (Name): Parameter name node
401
"""
402
403
@property
404
def star_count(self):
405
"""
406
Number of stars in parameter.
407
408
Returns:
409
int: 0 for normal, 1 for *args, 2 for **kwargs
410
"""
411
412
@property
413
def default(self):
414
"""
415
Parameter default value.
416
417
Returns:
418
NodeOrLeaf | None: Default value expression or None
419
"""
420
421
@property
422
def annotation(self):
423
"""
424
Parameter type annotation.
425
426
Returns:
427
NodeOrLeaf | None: Type annotation or None
428
"""
429
430
@property
431
def position_index(self):
432
"""
433
Positional index of parameter.
434
435
Returns:
436
int: Position index in parameter list
437
"""
438
439
def get_parent_function(self):
440
"""
441
Get the function containing this parameter.
442
443
Returns:
444
Function | Lambda: Parent function or lambda
445
"""
446
```
447
448
#### Usage Examples
449
450
```python
451
import parso
452
453
code = '''
454
def complex_func(pos_only, /, normal, *args, kw_only, **kwargs):
455
pass
456
457
def annotated_func(a: int, b: str = "default", c: list[int] = None):
458
pass
459
'''
460
461
module = parso.parse(code)
462
463
for func in module.iter_funcdefs():
464
print(f"\nFunction: {func.name.value}")
465
params = func.get_params()
466
467
for i, param in enumerate(params):
468
print(f" Parameter {i}: {param.name.value}")
469
print(f" Position index: {param.position_index}")
470
print(f" Star count: {param.star_count}")
471
472
if param.annotation:
473
print(f" Annotation: {param.annotation.get_code()}")
474
475
if param.default:
476
print(f" Default: {param.default.get_code()}")
477
478
parent_func = param.get_parent_function()
479
print(f" Parent function: {parent_func.name.value}")
480
```
481
482
### Name and Identifier Handling
483
484
Name nodes representing identifiers, variables, and function names.
485
486
```python { .api }
487
class Name(PythonLeaf):
488
"""
489
Identifier/name node.
490
491
Attributes:
492
type (str): 'name'
493
value (str): The identifier string
494
"""
495
496
def is_definition(self, include_setitem=False):
497
"""
498
Check if this name is being defined.
499
500
Args:
501
include_setitem (bool): Include item assignments like obj[key] = value
502
503
Returns:
504
bool: True if this is a definition
505
"""
506
507
def get_definition(self, import_name_always=False, include_setitem=False):
508
"""
509
Get the definition node for this name.
510
511
Args:
512
import_name_always (bool): Always consider import names as definitions
513
include_setitem (bool): Include item assignments
514
515
Returns:
516
NodeOrLeaf | None: Definition node or None
517
"""
518
```
519
520
#### Usage Examples
521
522
```python
523
import parso
524
525
code = '''
526
import os
527
from sys import path
528
529
x = 42
530
def func(param):
531
global x
532
x = param
533
local_var = "hello"
534
return local_var
535
536
class MyClass:
537
def __init__(self):
538
self.attr = "value"
539
'''
540
541
module = parso.parse(code)
542
543
# Analyze all name usages
544
used_names = module.get_used_names()
545
546
for name_str, name_nodes in used_names.items():
547
print(f"\nName '{name_str}' appears {len(name_nodes)} times:")
548
549
for name_node in name_nodes:
550
print(f" At {name_node.start_pos}: ", end="")
551
552
if name_node.is_definition():
553
print("DEFINITION", end="")
554
definition = name_node.get_definition()
555
if definition:
556
print(f" (in {definition.type})", end="")
557
else:
558
print("USAGE", end="")
559
definition = name_node.get_definition()
560
if definition:
561
print(f" (defined in {definition.type})", end="")
562
563
print()
564
565
# Focus on specific names
566
if 'x' in used_names:
567
print(f"\nVariable 'x' analysis:")
568
for x_node in used_names['x']:
569
if x_node.is_definition():
570
def_node = x_node.get_definition()
571
print(f" Defined at {x_node.start_pos} in {def_node.type}")
572
else:
573
print(f" Used at {x_node.start_pos}")
574
```
575
576
### Literal and Token Classes
577
578
Nodes representing Python literals and operators.
579
580
```python { .api }
581
class String(Literal):
582
"""
583
String literal node.
584
585
Attributes:
586
type (str): 'string'
587
value (str): Complete string including quotes and prefix
588
"""
589
590
@property
591
def string_prefix(self):
592
"""
593
Get string prefix (r, f, b, u, etc.).
594
595
Returns:
596
str: String prefix characters
597
"""
598
599
class Number(Literal):
600
"""
601
Numeric literal node.
602
603
Attributes:
604
type (str): 'number'
605
value (str): Number as string (preserves format)
606
"""
607
608
class Keyword(PythonLeaf):
609
"""
610
Python keyword node.
611
612
Attributes:
613
type (str): 'keyword'
614
value (str): Keyword string
615
"""
616
617
class Operator(PythonLeaf):
618
"""
619
Operator node.
620
621
Attributes:
622
type (str): 'operator'
623
value (str): Operator string
624
"""
625
```
626
627
#### Usage Examples
628
629
```python
630
import parso
631
632
code = '''
633
x = 42
634
y = 3.14
635
z = "hello"
636
w = r"raw string"
637
f_str = f"formatted {x}"
638
binary = 0b1010
639
hex_num = 0xFF
640
641
if x > 0:
642
print("positive")
643
'''
644
645
module = parso.parse(code)
646
647
def analyze_literals(node):
648
"""Find and analyze all literals in the tree."""
649
literals = {'strings': [], 'numbers': [], 'keywords': [], 'operators': []}
650
651
def walk(n):
652
if hasattr(n, 'type'):
653
if n.type == 'string':
654
prefix = n.string_prefix if hasattr(n, 'string_prefix') else ''
655
literals['strings'].append((n.value, prefix, n.start_pos))
656
elif n.type == 'number':
657
literals['numbers'].append((n.value, n.start_pos))
658
elif n.type == 'keyword':
659
literals['keywords'].append((n.value, n.start_pos))
660
elif n.type == 'operator':
661
literals['operators'].append((n.value, n.start_pos))
662
663
if hasattr(n, 'children'):
664
for child in n.children:
665
walk(child)
666
667
walk(node)
668
return literals
669
670
literals = analyze_literals(module)
671
672
print("String literals:")
673
for value, prefix, pos in literals['strings']:
674
print(f" {value} (prefix: '{prefix}') at {pos}")
675
676
print("\nNumber literals:")
677
for value, pos in literals['numbers']:
678
print(f" {value} at {pos}")
679
680
print("\nKeywords:")
681
for value, pos in literals['keywords']:
682
print(f" {value} at {pos}")
683
684
print("\nOperators:")
685
for value, pos in literals['operators']:
686
if value not in (',', ':', '(', ')', '[', ']'): # Skip common punctuation
687
print(f" {value} at {pos}")
688
```
689
690
### Control Flow Statements
691
692
Nodes representing Python control flow constructs.
693
694
```python { .api }
695
class IfStmt(Flow):
696
"""
697
If statement node.
698
699
Attributes:
700
type (str): 'if_stmt'
701
"""
702
703
def get_test_nodes(self):
704
"""
705
Get test expressions for if/elif clauses.
706
707
Yields:
708
NodeOrLeaf: Test expressions
709
"""
710
711
def is_node_after_else(self, node):
712
"""
713
Check if a node is in the else clause.
714
715
Args:
716
node (NodeOrLeaf): Node to check
717
718
Returns:
719
bool: True if node is after else
720
"""
721
722
class ForStmt(Flow):
723
"""
724
For loop node.
725
726
Attributes:
727
type (str): 'for_stmt'
728
"""
729
730
def get_testlist(self):
731
"""
732
Get the iteration target expression.
733
734
Returns:
735
NodeOrLeaf: Expression being iterated over
736
"""
737
738
def get_defined_names(self, include_setitem=False):
739
"""
740
Get names defined by loop variable.
741
742
Args:
743
include_setitem (bool): Include item assignments
744
745
Returns:
746
list[Name]: Names defined in loop target
747
"""
748
749
class WithStmt(Flow):
750
"""
751
With statement node.
752
753
Attributes:
754
type (str): 'with_stmt'
755
"""
756
757
def get_defined_names(self, include_setitem=False):
758
"""
759
Get names defined in 'as' clauses.
760
761
Args:
762
include_setitem (bool): Include item assignments
763
764
Returns:
765
list[Name]: Names defined by 'as' clauses
766
"""
767
768
class TryStmt(Flow):
769
"""
770
Try statement node.
771
772
Attributes:
773
type (str): 'try_stmt'
774
"""
775
776
def get_except_clause_tests(self):
777
"""
778
Get exception types from except clauses.
779
780
Yields:
781
NodeOrLeaf | None: Exception type expressions or None for bare except
782
"""
783
```
784
785
#### Usage Examples
786
787
```python
788
import parso
789
790
code = '''
791
if x > 0:
792
print("positive")
793
elif x < 0:
794
print("negative")
795
else:
796
print("zero")
797
798
for item in items:
799
process(item)
800
801
for i, value in enumerate(data):
802
print(f"{i}: {value}")
803
804
with open("file.txt") as f:
805
content = f.read()
806
807
with lock, open("file") as f:
808
data = f.read()
809
810
try:
811
result = risky_operation()
812
except ValueError as e:
813
print(f"ValueError: {e}")
814
except (TypeError, AttributeError):
815
print("Type or attribute error")
816
except:
817
print("Unknown error")
818
'''
819
820
module = parso.parse(code)
821
822
# Analyze control flow statements
823
def find_control_flow(node):
824
"""Find all control flow statements."""
825
control_flow = []
826
827
def walk(n):
828
if hasattr(n, 'type'):
829
if n.type in ('if_stmt', 'for_stmt', 'while_stmt', 'try_stmt', 'with_stmt'):
830
control_flow.append(n)
831
832
if hasattr(n, 'children'):
833
for child in n.children:
834
walk(child)
835
836
walk(node)
837
return control_flow
838
839
statements = find_control_flow(module)
840
841
for stmt in statements:
842
print(f"\n{stmt.type} at line {stmt.start_pos[0]}:")
843
844
if stmt.type == 'if_stmt':
845
tests = list(stmt.get_test_nodes())
846
print(f" Test conditions: {len(tests)}")
847
for i, test in enumerate(tests):
848
print(f" {i+1}: {test.get_code().strip()}")
849
850
elif stmt.type == 'for_stmt':
851
target = stmt.get_testlist()
852
defined_names = stmt.get_defined_names()
853
print(f" Iterating over: {target.get_code().strip()}")
854
print(f" Loop variables: {[n.value for n in defined_names]}")
855
856
elif stmt.type == 'with_stmt':
857
defined_names = stmt.get_defined_names()
858
if defined_names:
859
print(f" Context variables: {[n.value for n in defined_names]}")
860
861
elif stmt.type == 'try_stmt':
862
except_tests = list(stmt.get_except_clause_tests())
863
print(f" Exception handlers: {len(except_tests)}")
864
for i, test in enumerate(except_tests):
865
if test is None:
866
print(f" {i+1}: bare except")
867
else:
868
print(f" {i+1}: {test.get_code().strip()}")
869
```
870
871
### Import Statement Analysis
872
873
Specialized nodes for analyzing import statements and dependencies.
874
875
```python { .api }
876
class ImportName(Import):
877
"""
878
Regular import statement (import foo).
879
880
Attributes:
881
type (str): 'import_name'
882
"""
883
884
def get_defined_names(self, include_setitem=False):
885
"""Get names defined by this import."""
886
887
def get_paths(self):
888
"""Get import paths as lists of names."""
889
890
def is_nested(self):
891
"""Check if import defines nested names."""
892
893
class ImportFrom(Import):
894
"""
895
From import statement (from foo import bar).
896
897
Attributes:
898
type (str): 'import_from'
899
"""
900
901
def get_defined_names(self, include_setitem=False):
902
"""Get names defined by this import."""
903
904
def get_from_names(self):
905
"""Get the module names being imported from."""
906
907
def get_paths(self):
908
"""Get complete import paths."""
909
910
@property
911
def level(self):
912
"""
913
Get relative import level.
914
915
Returns:
916
int: Number of dots in relative import
917
"""
918
919
def is_star_import(self):
920
"""Check if this is a star import."""
921
```
922
923
#### Usage Examples
924
925
```python
926
import parso
927
928
code = '''
929
import os
930
import sys
931
from pathlib import Path
932
from . import local_module
933
from ..parent import parent_func
934
from foo import bar as baz
935
from typing import List, Dict, Optional
936
import numpy as np
937
from collections import *
938
'''
939
940
module = parso.parse(code)
941
942
imports = list(module.iter_imports())
943
944
for imp in imports:
945
print(f"\n{imp.type} at line {imp.start_pos[0]}:")
946
947
if imp.type == 'import_name':
948
defined = [n.value for n in imp.get_defined_names()]
949
paths = imp.get_paths()
950
951
print(f" Defines: {defined}")
952
print(f" Paths: {[[n.value for n in path] for path in paths]}")
953
print(f" Nested: {imp.is_nested()}")
954
955
elif imp.type == 'import_from':
956
from_names = [n.value for n in imp.get_from_names()]
957
defined = [n.value for n in imp.get_defined_names()]
958
level = imp.level
959
960
print(f" From: {'.'.join(from_names) if from_names else '(current)'}")
961
print(f" Level: {level} ({'absolute' if level == 0 else 'relative'})")
962
print(f" Defines: {defined}")
963
print(f" Star import: {imp.is_star_import()}")
964
965
paths = imp.get_paths()
966
print(f" Full paths: {[[n.value for n in path] for path in paths]}")
967
```