fallbacks

This commit is contained in:
Aria Desires 2025-12-16 12:30:55 -05:00
parent 8696a110ff
commit 6825e9e234
8 changed files with 65 additions and 34 deletions

View File

@ -36,7 +36,7 @@ class Point:
x: int
y: int
reveal_type(Point.__replace__) # revealed: (self: Point, *, x: int = int, y: int = int) -> Point
reveal_type(Point.__replace__) # revealed: (self: Point, *, x: int = ..., y: int = ...) -> Point
```
The `__replace__` method can either be called directly or through the `replace` function:

View File

@ -129,16 +129,16 @@ class C:
self.c = lambda positional_only=self.c, /: positional_only
self.d = lambda *, kw_only=self.d: kw_only
# revealed: (positional=Unknown | ((positional=Unknown) -> Unknown)) -> Unknown
# revealed: (positional=...) -> Unknown
reveal_type(self.a)
# revealed: (*, kw_only=Unknown | ((*, kw_only=Unknown) -> Unknown)) -> Unknown
# revealed: (*, kw_only=...) -> Unknown
reveal_type(self.b)
# revealed: (positional_only=Unknown | ((positional_only=Unknown, /) -> Unknown), /) -> Unknown
# revealed: (positional_only=..., /) -> Unknown
reveal_type(self.c)
# revealed: (*, kw_only=Unknown | ((*, kw_only=Unknown) -> Unknown)) -> Unknown
# revealed: (*, kw_only=...) -> Unknown
reveal_type(self.d)
```

View File

@ -749,8 +749,8 @@ class Outer:
outer_a: int = outer_field(init=False)
outer_b: str = inner_field(init=False)
reveal_type(Outer.__init__) # revealed: (self: Outer, outer_b: str = Any) -> None
reveal_type(Outer.Inner.__init__) # revealed: (self: Inner, inner_b: str = Any) -> None
reveal_type(Outer.__init__) # revealed: (self: Outer, outer_b: str = ...) -> None
reveal_type(Outer.Inner.__init__) # revealed: (self: Inner, inner_b: str = ...) -> None
```
## Overloaded dataclass-like decorators

View File

@ -1082,7 +1082,7 @@ class C(Base):
z: int = 10
x: int = 15
reveal_type(C.__init__) # revealed:(self: C, x: int = Literal[15], y: int = Literal[0], z: int = 10) -> None
reveal_type(C.__init__) # revealed:(self: C, x: int = 15, y: int = 0, z: int = 10) -> None
```
## Conditionally defined fields
@ -1243,7 +1243,7 @@ class UppercaseString:
class C:
upper: UppercaseString = UppercaseString()
reveal_type(C.__init__) # revealed: (self: C, upper: str = str) -> None
reveal_type(C.__init__) # revealed: (self: C, upper: str = ...) -> None
c = C("abc")
reveal_type(c.upper) # revealed: str
@ -1328,7 +1328,7 @@ class AcceptsStrAndInt:
class C:
field: AcceptsStrAndInt = AcceptsStrAndInt()
reveal_type(C.__init__) # revealed: (self: C, field: str | int = int) -> None
reveal_type(C.__init__) # revealed: (self: C, field: str | int = ...) -> None
```
## `dataclasses.field`

View File

@ -37,7 +37,7 @@ class Data:
content: list[int] = field(default_factory=list)
timestamp: datetime = field(default_factory=datetime.now, init=False)
# revealed: (self: Data, content: list[int] = list[int]) -> None
# revealed: (self: Data, content: list[int] = ...) -> None
reveal_type(Data.__init__)
data = Data([1, 2, 3])

View File

@ -591,9 +591,9 @@ try:
reveal_type(x) # revealed: B | D
reveal_type(x) # revealed: B | D
x = foo
reveal_type(x) # revealed: def foo(param=could_raise_returns_A()) -> Unknown
reveal_type(x) # revealed: def foo(param=...) -> Unknown
except:
reveal_type(x) # revealed: Literal[1] | (def foo(param=could_raise_returns_A()) -> Unknown)
reveal_type(x) # revealed: Literal[1] | (def foo(param=...) -> Unknown)
class Bar:
x = could_raise_returns_E()
@ -603,9 +603,9 @@ except:
reveal_type(x) # revealed: <class 'Bar'>
finally:
# TODO: should be `Literal[1] | <class 'foo'> | <class 'Bar'>`
reveal_type(x) # revealed: (def foo(param=could_raise_returns_A()) -> Unknown) | <class 'Bar'>
reveal_type(x) # revealed: (def foo(param=...) -> Unknown) | <class 'Bar'>
reveal_type(x) # revealed: (def foo(param=could_raise_returns_A()) -> Unknown) | <class 'Bar'>
reveal_type(x) # revealed: (def foo(param=...) -> Unknown) | <class 'Bar'>
```
[1]: https://astral-sh.notion.site/Exception-handler-control-flow-11348797e1ca80bb8ce1e9aedbbe439d

View File

@ -24,8 +24,8 @@ reveal_type(lambda a, b: a + b) # revealed: (a, b) -> Unknown
But, it can have default values:
```py
reveal_type(lambda a=1: a) # revealed: (a=Literal[1]) -> Unknown
reveal_type(lambda a, b=2: a) # revealed: (a, b=Literal[2]) -> Unknown
reveal_type(lambda a=1: a) # revealed: (a=1) -> Unknown
reveal_type(lambda a, b=2: a) # revealed: (a, b=2) -> Unknown
```
And, positional-only parameters:
@ -37,7 +37,7 @@ reveal_type(lambda a, b, /, c: c) # revealed: (a, b, /, c) -> Unknown
And, keyword-only parameters:
```py
reveal_type(lambda a, *, b=2, c: b) # revealed: (a, *, b=Literal[2], c) -> Unknown
reveal_type(lambda a, *, b=2, c: b) # revealed: (a, *, b=2, c) -> Unknown
```
And, variadic parameter:
@ -55,7 +55,7 @@ reveal_type(lambda **kwargs: kwargs) # revealed: (**kwargs) -> Unknown
Mixing all of them together:
```py
# revealed: (a, b, /, c=Literal[True], *args, *, d=Literal["default"], e=Literal[5], **kwargs) -> Unknown
# revealed: (a, b, /, c=True, *args, *, d="default", e=5, **kwargs) -> Unknown
reveal_type(lambda a, b, /, c=True, *args, d="default", e=5, **kwargs: None)
```
@ -94,7 +94,7 @@ Here, a `lambda` expression is used as the default value for a parameter in anot
expression.
```py
reveal_type(lambda a=lambda x, y: 0: 2) # revealed: (a=(x, y) -> Unknown) -> Unknown
reveal_type(lambda a=lambda x, y: 0: 2) # revealed: (a=...) -> Unknown
```
## Assignment

View File

@ -1844,16 +1844,47 @@ impl<'db> FmtDetailed<'db> for DisplayParameter<'_, 'db> {
} else {
f.write_str("=")?;
}
write!(f, "{default_value}")?;
// Omit long default values or ones that contain newlines
if default_value.len() > 12 || default_value.contains('\n') {
f.write_str("...")?;
} else {
f.write_str(default_value)?;
}
} else if let Some(default_type) = self.param.default_type() {
// Ideally we'd never end up in here, but complex situations like dataclasses
// can result in us computing the type Literal[1] but not having the default_value set
if self.param.annotated_type().is_some() {
f.write_str(" = ")?;
} else {
f.write_str("=")?;
}
default_type
.display_with(self.db, self.settings.clone())
.fmt_detailed(f)?;
match default_type {
Type::IntLiteral(_)
| Type::BooleanLiteral(_)
| Type::StringLiteral(_)
| Type::EnumLiteral(_)
| Type::BytesLiteral(_) => {
// For Literal types display the value without `Literal[..]` wrapping
let representation =
default_type.representation(self.db, self.settings.clone());
representation.fmt_detailed(f)?;
}
Type::NominalInstance(instance) => {
// Some key default types like `None` are worth showing
let class = instance.class(self.db);
match (class, class.known(self.db)) {
(_, Some(KnownClass::NoneType)) => {
f.with_type(default_type).write_str("None")?
}
(_, Some(KnownClass::NoDefaultType)) => {
f.with_type(default_type).write_str("NoDefault")?
}
_ => f.write_str("...")?,
}
}
_ => f.write_str("...")?,
}
}
} else if let Some(ty) = self.param.annotated_type() {
// This case is specifically for the `Callable` signature where name and default value
@ -2605,7 +2636,7 @@ mod tests {
],
Some(Type::none(&db))
),
@"(x=int, y: str = str) -> None"
@"(x=..., y: str = ...) -> None"
);
// All positional only parameters.
@ -2690,7 +2721,7 @@ mod tests {
],
Some(KnownClass::Bytes.to_instance(&db))
),
@"(a, b: int, c=Literal[1], d: int = Literal[2], /, e=Literal[3], f: int = Literal[4], *args: object, *, g=Literal[5], h: int = Literal[6], **kwargs: str) -> bytes"
@"(a, b: int, c=1, d: int = 2, /, e=3, f: int = 4, *args: object, *, g=5, h: int = 6, **kwargs: str) -> bytes"
);
}
@ -2732,8 +2763,8 @@ mod tests {
),
@r"
(
x=int,
y: str = str
x=...,
y: str = ...
) -> None
"
);
@ -2848,15 +2879,15 @@ mod tests {
(
a,
b: int,
c=Literal[1],
d: int = Literal[2],
c=1,
d: int = 2,
/,
e=Literal[3],
f: int = Literal[4],
e=3,
f: int = 4,
*args: object,
*,
g=Literal[5],
h: int = Literal[6],
g=5,
h: int = 6,
**kwargs: str
) -> bytes
"