Skip to content

Commit 03a5788

Browse files
authored
[red-knot] a few metaclass cleanups (#14142)
Just cleaning up a few small things I noticed in post-land review.
1 parent 626f716 commit 03a5788

File tree

2 files changed

+18
-11
lines changed

2 files changed

+18
-11
lines changed

crates/red_knot_python_semantic/resources/mdtest/metaclass.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ reveal_type(type.__class__) # revealed: Literal[type]
2121
## Basic
2222

2323
```py
24-
class A(type): ...
25-
class B(metaclass=A): ...
24+
class M(type): ...
25+
class B(metaclass=M): ...
2626

27-
reveal_type(B.__class__) # revealed: Literal[A]
27+
reveal_type(B.__class__) # revealed: Literal[M]
2828
```
2929

3030
## Invalid metaclass
3131

32-
If a class is a subclass of a class with a custom metaclass, then the subclass will also have that
33-
metaclass.
32+
A class which doesn't inherit `type` (and/or doesn't implement a custom `__new__` accepting the same
33+
arguments as `type.__new__`) isn't a valid metaclass.
3434

3535
```py
3636
class M: ...
@@ -139,13 +139,14 @@ from nonexistent_module import UnknownClass # error: [unresolved-import]
139139

140140
class C(UnknownClass): ...
141141

142+
# TODO: should be `type[type] & Unknown`
142143
reveal_type(C.__class__) # revealed: Literal[type]
143144

144145
class M(type): ...
145146
class A(metaclass=M): ...
146147
class B(A, UnknownClass): ...
147148

148-
# TODO: This should resolve to `type[M] | Unknown` instead
149+
# TODO: should be `type[M] & Unknown`
149150
reveal_type(B.__class__) # revealed: Literal[M]
150151
```
151152

@@ -161,12 +162,16 @@ reveal_type(B.__class__) # revealed: Literal[M]
161162

162163
## Non-class
163164

164-
When a class has an explicit `metaclass` that is not a class, the value should be returned as is.
165+
When a class has an explicit `metaclass` that is not a class, but is a callable that accepts
166+
`type.__new__` arguments, we should return the meta type of its return type.
165167

166168
```py
167-
class A(metaclass=1): ...
169+
def f(*args, **kwargs) -> int: ...
170+
171+
class A(metaclass=f): ...
168172

169-
reveal_type(A.__class__) # revealed: Literal[1]
173+
# TODO should be `type[int]`
174+
reveal_type(A.__class__) # revealed: @Todo
170175
```
171176

172177
## Cyclic

crates/red_knot_python_semantic/src/types.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,8 +2100,10 @@ impl<'db> Class<'db> {
21002100
};
21012101

21022102
let Type::ClassLiteral(mut candidate) = metaclass else {
2103-
// If the metaclass is not a class, return it directly.
2104-
return Ok(metaclass);
2103+
// TODO: If the metaclass is not a class, we should verify that it's a callable
2104+
// which accepts the same arguments as `type.__new__` (otherwise error), and return
2105+
// the meta-type of its return type. (And validate that is a class type?)
2106+
return Ok(Type::Todo);
21052107
};
21062108

21072109
// Reconcile all base classes' metaclasses with the candidate metaclass.

0 commit comments

Comments
 (0)