Skip to content

Commit a45f96d

Browse files
authored
[ty] stop special-casing str constructor (#24514)
1 parent 87a0f01 commit a45f96d

6 files changed

Lines changed: 32 additions & 71 deletions

File tree

crates/ty_python_semantic/resources/mdtest/call/builtins.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,36 @@ str(b"M\x00\xfc\x00s\x00l\x00i\x00", encoding="utf-16", errors="ignore")
3232

3333
str(bytearray.fromhex("4d c3 bc 73 6c 69"), "utf-8")
3434
str(bytearray(), "utf-8")
35+
str(memoryview(b"hello world"), "utf-8")
3536

3637
str(encoding="utf-8", object=b"M\xc3\xbcsli")
3738
str(b"", errors="replace")
38-
str(encoding="utf-8")
39-
str(errors="replace")
4039
```
4140

4241
### Invalid calls
4342

4443
```py
45-
# error: [invalid-argument-type] "Argument to class `str` is incorrect: Expected `bytes | bytearray`, found `Literal[1]`"
46-
# error: [invalid-argument-type] "Argument to class `str` is incorrect: Expected `str`, found `Literal[2]`"
44+
# These are valid at runtime, but the typeshed signature for `str.__new__` requires `object`
45+
# when `encoding` or `errors` are provided.
46+
# error: [no-matching-overload]
47+
str(encoding="utf-8")
48+
49+
# error: [no-matching-overload]
50+
str(errors="replace")
51+
52+
# error: [invalid-argument-type]
53+
# error: [invalid-argument-type]
4754
str(1, 2)
4855

4956
# error: [no-matching-overload]
5057
str(o=1)
5158

5259
# First argument is not a bytes-like object:
53-
# error: [invalid-argument-type] "Argument to class `str` is incorrect: Expected `bytes | bytearray`, found `Literal["Müsli"]`"
60+
# error: [invalid-argument-type]
5461
str("Müsli", "utf-8")
5562

5663
# Second argument is not a valid encoding:
57-
# error: [invalid-argument-type] "Argument to class `str` is incorrect: Expected `str`, found `Literal[b"utf-8"]`"
64+
# error: [invalid-argument-type]
5865
str(b"M\xc3\xbcsli", b"utf-8")
5966
```
6067

crates/ty_python_semantic/resources/mdtest/call/union.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,21 @@ def _(flag: bool):
212212

213213
## Union including a special-cased function
214214

215+
```toml
216+
[environment]
217+
python-version = "3.12"
218+
```
219+
215220
```py
221+
def identity[T](x: T) -> T:
222+
return x
223+
216224
def _(flag: bool):
217225
if flag:
218-
f = str
226+
f = identity
219227
else:
220228
f = repr
221-
reveal_type(str("string")) # revealed: Literal["string"]
229+
reveal_type(identity("string")) # revealed: Literal["string"]
222230
reveal_type(repr("string")) # revealed: Literal["'string'"]
223231
reveal_type(f("string")) # revealed: Literal["string", "'string'"]
224232
```

crates/ty_python_semantic/resources/mdtest/class/super.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ def f(x: C | D):
464464
s.b
465465

466466
def f(flag: bool):
467-
x = str() if flag else str("hello")
467+
x = "" if flag else "hello"
468468
reveal_type(x) # revealed: Literal["", "hello"]
469469
reveal_type(super(str, x)) # revealed: <super: <class 'str'>, str>
470470

crates/ty_python_semantic/resources/mdtest/type_properties/str_repr.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ def _(
1717
f: LiteralString,
1818
g: int,
1919
):
20-
reveal_type(str(a)) # revealed: Literal["1"]
21-
reveal_type(str(b)) # revealed: Literal["True"]
22-
reveal_type(str(c)) # revealed: Literal["False"]
23-
reveal_type(str(d)) # revealed: Literal["ab'cd"]
24-
reveal_type(str(e)) # revealed: Literal["Answer.YES"]
25-
reveal_type(str(f)) # revealed: LiteralString
20+
reveal_type(str(a)) # revealed: str
21+
reveal_type(str(b)) # revealed: str
22+
reveal_type(str(c)) # revealed: str
23+
reveal_type(str(d)) # revealed: str
24+
reveal_type(str(e)) # revealed: str
25+
reveal_type(str(f)) # revealed: str
2626
reveal_type(str(g)) # revealed: str
2727

2828
reveal_type(repr(a)) # revealed: Literal["1"]

crates/ty_python_semantic/src/types.rs

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4147,61 +4147,6 @@ impl<'db> Type<'db> {
41474147
)
41484148
}
41494149

4150-
KnownClass::Str => {
4151-
// ```py
4152-
// class str(Sequence[str]):
4153-
// @overload
4154-
// def __new__(cls, object: object = ...) -> Self: ...
4155-
// @overload
4156-
// def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
4157-
// ```
4158-
Some(
4159-
CallableBinding::from_overloads(
4160-
self,
4161-
[
4162-
Signature::new(
4163-
Parameters::new(
4164-
db,
4165-
[Parameter::positional_or_keyword(Name::new_static("object"))
4166-
.with_annotated_type(Type::object())
4167-
.with_default_type(Type::string_literal(db, ""))],
4168-
),
4169-
KnownClass::Str.to_instance(db),
4170-
),
4171-
Signature::new(
4172-
Parameters::new(
4173-
db,
4174-
[
4175-
Parameter::positional_or_keyword(Name::new_static(
4176-
"object",
4177-
))
4178-
// TODO: Should be `ReadableBuffer` instead of this union type:
4179-
.with_annotated_type(UnionType::from_two_elements(
4180-
db,
4181-
KnownClass::Bytes.to_instance(db),
4182-
KnownClass::Bytearray.to_instance(db),
4183-
))
4184-
.with_default_type(Type::bytes_literal(db, b"")),
4185-
Parameter::positional_or_keyword(Name::new_static(
4186-
"encoding",
4187-
))
4188-
.with_annotated_type(KnownClass::Str.to_instance(db))
4189-
.with_default_type(Type::string_literal(db, "utf-8")),
4190-
Parameter::positional_or_keyword(Name::new_static(
4191-
"errors",
4192-
))
4193-
.with_annotated_type(KnownClass::Str.to_instance(db))
4194-
.with_default_type(Type::string_literal(db, "strict")),
4195-
],
4196-
),
4197-
KnownClass::Str.to_instance(db),
4198-
),
4199-
],
4200-
)
4201-
.into(),
4202-
)
4203-
}
4204-
42054150
KnownClass::Object => {
42064151
// ```py
42074152
// class object:
@@ -4542,7 +4487,6 @@ impl<'db> Type<'db> {
45424487
known,
45434488
Some(
45444489
KnownClass::Bool
4545-
| KnownClass::Str
45464490
| KnownClass::Type
45474491
| KnownClass::Object
45484492
| KnownClass::Property

crates/ty_python_semantic/src/types/call/bind/constructor.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ impl<'db> ConstructorBinding<'db> {
351351
if self.constructor_kind().is_init() || self.constructed_class_literal(db).is_none() {
352352
return None;
353353
}
354+
354355
let matching_overloads = self
355356
.callable()
356357
.matching_overloads()
@@ -501,6 +502,7 @@ impl<'db> ConstructorBinding<'db> {
501502
fn constructed_class_literal(&self, db: &'db dyn Db) -> Option<ClassLiteral<'db>> {
502503
self.constructed_instance_type()
503504
.as_nominal_instance()
505+
// TODO may need to handle `Type::KnownInstance` here as well?
504506
.map(|instance| instance.class(db).class_literal(db))
505507
}
506508

0 commit comments

Comments
 (0)