ruff/crates/ty_python_semantic/resources/mdtest/call/open.md
Carl Meyer 6b7adb0537
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}) (push) Blocked by required conditions
CI / cargo test (macos-latest) (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
[ty] support PEP 613 type aliases (#21394)
Refs https://github.com/astral-sh/ty/issues/544

## Summary

Takes a more incremental approach to PEP 613 type alias support (vs
https://github.com/astral-sh/ruff/pull/20107). Instead of eagerly
inferring the RHS of a PEP 613 type alias as a type expression, infer it
as a value expression, just like we do for implicit type aliases, taking
advantage of the same support for e.g. unions and other type special
forms.

The main reason I'm following this path instead of the one in
https://github.com/astral-sh/ruff/pull/20107 is that we've realized that
people do sometimes use PEP 613 type aliases as values, not just as
types (because they are just a normal runtime assignment, unlike PEP 695
type aliases which create an opaque `TypeAliasType`).

This PR doesn't yet provide full support for recursive type aliases
(they don't panic, but they just fall back to `Unknown` at the recursion
point). This is future work.

## Test Plan

Added mdtests.

Many new ecosystem diagnostics, mostly because we
understand new types in lots of places.

Conformance suite changes are correct.

Performance regression is due to understanding lots of new
types; nothing we do in this PR is inherently expensive.
2025-11-20 17:59:35 -08:00

1.8 KiB

Calls to open()

builtins.open

We do not fully understand typeshed's overloads for open() yet, due to missing support for PEP-613 type aliases. However, we also do not emit false-positive diagnostics on common calls to open():

import pickle

reveal_type(open(""))  # revealed: TextIOWrapper[_WrappedBuffer]
reveal_type(open("", "r"))  # revealed: TextIOWrapper[_WrappedBuffer]
reveal_type(open("", "rb"))  # revealed: BufferedReader[_BufferedReaderStream]

with open("foo.pickle", "rb") as f:
    x = pickle.load(f)  # fine

def _(mode: str):
    reveal_type(open("", mode))  # revealed: IO[Any]

os.fdopen

The same is true for os.fdopen():

import pickle
import os

reveal_type(os.fdopen(0))  # revealed: TextIOWrapper[_WrappedBuffer]
reveal_type(os.fdopen(0, "r"))  # revealed: TextIOWrapper[_WrappedBuffer]
reveal_type(os.fdopen(0, "rb"))  # revealed: BufferedReader[_BufferedReaderStream]

with os.fdopen(0, "rb") as f:
    x = pickle.load(f)  # fine

Path.open

And similarly for Path.open():

from pathlib import Path
import pickle

reveal_type(Path("").open())  # revealed: TextIOWrapper[_WrappedBuffer]
reveal_type(Path("").open("r"))  # revealed: TextIOWrapper[_WrappedBuffer]
reveal_type(Path("").open("rb"))  # revealed: BufferedReader[_BufferedReaderStream]

with Path("foo.pickle").open("rb") as f:
    x = pickle.load(f)  # fine

NamedTemporaryFile

And similarly for tempfile.NamedTemporaryFile():

from tempfile import NamedTemporaryFile
import pickle

reveal_type(NamedTemporaryFile())  # revealed: _TemporaryFileWrapper[bytes]
reveal_type(NamedTemporaryFile("r"))  # revealed: _TemporaryFileWrapper[str]
reveal_type(NamedTemporaryFile("rb"))  # revealed: _TemporaryFileWrapper[bytes]

with NamedTemporaryFile("rb") as f:
    x = pickle.load(f)  # fine