Skip to content

Commit cba32ea

Browse files
authored
Update typeshed to latest (#11211)
* Update to latest version * Don't copy txt files * Skip .py files * Add toml files
1 parent 1576956 commit cba32ea

File tree

729 files changed

+42017
-4421
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

729 files changed

+42017
-4421
lines changed

build/updateTypeshed.py

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script to update the typeshed-fallback folder with the latest files from
4+
the typeshed repository (https://github.com/python/typeshed).
5+
6+
This script:
7+
1. Clones/downloads the typeshed repository to a temporary directory
8+
2. Copies the stdlib/ and stubs/ folders to typeshed-fallback
9+
3. Copies the LICENSE and README.md files
10+
4. Updates commit.txt with the current commit hash
11+
"""
12+
13+
import argparse
14+
import os
15+
import shutil
16+
import subprocess
17+
import sys
18+
import tempfile
19+
from pathlib import Path
20+
21+
22+
def get_script_dir() -> Path:
23+
"""Get the directory where this script is located."""
24+
return Path(__file__).parent.resolve()
25+
26+
27+
def get_typeshed_fallback_dir() -> Path:
28+
"""Get the path to the typeshed-fallback directory."""
29+
script_dir = get_script_dir()
30+
return script_dir.parent / "packages" / "pyright-internal" / "typeshed-fallback"
31+
32+
33+
def run_git_command(args: list[str], cwd: Path) -> subprocess.CompletedProcess:
34+
"""Run a git command and return the result."""
35+
return subprocess.run(
36+
["git"] + args,
37+
cwd=cwd,
38+
capture_output=True,
39+
text=True,
40+
check=True,
41+
)
42+
43+
44+
def clone_typeshed(target_dir: Path, commit: str | None = None) -> str:
45+
"""
46+
Clone the typeshed repository to the target directory.
47+
48+
Args:
49+
target_dir: Directory to clone into
50+
commit: Optional specific commit hash to checkout
51+
52+
Returns:
53+
The commit hash that was checked out
54+
"""
55+
typeshed_url = "https://github.com/python/typeshed.git"
56+
57+
print(f"Cloning typeshed repository to {target_dir}...")
58+
59+
# Clone with depth 1 for faster download (unless we need a specific commit)
60+
if commit:
61+
# Full clone needed for specific commit
62+
run_git_command(["clone", typeshed_url, str(target_dir)], cwd=target_dir.parent)
63+
run_git_command(["checkout", commit], cwd=target_dir)
64+
else:
65+
# Shallow clone for latest
66+
run_git_command(["clone", "--depth", "1", typeshed_url, str(target_dir)], cwd=target_dir.parent)
67+
68+
# Get the current commit hash
69+
result = run_git_command(["rev-parse", "HEAD"], cwd=target_dir)
70+
commit_hash = result.stdout.strip()
71+
72+
print(f"Checked out commit: {commit_hash}")
73+
return commit_hash
74+
75+
76+
def remove_directory_contents(dir_path: Path) -> None:
77+
"""Remove all contents of a directory but keep the directory itself."""
78+
if dir_path.exists():
79+
shutil.rmtree(dir_path)
80+
dir_path.mkdir(parents=True, exist_ok=True)
81+
82+
83+
def should_copy_file(file_path: Path) -> bool:
84+
"""Check if a file should be copied based on its extension or name."""
85+
allowed_extensions = {".pyi", ".toml"}
86+
allowed_names = {"VERSIONS"}
87+
return file_path.suffix.lower() in allowed_extensions or file_path.name in allowed_names
88+
89+
90+
def is_in_excluded_folder(file_path: Path, base_folder: Path) -> bool:
91+
"""Check if the file is inside a folder that starts with '@'."""
92+
rel_path = file_path.relative_to(base_folder)
93+
for part in rel_path.parts:
94+
if part.startswith("@"):
95+
return True
96+
return False
97+
98+
99+
def copy_tree_filtered(src_folder: Path, dst_folder: Path) -> None:
100+
"""
101+
Copy a directory tree, only including .pyi and VERSIONS files.
102+
Skips any folder starting with '@'.
103+
104+
Args:
105+
src_folder: Source directory
106+
dst_folder: Destination directory
107+
"""
108+
for src_path in src_folder.rglob("*"):
109+
if src_path.is_file() and should_copy_file(src_path):
110+
# Skip files in folders starting with '@'
111+
if is_in_excluded_folder(src_path, src_folder):
112+
continue
113+
114+
# Calculate relative path and destination
115+
rel_path = src_path.relative_to(src_folder)
116+
dst_path = dst_folder / rel_path
117+
118+
# Create parent directories if needed
119+
dst_path.parent.mkdir(parents=True, exist_ok=True)
120+
121+
# Copy the file
122+
shutil.copy2(src_path, dst_path)
123+
124+
125+
def copy_typeshed_files(source_dir: Path, dest_dir: Path) -> None:
126+
"""
127+
Copy the relevant typeshed files to the destination directory.
128+
Only .pyi and VERSIONS files are copied from folders.
129+
130+
Args:
131+
source_dir: The cloned typeshed repository directory
132+
dest_dir: The typeshed-fallback directory
133+
"""
134+
# Folders to copy
135+
folders_to_copy = ["stdlib", "stubs"]
136+
137+
# Copy folders (only .py and .pyi files)
138+
for folder in folders_to_copy:
139+
src_folder = source_dir / folder
140+
dst_folder = dest_dir / folder
141+
142+
if not src_folder.exists():
143+
print(f"Warning: Source folder {src_folder} does not exist, skipping...")
144+
continue
145+
146+
print(f"Copying {folder}/ (only .pyi and VERSIONS files)...")
147+
148+
# Remove existing folder contents
149+
remove_directory_contents(dst_folder)
150+
151+
# Copy the folder with filtering
152+
copy_tree_filtered(src_folder, dst_folder)
153+
154+
# Files to copy
155+
files_to_copy = ["LICENSE", "README.md"]
156+
157+
# Copy files
158+
for file in files_to_copy:
159+
src_file = source_dir / file
160+
dst_file = dest_dir / file
161+
162+
if not src_file.exists():
163+
print(f"Warning: Source file {src_file} does not exist, skipping...")
164+
continue
165+
166+
print(f"Copying {file}...")
167+
shutil.copy2(src_file, dst_file)
168+
169+
170+
def update_commit_file(dest_dir: Path, commit_hash: str) -> None:
171+
"""Update the commit.txt file with the new commit hash."""
172+
commit_file = dest_dir / "commit.txt"
173+
print(f"Updating commit.txt with {commit_hash}...")
174+
commit_file.write_text(commit_hash + "\n")
175+
176+
177+
def main() -> int:
178+
parser = argparse.ArgumentParser(
179+
description="Update typeshed-fallback with the latest typeshed files"
180+
)
181+
parser.add_argument(
182+
"--commit",
183+
"-c",
184+
type=str,
185+
default=None,
186+
help="Specific commit hash to checkout (default: latest main branch)",
187+
)
188+
parser.add_argument(
189+
"--dry-run",
190+
"-n",
191+
action="store_true",
192+
help="Show what would be done without making changes",
193+
)
194+
195+
args = parser.parse_args()
196+
197+
typeshed_fallback_dir = get_typeshed_fallback_dir()
198+
199+
if not typeshed_fallback_dir.exists():
200+
print(f"Error: typeshed-fallback directory not found at {typeshed_fallback_dir}")
201+
return 1
202+
203+
print(f"Typeshed fallback directory: {typeshed_fallback_dir}")
204+
205+
if args.dry_run:
206+
print("\n*** DRY RUN - No changes will be made ***\n")
207+
print("Would perform the following actions:")
208+
print(" 1. Clone typeshed repository to a temporary directory")
209+
if args.commit:
210+
print(f" 2. Checkout commit: {args.commit}")
211+
else:
212+
print(" 2. Use latest commit from main branch")
213+
print(" 3. Copy stdlib/ folder (only .pyi and VERSIONS files)")
214+
print(" 4. Copy stubs/ folder (only .pyi and VERSIONS files)")
215+
print(" 5. Copy LICENSE file")
216+
print(" 6. Copy README.md file")
217+
print(" 7. Update commit.txt with new commit hash")
218+
return 0
219+
220+
# Create a temporary directory for cloning
221+
with tempfile.TemporaryDirectory() as temp_dir:
222+
temp_path = Path(temp_dir)
223+
typeshed_clone_dir = temp_path / "typeshed"
224+
225+
try:
226+
# Clone typeshed
227+
commit_hash = clone_typeshed(typeshed_clone_dir, args.commit)
228+
229+
# Copy files
230+
copy_typeshed_files(typeshed_clone_dir, typeshed_fallback_dir)
231+
232+
# Update commit.txt
233+
update_commit_file(typeshed_fallback_dir, commit_hash)
234+
235+
print("\nTypeshed update complete!")
236+
print(f"Updated to commit: {commit_hash}")
237+
238+
except subprocess.CalledProcessError as e:
239+
print(f"Error running git command: {e}")
240+
print(f"stdout: {e.stdout}")
241+
print(f"stderr: {e.stderr}")
242+
return 1
243+
except Exception as e:
244+
print(f"Error: {e}")
245+
return 1
246+
247+
return 0
248+
249+
250+
if __name__ == "__main__":
251+
sys.exit(main())

packages/pyright-internal/typeshed-fallback/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
## About
88

99
Typeshed contains external type annotations for the Python standard library
10-
and Python builtins, as well as third party packages as contributed by
10+
and Python builtins, as well as third-party packages that are contributed by
1111
people external to those projects.
1212

13-
This data can e.g. be used for static analysis, type checking, type inference,
13+
This data can, e.g., be used for static analysis, type checking, type inference,
1414
and autocompletion.
1515

1616
For information on how to use typeshed, read below. Information for
@@ -29,8 +29,8 @@ If you're just using a type checker (e.g. [mypy](https://github.com/python/mypy/
2929
[pyright](https://github.com/microsoft/pyright), or PyCharm's built-in type
3030
checker), as opposed to
3131
developing it, you don't need to interact with the typeshed repo at
32-
all: a copy of standard library part of typeshed is bundled with type checkers.
33-
And type stubs for third party packages and modules you are using can
32+
all: a copy of the standard library part of typeshed is bundled with type checkers.
33+
And type stubs for third-party packages and modules you are using can
3434
be installed from PyPI. For example, if you are using `html5lib` and `requests`,
3535
you can install the type stubs using
3636

@@ -70,7 +70,7 @@ package you're using, each with its own tradeoffs:
7070
type checking due to changes in the stubs.
7171

7272
Another risk of this strategy is that stubs often lag behind
73-
the package being stubbed. You might want to force the package being stubbed
73+
the package that is being stubbed. You might want to force the package being stubbed
7474
to a certain minimum version because it fixes a critical bug, but if
7575
correspondingly updated stubs have not been released, your type
7676
checking results may not be fully accurate.
@@ -119,6 +119,6 @@ a review of your type annotations or stubs outside of typeshed, head over to
119119
[our discussion forum](https://github.com/python/typing/discussions).
120120
For less formal discussion, try the typing chat room on
121121
[gitter.im](https://gitter.im/python/typing). Some typeshed maintainers
122-
are almost always present; feel free to find us there and we're happy
122+
are almost always present; feel free to find us there, and we're happy
123123
to chat. Substantive technical discussion will be directed to the
124124
issue tracker.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
a205439338a4ad3debec1eeae7d300e3781c066d
1+
a564787bf23386e57338b750bf4733f3c978b701

packages/pyright-internal/typeshed-fallback/stdlib/_compression.pyi

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# _compression is replaced by compression._common._streams on Python 3.14+ (PEP-784)
22

3-
from _typeshed import Incomplete, WriteableBuffer
3+
from _typeshed import ReadableBuffer, WriteableBuffer
44
from collections.abc import Callable
55
from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase
66
from typing import Any, Protocol, type_check_only
@@ -13,13 +13,24 @@ class _Reader(Protocol):
1313
def seekable(self) -> bool: ...
1414
def seek(self, n: int, /) -> Any: ...
1515

16+
@type_check_only
17+
class _Decompressor(Protocol):
18+
def decompress(self, data: ReadableBuffer, /, max_length: int = ...) -> bytes: ...
19+
@property
20+
def unused_data(self) -> bytes: ...
21+
@property
22+
def eof(self) -> bool: ...
23+
# `zlib._Decompress` does not have next property, but `DecompressReader` calls it:
24+
# @property
25+
# def needs_input(self) -> bool: ...
26+
1627
class BaseStream(BufferedIOBase): ...
1728

1829
class DecompressReader(RawIOBase):
1930
def __init__(
2031
self,
2132
fp: _Reader,
22-
decomp_factory: Callable[..., Incomplete],
33+
decomp_factory: Callable[..., _Decompressor],
2334
trailing_error: type[Exception] | tuple[type[Exception], ...] = (),
2435
**decomp_args: Any, # These are passed to decomp_factory.
2536
) -> None: ...

0 commit comments

Comments
 (0)