19
19
Sequence ,
20
20
Set ,
21
21
Tuple ,
22
+ Union ,
22
23
)
23
24
24
25
import importlib_resources
@@ -127,10 +128,10 @@ def get_all_stub_files(
127
128
# third-party packages
128
129
for stub_packages in (True , False ):
129
130
for search_path_entry in search_context .search_path :
130
- if not search_path_entry . exists ( ):
131
+ if not safe_exists ( search_path_entry ):
131
132
continue
132
- for directory in os . scandir (search_path_entry ):
133
- if not directory . is_dir ( ):
133
+ for directory in safe_scandir (search_path_entry ):
134
+ if not safe_is_dir ( directory ):
134
135
continue
135
136
condition = (
136
137
directory .name .endswith ("-stubs" )
@@ -150,10 +151,10 @@ def get_all_stub_files(
150
151
typeshed_dirs .insert (0 , search_context .typeshed / "@python2" )
151
152
152
153
for typeshed_dir in typeshed_dirs :
153
- for entry in os . scandir (typeshed_dir ):
154
- if entry . is_dir ( ) and entry .name .isidentifier ():
154
+ for entry in safe_scandir (typeshed_dir ):
155
+ if safe_is_dir ( entry ) and entry .name .isidentifier ():
155
156
module_name = entry .name
156
- elif entry . is_file ( ) and entry .name .endswith (".pyi" ):
157
+ elif safe_is_file ( entry ) and entry .name .endswith (".pyi" ):
157
158
module_name = entry .name [: - len (".pyi" )]
158
159
else :
159
160
continue
@@ -168,7 +169,7 @@ def get_all_stub_files(
168
169
and version .in_python2
169
170
):
170
171
continue
171
- if entry . is_dir ( ):
172
+ if safe_is_dir ( entry ):
172
173
seen = yield from _get_all_stub_files_from_directory (
173
174
entry , typeshed_dir , seen
174
175
)
@@ -188,16 +189,16 @@ def _get_all_stub_files_from_directory(
188
189
to_do : List [os .PathLike [str ]] = [directory ]
189
190
while to_do :
190
191
current_dir = to_do .pop ()
191
- for dir_entry in os . scandir (current_dir ):
192
- if dir_entry . is_dir ( ):
192
+ for dir_entry in safe_scandir (current_dir ):
193
+ if safe_is_dir ( dir_entry ):
193
194
if not dir_entry .name .isidentifier ():
194
195
continue
195
196
path = Path (dir_entry )
196
- if (path / "__init__.pyi" ). is_file () or (
197
+ if safe_is_file (path / "__init__.pyi" ) or safe_is_file (
197
198
path / "__init__.py"
198
- ). is_file () :
199
+ ):
199
200
to_do .append (path )
200
- elif dir_entry . is_file ( ):
201
+ elif safe_is_dir ( dir_entry ):
201
202
path = Path (dir_entry )
202
203
if path .suffix != ".pyi" :
203
204
continue
@@ -225,11 +226,43 @@ def get_search_path(typeshed_dir: Path, pyversion: Tuple[int, int]) -> Tuple[Pat
225
226
for version in [* versions , str (pyversion [0 ]), "2and3" ]:
226
227
for lib_type in ("stdlib" , "third_party" ):
227
228
stubdir = typeshed_dir / lib_type / version
228
- if stubdir . is_dir ( ):
229
+ if safe_is_dir ( stubdir ):
229
230
path .append (stubdir )
230
231
return tuple (path )
231
232
232
233
234
+ def safe_exists (path : Path ) -> bool :
235
+ """Return whether a path exists, assuming it doesn't if we get an error."""
236
+ try :
237
+ return path .exists ()
238
+ except OSError :
239
+ return False
240
+
241
+
242
+ def safe_is_dir (path : Union [Path , _DirEntry ]) -> bool :
243
+ """Return whether a path is a directory, assuming it isn't if we get an error."""
244
+ try :
245
+ return path .is_dir ()
246
+ except OSError :
247
+ return False
248
+
249
+
250
+ def safe_is_file (path : Union [Path , _DirEntry ]) -> bool :
251
+ """Return whether a path is a file, assuming it isn't if we get an error."""
252
+ try :
253
+ return path .is_file ()
254
+ except OSError :
255
+ return False
256
+
257
+
258
+ def safe_scandir (path : os .PathLike [str ]) -> Iterable [_DirEntry ]:
259
+ """Return an iterator over the entries in a directory, or no entries if we get an error."""
260
+ try :
261
+ return os .scandir (path )
262
+ except OSError :
263
+ return iter ([])
264
+
265
+
233
266
def get_stub_file_name (
234
267
module_name : ModulePath , search_context : SearchContext
235
268
) -> Optional [Path ]:
@@ -242,15 +275,15 @@ def get_stub_file_name(
242
275
stubs_package = f"{ top_level_name } -stubs"
243
276
for path in search_context .search_path :
244
277
stubdir = path / stubs_package
245
- if stubdir . exists ( ):
278
+ if safe_exists ( stubdir ):
246
279
stub = _find_stub_in_dir (stubdir , rest_module_path )
247
280
if stub is not None :
248
281
return stub
249
282
250
283
# 4. stubs in normal packages
251
284
for path in search_context .search_path :
252
285
stubdir = path / top_level_name
253
- if stubdir . exists ( ):
286
+ if safe_exists ( stubdir ):
254
287
stub = _find_stub_in_dir (stubdir , rest_module_path )
255
288
if stub is not None :
256
289
return stub
@@ -314,16 +347,16 @@ def _parse_version(version: str) -> PythonVersion:
314
347
def _find_stub_in_dir (stubdir : Path , module : ModulePath ) -> Optional [Path ]:
315
348
if not module :
316
349
init_name = stubdir / "__init__.pyi"
317
- if init_name . exists ( ):
350
+ if safe_exists ( init_name ):
318
351
return init_name
319
352
return None
320
353
if len (module ) == 1 :
321
354
stub_name = stubdir / f"{ module [0 ]} .pyi"
322
- if stub_name . exists ( ):
355
+ if safe_exists ( stub_name ):
323
356
return stub_name
324
357
next_name , * rest = module
325
358
next_dir = stubdir / next_name
326
- if next_dir . exists ( ):
359
+ if safe_exists ( next_dir ):
327
360
return _find_stub_in_dir (next_dir , ModulePath (tuple (rest )))
328
361
return None
329
362
0 commit comments