Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNM: Test dataclasses.field assignments in stubs #13415

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

cdce8p
Copy link
Contributor

@cdce8p cdce8p commented Jan 19, 2025

Test the impact of keeping dataclasses.field assignments in stub files.
Ref: python/mypy#18430

@cdce8p cdce8p marked this pull request as draft January 19, 2025 21:35
Comment on lines 54 to 56
percall_cumtime: float = ...
file_name: str = ...
line_number: int = ...
Copy link
Member

@AlexWaygood AlexWaygood Jan 19, 2025

Choose a reason for hiding this comment

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

I'd be more interested in an ast.Call node on the r.h.s., e.g.

Suggested change
percall_cumtime: float = ...
file_name: str = ...
line_number: int = ...
percall_cumtime: float = field(init=False, default_factory=list)
file_name: str = Field(default_factory=Incomplete)
line_number: int = ...

with field and Field imported from dataclasses at the top of the file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Working on it 😅 Seems ruff didn't like it and reformatted it during the auto pre-commit run.

Copy link
Member

Choose a reason for hiding this comment

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

Aah I see! This kind of thing violates some assumptions made by https://docs.astral.sh/ruff/rules/typed-argument-default-in-stub/ (and the same rule in the flake8-pyi plugin as well), I suppose

This comment has been minimized.

@AlexWaygood
Copy link
Member

There's already a pytype crash on the latest version, though pytype hasn't finished running yet:

Traceback (most recent call last):
  File "/home/runner/work/typeshed/typeshed/./tests/pytype_test.py", line 88, in run_pytype
    ast = loader.load_file(_get_module_name(filename), filename)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/load_pytd.py", line 481, in load_file
    return self.load_module(ModuleInfo(module_name, filename), mod_ast=mod_ast)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/load_pytd.py", line 493, in load_module
    mod_ast = self._module_loader.load_ast(mod_info)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/imports/module_loader.py", line 124, in load_ast
    return self._load_pyi(mod_info)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/imports/module_loader.py", line 106, in _load_pyi
    mod_ast = parser.parse_string(
              ^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/parser.py", line 897, in parse_string
    return parse_pyi(src, filename=filename, module_name=name, options=options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/parser.py", line 916, in parse_pyi
    root = gen_pytd.visit(root)
           ^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 34, in visit
    raise _ParseError.from_exc(e).at(node, self.filename, self.src_code)
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 32, in visit
    return super().visit(node)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 31, in visit
    ret = self.visit(v)
          ^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 34, in visit
    raise _ParseError.from_exc(e).at(node, self.filename, self.src_code)
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 32, in visit
    return super().visit(node)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 40, in visit
    ret = self.visit(v)
          ^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 34, in visit
    raise _ParseError.from_exc(e).at(node, self.filename, self.src_code)
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 32, in visit
    return super().visit(node)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 31, in visit
    ret = self.visit(v)
          ^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 34, in visit
    raise _ParseError.from_exc(e).at(node, self.filename, self.src_code)
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 32, in visit
    return super().visit(node)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 40, in visit
    ret = self.visit(v)
          ^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 34, in visit
    raise _ParseError.from_exc(e).at(node, self.filename, self.src_code)
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 32, in visit
    return super().visit(node)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 31, in visit
    ret = self.visit(v)
          ^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 34, in visit
    raise _ParseError.from_exc(e).at(node, self.filename, self.src_code)
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 32, in visit
    return super().visit(node)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 40, in visit
    ret = self.visit(v)
          ^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 34, in visit
    raise _ParseError.from_exc(e).at(node, self.filename, self.src_code)
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/visitor.py", line 32, in visit
    return super().visit(node)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 34, in visit
    out = self._call_visitor(node)
          ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/ast/visitor.py", line 55, in _call_visitor
    return visitor(node)
           ^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/parser.py", line 424, in visit_AnnAssign
    return self._ann_assign(node.target, node.annotation, node.value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/pytype/pyi/parser.py", line 475, in _ann_assign
    raise ParseError(
pytype.pyi.types.ParseError:   File: "/home/runner/work/typeshed/typeshed/stdlib/pstats.pyi", line 55
    percall_cumtime: float = field(default=2)  # noqa: Y015
   ^
ParseError: Default value for percall_cumtime: float can only be '...' or a literal constant, got $external$dataclasses.field

@cdce8p
Copy link
Contributor Author

cdce8p commented Jan 19, 2025

There's already a pytype crash on the latest version, though pytype hasn't finished running yet

Yeah, saw that as well. A bit unfortunate tbh. Using the field call directly would provide better stubs e.g. in with kw_only=True. Without it the stub has to add _: KW_ONLY to archive the same thing.

Copy link
Contributor

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

@AlexWaygood
Copy link
Member

I don't know though, I think it's probably okay for stubgen to generate stubs that cause pytype to crash? That doesn't really feel like something mypy should have to worry about. It's true that it'll be a little annoying if somebody generates a typeshed PR using stubgen that then causes pytype to crash in our CI. That feels like it'll be quite rare, however; it's fairly easily fixed in a typeshed PR; and typeshed isn't the only user of stubgen.

@cdce8p
Copy link
Contributor Author

cdce8p commented Jan 19, 2025

It's true that it'll be a little annoying if somebody generates a typeshed PR using stubgen that then causes pytype to crash in our CI.

If it even gets that far. With the current linting rules, flake-pyi and ruff will have reformatted these before it even gets to the PR stage.

@hamdanal
Copy link
Contributor

If it even gets that far. With the current linting rules, flake-pyi and ruff will have reformatted these before it even gets to the PR stage.

Now that you mention it, the ruff autofix isn’t safe and should be deactivated. Now that stubgen doesn’t generate an __init__ method for dataclasses, it is not always safe to remove the field assignments.

@AlexWaygood
Copy link
Member

We should probably add some logic to Ruff and flake8-pyi to ignore that rule for ast.Call nodes on the r.h.s. of assignments in dataclass class bodies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants