@@ -320,13 +320,21 @@ def find_module(self, id: str, *, fast_path: bool = False) -> ModuleSearchResult
320
320
use_typeshed = self ._typeshed_has_version (id )
321
321
elif top_level in self .stdlib_py_versions :
322
322
use_typeshed = self ._typeshed_has_version (top_level )
323
- self .results [id ] = self ._find_module (id , use_typeshed )
324
- if (
325
- not (fast_path or (self .options is not None and self .options .fast_module_lookup ))
326
- and self .results [id ] is ModuleNotFoundReason .NOT_FOUND
327
- and self ._can_find_module_in_parent_dir (id )
328
- ):
329
- self .results [id ] = ModuleNotFoundReason .WRONG_WORKING_DIRECTORY
323
+ result , should_cache = self ._find_module (id , use_typeshed )
324
+ if should_cache :
325
+ if (
326
+ not (
327
+ fast_path or (self .options is not None and self .options .fast_module_lookup )
328
+ )
329
+ and result is ModuleNotFoundReason .NOT_FOUND
330
+ and self ._can_find_module_in_parent_dir (id )
331
+ ):
332
+ self .results [id ] = ModuleNotFoundReason .WRONG_WORKING_DIRECTORY
333
+ else :
334
+ self .results [id ] = result
335
+ return self .results [id ]
336
+ else :
337
+ return result
330
338
return self .results [id ]
331
339
332
340
def _typeshed_has_version (self , module : str ) -> bool :
@@ -384,11 +392,16 @@ def _can_find_module_in_parent_dir(self, id: str) -> bool:
384
392
while any (is_init_file (file ) for file in os .listdir (working_dir )):
385
393
working_dir = os .path .dirname (working_dir )
386
394
parent_search .search_paths = SearchPaths ((working_dir ,), (), (), ())
387
- if not isinstance (parent_search ._find_module (id , False ), ModuleNotFoundReason ):
395
+ if not isinstance (parent_search ._find_module (id , False )[ 0 ] , ModuleNotFoundReason ):
388
396
return True
389
397
return False
390
398
391
- def _find_module (self , id : str , use_typeshed : bool ) -> ModuleSearchResult :
399
+ def _find_module (self , id : str , use_typeshed : bool ) -> tuple [ModuleSearchResult , bool ]:
400
+ """Try to find a module in all available sources.
401
+
402
+ Returns:
403
+ ``(result, can_be_cached)`` pair.
404
+ """
392
405
fscache = self .fscache
393
406
394
407
# Fast path for any modules in the current source set.
@@ -424,7 +437,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
424
437
else None
425
438
)
426
439
if p :
427
- return p
440
+ return p , True
428
441
429
442
# If we're looking for a module like 'foo.bar.baz', it's likely that most of the
430
443
# many elements of lib_path don't even have a subdirectory 'foo/bar'. Discover
@@ -444,6 +457,9 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
444
457
for component in (components [0 ], components [0 ] + "-stubs" )
445
458
for package_dir in self .find_lib_path_dirs (component , self .search_paths .package_path )
446
459
}
460
+ # Caching FOUND_WITHOUT_TYPE_HINTS is not always safe. That causes issues with
461
+ # typed subpackages in namespace packages.
462
+ can_cache_any_result = True
447
463
for pkg_dir in self .search_paths .package_path :
448
464
if pkg_dir not in candidate_package_dirs :
449
465
continue
@@ -475,6 +491,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
475
491
if isinstance (non_stub_match , ModuleNotFoundReason ):
476
492
if non_stub_match is ModuleNotFoundReason .FOUND_WITHOUT_TYPE_HINTS :
477
493
found_possible_third_party_missing_type_hints = True
494
+ can_cache_any_result = False
478
495
else :
479
496
third_party_inline_dirs .append (non_stub_match )
480
497
self ._update_ns_ancestors (components , non_stub_match )
@@ -513,7 +530,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
513
530
if verify and not verify_module (fscache , id , path_stubs , dir_prefix ):
514
531
near_misses .append ((path_stubs , dir_prefix ))
515
532
else :
516
- return path_stubs
533
+ return path_stubs , True
517
534
518
535
# Prefer package over module, i.e. baz/__init__.py* over baz.py*.
519
536
for extension in PYTHON_EXTENSIONS :
@@ -523,7 +540,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
523
540
if verify and not verify_module (fscache , id , path , dir_prefix ):
524
541
near_misses .append ((path , dir_prefix ))
525
542
continue
526
- return path
543
+ return path , True
527
544
528
545
# In namespace mode, register a potential namespace package
529
546
if self .options and self .options .namespace_packages :
@@ -541,7 +558,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
541
558
if verify and not verify_module (fscache , id , path , dir_prefix ):
542
559
near_misses .append ((path , dir_prefix ))
543
560
continue
544
- return path
561
+ return path , True
545
562
546
563
# In namespace mode, re-check those entries that had 'verify'.
547
564
# Assume search path entries xxx, yyy and zzz, and we're
@@ -570,35 +587,35 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
570
587
for path , dir_prefix in near_misses
571
588
]
572
589
index = levels .index (max (levels ))
573
- return near_misses [index ][0 ]
590
+ return near_misses [index ][0 ], True
574
591
575
592
# Finally, we may be asked to produce an ancestor for an
576
593
# installed package with a py.typed marker that is a
577
594
# subpackage of a namespace package. We only fess up to these
578
595
# if we would otherwise return "not found".
579
596
ancestor = self .ns_ancestors .get (id )
580
597
if ancestor is not None :
581
- return ancestor
598
+ return ancestor , True
582
599
583
600
approved_dist_name = stub_distribution_name (id )
584
601
if approved_dist_name :
585
602
if len (components ) == 1 :
586
- return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED
603
+ return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED , True
587
604
# If we're a missing submodule of an already installed approved stubs, we don't want to
588
605
# error with APPROVED_STUBS_NOT_INSTALLED, but rather want to return NOT_FOUND.
589
606
for i in range (1 , len (components )):
590
607
parent_id = "." .join (components [:i ])
591
608
if stub_distribution_name (parent_id ) == approved_dist_name :
592
609
break
593
610
else :
594
- return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED
611
+ return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED , True
595
612
if self .find_module (parent_id ) is ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED :
596
- return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED
597
- return ModuleNotFoundReason .NOT_FOUND
613
+ return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED , True
614
+ return ModuleNotFoundReason .NOT_FOUND , True
598
615
599
616
if found_possible_third_party_missing_type_hints :
600
- return ModuleNotFoundReason .FOUND_WITHOUT_TYPE_HINTS
601
- return ModuleNotFoundReason .NOT_FOUND
617
+ return ModuleNotFoundReason .FOUND_WITHOUT_TYPE_HINTS , can_cache_any_result
618
+ return ModuleNotFoundReason .NOT_FOUND , True
602
619
603
620
def find_modules_recursive (self , module : str ) -> list [BuildSource ]:
604
621
module_path = self .find_module (module , fast_path = True )
0 commit comments