Skip to content

Annotating ast.ImportFrom default values #14338

Closed
@hunterhogan

Description

@hunterhogan

Current annotation

typeshed/stdlib/ast.pyi

Lines 733 to 758 in 7aad255

class ImportFrom(stmt):
if sys.version_info >= (3, 10):
__match_args__ = ("module", "names", "level")
module: str | None
names: list[alias]
level: int
if sys.version_info >= (3, 13):
@overload
def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ...
@overload
def __init__(
self, module: str | None = None, names: list[alias] = ..., *, level: int, **kwargs: Unpack[_Attributes]
) -> None: ...
else:
@overload
def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ...
@overload
def __init__(
self, module: str | None = None, *, names: list[alias], level: int, **kwargs: Unpack[_Attributes]
) -> None: ...
if sys.version_info >= (3, 14):
def __replace__(
self, *, module: str | None = ..., names: list[alias] = ..., level: int = ..., **kwargs: Unpack[_Attributes]
) -> Self: ...

Actual values in Python 3.13

(.313) C:\clones\typeshed>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
>>> turkey = ast.ImportFrom()
>>> print(f"{turkey.module = }, {turkey.names = }, {turkey.level = }")
turkey.module = None, turkey.names = [], turkey.level = None

More comprehensive examples

3.15

Details

(.venv) C:\clones\cpython>pcbuild\amd64\python_d
Python 3.15.0a0 (heads/main:28c71ee4b2e, Jun 17 2025, 22:00:57) [MSC v.1944 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> ast.ImportFrom()
ImportFrom(module=None, names=[], level=None)
>>> 

3.14.0b3

Details

(.314) C:\clones\typeshed>py
Python 3.14.0b3 (tags/v3.14.0b3:26d485d, Jun 17 2025, 16:21:24) [MSC v.1943 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> hammering = ast.ImportFrom()
>>> print(f"{hammering.module = }, {hammering.names = }, {hammering.level = }")
hammering.module = None, hammering.names = [], hammering.level = None
>>> ast.unparse(hammering)
'from  import '
>>> 

3.13

Default values

Details

(.313) C:\clones\typeshed>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
>>> turkey = ast.ImportFrom()
>>> print(f"{turkey.module = }, {turkey.names = }, {turkey.level = }")
turkey.module = None, turkey.names = [], turkey.level = None
>>> ast.unparse(turkey)
'from  import '
>>> 

overload still necessary

Details

(.313) C:\clones\typeshed>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
>>> deepEnd = ast.ImportFrom([ast.alias('thePool')])                                                                                                                   
>>> print(f"{deepEnd.module = }, {deepEnd.names = }, {deepEnd.level = }")
deepEnd.module = [<ast.alias object at 0x000001E3BDF2F310>], deepEnd.names = [], deepEnd.level = None
>>> deepEnd = ast.ImportFrom(None, [ast.alias('thePool')])
>>> print(f"{deepEnd.module = }, {deepEnd.names = }, {deepEnd.level = }")
deepEnd.module = None, deepEnd.names = [<ast.alias object at 0x000001E3BDF60090>], deepEnd.level = None

3.12

keyword usage

Details

>>> import ast
>>> silky = ast.ImportFrom()
>>> print(f"{silky.module = }, {silky.names = }, {silky.level = }")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'ImportFrom' object has no attribute 'names'
>>> silky = ast.ImportFrom(names=[ast.alias('punto', 'period')])
>>> print(f"{silky.module = }, {silky.names = }, {silky.level = }")
silky.module = None, silky.names = [<ast.alias object at 0x000001EB300D9050>], silky.level = None
>>> ast.unparse(silky)
'from  import punto as period'
>>>

no keyword, no happy

Details

>>> tired = ast.ImportFrom([ast.alias('stomachache')])
>>> print(f"{tired.module = }, {tired.names = }, {tired.level = }")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'ImportFrom' object has no attribute 'names'
>>> tired.module
[<ast.alias object at 0x000001EB3023AF90>]
>>> print(f"{tired.level = }")                                     
tired.level = None
>>> tired = ast.ImportFrom(None, [ast.alias('stomachache')])
>>> print(f"{tired.module = }, {tired.names = }, {tired.level = }")
tired.module = None, tired.names = [<ast.alias object at 0x000001EB3023AA10>], tired.level = None

3.11

Details

(.311) C:\clones\typeshed>py
Python 3.11.9 (tags/v3.11.9:de54cf5, Apr  2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> blue = ast.ImportFrom(names=[ast.alias('moon')])
>>> print(f"{blue.module = }, {blue.names = }, {blue.level = }")
blue.module = None, blue.names = [<ast.alias object at 0x00000156222876D0>], blue.level = None
>>> ast.unparse(blue)
'from  import moon'
>>>

3.10

Details

(.310) C:\clones\typeshed>py
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> quizas = ast.ImportFrom(names=[ast.alias('manana')])
>>> print(f"{quizas.module = }, {quizas.names = }, {quizas.level = }")
quizas.module = None, quizas.names = [<ast.alias object at 0x00000194028DE050>], quizas.level = None
>>> ast.unparse(quizas)
'from  import manana'
>>>

3.9

Details

(.309) C:\clones\typeshed>py
Python 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> play = ast.ImportFrom(names=[ast.alias('Mad_Libs')])
>>> print(f"{play.module = }, {play.names = }, {play.level = }")
play.module = None, play.names = [<ast.alias object at 0x000002590DBB7FD0>], play.level = None
>>> ast.unparse(play)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\hunte\AppData\Local\Programs\Python\Python39\lib\ast.py", line 1572, in unparse
    return unparser.visit(ast_obj)
  File "C:\Users\hunte\AppData\Local\Programs\Python\Python39\lib\ast.py", line 801, in visit
    self.traverse(node)
  File "C:\Users\hunte\AppData\Local\Programs\Python\Python39\lib\ast.py", line 795, in traverse
    super().visit(node)
  File "C:\Users\hunte\AppData\Local\Programs\Python\Python39\lib\ast.py", line 407, in visit
    return visitor(node)
  File "C:\Users\hunte\AppData\Local\Programs\Python\Python39\lib\ast.py", line 846, in visit_ImportFrom
    self.write("." * node.level)
TypeError: can't multiply sequence by non-int of type 'NoneType'
>>> import ast
>>> Europe = ast.ImportFrom(names=[ast.alias('zero', 'Arabic_numerals')], level = 0)
>>> print(f"{Europe.module = }, {Europe.names = }, {Europe.level = }")
Europe.module = None, Europe.names = [<ast.alias object at 0x000002590D88E4C0>], Europe.level = 0
>>> ast.unparse(Europe)
'from  import zero as Arabic_numerals'
>>>

So, level is required, and it cannot be None?

#14308

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions