Fast path to find modules by looking through the input sources This is only used when --fast-module-lookup is passed on the command line.
(self, id: str)
| 211 | self.ns_ancestors.clear() |
| 212 | |
| 213 | def find_module_via_source_set(self, id: str) -> ModuleSearchResult | None: |
| 214 | """Fast path to find modules by looking through the input sources |
| 215 | |
| 216 | This is only used when --fast-module-lookup is passed on the command line.""" |
| 217 | if not self.source_set: |
| 218 | return None |
| 219 | |
| 220 | p = self.source_set.source_modules.get(id, None) |
| 221 | if p and self.fscache.isfile(p): |
| 222 | # We need to make sure we still have __init__.py all the way up |
| 223 | # otherwise we might have false positives compared to slow path |
| 224 | # in case of deletion of init files, which is covered by some tests. |
| 225 | # TODO: are there some combination of flags in which this check should be skipped? |
| 226 | d = os.path.dirname(p) |
| 227 | for _ in range(id.count(".")): |
| 228 | if not any( |
| 229 | self.fscache.isfile(os_path_join(d, "__init__" + x)) for x in PYTHON_EXTENSIONS |
| 230 | ): |
| 231 | return None |
| 232 | d = os.path.dirname(d) |
| 233 | return p |
| 234 | |
| 235 | idx = id.rfind(".") |
| 236 | if idx != -1: |
| 237 | # When we're looking for foo.bar.baz and can't find a matching module |
| 238 | # in the source set, look up for a foo.bar module. |
| 239 | parent = self.find_module_via_source_set(id[:idx]) |
| 240 | if parent is None or not isinstance(parent, str): |
| 241 | return None |
| 242 | |
| 243 | basename, ext = os.path.splitext(parent) |
| 244 | if not any(parent.endswith("__init__" + x) for x in PYTHON_EXTENSIONS) and ( |
| 245 | ext in PYTHON_EXTENSIONS and not self.fscache.isdir(basename) |
| 246 | ): |
| 247 | # If we do find such a *module* (and crucially, we don't want a package, |
| 248 | # hence the filtering out of __init__ files, and checking for the presence |
| 249 | # of a folder with a matching name), then we can be pretty confident that |
| 250 | # 'baz' will either be a top-level variable in foo.bar, or will not exist. |
| 251 | # |
| 252 | # Either way, spelunking in other search paths for another 'foo.bar.baz' |
| 253 | # module should be avoided because: |
| 254 | # 1. in the unlikely event that one were found, it's highly likely that |
| 255 | # it would be unrelated to the source being typechecked and therefore |
| 256 | # more likely to lead to erroneous results |
| 257 | # 2. as described in _find_module, in some cases the search itself could |
| 258 | # potentially waste significant amounts of time |
| 259 | return ModuleNotFoundReason.NOT_FOUND |
| 260 | return None |
| 261 | |
| 262 | def find_lib_path_dirs(self, id: str, lib_path: tuple[str, ...]) -> PackageDirs: |
| 263 | """Find which elements of a lib_path have the directory a module needs to exist.""" |
no test coverage detected