Skip to content

fix Support inline namedtuple definition #2811#2815

Open
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2811
Open

fix Support inline namedtuple definition #2811#2815
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2811

Conversation

@asukaminato0721
Copy link
Contributor

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

@meta-cla meta-cla bot added the cla signed label Mar 17, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as ready for review March 17, 2026 07:37
Copilot AI review requested due to automatic review settings March 17, 2026 07:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +224 to +234
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())
}
Comment on lines +591 to +594
self.insert_binding(
Key::Anon(call.range),
Binding::ClassDef(class_idx, Box::new([])),
);
@github-actions
Copy link

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]

@github-actions
Copy link

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: bad-argument-count, bad-class-definition, not-callable. caused by bind_inline_functional_named_tuple(). 9 improvement(s) across dd-trace-py, cloud-init, mypy, core, spack, paasta, spark, scipy-stubs, scipy.

Project Verdict Changes Error Kinds Root Cause
dd-trace-py ✅ Improvement -2 bad-argument-count, bad-argument-type synthesized_functional_class_type()
vision ❓ Needs Review -1 bad-override
cloud-init ✅ Improvement -2 bad-argument-count bind_inline_functional_named_tuple()
rotki ❌ Regression +2, -2 bad-argument-count, bad-class-definition bind_inline_functional_named_tuple()
mypy ✅ Improvement -1 bad-override pyrefly/lib/alt/class/class_field.rs
core ✅ Improvement -1 bad-override pyrefly/lib/alt/class/class_field.rs
spack ✅ Improvement -1 bad-override pyrefly/lib/alt/class/class_field.rs
paasta ✅ Improvement -1 bad-override pyrefly/lib/alt/class/class_field.rs
spark ✅ Improvement -1 bad-argument-type pyrefly/lib/binding/expr.rs
scipy-stubs ✅ Improvement +3 unused-ignore pyrefly/lib/alt/class/class_field.rs
scipy ✅ Improvement -3 bad-override pyrefly/lib/alt/class/class_field.rs
Detailed analysis

❌ Regression (1)

rotki (+2, -2)

This is a regression. The PR aimed to fix inline namedtuple support but introduced new false positives. The code NamedTuple('Version', ['version'])(version=1) is valid Python that creates and instantiates a namedtuple in one expression. The fact that both new errors are pyrefly-only while the removed errors were about pyrefly not understanding namedtuples suggests the fix is incomplete - it partially recognizes inline namedtuples but still has bugs in the implementation.
Attribution: The changes in pyrefly/lib/binding/expr.rs added bind_inline_functional_named_tuple() to handle inline namedtuple creation, and pyrefly/lib/alt/expr.rs added synthesized_functional_class_type() to recognize these synthesized classes. However, the implementation appears incomplete as it's generating new false positive errors

✅ Improvement (9)

dd-trace-py (-2)

Pyrefly was incorrectly treating inline namedtuple definitions. The pattern namedtuple('scputimes', 'user system idle')(0.0, 0.0, 0.0) is valid Python that creates a namedtuple class with 3 fields and immediately instantiates it. Pyrefly was failing to recognize this pattern and incorrectly thought the arguments were being passed to tuple.__new__. The PR fixes this by synthesizing anonymous functional namedtuple classes when the pattern is detected. This is an improvement - removing false positives.
Attribution: The changes to pyrefly/lib/alt/expr.rs (adding synthesized_functional_class_type()) and pyrefly/lib/binding/expr.rs (adding bind_inline_functional_named_tuple()) fixed pyrefly's handling of inline namedtuple definitions, allowing it to recognize that namedtuple(...) returns a class that can be called with the appropriate number of arguments

cloud-init (-2)

Pyrefly previously failed to recognize inline namedtuple definitions like namedtuple('Name', fields)(args). It incorrectly treated these as calls to tuple.__new__ which only accepts 1 argument, causing false positive 'bad-argument-count' errors. The PR fixes this by synthesizing anonymous functional namedtuple classes when namedtuple appears inline, allowing the correct number of arguments to be passed to the generated constructor. This is an improvement - removing false positives.
Attribution: The changes to pyrefly/lib/binding/expr.rs (adding bind_inline_functional_named_tuple()) and pyrefly/lib/alt/expr.rs (adding synthesized_functional_class_type()) now properly synthesize anonymous namedtuple classes for inline usage, fixing the false positive errors

mypy (-1)

The removed error was a false positive. Pyrefly was incorrectly flagging a valid pattern where a NamedTuple field is overridden with a property in a subclass. The PR's changes to properly handle inline NamedTuple synthesis included a fix to skip override checking for NamedTuple elements, which correctly removes this false positive. This is an improvement - pyrefly now properly recognizes that NamedTuple fields can be overridden with properties without violating type safety.
Attribution: The change to skip override checking for NamedTuple elements in pyrefly/lib/alt/class/class_field.rs (specifically the is_named_tuple_element check added at lines 3037-3038 and 3064-3066) removed this false positive.

core (-1)

The removed error was a false positive. Pyrefly was incorrectly treating namedtuple fields as class member overrides. The PR fixed this by properly synthesizing inline namedtuple classes and exempting namedtuple elements from override checking. This is an improvement - pyrefly now correctly handles inline namedtuples like collections.namedtuple('Name', ['count', ...]) without spurious override errors.
Attribution: The changes to pyrefly/lib/alt/class/class_field.rs (adding is_named_tuple_element check) and pyrefly/lib/binding/expr.rs (adding bind_inline_functional_named_tuple) fixed the false positive by properly recognizing inline namedtuples and skipping override checks for their fields

spack (-1)

The error was a false positive. TimeTracker is a namedtuple with a count field, which is perfectly valid. Namedtuple fields are properties/attributes, not method overrides, so they shouldn't trigger bad-override errors. The PR correctly exempts namedtuple elements from override checking, removing this incorrect error.
Attribution: The change in pyrefly/lib/alt/class/class_field.rs that adds is_named_tuple_element check and skips override validation for namedtuple elements (if is_named_tuple_element { continue; }) fixed this false positive.

paasta (-1)

This is an improvement. The error was a false positive - count is a field defined by the namedtuple declaration itself, not an override of a parent class member. Named tuple fields are synthesized as properties on the generated class and are not subject to override consistency rules. The PR correctly recognizes this and skips override validation for named tuple elements.
Attribution: The change in pyrefly/lib/alt/class/class_field.rs that adds if is_named_tuple_element { continue; } skips override checking for named tuple elements, fixing the false positive.

spark (-1)

The removed error was a false positive. Before this PR, pyrefly didn't synthesize namedtuple classes when namedtuple(...) appeared inline in expressions. At line 273, namedtuple('MockGatewayParameters', 'auth_token')(None) creates a namedtuple class inline and immediately instantiates it with None for the auth_token field. Without proper synthesis, pyrefly incorrectly treated this as calling tuple.__new__ with None (which would require an iterable). With the PR's changes, pyrefly now correctly synthesizes the namedtuple class with its proper constructor that accepts individual field values, so passing None is valid. This is an improvement - pyrefly now handles a common Python pattern it previously misunderstood.
Attribution: The changes to pyrefly/lib/binding/expr.rs (adding bind_inline_functional_named_tuple) and pyrefly/lib/alt/expr.rs (adding synthesized_functional_class_type) now properly synthesize namedtuple classes when used inline, fixing the false positive.

scipy-stubs (+3)

Pyrefly fixed a bug where it was incorrectly applying method override rules to NamedTuple fields. NamedTuples are special constructs where fields intentionally shadow tuple methods with potentially different types. The removed bad-override errors were false positives.
Attribution: The change in pyrefly/lib/alt/class/class_field.rs that adds is_named_tuple_element check and skips override validation for NamedTuple elements directly caused these unused-ignore errors to appear, as the bad-override errors are no longer raised.

scipy (-3)

These were false positive errors where pyrefly incorrectly treated namedtuple fields as overrides of parent class members. The PR correctly fixes this by exempting namedtuple elements from override consistency checking. Removing these incorrect errors is an improvement.
Attribution: The change in pyrefly/lib/alt/class/class_field.rs that adds is_named_tuple_element check and skips override validation for namedtuple elements fixed these false positives.

❓ Needs Review (1)

vision (-1)

LLM classification failed: Could not parse LLM response as classification JSON: Looking at this change, I need to analyze what happened:

  1. The PR adds support for inline namedtuple definitions (e.g., collections.namedtuple("Name", ["field1", "field2"]) used directly in expressions)
  2. The removed error was: Class member CSV.index overrides parent class NamedTupleFallback in an inconsistent manner [bad-override]
  3. The source shows CSV = namedtuple("CSV", ["header", "index", "data"]) on line 13

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 pyrefly/lib/binding/expr.rs added bind_inline_functional_named_tuple() which properly synthesizes namedtuple classes for inline usage. The change in pyrefly/lib/alt/expr.rs added synthesized_functional_class_type() to recognize these synthesized classes. This allows pyrefly to understand that CSV is a proper namedtuple class with legitimate fields, not a class with bad overrides.", "reason": "The error was a false positive. When namedtuple("CSV", ["header", "index", "data"]) is called, it creates a class where 'index' is a field attribute. While tuple has an index() method, namedtuple fields legitimately shadow inherited methods - this is documented Python behavior, not a type violation. The PR fixed pyrefly's understanding of inline namedtuple definitions, allowing it to recognize CSV as a proper namedtuple rather than misinterpreting it as having override issues."}. Non-trivial change (0 added, 1 removed).

Suggested fixes

Summary: 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 bind_inline_functional_named_tuple() in pyrefly/lib/binding/expr.rs, the function needs to handle the full call expression including instantiation with keyword arguments. Currently it only synthesizes the class definition but doesn't properly handle the subsequent instantiation call with keyword arguments like (version=1).

Files: pyrefly/lib/binding/expr.rs
Confidence: high
Affected projects: rotki
Fixes: bad-argument-count, unexpected-keyword-argument
The rotki regression shows NamedTuple('Version', ['version'])(version=1) is being flagged incorrectly. The bind_inline_functional_named_tuple() function synthesizes the namedtuple class but doesn't handle the case where the namedtuple is immediately instantiated with keyword arguments. The function needs to either: 1) Transform the entire expression to handle both class creation and instantiation, or 2) Ensure the synthesized class properly accepts keyword arguments matching the field names. This would eliminate the 2 pyrefly-only errors in rotki.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (1 heuristic, 10 LLM)

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.

Support inline namedtuple definition

2 participants