Skip to content

Commit 480b153

Browse files
authored
Fix performance regression caused by #5465: daemon part (#5556)
This is part of the original PR #5544 that takes care of the daemon performance regression caused by #5465. This change should not affect semantics, only performance by avoiding cloning options for lots of modules in situation with `--follow-imports=skip`.
1 parent 2c9d11e commit 480b153

File tree

2 files changed

+195
-3
lines changed

2 files changed

+195
-3
lines changed

mypy/build.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,6 +2369,12 @@ def find_module_and_diagnose(manager: BuildManager,
23692369
raise CompileError(["mypy: can't find module '%s'" % id])
23702370

23712371

2372+
def find_module_simple(id: str, manager: BuildManager) -> Optional[str]:
2373+
"""Find a filesystem path for module `id` or `None` if not found."""
2374+
return manager.find_module_cache.find_module(id, manager.search_paths,
2375+
manager.options.python_executable)
2376+
2377+
23722378
def in_partial_package(id: str, manager: BuildManager) -> bool:
23732379
"""Check if a missing module can potentially be a part of a package.
23742380
@@ -2643,9 +2649,17 @@ def load_graph(sources: List[BuildSource], manager: BuildManager,
26432649
# (since direct dependencies reflect the imports found in the source)
26442650
# but A's cached *indirect* dependency on C is wrong.
26452651
dependencies = [dep for dep in st.dependencies if st.priorities.get(dep) != PRI_INDIRECT]
2646-
added = [dep for dep in st.suppressed
2647-
if manager.find_module_cache.find_module(dep, manager.search_paths,
2648-
manager.options.python_executable)]
2652+
if not manager.use_fine_grained_cache():
2653+
# TODO: Ideally we could skip here modules that appeared in st.suppressed
2654+
# because they are not in build with `follow-imports=skip`.
2655+
# This way we could avoid overhead of cloning options in `State.__init__()`
2656+
# below to get the option value. This is quite minor performance loss however.
2657+
added = [dep for dep in st.suppressed if find_module_simple(dep, manager)]
2658+
else:
2659+
# During initial loading we don't care about newly added modules,
2660+
# they will be taken care of during fine grained update. See also
2661+
# comment about this in `State.__init__()`.
2662+
added = []
26492663
for dep in st.ancestors + dependencies + st.suppressed:
26502664
# We don't want to recheck imports marked with '# type: ignore'
26512665
# so we ignore any suppressed module not explicitly re-included

test-data/unit/fine-grained-modules.test

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,3 +1947,181 @@ from b.foo import bar
19471947
x = '10'
19481948
[out]
19491949
==
1950+
1951+
[case testFineAddedMissingStubs]
1952+
# flags: --ignore-missing-imports
1953+
from missing import f
1954+
f(int())
1955+
[file missing.pyi.2]
1956+
def f(x: str) -> None: pass
1957+
[out]
1958+
==
1959+
main:3: error: Argument 1 to "f" has incompatible type "int"; expected "str"
1960+
1961+
[case testFineAddedMissingStubsPackage]
1962+
# flags: --ignore-missing-imports
1963+
import package.missing
1964+
package.missing.f(int())
1965+
[file package/__init__.pyi.2]
1966+
[file package/missing.pyi.2]
1967+
def f(x: str) -> None: pass
1968+
[out]
1969+
==
1970+
main:3: error: Argument 1 to "f" has incompatible type "int"; expected "str"
1971+
1972+
[case testFineAddedMissingStubsPackageFrom]
1973+
# flags: --ignore-missing-imports
1974+
from package import missing
1975+
missing.f(int())
1976+
[file package/__init__.pyi.2]
1977+
[file package/missing.pyi.2]
1978+
def f(x: str) -> None: pass
1979+
[out]
1980+
==
1981+
main:3: error: Argument 1 to "f" has incompatible type "int"; expected "str"
1982+
1983+
[case testFineAddedMissingStubsPackagePartial]
1984+
# flags: --ignore-missing-imports
1985+
import package.missing
1986+
package.missing.f(int())
1987+
[file package/__init__.pyi]
1988+
[file package/missing.pyi.2]
1989+
def f(x: str) -> None: pass
1990+
[out]
1991+
==
1992+
main:3: error: Argument 1 to "f" has incompatible type "int"; expected "str"
1993+
1994+
[case testFineAddedMissingStubsPackagePartialGetAttr]
1995+
import package.missing
1996+
package.missing.f(int())
1997+
[file package/__init__.pyi]
1998+
from typing import Any
1999+
def __getattr__(attr: str) -> Any: ...
2000+
[file package/missing.pyi.2]
2001+
def f(x: str) -> None: pass
2002+
[out]
2003+
==
2004+
main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str"
2005+
2006+
[case testFineAddedMissingStubsIgnore]
2007+
from missing import f # type: ignore
2008+
f(int())
2009+
[file missing.pyi.2]
2010+
def f(x: str) -> None: pass
2011+
[out]
2012+
==
2013+
main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str"
2014+
2015+
[case testFineAddedMissingStubsIgnorePackage]
2016+
import package.missing # type: ignore
2017+
package.missing.f(int())
2018+
[file package/__init__.pyi.2]
2019+
[file package/missing.pyi.2]
2020+
def f(x: str) -> None: pass
2021+
[out]
2022+
==
2023+
main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str"
2024+
2025+
[case testFineAddedMissingStubsIgnorePackageFrom]
2026+
from package import missing # type: ignore
2027+
missing.f(int())
2028+
[file package/__init__.pyi.2]
2029+
[file package/missing.pyi.2]
2030+
def f(x: str) -> None: pass
2031+
[out]
2032+
==
2033+
main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str"
2034+
2035+
[case testFineAddedMissingStubsIgnorePackagePartial]
2036+
import package.missing # type: ignore
2037+
package.missing.f(int())
2038+
[file package/__init__.pyi]
2039+
[file package/missing.pyi.2]
2040+
def f(x: str) -> None: pass
2041+
[out]
2042+
==
2043+
main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str"
2044+
2045+
[case testFineFollowImportSkipNotInvalidatedOnPresent]
2046+
# flags: --follow-imports=skip
2047+
# cmd: mypy main.py
2048+
[file main.py]
2049+
import other
2050+
[file other.py]
2051+
x = 1
2052+
[file other.py.2]
2053+
x = 'hi'
2054+
[stale]
2055+
[rechecked]
2056+
[out]
2057+
==
2058+
2059+
[case testFineFollowImportSkipNotInvalidatedOnPresentPackage]
2060+
# flags: --follow-imports=skip
2061+
# cmd: mypy main.py
2062+
[file main.py]
2063+
import other
2064+
[file other/__init__.py]
2065+
x = 1
2066+
[file other/__init__.py.2]
2067+
x = 'hi'
2068+
[stale]
2069+
[rechecked]
2070+
[out]
2071+
==
2072+
2073+
[case testFineFollowImportSkipNotInvalidatedOnAdded]
2074+
# flags: --follow-imports=skip --ignore-missing-imports
2075+
# cmd: mypy main.py
2076+
[file main.py]
2077+
import other
2078+
[file other.py.2]
2079+
x = 1
2080+
[stale]
2081+
[rechecked]
2082+
[out]
2083+
==
2084+
2085+
-- TODO: Fix this: stubs should be followed normally even with follow-imports=skip
2086+
[case testFineFollowImportSkipInvalidatedOnAddedStub-skip]
2087+
# flags: --follow-imports=skip --ignore-missing-imports
2088+
# cmd: mypy main.py
2089+
[file main.py]
2090+
import other
2091+
x: str = other.x
2092+
[file other.pyi.2]
2093+
x = 1
2094+
[stale main, other]
2095+
[rechecked main, other]
2096+
[out]
2097+
==
2098+
main:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")
2099+
2100+
[case testFineFollowImportSkipNotInvalidatedOnAddedStubOnFollowForStubs]
2101+
# flags: --follow-imports=skip --ignore-missing-imports --config-file=tmp/mypy.ini
2102+
# cmd: mypy main.py
2103+
[file main.py]
2104+
import other
2105+
[file other.pyi.2]
2106+
x = 1
2107+
[file mypy.ini]
2108+
[[mypy]
2109+
follow_imports_for_stubs = True
2110+
[stale]
2111+
[rechecked]
2112+
[out]
2113+
==
2114+
2115+
[case testFineAddedSkippedStubsPackageFrom]
2116+
# flags: --follow-imports=skip --ignore-missing-imports
2117+
# cmd: mypy main.py
2118+
# cmd2: mypy main.py package/__init__.py package/missing.py
2119+
[file main.py]
2120+
from package import missing
2121+
missing.f(int())
2122+
[file package/__init__.py]
2123+
[file package/missing.py]
2124+
def f(x: str) -> None: pass
2125+
[out]
2126+
==
2127+
main.py:2: error: Argument 1 to "f" has incompatible type "int"; expected "str"

0 commit comments

Comments
 (0)