Skip to content

fix: ast.NameConstant missing fields - #14353

Closed
hunterhogan wants to merge 4 commits into
python:mainfrom
hunterhogan:astNameConstant
Closed

hunterhogan wants to merge 4 commits into
python:mainfrom
hunterhogan:astNameConstant

Conversation

@hunterhogan

Copy link
Copy Markdown
Contributor
(.venv) C:\apps\astToolkit>py
Python 3.13.5 (tags/v3.13.5:6cb20a2, Jun 11 2025, 16:15:46) [MSC v.1943 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> ast.Str._fields
<stdin>-1:1: DeprecationWarning: ast.Str is deprecated and will be removed in Python 3.14; use ast.Constant instead
('s',)
>>> ast.Bytes._fields
<stdin>-2:1: DeprecationWarning: ast.Bytes is deprecated and will be removed in Python 3.14; use ast.Constant instead
('s',)
>>> ast.Num._fields  
<stdin>-3:1: DeprecationWarning: ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead
('n',)
>>> ast.NameConstant._fields
<stdin>-4:1: DeprecationWarning: ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead
('value', 'kind')

Comment thread stdlib/ast.pyi
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@hunterhogan

Copy link
Copy Markdown
Contributor Author

Is there something to change?

@github-actions

github-actions Bot commented Jul 9, 2025

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@brianschubert

brianschubert commented Jul 9, 2025 •

Copy link
Copy Markdown
Collaborator

I think it's not fully clear what the purpose of this change is per #14353 (comment)

It may help if you could share an example of some code that currently doesn't pass type checking but which would after making this change.

@hunterhogan

Copy link
Copy Markdown
Contributor Author

I think it's not fully clear what the purpose of this change is per #14353 (comment)

It may help if you could share an example of some code that currently doesn't pass type checking but which would after making this change.

I'm not sure I understand what information you need, even though your request is clear and specific. Or perhaps, I don't know enough to create a simpler example based on my actual problem, which is a complex piece of code.

In my inexpert opinion, the purpose is to keep typeshed as the SSOT for type information, which requires accuracy for the sake of accuracy.

Excerpt from astToolFactory

def _get_astModule_astStub() -> ast.Module:
	ImaSearchContext: typeshed_client.SearchContext = typeshed_client.get_search_context(typeshed=pathRoot_typeshed)
	return parsePathFilename2astModule(raiseIfNone(typeshed_client.get_stub_file("ast", search_context=ImaSearchContext)))

...

    dictionaryClassDef: dict[str, ast.ClassDef] = makeDictionaryClassDef(_get_astModule_astStub())

...

        def get_type_ast_expr(dddataframeee: pandas.DataFrame) -> tuple[pandas.Series, pandas.Series]:
            attribute: str = cast("str", dddataframeee["attribute"])
            if attribute == 'n':
                # TODO remove when `ast.Num` is updated
                attribute = 'value'
            getAnnotation = NodeTourist[ast.AnnAssign, ast.expr](
                findThis = Be.AnnAssign.targetIs(IfThis.isNameIdentifier(attribute))
                , doThat = Then.extractIt(DOT.annotation))

            ClassDefIdentifier: str = cast("str", dddataframeee["ClassDefIdentifier"])
            if ClassDefIdentifier == 'NameConstant':
                ClassDefIdentifier = 'Constant'

            type_ast_expr: ast.expr = raiseIfNone(getAnnotation.captureLastMatch(dictionaryClassDef[ClassDefIdentifier]))

            NodeChanger(findThis=Be.Subscript.valueIs(IfThis.isNameIdentifier("Literal")), doThat=Then.replaceWith(Make.Name("bool"))
                ).visit(type_ast_expr)

            dddataframeee["type_ast_expr"] = NodeChanger[ast.Name, ast.expr](
                findThis=lambda node: Be.Name(node) and isinstance(eval(node.id), type) and issubclass(eval(node.id), ast.AST)  # noqa: S307
                , doThat=lambda node: Make.Attribute(Make.Name("ast"), eval(node.id).__name__)  # noqa: S307
            ).visit(type_ast_expr)
            dddataframeee["attributeType"] = ast.unparse(cast("ast.AST", dddataframeee["type_ast_expr"]))

            return dddataframeee["type_ast_expr"], dddataframeee["attributeType"]

For each subclass (ClassDefIdentifier), I get the "attribute" names from the interpreter using ._fields. Then I use the code above to get the type of each field from typeshed. Because ast.Num.NameConstant is not defined in typeshed, I have inserted the if statement. In contrast, I don't have to worry about ast.Bytes or ast.Str. ast.Num.n is the only other class or attribute in ast.AST that isn't defined in typeshed. #14355

Oh, wait, was. I need to update that code.

I will continue trying to think of a way to create a simpler example. Sorry.

@srittau

srittau commented Jul 11, 2025

Copy link
Copy Markdown
Collaborator

I've opened #14397, which is an alternative that aligns the stubs closer with the implementation by adding __new__ to the deprecated classes and removing the fields (which are already implemented by Constant).

@hunterhogan

Copy link
Copy Markdown
Contributor Author

I've opened #14397, which is an alternative that aligns the stubs closer with the implementation by adding __new__ to the deprecated classes and removing the fields (which are already implemented by Constant).

Fantastic! I didn't have the knowledge to see that option, and it seems like a far better solution.

@hunterhogan
hunterhogan deleted the astNameConstant branch July 11, 2025 19:54
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.

3 participants