CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-parso

A Python parser that supports error recovery and round-trip parsing for different Python versions

Pending
Overview
Eval results
Files

python-elements.mddocs/

Python Syntax Elements

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.

Capabilities

Base Python Classes

Foundation classes that provide Python-specific functionality to all syntax tree elements.

class PythonMixin:
    """
    Mixin providing Python-specific functionality to nodes and leaves.
    """

class PythonLeaf(PythonMixin, Leaf):
    """
    Base class for Python leaf nodes (tokens).
    
    Inherits all functionality from Leaf with Python-specific enhancements.
    """

class PythonBaseNode(PythonMixin, BaseNode):
    """
    Base class for Python nodes with children.
    
    Inherits all functionality from BaseNode with Python-specific enhancements.
    """

class PythonNode(PythonMixin, Node):
    """
    Standard Python node implementation.
    
    Used for most Python syntax constructs.
    """

class PythonErrorNode(PythonMixin, ErrorNode):
    """
    Error node for invalid Python syntax.
    
    Contains partial or invalid syntax with error recovery.
    """

class PythonErrorLeaf(ErrorLeaf, PythonLeaf):
    """
    Error leaf for invalid Python tokens.
    
    Contains partial or invalid tokens with error recovery.
    """

Module and Scope Classes

Top-level containers and scoped constructs that organize Python code.

class Module(Scope):
    """
    The top-level module node representing a Python file.
    
    Attributes:
        type (str): Always 'file_input'
    """
    
    def get_used_names(self):
        """
        Get all name usages in this module.
        
        Returns:
            UsedNamesMapping: Immutable mapping from name strings to lists of Name nodes
        """
    
    def iter_imports(self):
        """
        Get all import statements in this module.
        
        Yields:
            Import: ImportName and ImportFrom nodes
        """
    
    def iter_funcdefs(self):
        """
        Get all function definitions in this module.
        
        Yields:
            Function: Function definition nodes
        """
    
    def iter_classdefs(self):
        """
        Get all class definitions in this module.
        
        Yields:
            Class: Class definition nodes
        """
class Scope(PythonBaseNode):
    """
    Base class for scoped constructs (functions, classes, modules).
    """
    
    def iter_funcdefs(self):
        """Get function definitions within this scope."""
    
    def iter_classdefs(self):
        """Get class definitions within this scope."""
    
    def iter_imports(self):
        """Get import statements within this scope."""
    
    def get_suite(self):
        """
        Get the executable body of this scope.
        
        Returns:
            BaseNode: Suite node containing the body statements
        """

Usage Examples

import parso

code = '''
import os
import sys
from pathlib import Path

def function1():
    pass

class MyClass:
    def method(self):
        pass

def function2():
    return 42
'''

module = parso.parse(code)

# Get all imports
imports = list(module.iter_imports())
print(f"Found {len(imports)} import statements")
for imp in imports:
    if imp.type == 'import_name':
        names = [path[-1].value for path in imp.get_paths()]
        print(f"Import: {', '.join(names)}")
    elif imp.type == 'import_from':
        from_names = [n.value for n in imp.get_from_names()]
        defined = [n.value for n in imp.get_defined_names()]
        print(f"From {'.'.join(from_names)} import {', '.join(defined)}")

# Get all functions
functions = list(module.iter_funcdefs())
print(f"Found {len(functions)} functions:")
for func in functions:
    print(f"  {func.name.value}() at line {func.start_pos[0]}")

# Get all classes
classes = list(module.iter_classdefs())
print(f"Found {len(classes)} classes:")
for cls in classes:
    print(f"  {cls.name.value} at line {cls.start_pos[0]}")

# Analyze name usage
used_names = module.get_used_names()
print(f"Name 'os' used {len(used_names.get('os', []))} times")

Function and Class Definitions

Definitions for functions, methods, and classes with specialized analysis methods.

class Function(ClassOrFunc):
    """
    Function definition node.
    
    Attributes:
        type (str): 'funcdef'
        name (Name): Function name node
    """
    
    def get_params(self):
        """
        Get function parameters.
        
        Returns:
            list[Param]: List of parameter objects
        """
    
    def iter_yield_exprs(self):
        """
        Find yield expressions in function body.
        
        Yields:
            YieldExpr: Yield expression nodes
        """
    
    def iter_return_stmts(self):
        """
        Find return statements in function body.
        
        Yields:
            ReturnStmt: Return statement nodes
        """
    
    def iter_raise_stmts(self):
        """
        Find raise statements in function body.
        
        Yields:
            KeywordStatement: Raise statement nodes
        """
    
    def is_generator(self):
        """
        Check if function is a generator.
        
        Returns:
            bool: True if function contains yield expressions
        """
    
    @property
    def annotation(self):
        """
        Get return type annotation.
        
        Returns:
            NodeOrLeaf | None: Return annotation or None
        """
class Class(ClassOrFunc):
    """
    Class definition node.
    
    Attributes:
        type (str): 'classdef'
        name (Name): Class name node
    """
    
    def get_super_arglist(self):
        """
        Get superclass argument list.
        
        Returns:
            BaseNode | None: Arglist node with superclasses or None
        """
class Lambda(Function):
    """
    Lambda expression node.
    
    Attributes:
        type (str): 'lambdef'
    """
    
    @property
    def name(self):
        """
        Lambda name property.
        
        Raises:
            AttributeError: Lambdas don't have names
        """
    
    @property
    def annotation(self):
        """
        Lambda annotation property.
        
        Returns:
            None: Lambdas don't have return annotations
        """

Usage Examples

import parso

code = '''
def regular_func(a: int, b: str = "default") -> str:
    """A regular function."""
    return f"{a}: {b}"

def generator_func():
    yield 1
    yield 2
    return "done"

class Parent:
    pass

class Child(Parent, dict):
    def method(self, x, y=None):
        if x:
            return x
        raise ValueError("Invalid input")

lambda_expr = lambda x, y: x + y
'''

module = parso.parse(code)

# Analyze function definitions
for func in module.iter_funcdefs():
    print(f"\nFunction: {func.name.value}")
    
    # Check if it's a generator
    if func.is_generator():
        print("  Type: Generator")
        yields = list(func.iter_yield_exprs())
        print(f"  Yield expressions: {len(yields)}")
    else:
        print("  Type: Regular function")
    
    # Get parameters
    params = func.get_params()
    print(f"  Parameters: {len(params)}")
    for param in params:
        param_info = [param.name.value]
        if param.annotation:
            param_info.append(f"annotation={param.annotation.get_code()}")
        if param.default:
            param_info.append(f"default={param.default.get_code()}")
        if param.star_count:
            param_info.append(f"star_count={param.star_count}")
        print(f"    {', '.join(param_info)}")
    
    # Check return annotation
    if func.annotation:
        print(f"  Return annotation: {func.annotation.get_code()}")
    
    # Count return statements
    returns = list(func.iter_return_stmts())
    print(f"  Return statements: {len(returns)}")

# Analyze class definitions
for cls in module.iter_classdefs():
    print(f"\nClass: {cls.name.value}")
    
    # Get superclasses
    super_arglist = cls.get_super_arglist()
    if super_arglist:
        # Extract superclass names
        superclasses = []
        for child in super_arglist.children:
            if hasattr(child, 'value') and child.value not in (',', ):
                superclasses.append(child.value)
        if superclasses:
            print(f"  Inherits from: {', '.join(superclasses)}")
    else:
        print("  No explicit superclasses")
    
    # Get methods in class
    methods = list(cls.iter_funcdefs())
    print(f"  Methods: {len(methods)}")
    for method in methods:
        print(f"    {method.name.value}()")

# Find lambda expressions
def find_lambdas(node):
    """Find all lambda expressions in a tree."""
    lambdas = []
    if hasattr(node, 'type') and node.type == 'lambdef':
        lambdas.append(node)
    if hasattr(node, 'children'):
        for child in node.children:
            lambdas.extend(find_lambdas(child))
    return lambdas

lambdas = find_lambdas(module)
print(f"\nFound {len(lambdas)} lambda expressions")
for lam in lambdas:
    params = lam.get_params()
    param_names = [p.name.value for p in params]
    print(f"  lambda {', '.join(param_names)}: ...")

Parameter Handling

Specialized parameter objects for function analysis.

class Param(PythonBaseNode):
    """
    Function parameter node.
    
    Attributes:
        type (str): 'param'
        name (Name): Parameter name node
    """
    
    @property
    def star_count(self):
        """
        Number of stars in parameter.
        
        Returns:
            int: 0 for normal, 1 for *args, 2 for **kwargs
        """
    
    @property
    def default(self):
        """
        Parameter default value.
        
        Returns:
            NodeOrLeaf | None: Default value expression or None
        """
    
    @property
    def annotation(self):
        """
        Parameter type annotation.
        
        Returns:
            NodeOrLeaf | None: Type annotation or None
        """
    
    @property
    def position_index(self):
        """
        Positional index of parameter.
        
        Returns:
            int: Position index in parameter list
        """
    
    def get_parent_function(self):
        """
        Get the function containing this parameter.
        
        Returns:
            Function | Lambda: Parent function or lambda
        """

Usage Examples

import parso

code = '''
def complex_func(pos_only, /, normal, *args, kw_only, **kwargs):
    pass

def annotated_func(a: int, b: str = "default", c: list[int] = None):
    pass
'''

module = parso.parse(code)

for func in module.iter_funcdefs():
    print(f"\nFunction: {func.name.value}")
    params = func.get_params()
    
    for i, param in enumerate(params):
        print(f"  Parameter {i}: {param.name.value}")
        print(f"    Position index: {param.position_index}")
        print(f"    Star count: {param.star_count}")
        
        if param.annotation:
            print(f"    Annotation: {param.annotation.get_code()}")
        
        if param.default:
            print(f"    Default: {param.default.get_code()}")
        
        parent_func = param.get_parent_function()
        print(f"    Parent function: {parent_func.name.value}")

Name and Identifier Handling

Name nodes representing identifiers, variables, and function names.

class Name(PythonLeaf):
    """
    Identifier/name node.
    
    Attributes:
        type (str): 'name'
        value (str): The identifier string
    """
    
    def is_definition(self, include_setitem=False):
        """
        Check if this name is being defined.
        
        Args:
            include_setitem (bool): Include item assignments like obj[key] = value
            
        Returns:
            bool: True if this is a definition
        """
    
    def get_definition(self, import_name_always=False, include_setitem=False):
        """
        Get the definition node for this name.
        
        Args:
            import_name_always (bool): Always consider import names as definitions
            include_setitem (bool): Include item assignments
            
        Returns:
            NodeOrLeaf | None: Definition node or None
        """

Usage Examples

import parso

code = '''
import os
from sys import path

x = 42
def func(param):
    global x
    x = param
    local_var = "hello"
    return local_var

class MyClass:
    def __init__(self):
        self.attr = "value"
'''

module = parso.parse(code)

# Analyze all name usages
used_names = module.get_used_names()

for name_str, name_nodes in used_names.items():
    print(f"\nName '{name_str}' appears {len(name_nodes)} times:")
    
    for name_node in name_nodes:
        print(f"  At {name_node.start_pos}: ", end="")
        
        if name_node.is_definition():
            print("DEFINITION", end="")
            definition = name_node.get_definition()
            if definition:
                print(f" (in {definition.type})", end="")
        else:
            print("USAGE", end="")
            definition = name_node.get_definition()
            if definition:
                print(f" (defined in {definition.type})", end="")
        
        print()

# Focus on specific names
if 'x' in used_names:
    print(f"\nVariable 'x' analysis:")
    for x_node in used_names['x']:
        if x_node.is_definition():
            def_node = x_node.get_definition()
            print(f"  Defined at {x_node.start_pos} in {def_node.type}")
        else:
            print(f"  Used at {x_node.start_pos}")

Literal and Token Classes

Nodes representing Python literals and operators.

class String(Literal):
    """
    String literal node.
    
    Attributes:
        type (str): 'string'
        value (str): Complete string including quotes and prefix
    """
    
    @property
    def string_prefix(self):
        """
        Get string prefix (r, f, b, u, etc.).
        
        Returns:
            str: String prefix characters
        """

class Number(Literal):
    """
    Numeric literal node.
    
    Attributes:
        type (str): 'number'
        value (str): Number as string (preserves format)
    """

class Keyword(PythonLeaf):
    """
    Python keyword node.
    
    Attributes:
        type (str): 'keyword'
        value (str): Keyword string
    """

class Operator(PythonLeaf):
    """
    Operator node.
    
    Attributes:
        type (str): 'operator'
        value (str): Operator string
    """

Usage Examples

import parso

code = '''
x = 42
y = 3.14
z = "hello"
w = r"raw string"
f_str = f"formatted {x}"
binary = 0b1010
hex_num = 0xFF

if x > 0:
    print("positive")
'''

module = parso.parse(code)

def analyze_literals(node):
    """Find and analyze all literals in the tree."""
    literals = {'strings': [], 'numbers': [], 'keywords': [], 'operators': []}
    
    def walk(n):
        if hasattr(n, 'type'):
            if n.type == 'string':
                prefix = n.string_prefix if hasattr(n, 'string_prefix') else ''
                literals['strings'].append((n.value, prefix, n.start_pos))
            elif n.type == 'number':
                literals['numbers'].append((n.value, n.start_pos))
            elif n.type == 'keyword':
                literals['keywords'].append((n.value, n.start_pos))
            elif n.type == 'operator':
                literals['operators'].append((n.value, n.start_pos))
        
        if hasattr(n, 'children'):
            for child in n.children:
                walk(child)
    
    walk(node)
    return literals

literals = analyze_literals(module)

print("String literals:")
for value, prefix, pos in literals['strings']:
    print(f"  {value} (prefix: '{prefix}') at {pos}")

print("\nNumber literals:")
for value, pos in literals['numbers']:
    print(f"  {value} at {pos}")

print("\nKeywords:")
for value, pos in literals['keywords']:
    print(f"  {value} at {pos}")

print("\nOperators:")
for value, pos in literals['operators']:
    if value not in (',', ':', '(', ')', '[', ']'):  # Skip common punctuation
        print(f"  {value} at {pos}")

Control Flow Statements

Nodes representing Python control flow constructs.

class IfStmt(Flow):
    """
    If statement node.
    
    Attributes:
        type (str): 'if_stmt'
    """
    
    def get_test_nodes(self):
        """
        Get test expressions for if/elif clauses.
        
        Yields:
            NodeOrLeaf: Test expressions
        """
    
    def is_node_after_else(self, node):
        """
        Check if a node is in the else clause.
        
        Args:
            node (NodeOrLeaf): Node to check
            
        Returns:
            bool: True if node is after else
        """

class ForStmt(Flow):
    """
    For loop node.
    
    Attributes:
        type (str): 'for_stmt'
    """
    
    def get_testlist(self):
        """
        Get the iteration target expression.
        
        Returns:
            NodeOrLeaf: Expression being iterated over
        """
    
    def get_defined_names(self, include_setitem=False):
        """
        Get names defined by loop variable.
        
        Args:
            include_setitem (bool): Include item assignments
            
        Returns:
            list[Name]: Names defined in loop target
        """

class WithStmt(Flow):
    """
    With statement node.
    
    Attributes:
        type (str): 'with_stmt'
    """
    
    def get_defined_names(self, include_setitem=False):
        """
        Get names defined in 'as' clauses.
        
        Args:
            include_setitem (bool): Include item assignments
            
        Returns:
            list[Name]: Names defined by 'as' clauses
        """

class TryStmt(Flow):
    """
    Try statement node.
    
    Attributes:
        type (str): 'try_stmt'
    """
    
    def get_except_clause_tests(self):
        """
        Get exception types from except clauses.
        
        Yields:
            NodeOrLeaf | None: Exception type expressions or None for bare except
        """

Usage Examples

import parso

code = '''
if x > 0:
    print("positive")
elif x < 0:
    print("negative")
else:
    print("zero")

for item in items:
    process(item)

for i, value in enumerate(data):
    print(f"{i}: {value}")

with open("file.txt") as f:
    content = f.read()

with lock, open("file") as f:
    data = f.read()

try:
    result = risky_operation()
except ValueError as e:
    print(f"ValueError: {e}")
except (TypeError, AttributeError):
    print("Type or attribute error")
except:
    print("Unknown error")
'''

module = parso.parse(code)

# Analyze control flow statements
def find_control_flow(node):
    """Find all control flow statements."""
    control_flow = []
    
    def walk(n):
        if hasattr(n, 'type'):
            if n.type in ('if_stmt', 'for_stmt', 'while_stmt', 'try_stmt', 'with_stmt'):
                control_flow.append(n)
        
        if hasattr(n, 'children'):
            for child in n.children:
                walk(child)
    
    walk(node)
    return control_flow

statements = find_control_flow(module)

for stmt in statements:
    print(f"\n{stmt.type} at line {stmt.start_pos[0]}:")
    
    if stmt.type == 'if_stmt':
        tests = list(stmt.get_test_nodes())
        print(f"  Test conditions: {len(tests)}")
        for i, test in enumerate(tests):
            print(f"    {i+1}: {test.get_code().strip()}")
    
    elif stmt.type == 'for_stmt':
        target = stmt.get_testlist()
        defined_names = stmt.get_defined_names()
        print(f"  Iterating over: {target.get_code().strip()}")
        print(f"  Loop variables: {[n.value for n in defined_names]}")
    
    elif stmt.type == 'with_stmt':
        defined_names = stmt.get_defined_names()
        if defined_names:
            print(f"  Context variables: {[n.value for n in defined_names]}")
    
    elif stmt.type == 'try_stmt':
        except_tests = list(stmt.get_except_clause_tests())
        print(f"  Exception handlers: {len(except_tests)}")
        for i, test in enumerate(except_tests):
            if test is None:
                print(f"    {i+1}: bare except")
            else:
                print(f"    {i+1}: {test.get_code().strip()}")

Import Statement Analysis

Specialized nodes for analyzing import statements and dependencies.

class ImportName(Import):
    """
    Regular import statement (import foo).
    
    Attributes:
        type (str): 'import_name'
    """
    
    def get_defined_names(self, include_setitem=False):
        """Get names defined by this import."""
    
    def get_paths(self):
        """Get import paths as lists of names."""
    
    def is_nested(self):
        """Check if import defines nested names."""

class ImportFrom(Import):
    """
    From import statement (from foo import bar).
    
    Attributes:
        type (str): 'import_from'
    """
    
    def get_defined_names(self, include_setitem=False):
        """Get names defined by this import."""
    
    def get_from_names(self):
        """Get the module names being imported from."""
    
    def get_paths(self):
        """Get complete import paths."""
    
    @property
    def level(self):
        """
        Get relative import level.
        
        Returns:
            int: Number of dots in relative import
        """
    
    def is_star_import(self):
        """Check if this is a star import."""

Usage Examples

import parso

code = '''
import os
import sys
from pathlib import Path
from . import local_module
from ..parent import parent_func
from foo import bar as baz
from typing import List, Dict, Optional
import numpy as np
from collections import *
'''

module = parso.parse(code)

imports = list(module.iter_imports())

for imp in imports:
    print(f"\n{imp.type} at line {imp.start_pos[0]}:")
    
    if imp.type == 'import_name':
        defined = [n.value for n in imp.get_defined_names()]
        paths = imp.get_paths()
        
        print(f"  Defines: {defined}")
        print(f"  Paths: {[[n.value for n in path] for path in paths]}")
        print(f"  Nested: {imp.is_nested()}")
    
    elif imp.type == 'import_from':
        from_names = [n.value for n in imp.get_from_names()]
        defined = [n.value for n in imp.get_defined_names()]
        level = imp.level
        
        print(f"  From: {'.'.join(from_names) if from_names else '(current)'}")
        print(f"  Level: {level} ({'absolute' if level == 0 else 'relative'})")
        print(f"  Defines: {defined}")
        print(f"  Star import: {imp.is_star_import()}")
        
        paths = imp.get_paths()
        print(f"  Full paths: {[[n.value for n in path] for path in paths]}")

Install with Tessl CLI

npx tessl i tessl/pypi-parso

docs

core-parsing.md

error-handling.md

grammar-system.md

index.md

python-elements.md

tokenization.md

tree-navigation.md

utilities.md

tile.json