Skip to content

Implement simple type hints for _ast_gen.py and c_ast.py#600

Closed
Repiteo wants to merge 1 commit into
eliben:mainfrom
Repiteo:type-hints-ast
Closed

Implement simple type hints for _ast_gen.py and c_ast.py#600
Repiteo wants to merge 1 commit into
eliben:mainfrom
Repiteo:type-hints-ast

Conversation

@Repiteo

@Repiteo Repiteo commented Apr 19, 2026

Copy link
Copy Markdown

The typing logic for this was derived exclusively from _c_ast.cfg; as such, it's quite rudamentary. Anything more complex would necessitate expanding the configuation with explicit typing data, or making a whitelist for "expected" types. Part of the type validation process meant updating certain semantics or syntax, as well as exposing a few bugs that've since been resolved.

@eliben eliben left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please isolate just the typing changes for the AST so I can review?

No need to add annotations to ast_gen.py itself -- it's just a simple script that glues strings together

@Repiteo

Repiteo commented Apr 20, 2026

Copy link
Copy Markdown
Author

Typing changes isolated!

@Repiteo Repiteo left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the typing changes are now isolated, this consequently re-reveals the bugs that they identified. Here's the most obvious offenders:

Comment thread pycparser/c_ast.py
Comment on lines +70 to 72
def children(self) -> None:
"""A sequence of all children that are Nodes"""
pass

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typing systems expect subclass return types to match their base class. Given this is an abstract method that's never called, this should be adjusted to:

Suggested change
def children(self) -> None:
"""A sequence of all children that are Nodes"""
pass
def children(self) -> tuple[tuple[str, Node], ...]:
"""A sequence of all children that are Nodes"""
raise NotImplementedException()

Comment thread pycparser/c_ast.py
Comment on lines +37 to 39
__slots__: ClassVar[tuple[str, ...]] = ()
""" Abstract base class for AST nodes.
"""

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be moved below the docstring so it actually can function as a docstring

Suggested change
__slots__: ClassVar[tuple[str, ...]] = ()
""" Abstract base class for AST nodes.
"""
"""Abstract base class for AST nodes."""
__slots__: ClassVar[tuple[str, ...]] = ()

Comment thread pycparser/c_ast.py
Comment on lines +116 to 117
def is_empty(v: str | None) -> None:
v is None or (hasattr(v, "__len__") and len(v) == 0)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was definitely intended as a conditional, but it never returns

Suggested change
def is_empty(v: str | None) -> None:
v is None or (hasattr(v, "__len__") and len(v) == 0)
def is_empty(v: str | None) -> bool:
return v is None or (hasattr(v, "__len__") and len(v) == 0)

Comment thread pycparser/c_ast.py Outdated
Comment on lines 1462 to 1464
def __iter__(self) -> Generator[Node, None, None]:
return
yield

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a typing warning/error per se, but this yield is considered unreachable; a way to handle this without making the IDE yell would be

Suggested change
def __iter__(self) -> Generator[Node, None, None]:
return
yield
def __iter__(self) -> Generator[Node, None, None]:
yield from ()

Comment thread pycparser/c_ast.py
from collections.abc import Callable, Generator
from typing import Any, ClassVar, IO, TYPE_CHECKING

if TYPE_CHECKING:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to use this check?
In other parts of pycparser from typing .... imports are done unguarded

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do it unguarded, but it'll cause circular imports down the road. If I tried this without the conditional, from .c_parser import Coord would fail for that reason.

Given you're only doing those imports for type hints in pycparser, they should probably be behind a TYPE_CHECKING conditional too, but that's outside the scope of this PR

Comment thread pycparser/_ast_gen.py
from .c_parser import Coord

def _repr(obj):
def _repr(obj: Any | None) -> str:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What benefit does "Any | None" provide?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making the typing explicit; that's about it.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't Any already subsume the | None part, making it obsolete?

Comment thread pycparser/c_ast.py

class Node:
__slots__ = ()
__slots__: ClassVar[tuple[str, ...]] = ()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it really necessary to annotate __slots__? what check fails if we don't?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mypy gives an error in __repr__ when trying to iterate over self.__slots__[:-2]:

Unsupported left operand type for + ("Never") Mypy[operator]

Without the hint, it resolves __slots__ as a tuple with no entries. The hint makes it see a tuple with an indeterminite amount of entries, so it will no longer complain

Comment thread pycparser/c_ast.py Outdated
@Repiteo

Repiteo commented Apr 20, 2026

Copy link
Copy Markdown
Author

Rebased with the Generator[Node] fix, but the ty checker really didn't like these changes in isolation. This might not be a functional baseline after all, unless you want to actively add more None conditionals/fallbacks

eliben added a commit that referenced this pull request Apr 21, 2026
@eliben

eliben commented Apr 21, 2026

Copy link
Copy Markdown
Owner

I fixed the missed return you've mentioned, but otherwise I don't feel this is worthwhile at this time. I personally only prefer using ty as the typechecker and don't want to be dealing with appeasing multiple type checkers (personal preference, I know it's not for everyone). Can wait a bit more until its functionality improves.

@eliben eliben closed this Apr 21, 2026
@Repiteo

Repiteo commented Apr 21, 2026

Copy link
Copy Markdown
Author

Fair enough. If I do any future passes, it'll be via ty exclusively

@Repiteo Repiteo deleted the type-hints-ast branch April 21, 2026 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants