Skip to content

Commit ee0ce1c

Browse files
rchen152stroxler
authored andcommitted
Support iterating over a class object
Reviewed By: yangdanny97 Differential Revision: D67615541 fbshipit-source-id: f227cefb10b03883c48bc6a1c1e5fdc270dcdd5e
1 parent 9b67a3e commit ee0ce1c

File tree

8 files changed

+64
-26
lines changed

8 files changed

+64
-26
lines changed

pyre2/conformance/third_party/conformance.exp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8326,26 +8326,6 @@
83268326
}
83278327
],
83288328
"enums_behaviors.py": [
8329-
{
8330-
"code": -2,
8331-
"column": 14,
8332-
"concise_description": "TODO: Answers::solve_binding - Binding::IterableValue",
8333-
"description": "TODO: Answers::solve_binding - Binding::IterableValue",
8334-
"line": 19,
8335-
"name": "PyreError",
8336-
"stop_column": 19,
8337-
"stop_line": 19
8338-
},
8339-
{
8340-
"code": -2,
8341-
"column": 5,
8342-
"concise_description": "assert_type(Any, Color) failed",
8343-
"description": "assert_type(Any, Color) failed",
8344-
"line": 20,
8345-
"name": "PyreError",
8346-
"stop_column": 30,
8347-
"stop_line": 20
8348-
},
83498329
{
83508330
"code": -2,
83518331
"column": 1,

pyre2/conformance/third_party/conformance.result

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,8 +603,6 @@
603603
"directives_version_platform.py": [],
604604
"enums_behaviors.py": [
605605
"Line 39: Expected 1 errors",
606-
"Line 19: Unexpected errors ['TODO: Answers::solve_binding - Binding::IterableValue']",
607-
"Line 20: Unexpected errors ['assert_type(Any, Color) failed']",
608606
"Line 26: Unexpected errors ['assert_type(type[Color], Color) failed', 'Expected 0 type arguments for class `Color`, got 1.', \"untype, got Literal['RED']\"]",
609607
"Line 27: Unexpected errors ['assert_type(Enum, Color) failed']"
610608
],

pyre2/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": 8,
44
"fail": 125,
55
"pass_rate": 0.06,
6-
"differences": 1440,
6+
"differences": 1438,
77
"passing": [
88
"annotations_coroutines.py",
99
"directives_no_type_check.py",
@@ -59,7 +59,7 @@
5959
"directives_reveal_type.py": 4,
6060
"directives_type_checking.py": 3,
6161
"directives_type_ignore_file1.py": 1,
62-
"enums_behaviors.py": 5,
62+
"enums_behaviors.py": 3,
6363
"enums_definition.py": 1,
6464
"enums_expansion.py": 5,
6565
"enums_member_values.py": 2,

pyre2/pyre2/bin/alt/answers.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ use crate::types::annotation::Qualifier;
6666
use crate::types::callable::Param;
6767
use crate::types::callable::Required;
6868
use crate::types::class::Class;
69+
use crate::types::class::ClassType;
6970
use crate::types::class_metadata::ClassMetadata;
7071
use crate::types::literal::Lit;
7172
use crate::types::module::Module;
@@ -671,6 +672,19 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
671672
}
672673
}
673674

675+
fn iterate_by_metaclass(&self, cls: &ClassType, range: TextRange) -> Iterable {
676+
let call_method =
677+
|method_name: &Name, range: TextRange, args: &[CallArg], keywords: &[Keyword]| {
678+
self.call_metaclass_method(cls, method_name, range, args, keywords)
679+
};
680+
let ty = self
681+
.iterate_by_method(&call_method, range)
682+
.unwrap_or_else(|| {
683+
self.error(range, format!("Class object `{}` is not iterable", cls))
684+
});
685+
Iterable::OfType(ty)
686+
}
687+
674688
fn iterate(&self, iterable: &Type, range: TextRange) -> Iterable {
675689
match iterable {
676690
Type::ClassType(cls) => {
@@ -695,6 +709,10 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
695709
Type::Var(v) if let Some(_guard) = self.recurser.recurse(*v) => {
696710
self.iterate(&self.solver().force_var(*v), range)
697711
}
712+
Type::ClassDef(cls) => {
713+
self.iterate_by_metaclass(&self.promote_to_class_type(cls, range), range)
714+
}
715+
Type::Type(box Type::ClassType(cls)) => self.iterate_by_metaclass(cls, range),
698716
_ => Iterable::OfType(
699717
self.error_todo("Answers::solve_binding - Binding::IterableValue", range),
700718
),

pyre2/pyre2/bin/alt/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
267267

268268
/// Calls a method on the metaclass of `cls`. Returns None without attempting a call if we
269269
/// don't find a method defined on a metaclass that isn't the default `builtins.type`.
270-
fn call_metaclass_method(
270+
pub fn call_metaclass_method(
271271
&self,
272272
cls: &ClassType,
273273
method_name: &Name,

pyre2/pyre2/bin/test/enums.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,16 @@ class MyEnum(Enum):
2020
assert_type(MyEnum.X, Literal[MyEnum.X])
2121
"#,
2222
);
23+
24+
testcase!(
25+
test_iterate,
26+
r#"
27+
from typing import assert_type
28+
from enum import Enum
29+
class E(Enum):
30+
X = 1
31+
Y = 2
32+
for e in E:
33+
assert_type(e, E)
34+
"#,
35+
);

pyre2/pyre2/bin/test/simple.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,33 @@ def f(x: A):
747747
"#,
748748
);
749749

750+
testcase!(
751+
test_iterable_class_error,
752+
r#"
753+
class A:
754+
pass
755+
for _ in A: # E: Class object `A` is not iterable
756+
pass
757+
"#,
758+
);
759+
760+
testcase!(
761+
test_iterable_generic_class,
762+
r#"
763+
from typing import Iterator, assert_type
764+
class M(type):
765+
def __iter__(self) -> Iterator[int]: ...
766+
class A[T](metaclass=M):
767+
pass
768+
class B[T]:
769+
pass
770+
for x in A[str]:
771+
assert_type(x, int)
772+
for _ in B[str]: # E: Class object `B[str]` is not iterable
773+
pass
774+
"#,
775+
);
776+
750777
testcase!(
751778
test_iterable_bad_callable,
752779
r#"

pyre2/pyre2/bin/test/stdlib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
use crate::module::module_name::ModuleName;
1212

1313
static ENUM: &str = r#"
14-
class Enum: ...
14+
class EnumMeta(type):
15+
def __iter__[EnumMemberT](self: type[EnumMemberT]) -> Iterator[EnumMemberT]: ...
16+
class Enum(metaclass=EnumMeta): ...
1517
class StrEnum(str, Enum): ...
1618
class IntEnum(int, Enum): ...
1719
"#;

0 commit comments

Comments
 (0)