Skip to content

Commit b580b74

Browse files
authored
Vendoring logic cleanups + Move typing stub generation to dedic… (#7334)
Vendoring logic cleanups + Move typing stub generation to dedicated module
2 parents 0801e97 + b770679 commit b580b74

File tree

2 files changed

+63
-31
lines changed

2 files changed

+63
-31
lines changed

tools/automation/vendoring/__init__.py

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# The following comment should be removed at some point in the future.
44
# mypy: disallow-untyped-defs=False
55

6-
import os
76
import re
87
import shutil
98
import tarfile
@@ -13,7 +12,7 @@
1312
import invoke
1413
import requests
1514

16-
TASK_NAME = 'update'
15+
from .typing import generate_stubs
1716

1817
FILE_WHITE_LIST = (
1918
'Makefile',
@@ -49,7 +48,7 @@ def remove_all(paths):
4948

5049

5150
def log(msg):
52-
print('[vendoring.%s] %s' % (TASK_NAME, msg))
51+
print('[vendoring.update] ' + msg)
5352

5453

5554
def _get_vendor_dir(ctx):
@@ -270,36 +269,10 @@ def update_stubs(ctx):
270269
vendored_libs = detect_vendored_libs(vendor_dir)
271270

272271
print("[vendoring.update_stubs] Add mypy stubs")
272+
generate_stubs(vendor_dir, vendored_libs)
273273

274-
extra_stubs_needed = {
275-
# Some projects need stubs other than a simple <name>.pyi
276-
"six": [
277-
"six.__init__",
278-
"six.moves.__init__",
279-
"six.moves.configparser",
280-
],
281-
# Some projects should not have stubs coz they're single file modules
282-
"appdirs": [],
283-
"contextlib2": [],
284-
}
285274

286-
for lib in vendored_libs:
287-
if lib not in extra_stubs_needed:
288-
(vendor_dir / (lib + ".pyi")).write_text("from %s import *" % lib)
289-
continue
290-
291-
for selector in extra_stubs_needed[lib]:
292-
fname = selector.replace(".", os.sep) + ".pyi"
293-
if selector.endswith(".__init__"):
294-
selector = selector[:-9]
295-
296-
f_path = vendor_dir / fname
297-
if not f_path.parent.exists():
298-
f_path.parent.mkdir()
299-
f_path.write_text("from %s import *" % selector)
300-
301-
302-
@invoke.task(name=TASK_NAME, post=[update_stubs])
275+
@invoke.task(name="update", post=[update_stubs])
303276
def main(ctx):
304277
vendor_dir = _get_vendor_dir(ctx)
305278
log('Using vendor dir: %s' % vendor_dir)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Logic for adding static typing related stubs of vendored dependencies.
2+
3+
We autogenerate `.pyi` stub files for the vendored modules, when vendoring.
4+
These .pyi files are not distributed (thanks to MANIFEST.in). The stub files
5+
are merely `from ... import *` but they do what they're supposed to and mypy
6+
is able to find the correct declarations using these files.
7+
"""
8+
9+
import os
10+
from pathlib import Path
11+
from typing import Dict, Iterable, List, Tuple
12+
13+
EXTRA_STUBS_NEEDED = {
14+
# Some projects need stubs other than a simple <name>.pyi
15+
"six": [
16+
"six.__init__",
17+
"six.moves.__init__",
18+
"six.moves.configparser",
19+
],
20+
# Some projects should not have stubs because they're a single module
21+
"appdirs": [],
22+
"contextlib2": [],
23+
} # type: Dict[str, List[str]]
24+
25+
26+
def determine_stub_files(lib):
27+
# type: (str) -> Iterable[Tuple[str, str]]
28+
# There's no special handling needed -- a <libname>.pyi file is good enough
29+
if lib not in EXTRA_STUBS_NEEDED:
30+
yield lib + ".pyi", lib
31+
return
32+
33+
# Need to generate the given stubs, with the correct import names
34+
for import_name in EXTRA_STUBS_NEEDED[lib]:
35+
rel_location = import_name.replace(".", os.sep) + ".pyi"
36+
37+
# Writing an __init__.pyi file -> don't import from `pkg.__init__`
38+
if import_name.endswith(".__init__"):
39+
import_name = import_name[:-9]
40+
41+
yield rel_location, import_name
42+
43+
44+
def write_stub(destination, import_name):
45+
# type: (Path, str) -> None
46+
# Create the parent directories if needed.
47+
if not destination.parent.exists():
48+
destination.parent.mkdir()
49+
50+
# Write `from ... import *` in the stub file.
51+
destination.write_text("from %s import *" % import_name)
52+
53+
54+
def generate_stubs(vendor_dir, libraries):
55+
# type: (Path, List[str]) -> None
56+
for lib in libraries:
57+
for rel_location, import_name in determine_stub_files(lib):
58+
destination = vendor_dir / rel_location
59+
write_stub(destination, import_name)

0 commit comments

Comments
 (0)