1
1
#!/usr/bin/env python3
2
2
3
- # For various reasons we need the contents of certain files to be
3
+ # For security (and simplicity) reasons, only a limited kind of files can be
4
+ # present in /stdlib and /stubs directories, see README for detail. Here we
5
+ # verify these constraints.
6
+
7
+ # In addition, for various reasons we need the contents of certain files to be
4
8
# duplicated in two places, for example stdlib/@python2/builtins.pyi and
5
9
# stdlib/@python2/__builtin__.pyi must be identical. In the past we used
6
10
# symlinks but that doesn't always work on Windows, so now you must
10
14
import filecmp
11
15
import os
12
16
17
+ import toml
18
+
13
19
consistent_files = [
14
20
{"stdlib/@python2/builtins.pyi" , "stdlib/@python2/__builtin__.pyi" },
15
21
{"stdlib/threading.pyi" , "stdlib/_dummy_threading.pyi" },
16
22
]
17
23
18
24
19
- def main ():
25
+ def assert_stubs_only (directory ):
26
+ """Check that given directory contains only valid stub files."""
27
+ top = directory .split (os .sep )[- 1 ]
28
+ assert top .isidentifier (), f"Bad directory name: { top } "
29
+ for _ , dirs , files in os .walk (directory ):
30
+ for file in files :
31
+ name , ext = os .path .splitext (file )
32
+ assert name .isidentifier (), f"Files must be valid modules, got: { name } "
33
+ assert ext == ".pyi" , f"Only stub flies allowed. Got: { file } in { directory } "
34
+ for subdir in dirs :
35
+ assert subdir .isidentifier (), f"Directories must be valid packages, got: { subdir } "
36
+
37
+
38
+ def check_stdlib ():
39
+ for entry in os .listdir ("stdlib" ):
40
+ if os .path .isfile (os .path .join ("stdlib" , entry )):
41
+ name , ext = os .path .splitext (entry )
42
+ if ext != ".pyi" :
43
+ assert entry == "VERSIONS" , f"Unexpected file in stdlib root: { entry } "
44
+ assert name .isidentifier (), "Bad file name in stdlib"
45
+ else :
46
+ if entry == "@python2" :
47
+ continue
48
+ assert_stubs_only (os .path .join ("stdlib" , entry ))
49
+ for entry in os .listdir ("stdlib/@python2" ):
50
+ if os .path .isfile (os .path .join ("stdlib/@python2" , entry )):
51
+ name , ext = os .path .splitext (entry )
52
+ assert name .isidentifier (), "Bad file name in stdlib"
53
+ assert ext == ".pyi" , "Unexpected file in stdlib/@python2 root"
54
+ else :
55
+ assert_stubs_only (os .path .join ("stdlib/@python2" , entry ))
56
+
57
+
58
+ def check_stubs ():
59
+ for distribution in os .listdir ("stubs" ):
60
+ assert not os .path .isfile (distribution ), f"Only directories allowed in stubs, got { distribution } "
61
+ for entry in os .listdir (os .path .join ("stubs" , distribution )):
62
+ if os .path .isfile (os .path .join ("stubs" , distribution , entry )):
63
+ name , ext = os .path .splitext (entry )
64
+ if ext != ".pyi" :
65
+ assert entry in {"METADATA.toml" , "README" , "README.md" , "README.rst" }, entry
66
+ else :
67
+ assert name .isidentifier (), f"Bad file name '{ entry } ' in stubs"
68
+ else :
69
+ if entry == "@python2" :
70
+ continue
71
+ assert_stubs_only (os .path .join ("stubs" , distribution , entry ))
72
+ if os .path .isdir (os .path .join ("stubs" , distribution , "@python2" )):
73
+ for entry in os .listdir (os .path .join ("stubs" , distribution , "@python2" )):
74
+ if os .path .isfile (os .path .join ("stubs" , distribution , "@python2" , entry )):
75
+ name , ext = os .path .splitext (entry )
76
+ assert name .isidentifier (), f"Bad file name '{ entry } ' in stubs"
77
+ assert ext == ".pyi" , f"Unexpected file { entry } in @python2 stubs"
78
+ else :
79
+ assert_stubs_only (os .path .join ("stubs" , distribution , "@python2" , entry ))
80
+
81
+
82
+ def check_same_files ():
20
83
files = [os .path .join (root , file ) for root , dir , files in os .walk ("." ) for file in files ]
21
84
no_symlink = "You cannot use symlinks in typeshed, please copy {} to its link."
22
85
for file in files :
@@ -34,5 +97,63 @@ def main():
34
97
)
35
98
36
99
100
+ def check_versions ():
101
+ versions = {}
102
+ with open ("stdlib/VERSIONS" ) as f :
103
+ data = f .read ().splitlines ()
104
+ for line in data :
105
+ if not line or line .lstrip ().startswith ("#" ):
106
+ continue
107
+ assert ": " in line , f"Bad line in VERSIONS: { line } "
108
+ module , version = line .split (": " )
109
+ msg = f"Unsupported Python version{ version } "
110
+ assert version .count ("." ) == 1 , msg
111
+ major , minor = version .split ("." )
112
+ assert major in {"2" , "3" }, msg
113
+ assert minor .isdigit (), msg
114
+ assert module not in versions , f"Duplicate module { module } in VERSIONS"
115
+ versions [module ] = (int (major ), int (minor ))
116
+ modules = set ()
117
+ for entry in os .listdir ("stdlib" ):
118
+ if entry == "@python2" or entry == "VERSIONS" :
119
+ continue
120
+ if os .path .isfile (os .path .join ("stdlib" , entry )):
121
+ mod , _ = os .path .splitext (entry )
122
+ modules .add (mod )
123
+ else :
124
+ modules .add (entry )
125
+ extra = modules - set (versions )
126
+ assert not extra , f"Modules not in versions: { extra } "
127
+ extra = set (versions ) - modules
128
+ assert not extra , f"Versions not in modules: { extra } "
129
+
130
+
131
+ def check_metadata ():
132
+ for distribution in os .listdir ("stubs" ):
133
+ with open (os .path .join ("stubs" , distribution , "METADATA.toml" )) as f :
134
+ data = toml .loads (f .read ())
135
+ assert "version" in data , f"Missing version for { distribution } "
136
+ version = data ["version" ]
137
+ msg = f"Unsupported Python version { version } "
138
+ assert version .count ("." ) == 1 , msg
139
+ major , minor = version .split ("." )
140
+ assert major .isdigit () and minor .isdigit (), msg
141
+ for key in data :
142
+ assert key in {
143
+ "version" , "python2" , "python3" , "requires"
144
+ }, f"Unexpected key { key } for { distribution } "
145
+ assert isinstance (data .get ("python2" , False ), bool ), f"Invalid python2 value for { distribution } "
146
+ assert isinstance (data .get ("python3" , True ), bool ), f"Invalid python3 value for { distribution } "
147
+ assert isinstance (data .get ("requires" , []), list ), f"Invalid requires value for { distribution } "
148
+ for dep in data .get ("requires" , []):
149
+ # TODO: add more validation here.
150
+ assert isinstance (dep , str ), f"Invalid dependency { dep } for { distribution } "
151
+ assert dep .startswith ("types-" ), f"Only stub dependencies supported, got { dep } "
152
+
153
+
37
154
if __name__ == "__main__" :
38
- main ()
155
+ check_stdlib ()
156
+ check_versions ()
157
+ check_stubs ()
158
+ check_metadata ()
159
+ check_same_files ()
0 commit comments