fix Support inline namedtuple definition #2811#2815
fix Support inline namedtuple definition #2811#2815asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR extends pyrefly’s handling of functional namedtuple definitions so that collections.namedtuple(...) / typing.NamedTuple(...) used inline in expression position (not just assigned to a name or used as a base class) synthesize an anonymous class that the solver can type correctly, addressing issue #2811.
Changes:
- Add binding-time synthesis for inline functional namedtuple calls (
collections.namedtuple/typing.NamedTuple) when the first argument is a string literal. - Add solver support to recognize and return the synthesized class type for these functional namedtuple call expressions.
- Add a regression test covering the inline-constructor pattern from #2811.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
pyrefly/lib/test/named_tuple.rs |
Adds regression test for inline collections.namedtuple(...)(...) constructor usage. |
pyrefly/lib/binding/expr.rs |
Synthesizes anonymous namedtuple classes during expression binding when functional namedtuple calls appear inline. |
pyrefly/lib/alt/expr.rs |
Returns synthesized class types during call expression inference when a matching anonymous class binding exists. |
pyrefly/lib/alt/class/class_field.rs |
Adjusts override-consistency checking to avoid treating namedtuple elements as regular override targets. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| fn synthesized_functional_class_type(&self, call: &ExprCall) -> Option<Type> { | ||
| let anon_key = match call.arguments.args.first()? { | ||
| Expr::StringLiteral(name) => Key::Anon(name.range()), | ||
| _ => Key::Anon(call.range), | ||
| }; | ||
| let idx = self | ||
| .bindings() | ||
| .key_to_idx_hashed_opt(Hashed::new(&anon_key))?; | ||
| matches!(self.bindings().get(idx), Binding::ClassDef(..)) | ||
| .then(|| self.get_hashed(Hashed::new(&anon_key)).ty().clone()) | ||
| } |
| self.insert_binding( | ||
| Key::Anon(call.range), | ||
| Binding::ClassDef(class_idx, Box::new([])), | ||
| ); |
|
Diff from mypy_primer, showing the effect of this PR on open source code: dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ERROR ddtrace/vendor/psutil/_pslinux.py:274:61-64: Argument `float` is not assignable to parameter `iterable` with type `Iterable[Any]` in function `tuple.__new__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/_pslinux.py:274:66-69: Expected 1 positional argument, got 3 in function `tuple.__new__` [bad-argument-count]
vision (https://github.com/pytorch/vision)
- ERROR torchvision/datasets/celeba.py:13:36-43: Class member `CSV.index` overrides parent class `NamedTupleFallback` in an inconsistent manner [bad-override]
cloud-init (https://github.com/canonical/cloud-init)
- ERROR tests/unittests/analyze/test_show.py:13:64-71: Expected 1 positional argument, got 2 in function `tuple.__new__` [bad-argument-count]
- ERROR tests/unittests/config/test_cc_install_hotplug.py:29:9-24: Expected 1 positional argument, got 6 in function `tuple.__new__` [bad-argument-count]
rotki (https://github.com/rotki/rotki)
- ERROR rotkehlchen/tests/utils/mock.py:74:16-50: Expected a callable, got `NamedTuple` [not-callable]
- ERROR rotkehlchen/tests/utils/mock.py:74:38-49: Expected 1 positional argument, got 2 in function `tuple.__new__` [bad-argument-count]
+ ERROR rotkehlchen/tests/utils/mock.py:74:27-36: Expected valid functional named tuple definition [bad-class-definition]
+ ERROR rotkehlchen/tests/utils/mock.py:74:51-58: Unexpected keyword argument `version` in function `Version.__new__` [unexpected-keyword]
mypy (https://github.com/python/mypy)
- ERROR mypy/typeshed/stdlib/platform.pyi:55:13-22: Class member `uname_result.processor` overrides parent class `_uname_result_base` in an inconsistent manner [bad-override]
core (https://github.com/home-assistant/core)
- ERROR homeassistant/components/modbus/validators.py:72:9-16: Class member `PARM_IS_LEGAL.count` overrides parent class `NamedTupleFallback` in an inconsistent manner [bad-override]
spack (https://github.com/spack/spack)
- ERROR lib/spack/spack/util/timer.py:21:72-79: Class member `TimeTracker.count` overrides parent class `NamedTupleFallback` in an inconsistent manner [bad-override]
paasta (https://github.com/yelp/paasta)
- ERROR paasta_tools/mesos_tools.py:67:48-55: Class member `SlaveTaskCount.count` overrides parent class `NamedTupleFallback` in an inconsistent manner [bad-override]
spark (https://github.com/apache/spark)
- ERROR python/pyspark/tests/test_context.py:273:72-76: Argument `None` is not assignable to parameter `iterable` with type `Iterable[Any]` in function `tuple.__new__` [bad-argument-type]
scipy-stubs (https://github.com/scipy/scipy-stubs)
+ ERROR scipy-stubs/stats/_mstats_basic.pyi:123:1-2: Unused `# pyrefly: ignore` comment for code(s): bad-override [unused-ignore]
+ ERROR scipy-stubs/stats/_stats_py.pyi:206:1-2: Unused `# pyrefly: ignore` comment for code(s): bad-override [unused-ignore]
+ ERROR scipy-stubs/stats/_stats_py.pyi:210:1-2: Unused `# pyrefly: ignore` comment for code(s): bad-override [unused-ignore]
scipy (https://github.com/scipy/scipy)
- ERROR scipy/stats/_mstats_basic.py:311:48-55: Class member `ModeResult.count` overrides parent class `NamedTupleFallback` in an inconsistent manner [bad-override]
- ERROR scipy/stats/_stats_py.py:471:48-55: Class member `ModeResult.count` overrides parent class `NamedTupleFallback` in an inconsistent manner [bad-override]
- ERROR scipy/stats/_stats_py.py:2234:31-38: Class member `HistogramResult.count` overrides parent class `NamedTupleFallback` in an inconsistent manner [bad-override]
|
Primer Diff Classification❌ 1 regression(s) | ✅ 9 improvement(s) | ❓ 1 needs review | 11 project(s) total | +5, -15 errors 1 regression(s) across rotki. error kinds:
Detailed analysis❌ Regression (1)rotki (+2, -2)
✅ Improvement (9)dd-trace-py (-2)
cloud-init (-2)
mypy (-1)
core (-1)
spack (-1)
paasta (-1)
spark (-1)
scipy-stubs (+3)
scipy (-3)
❓ Needs Review (1)vision (-1)
Let me trace through what's happening: {"spec_check": "The typing spec doesn't explicitly address inline namedtuple synthesis, but namedtuples created via collections.namedtuple() should behave identically whether assigned to a variable or used inline. There's no spec rule being violated here.", "runtime_behavior": "No runtime error. The code creates a valid namedtuple class with fields 'header', 'index', and 'data'. The .index attribute is a legitimate field of the CSV namedtuple, not an override issue.", "mypy_pyright": "No cross-check annotations provided, but this appears to be a pyrefly-specific false positive related to how it was handling namedtuple synthesis.", "removal_assessment": "The removed error was incorrect (false positive). The CSV.index field is a legitimate namedtuple field, not a bad override. NamedTuple classes have an .index() method from tuple, but namedtuple fields take precedence over inherited methods - this is standard Python behavior.", "pr_attribution": "The changes in Suggested fixesSummary: The PR's inline namedtuple support is incomplete - it doesn't handle keyword arguments in the instantiation call, causing false positives in rotki. 1. In
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (1 heuristic, 10 LLM) |
Summary
Fixes #2811
now synthesizes anonymous functional namedtuple classes when collections.namedtuple(...) or typing.NamedTuple(...) appears inline in expression position, instead of only when bound to a name or used as a base class.
Test Plan
add test