Skip to content

Commit 9c9e725

Browse files
grievejiafacebook-github-bot
authored andcommitted
Consider all class fields in stub files to be always magically initialized
Summary: See [this github issue](#123). Pyrefly currently complains on the following code: ``` class Foo: x: int Foo.x # ERROR! Instance-only attribute `x` of class `Foo` is not visible on the class ``` Problem is, typeshed doesn't really differentiate between uninitialized and initialized classlevel field. Meaning that this: ``` class object: __dict__: dict[str, Any] ``` and this: ``` class object: __dict__: dict[str, Any] = ... ``` are treated as equivalent (see [this PR](python/typeshed#13875) for further details). Based on the feedback of the PR, this is unlikely to be something that's going to change in the short term. And the suggested action was to avoid reporting the error when the containing class of the field comes from a stub file. Fixes #123 Reviewed By: rchen152 Differential Revision: D74035159 fbshipit-source-id: 72db88a9506294f4395d049549f3220e6fa2b4c1
1 parent 8adb648 commit 9c9e725

File tree

5 files changed

+16
-49
lines changed

5 files changed

+16
-49
lines changed

conformance/third_party/conformance.exp

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4763,46 +4763,6 @@
47634763
"stop_column": 24,
47644764
"stop_line": 28
47654765
},
4766-
{
4767-
"code": -2,
4768-
"column": 1,
4769-
"concise_description": "assert_type(Any, str) failed",
4770-
"description": "assert_type(Any, str) failed",
4771-
"line": 35,
4772-
"name": "assert-type",
4773-
"stop_column": 29,
4774-
"stop_line": 35
4775-
},
4776-
{
4777-
"code": -2,
4778-
"column": 13,
4779-
"concise_description": "Instance-only attribute `genus` of class `Pet2` is not visible on the class",
4780-
"description": "Instance-only attribute `genus` of class `Pet2` is not visible on the class",
4781-
"line": 35,
4782-
"name": "missing-attribute",
4783-
"stop_column": 23,
4784-
"stop_line": 35
4785-
},
4786-
{
4787-
"code": -2,
4788-
"column": 1,
4789-
"concise_description": "assert_type(Any, str) failed",
4790-
"description": "assert_type(Any, str) failed",
4791-
"line": 36,
4792-
"name": "assert-type",
4793-
"stop_column": 31,
4794-
"stop_line": 36
4795-
},
4796-
{
4797-
"code": -2,
4798-
"column": 13,
4799-
"concise_description": "Instance-only attribute `species` of class `Pet2` is not visible on the class",
4800-
"description": "Instance-only attribute `species` of class `Pet2` is not visible on the class",
4801-
"line": 36,
4802-
"name": "missing-attribute",
4803-
"stop_column": 25,
4804-
"stop_line": 36
4805-
},
48064766
{
48074767
"code": -2,
48084768
"column": 5,

conformance/third_party/conformance.result

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,6 @@
270270
"enums_members.py": [
271271
"Line 27: Unexpected errors ['assert_type(Any, str) failed', 'Instance-only attribute `genus` of class `Pet` is not visible on the class']",
272272
"Line 28: Unexpected errors ['assert_type(Any, str) failed', 'Instance-only attribute `species` of class `Pet` is not visible on the class']",
273-
"Line 35: Unexpected errors ['assert_type(Any, str) failed', 'Instance-only attribute `genus` of class `Pet2` is not visible on the class']",
274-
"Line 36: Unexpected errors ['assert_type(Any, str) failed', 'Instance-only attribute `species` of class `Pet2` is not visible on the class']",
275273
"Line 100: Unexpected errors ['assert_type(Literal[TrafficLight.AMBER], Literal[TrafficLight.YELLOW]) failed']",
276274
"Line 128: Unexpected errors ['Could not find name `reveal_type`']"
277275
],

conformance/third_party/results.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"pass": 49,
44
"fail": 87,
55
"pass_rate": 0.36,
6-
"differences": 443,
6+
"differences": 441,
77
"passing": [
88
"aliases_newtype.py",
99
"annotations_coroutines.py",
@@ -87,7 +87,7 @@
8787
"directives_deprecated.py": 12,
8888
"directives_type_ignore_file1.py": 1,
8989
"enums_behaviors.py": 4,
90-
"enums_members.py": 6,
90+
"enums_members.py": 4,
9191
"exceptions_context_managers.py": 2,
9292
"generics_base_class.py": 3,
9393
"generics_basic.py": 7,

pyrefly/lib/alt/class/class_field.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,11 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
544544
}
545545
};
546546
let metadata = self.get_metadata_for_class(class);
547-
let initialization = self.get_class_field_initialization(&metadata, initial_value);
547+
let initialization = self.get_class_field_initialization(
548+
&metadata,
549+
initial_value,
550+
class.module_info().path().is_interface(),
551+
);
548552

549553
// Ban typed dict from containing values; fields should be annotation-only.
550554
// TODO(stroxler): we ought to look into this more: class-level attributes make sense on a `TypedDict` class;
@@ -743,9 +747,14 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
743747
&self,
744748
metadata: &ClassMetadata,
745749
initial_value: &ClassFieldInitialValue,
750+
is_stub: bool,
746751
) -> ClassFieldInitialization {
747752
match initial_value {
748-
ClassFieldInitialValue::Instance(_) => ClassFieldInitialization::Instance(false),
753+
ClassFieldInitialValue::Instance(_) => {
754+
// We consider fields to be always-initialized if it's defined within stub files.
755+
// See https://github.com/python/typeshed/pull/13875 for reasoning.
756+
ClassFieldInitialization::Instance(is_stub)
757+
}
749758
ClassFieldInitialValue::Class(None) => ClassFieldInitialization::Class(None),
750759
ClassFieldInitialValue::Class(Some(e)) => {
751760
// If this field was created via a call to a dataclass field specifier, extract field flags from the call.
@@ -919,7 +928,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
919928
)
920929
}
921930
ClassFieldInner::Simple {
922-
initialization: ClassFieldInitialization::Instance(_),
931+
initialization: ClassFieldInitialization::Instance(false),
923932
annotation,
924933
..
925934
} if annotation

pyrefly/lib/test/attributes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,14 +548,14 @@ class A:
548548
}
549549

550550
testcase!(
551-
test_stub_initializes_attr_with_ellipses,
551+
test_stub_initializes_attr,
552552
env_with_stub(),
553553
r#"
554554
from typing import assert_type
555555
from foo import A
556556
557557
assert_type(A.x, int)
558-
A.y # E: Instance-only attribute `y` of class `A` is not visible on the class
558+
assert_type(A.y, int)
559559
"#,
560560
);
561561

0 commit comments

Comments
 (0)