Skip to content

Commit 1254b5f

Browse files
committed
[ty] Todo-types for os.fdopen, NamedTemporaryFile, and Path.open
1 parent e54903f commit 1254b5f

File tree

8 files changed

+246
-100
lines changed

8 files changed

+246
-100
lines changed

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

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -162,22 +162,3 @@ def _(x: A | B, y: list[int]):
162162
reveal_type(x) # revealed: B & ~A
163163
reveal_type(isinstance(x, B)) # revealed: Literal[True]
164164
```
165-
166-
## Calls to `open()`
167-
168-
We do not fully understand typeshed's overloads for `open()` yet, due to missing support for PEP-613
169-
type aliases. However, we also do not emit false-positive diagnostics on common calls to `open()`:
170-
171-
```py
172-
import pickle
173-
174-
reveal_type(open("")) # revealed: TextIOWrapper[_WrappedBuffer]
175-
reveal_type(open("", "r")) # revealed: TextIOWrapper[_WrappedBuffer]
176-
reveal_type(open("", "rb")) # revealed: @Todo(`builtins.open` return type)
177-
178-
with open("foo.pickle", "rb") as f:
179-
x = pickle.load(f) # fine
180-
181-
def _(mode: str):
182-
reveal_type(open("", mode)) # revealed: @Todo(`builtins.open` return type)
183-
```
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Calls to `open()`
2+
3+
## `builtins.open`
4+
5+
We do not fully understand typeshed's overloads for `open()` yet, due to missing support for PEP-613
6+
type aliases. However, we also do not emit false-positive diagnostics on common calls to `open()`:
7+
8+
```py
9+
import pickle
10+
11+
reveal_type(open("")) # revealed: TextIOWrapper[_WrappedBuffer]
12+
reveal_type(open("", "r")) # revealed: TextIOWrapper[_WrappedBuffer]
13+
reveal_type(open("", "rb")) # revealed: @Todo(`builtins.open` return type)
14+
15+
with open("foo.pickle", "rb") as f:
16+
x = pickle.load(f) # fine
17+
18+
def _(mode: str):
19+
reveal_type(open("", mode)) # revealed: @Todo(`builtins.open` return type)
20+
```
21+
22+
## `os.fdopen`
23+
24+
The same is true for `os.fdopen()`:
25+
26+
```py
27+
import pickle
28+
import os
29+
30+
reveal_type(os.fdopen(0)) # revealed: TextIOWrapper[_WrappedBuffer]
31+
reveal_type(os.fdopen(0, "r")) # revealed: TextIOWrapper[_WrappedBuffer]
32+
reveal_type(os.fdopen(0, "rb")) # revealed: @Todo(`os.fdopen` return type)
33+
34+
with os.fdopen(0, "rb") as f:
35+
x = pickle.load(f) # fine
36+
```
37+
38+
## `Path.open`
39+
40+
And similarly for `Path.open()`:
41+
42+
```py
43+
from pathlib import Path
44+
import pickle
45+
46+
reveal_type(Path("").open()) # revealed: @Todo(`Path.open` return type)
47+
reveal_type(Path("").open("r")) # revealed: @Todo(`Path.open` return type)
48+
reveal_type(Path("").open("rb")) # revealed: @Todo(`Path.open` return type)
49+
50+
with Path("foo.pickle").open("rb") as f:
51+
x = pickle.load(f) # fine
52+
```
53+
54+
## `NamedTemporaryFile`
55+
56+
And similarly for `tempfile.NamedTemporaryFile()`:
57+
58+
```py
59+
from tempfile import NamedTemporaryFile
60+
import pickle
61+
62+
reveal_type(NamedTemporaryFile()) # revealed: _TemporaryFileWrapper[bytes]
63+
reveal_type(NamedTemporaryFile("r")) # revealed: _TemporaryFileWrapper[str]
64+
reveal_type(NamedTemporaryFile("rb")) # revealed: @Todo(`tempfile.NamedTemporaryFile` return type)
65+
66+
with NamedTemporaryFile("rb") as f:
67+
x = pickle.load(f) # fine
68+
```

crates/ty_python_semantic/src/module_resolver/module.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ pub enum KnownModule {
318318
TypingExtensions,
319319
Typing,
320320
Sys,
321+
Os,
322+
Tempfile,
323+
Pathlib,
321324
Abc,
322325
Dataclasses,
323326
Collections,
@@ -347,6 +350,9 @@ impl KnownModule {
347350
Self::Typeshed => "_typeshed",
348351
Self::TypingExtensions => "typing_extensions",
349352
Self::Sys => "sys",
353+
Self::Os => "os",
354+
Self::Tempfile => "tempfile",
355+
Self::Pathlib => "pathlib",
350356
Self::Abc => "abc",
351357
Self::Dataclasses => "dataclasses",
352358
Self::Collections => "collections",

crates/ty_python_semantic/src/types.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ mod infer;
9494
mod instance;
9595
mod mro;
9696
mod narrow;
97+
mod open_calls;
9798
mod protocol_class;
9899
mod signatures;
99100
mod special_form;
@@ -3446,6 +3447,11 @@ impl<'db> Type<'db> {
34463447
Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(literal)),
34473448
)
34483449
.into(),
3450+
Type::NominalInstance(instance)
3451+
if instance.has_known_class(db, KnownClass::Path) && name == "open" =>
3452+
{
3453+
Place::bound(Type::KnownBoundMethod(KnownBoundMethodType::PathOpen)).into()
3454+
}
34493455

34503456
Type::ClassLiteral(class)
34513457
if name == "__get__" && class.is_known(db, KnownClass::FunctionType) =>
@@ -6171,7 +6177,7 @@ impl<'db> Type<'db> {
61716177
| Type::AlwaysTruthy
61726178
| Type::AlwaysFalsy
61736179
| Type::WrapperDescriptor(_)
6174-
| Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(_))
6180+
| Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(_) | KnownBoundMethodType::PathOpen)
61756181
| Type::DataclassDecorator(_)
61766182
| Type::DataclassTransformer(_)
61776183
// A non-generic class never needs to be specialized. A generic class is specialized
@@ -6316,7 +6322,9 @@ impl<'db> Type<'db> {
63166322
| Type::AlwaysTruthy
63176323
| Type::AlwaysFalsy
63186324
| Type::WrapperDescriptor(_)
6319-
| Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(_))
6325+
| Type::KnownBoundMethod(
6326+
KnownBoundMethodType::StrStartswith(_) | KnownBoundMethodType::PathOpen,
6327+
)
63206328
| Type::DataclassDecorator(_)
63216329
| Type::DataclassTransformer(_)
63226330
| Type::ModuleLiteral(_)
@@ -9397,6 +9405,8 @@ pub enum KnownBoundMethodType<'db> {
93979405
/// this allows us to understand statically known branches for common tests such as
93989406
/// `if sys.platform.startswith("freebsd")`.
93999407
StrStartswith(StringLiteralType<'db>),
9408+
/// Method wrapper for `Path.open`,
9409+
PathOpen,
94009410
}
94019411

94029412
pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
@@ -9420,6 +9430,7 @@ pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Size
94209430
KnownBoundMethodType::StrStartswith(string_literal) => {
94219431
visitor.visit_type(db, Type::StringLiteral(string_literal));
94229432
}
9433+
KnownBoundMethodType::PathOpen => {}
94239434
}
94249435
}
94259436

@@ -9455,17 +9466,23 @@ impl<'db> KnownBoundMethodType<'db> {
94559466
ConstraintSet::from(self == other)
94569467
}
94579468

9469+
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen) => {
9470+
ConstraintSet::from(true)
9471+
}
9472+
94589473
(
94599474
KnownBoundMethodType::FunctionTypeDunderGet(_)
94609475
| KnownBoundMethodType::FunctionTypeDunderCall(_)
94619476
| KnownBoundMethodType::PropertyDunderGet(_)
94629477
| KnownBoundMethodType::PropertyDunderSet(_)
9463-
| KnownBoundMethodType::StrStartswith(_),
9478+
| KnownBoundMethodType::StrStartswith(_)
9479+
| KnownBoundMethodType::PathOpen,
94649480
KnownBoundMethodType::FunctionTypeDunderGet(_)
94659481
| KnownBoundMethodType::FunctionTypeDunderCall(_)
94669482
| KnownBoundMethodType::PropertyDunderGet(_)
94679483
| KnownBoundMethodType::PropertyDunderSet(_)
9468-
| KnownBoundMethodType::StrStartswith(_),
9484+
| KnownBoundMethodType::StrStartswith(_)
9485+
| KnownBoundMethodType::PathOpen,
94699486
) => ConstraintSet::from(false),
94709487
}
94719488
}
@@ -9500,17 +9517,23 @@ impl<'db> KnownBoundMethodType<'db> {
95009517
ConstraintSet::from(self == other)
95019518
}
95029519

9520+
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen) => {
9521+
ConstraintSet::from(true)
9522+
}
9523+
95039524
(
95049525
KnownBoundMethodType::FunctionTypeDunderGet(_)
95059526
| KnownBoundMethodType::FunctionTypeDunderCall(_)
95069527
| KnownBoundMethodType::PropertyDunderGet(_)
95079528
| KnownBoundMethodType::PropertyDunderSet(_)
9508-
| KnownBoundMethodType::StrStartswith(_),
9529+
| KnownBoundMethodType::StrStartswith(_)
9530+
| KnownBoundMethodType::PathOpen,
95099531
KnownBoundMethodType::FunctionTypeDunderGet(_)
95109532
| KnownBoundMethodType::FunctionTypeDunderCall(_)
95119533
| KnownBoundMethodType::PropertyDunderGet(_)
95129534
| KnownBoundMethodType::PropertyDunderSet(_)
9513-
| KnownBoundMethodType::StrStartswith(_),
9535+
| KnownBoundMethodType::StrStartswith(_)
9536+
| KnownBoundMethodType::PathOpen,
95149537
) => ConstraintSet::from(false),
95159538
}
95169539
}
@@ -9529,7 +9552,7 @@ impl<'db> KnownBoundMethodType<'db> {
95299552
KnownBoundMethodType::PropertyDunderSet(property) => {
95309553
KnownBoundMethodType::PropertyDunderSet(property.normalized_impl(db, visitor))
95319554
}
9532-
KnownBoundMethodType::StrStartswith(_) => self,
9555+
KnownBoundMethodType::StrStartswith(_) | KnownBoundMethodType::PathOpen => self,
95339556
}
95349557
}
95359558

@@ -9541,6 +9564,7 @@ impl<'db> KnownBoundMethodType<'db> {
95419564
| KnownBoundMethodType::PropertyDunderGet(_)
95429565
| KnownBoundMethodType::PropertyDunderSet(_) => KnownClass::MethodWrapperType,
95439566
KnownBoundMethodType::StrStartswith(_) => KnownClass::BuiltinFunctionType,
9567+
KnownBoundMethodType::PathOpen => KnownClass::Path,
95449568
}
95459569
}
95469570

@@ -9637,6 +9661,9 @@ impl<'db> KnownBoundMethodType<'db> {
96379661
Some(KnownClass::Bool.to_instance(db)),
96389662
)))
96399663
}
9664+
KnownBoundMethodType::PathOpen => {
9665+
Either::Right(std::iter::once(Signature::todo("`Path.open` return type")))
9666+
}
96409667
}
96419668
}
96429669
}

crates/ty_python_semantic/src/types/class.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3740,6 +3740,8 @@ pub enum KnownClass {
37403740
TypedDictFallback,
37413741
// string.templatelib
37423742
Template,
3743+
// pathlib
3744+
Path,
37433745
// ty_extensions
37443746
ConstraintSet,
37453747
}
@@ -3786,7 +3788,8 @@ impl KnownClass {
37863788
| Self::MethodWrapperType
37873789
| Self::CoroutineType
37883790
| Self::BuiltinFunctionType
3789-
| Self::Template => Some(Truthiness::AlwaysTrue),
3791+
| Self::Template
3792+
| Self::Path => Some(Truthiness::AlwaysTrue),
37903793

37913794
Self::NoneType => Some(Truthiness::AlwaysFalse),
37923795

@@ -3928,7 +3931,8 @@ impl KnownClass {
39283931
| KnownClass::TypedDictFallback
39293932
| KnownClass::BuiltinFunctionType
39303933
| KnownClass::ProtocolMeta
3931-
| KnownClass::Template => false,
3934+
| KnownClass::Template
3935+
| KnownClass::Path => false,
39323936
}
39333937
}
39343938

@@ -4009,7 +4013,8 @@ impl KnownClass {
40094013
| KnownClass::TypedDictFallback
40104014
| KnownClass::BuiltinFunctionType
40114015
| KnownClass::ProtocolMeta
4012-
| KnownClass::Template => false,
4016+
| KnownClass::Template
4017+
| KnownClass::Path => false,
40134018
}
40144019
}
40154020

@@ -4089,7 +4094,8 @@ impl KnownClass {
40894094
| KnownClass::ConstraintSet
40904095
| KnownClass::BuiltinFunctionType
40914096
| KnownClass::ProtocolMeta
4092-
| KnownClass::Template => false,
4097+
| KnownClass::Template
4098+
| KnownClass::Path => false,
40934099
}
40944100
}
40954101

@@ -4182,7 +4188,8 @@ impl KnownClass {
41824188
| Self::TypedDictFallback
41834189
| Self::BuiltinFunctionType
41844190
| Self::ProtocolMeta
4185-
| Self::Template => false,
4191+
| Self::Template
4192+
| KnownClass::Path => false,
41864193
}
41874194
}
41884195

@@ -4264,6 +4271,7 @@ impl KnownClass {
42644271
| KnownClass::KwOnly
42654272
| KnownClass::NamedTupleLike
42664273
| KnownClass::Template
4274+
| KnownClass::Path
42674275
| KnownClass::ConstraintSet
42684276
| KnownClass::InitVar => false,
42694277
KnownClass::NamedTupleFallback | KnownClass::TypedDictFallback => true,
@@ -4366,6 +4374,7 @@ impl KnownClass {
43664374
Self::ConstraintSet => "ConstraintSet",
43674375
Self::TypedDictFallback => "TypedDictFallback",
43684376
Self::Template => "Template",
4377+
Self::Path => "Path",
43694378
Self::ProtocolMeta => "_ProtocolMeta",
43704379
}
43714380
}
@@ -4637,6 +4646,7 @@ impl KnownClass {
46374646
Self::NamedTupleFallback | Self::TypedDictFallback => KnownModule::TypeCheckerInternals,
46384647
Self::NamedTupleLike | Self::ConstraintSet => KnownModule::TyExtensions,
46394648
Self::Template => KnownModule::Templatelib,
4649+
Self::Path => KnownModule::Pathlib,
46404650
}
46414651
}
46424652

@@ -4719,7 +4729,8 @@ impl KnownClass {
47194729
| Self::TypedDictFallback
47204730
| Self::BuiltinFunctionType
47214731
| Self::ProtocolMeta
4722-
| Self::Template => Some(false),
4732+
| Self::Template
4733+
| Self::Path => Some(false),
47234734

47244735
Self::Tuple => None,
47254736
}
@@ -4805,7 +4816,8 @@ impl KnownClass {
48054816
| Self::TypedDictFallback
48064817
| Self::BuiltinFunctionType
48074818
| Self::ProtocolMeta
4808-
| Self::Template => false,
4819+
| Self::Template
4820+
| Self::Path => false,
48094821
}
48104822
}
48114823

@@ -4901,6 +4913,7 @@ impl KnownClass {
49014913
"ConstraintSet" => Self::ConstraintSet,
49024914
"TypedDictFallback" => Self::TypedDictFallback,
49034915
"Template" => Self::Template,
4916+
"Path" => Self::Path,
49044917
"_ProtocolMeta" => Self::ProtocolMeta,
49054918
_ => return None,
49064919
};
@@ -4972,7 +4985,8 @@ impl KnownClass {
49724985
| Self::ConstraintSet
49734986
| Self::Awaitable
49744987
| Self::Generator
4975-
| Self::Template => module == self.canonical_module(db),
4988+
| Self::Template
4989+
| Self::Path => module == self.canonical_module(db),
49764990
Self::NoneType => matches!(module, KnownModule::Typeshed | KnownModule::Types),
49774991
Self::SpecialForm
49784992
| Self::TypeVar

crates/ty_python_semantic/src/types/display.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ impl Display for DisplayRepresentation<'_> {
387387
Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(_)) => {
388388
f.write_str("<method-wrapper `startswith` of `str` object>")
389389
}
390+
Type::KnownBoundMethod(KnownBoundMethodType::PathOpen) => {
391+
f.write_str("<method-wrapper `open` of `Path` object>")
392+
}
390393
Type::WrapperDescriptor(kind) => {
391394
let (method, object) = match kind {
392395
WrapperDescriptorKind::FunctionTypeDunderGet => ("__get__", "function"),

0 commit comments

Comments
 (0)