@@ -320,13 +320,21 @@ def find_module(self, id: str, *, fast_path: bool = False) -> ModuleSearchResult
320320 use_typeshed = self ._typeshed_has_version (id )
321321 elif top_level in self .stdlib_py_versions :
322322 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
330338 return self .results [id ]
331339
332340 def _typeshed_has_version (self , module : str ) -> bool :
@@ -384,11 +392,11 @@ def _can_find_module_in_parent_dir(self, id: str) -> bool:
384392 while any (is_init_file (file ) for file in os .listdir (working_dir )):
385393 working_dir = os .path .dirname (working_dir )
386394 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 ):
388396 return True
389397 return False
390398
391- def _find_module (self , id : str , use_typeshed : bool ) -> ModuleSearchResult :
399+ def _find_module (self , id : str , use_typeshed : bool ) -> tuple [ ModuleSearchResult , bool ] :
392400 fscache = self .fscache
393401
394402 # Fast path for any modules in the current source set.
@@ -424,7 +432,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
424432 else None
425433 )
426434 if p :
427- return p
435+ return p , True
428436
429437 # If we're looking for a module like 'foo.bar.baz', it's likely that most of the
430438 # many elements of lib_path don't even have a subdirectory 'foo/bar'. Discover
@@ -444,6 +452,9 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
444452 for component in (components [0 ], components [0 ] + "-stubs" )
445453 for package_dir in self .find_lib_path_dirs (component , self .search_paths .package_path )
446454 }
455+ # Caching FOUND_WITHOUT_TYPE_HINTS is not always safe. That causes issues with
456+ # typed subpackages in namespace packages.
457+ can_cache_any_result = True
447458 for pkg_dir in self .search_paths .package_path :
448459 if pkg_dir not in candidate_package_dirs :
449460 continue
@@ -475,6 +486,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
475486 if isinstance (non_stub_match , ModuleNotFoundReason ):
476487 if non_stub_match is ModuleNotFoundReason .FOUND_WITHOUT_TYPE_HINTS :
477488 found_possible_third_party_missing_type_hints = True
489+ can_cache_any_result = False
478490 else :
479491 third_party_inline_dirs .append (non_stub_match )
480492 self ._update_ns_ancestors (components , non_stub_match )
@@ -513,7 +525,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
513525 if verify and not verify_module (fscache , id , path_stubs , dir_prefix ):
514526 near_misses .append ((path_stubs , dir_prefix ))
515527 else :
516- return path_stubs
528+ return path_stubs , True
517529
518530 # Prefer package over module, i.e. baz/__init__.py* over baz.py*.
519531 for extension in PYTHON_EXTENSIONS :
@@ -523,7 +535,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
523535 if verify and not verify_module (fscache , id , path , dir_prefix ):
524536 near_misses .append ((path , dir_prefix ))
525537 continue
526- return path
538+ return path , True
527539
528540 # In namespace mode, register a potential namespace package
529541 if self .options and self .options .namespace_packages :
@@ -541,7 +553,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
541553 if verify and not verify_module (fscache , id , path , dir_prefix ):
542554 near_misses .append ((path , dir_prefix ))
543555 continue
544- return path
556+ return path , True
545557
546558 # In namespace mode, re-check those entries that had 'verify'.
547559 # Assume search path entries xxx, yyy and zzz, and we're
@@ -570,35 +582,35 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
570582 for path , dir_prefix in near_misses
571583 ]
572584 index = levels .index (max (levels ))
573- return near_misses [index ][0 ]
585+ return near_misses [index ][0 ], True
574586
575587 # Finally, we may be asked to produce an ancestor for an
576588 # installed package with a py.typed marker that is a
577589 # subpackage of a namespace package. We only fess up to these
578590 # if we would otherwise return "not found".
579591 ancestor = self .ns_ancestors .get (id )
580592 if ancestor is not None :
581- return ancestor
593+ return ancestor , True
582594
583595 approved_dist_name = stub_distribution_name (id )
584596 if approved_dist_name :
585597 if len (components ) == 1 :
586- return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED
598+ return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED , True
587599 # If we're a missing submodule of an already installed approved stubs, we don't want to
588600 # error with APPROVED_STUBS_NOT_INSTALLED, but rather want to return NOT_FOUND.
589601 for i in range (1 , len (components )):
590602 parent_id = "." .join (components [:i ])
591603 if stub_distribution_name (parent_id ) == approved_dist_name :
592604 break
593605 else :
594- return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED
606+ return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED , True
595607 if self .find_module (parent_id ) is ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED :
596- return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED
597- return ModuleNotFoundReason .NOT_FOUND
608+ return ModuleNotFoundReason .APPROVED_STUBS_NOT_INSTALLED , True
609+ return ModuleNotFoundReason .NOT_FOUND , True
598610
599611 if found_possible_third_party_missing_type_hints :
600- return ModuleNotFoundReason .FOUND_WITHOUT_TYPE_HINTS
601- return ModuleNotFoundReason .NOT_FOUND
612+ return ModuleNotFoundReason .FOUND_WITHOUT_TYPE_HINTS , can_cache_any_result
613+ return ModuleNotFoundReason .NOT_FOUND , True
602614
603615 def find_modules_recursive (self , module : str ) -> list [BuildSource ]:
604616 module_path = self .find_module (module , fast_path = True )
0 commit comments