/
sync-typeshed.py
104 lines (85 loc) · 3.55 KB
/
sync-typeshed.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""Sync stdlib stubs (and a few other files) from typeshed.
Usage:
python3 misc/sync-typeshed.py [--commit hash] [--typeshed-dir dir]
By default, sync to the latest typeshed commit.
"""
import argparse
import glob
import os
import shutil
import subprocess
import sys
import tempfile
import textwrap
from typing import Optional
def check_state() -> None:
if not os.path.isfile('README.md'):
sys.exit('error: The current working directory must be the mypy repository root')
out = subprocess.check_output(['git', 'status', '-s', os.path.join('mypy', 'typeshed')])
if out:
# If there are local changes under mypy/typeshed, they would be lost.
sys.exit('error: Output of "git status -s mypy/typeshed" must be empty')
def update_typeshed(typeshed_dir: str, commit: Optional[str]) -> str:
"""Update contents of local typeshed copy.
Return the normalized typeshed commit hash.
"""
assert os.path.isdir(os.path.join(typeshed_dir, 'stdlib'))
assert os.path.isdir(os.path.join(typeshed_dir, 'stubs'))
if commit:
subprocess.run(['git', 'checkout', commit], check=True, cwd=typeshed_dir)
commit = git_head_commit(typeshed_dir)
stdlib_dir = os.path.join('mypy', 'typeshed', 'stdlib')
# Remove existing stubs.
shutil.rmtree(stdlib_dir)
# Copy new stdlib stubs.
shutil.copytree(os.path.join(typeshed_dir, 'stdlib'), stdlib_dir)
# Copy mypy_extensions stubs. We don't want to use a stub package, since it's
# treated specially by mypy and we make assumptions about what's there.
stubs_dir = os.path.join('mypy', 'typeshed', 'stubs')
shutil.rmtree(stubs_dir)
os.makedirs(stubs_dir)
shutil.copytree(os.path.join(typeshed_dir, 'stubs', 'mypy-extensions'),
os.path.join(stubs_dir, 'mypy-extensions'))
shutil.copy(os.path.join(typeshed_dir, 'LICENSE'), os.path.join('mypy', 'typeshed'))
return commit
def git_head_commit(repo: str) -> str:
commit = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=repo).decode('ascii')
return commit.strip()
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"--commit", default=None,
help="Typeshed commit (default to latest master if using a repository clone)"
)
parser.add_argument(
"--typeshed-dir", default=None,
help="Location of typeshed (default to a temporary repository clone)"
)
args = parser.parse_args()
check_state()
print('Update contents of mypy/typeshed from typeshed? [yN] ', end='')
answer = input()
if answer.lower() != 'y':
sys.exit('Aborting')
if not args.typeshed_dir:
# Clone typeshed repo if no directory given.
with tempfile.TemporaryDirectory() as tempdir:
print('Cloning typeshed in {}...'.format(tempdir))
subprocess.run(['git', 'clone', 'https://github.com/python/typeshed.git'],
check=True, cwd=tempdir)
repo = os.path.join(tempdir, 'typeshed')
commit = update_typeshed(repo, args.commit)
else:
commit = update_typeshed(args.typeshed_dir, args.commit)
assert commit
# Create a commit
message = textwrap.dedent("""\
Sync typeshed
Source commit:
https://github.com/python/typeshed/commit/{commit}
""".format(commit=commit))
subprocess.run(['git', 'add', '--all', os.path.join('mypy', 'typeshed')], check=True)
subprocess.run(['git', 'commit', '-m', message], check=True)
print('Created typeshed sync commit.')
if __name__ == '__main__':
main()