Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions crates/ty_python_semantic/resources/mdtest/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,7 @@ class Foo(Protocol):
def method_member(self) -> bytes:
return b"foo"

# TODO: actually a frozenset (requires support for legacy generics)
reveal_type(get_protocol_members(Foo)) # revealed: tuple[Literal["method_member"], Literal["x"], Literal["y"], Literal["z"]]
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["method_member", "x", "y", "z"]]
```

Certain special attributes and methods are not considered protocol members at runtime, and should
Expand All @@ -394,8 +393,7 @@ class Lumberjack(Protocol):
def __init__(self, x: int) -> None:
self.x = x

# TODO: actually a frozenset
reveal_type(get_protocol_members(Lumberjack)) # revealed: tuple[Literal["x"]]
reveal_type(get_protocol_members(Lumberjack)) # revealed: frozenset[Literal["x"]]
```

A sub-protocol inherits and extends the members of its superclass protocol(s):
Expand All @@ -407,13 +405,11 @@ class Bar(Protocol):
class Baz(Bar, Protocol):
ham: memoryview

# TODO: actually a frozenset
reveal_type(get_protocol_members(Baz)) # revealed: tuple[Literal["ham"], Literal["spam"]]
reveal_type(get_protocol_members(Baz)) # revealed: frozenset[Literal["ham", "spam"]]

class Baz2(Bar, Foo, Protocol): ...

# TODO: actually a frozenset
# revealed: tuple[Literal["method_member"], Literal["spam"], Literal["x"], Literal["y"], Literal["z"]]
# revealed: frozenset[Literal["method_member", "spam", "x", "y", "z"]]
reveal_type(get_protocol_members(Baz2))
```

Expand Down Expand Up @@ -441,8 +437,7 @@ class Foo(Protocol):
e = 56
def f(self) -> None: ...

# TODO: actually a frozenset
reveal_type(get_protocol_members(Foo)) # revealed: tuple[Literal["d"], Literal["e"], Literal["f"]]
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["d", "e", "f"]]
```

## Invalid calls to `get_protocol_members()`
Expand Down Expand Up @@ -673,8 +668,7 @@ class LotsOfBindings(Protocol):
case l: # TODO: this should error with `[invalid-protocol]` (`l` is not declared)
...

# TODO: actually a frozenset
# revealed: tuple[Literal["Nested"], Literal["NestedProtocol"], Literal["a"], Literal["b"], Literal["c"], Literal["d"], Literal["e"], Literal["f"], Literal["g"], Literal["h"], Literal["i"], Literal["j"], Literal["k"], Literal["l"]]
# revealed: frozenset[Literal["Nested", "NestedProtocol", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]]
reveal_type(get_protocol_members(LotsOfBindings))
```

Expand Down Expand Up @@ -702,9 +696,7 @@ class Foo(Protocol):

# Note: the list of members does not include `a`, `b` or `c`,
# as none of these attributes is declared in the class body.
#
# TODO: actually a frozenset
reveal_type(get_protocol_members(Foo)) # revealed: tuple[Literal["non_init_method"], Literal["x"], Literal["y"]]
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["non_init_method", "x", "y"]]
```

If a member is declared in a superclass of a protocol class, it is fine for it to be assigned to in
Expand All @@ -717,9 +709,8 @@ class Super(Protocol):
class Sub(Super, Protocol):
x = 42 # no error here, since it's declared in the superclass

# TODO: actually frozensets
reveal_type(get_protocol_members(Super)) # revealed: tuple[Literal["x"]]
reveal_type(get_protocol_members(Sub)) # revealed: tuple[Literal["x"]]
reveal_type(get_protocol_members(Super)) # revealed: frozenset[Literal["x"]]
reveal_type(get_protocol_members(Sub)) # revealed: frozenset[Literal["x"]]
```

If a protocol has 0 members, then all other types are assignable to it, and all fully static types
Expand Down
18 changes: 9 additions & 9 deletions crates/ty_python_semantic/src/types/call/bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,15 +667,15 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::GetProtocolMembers) => {
if let [Some(Type::ClassLiteral(class))] = overload.parameter_types() {
if let Some(protocol_class) = class.into_protocol_class(db) {
// TODO: actually a frozenset at runtime (requires support for legacy generic classes)
overload.set_return_type(Type::Tuple(TupleType::new(
db,
protocol_class
.interface(db)
.members(db)
.map(|member| Type::string_literal(db, member.name()))
.collect::<Box<[Type<'db>]>>(),
)));
let member_names = protocol_class
.interface(db)
.members(db)
.map(|member| Type::string_literal(db, member.name()));
let specialization = UnionType::from_elements(db, member_names);
overload.set_return_type(
KnownClass::FrozenSet
.to_specialized_instance(db, [specialization]),
);
}
}
}
Expand Down
Loading