Skip to content

Commit 4c1d5de

Browse files
Michael0x2agvanrossum
authored andcommitted
Reset cache on mypy change (#1952)
* Make modify invalidate cache if version id changes This commit modifies build.py to add an extra "version_id" attribute to the cache metadata. If the version id specified in mypy/version.py differs from the id stored in the cached data, mypy will automatically invalidate that cached data. * Include git hash in version.py during setup This commit modifies setup.py to automatically edit the installed mypy/version.py file during installation to include the git hash if applicable. If setup.py is not being run from a git repo, or if a git executable can't be found in the system, mypy will default to using the original version id.
1 parent 9a2352d commit 4c1d5de

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

mypy/build.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def build(sources: List[BuildSource],
137137
# Use stub builtins (to speed up test cases and to make them easier to
138138
# debug). This is a test-only feature, so assume our files are laid out
139139
# as in the source tree.
140-
root_dir = os.path.dirname(os.path.dirname(__file__))
140+
root_dir = dirname(dirname(__file__))
141141
lib_path.insert(0, os.path.join(root_dir, 'test-data', 'unit', 'lib-stub'))
142142
else:
143143
for source in sources:
@@ -172,7 +172,9 @@ def build(sources: List[BuildSource],
172172
ignore_prefix=os.getcwd(),
173173
source_set=source_set,
174174
reports=reports,
175-
options=options)
175+
options=options,
176+
version_id=__version__,
177+
)
176178

177179
try:
178180
dispatch(sources, manager)
@@ -285,6 +287,7 @@ def default_lib_path(data_dir: str, pyversion: Tuple[int, int]) -> List[str]:
285287
('child_modules', List[str]), # all submodules of the given module
286288
('options', Optional[Dict[str, bool]]), # build options
287289
('dep_prios', List[int]),
290+
('version_id', str), # mypy version for cache invalidation
288291
])
289292
# NOTE: dependencies + suppressed == all reachable imports;
290293
# suppressed contains those reachable imports that were prevented by
@@ -320,14 +323,16 @@ class BuildManager:
320323
options: Build options
321324
missing_modules: Set of modules that could not be imported encountered so far
322325
stale_modules: Set of modules that needed to be rechecked
326+
version_id: The current mypy version (based on commit id when possible)
323327
"""
324328

325329
def __init__(self, data_dir: str,
326330
lib_path: List[str],
327331
ignore_prefix: str,
328332
source_set: BuildSourceSet,
329333
reports: Reports,
330-
options: Options) -> None:
334+
options: Options,
335+
version_id: str) -> None:
331336
self.start_time = time.time()
332337
self.data_dir = data_dir
333338
self.errors = Errors(options.suppress_error_context)
@@ -336,6 +341,7 @@ def __init__(self, data_dir: str,
336341
self.source_set = source_set
337342
self.reports = reports
338343
self.options = options
344+
self.version_id = version_id
339345
self.semantic_analyzer = SemanticAnalyzer(lib_path, self.errors, options=options)
340346
self.modules = self.semantic_analyzer.modules
341347
self.semantic_analyzer_pass3 = ThirdPass(self.modules, self.errors)
@@ -717,14 +723,17 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache
717723
meta.get('child_modules', []),
718724
meta.get('options'),
719725
meta.get('dep_prios', []),
726+
meta.get('version_id'),
720727
)
721728
if (m.id != id or m.path != path or
722729
m.mtime is None or m.size is None or
723730
m.dependencies is None or m.data_mtime is None):
724731
return None
725732

726733
# Ignore cache if generated by an older mypy version.
727-
if m.options is None or len(m.dependencies) != len(m.dep_prios):
734+
if (m.version_id != manager.version_id
735+
or m.options is None
736+
or len(m.dependencies) != len(m.dep_prios)):
728737
return None
729738

730739
# Ignore cache if (relevant) options aren't the same.
@@ -810,6 +819,7 @@ def write_cache(id: str, path: str, tree: MypyFile,
810819
'child_modules': child_modules,
811820
'options': select_options_affecting_cache(manager.options),
812821
'dep_prios': dep_prios,
822+
'version_id': manager.version_id,
813823
}
814824
with open(meta_json_tmp, 'w') as f:
815825
json.dump(meta, f, sort_keys=True)

mypy/test/testgraph.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from mypy.myunit import Suite, assert_equal
66
from mypy.build import BuildManager, State
77
from mypy.build import topsort, strongly_connected_components, sorted_components, order_ascc
8+
from mypy.version import __version__
89
from mypy.options import Options
910

1011

@@ -38,7 +39,9 @@ def _make_manager(self):
3839
ignore_prefix='',
3940
source_set=None,
4041
reports=None,
41-
options=Options())
42+
options=Options(),
43+
version_id=__version__,
44+
)
4245
return manager
4346

4447
def test_sorted_components(self) -> None:

setup.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
exit(1)
1111

1212
from distutils.core import setup
13+
from distutils.command.build_py import build_py
1314
from mypy.version import __version__
1415
from mypy import git
1516

@@ -30,6 +31,19 @@
3031
'''.lstrip()
3132

3233

34+
def cache_version_id():
35+
"""Returns the version id to use for the incremental hash.
36+
37+
If setup.py is run from a git repo, the git commit hash will be
38+
included if possible. If not, then this function will fall back to
39+
using the default version id from mypy/version.py."""
40+
if git.is_git_repo('.') and git.have_git():
41+
return __version__ + '-' + git.git_revision('.').decode('utf-8')
42+
else:
43+
# Default fallback
44+
return __version__
45+
46+
3347
def find_data_files(base, globs):
3448
"""Find all interesting data files, for setup(data_files=)
3549
@@ -51,6 +65,19 @@ def find_data_files(base, globs):
5165

5266
return rv
5367

68+
69+
class CustomPythonBuild(build_py):
70+
def pin_version(self):
71+
path = os.path.join(self.build_lib, 'mypy')
72+
self.mkpath(path)
73+
with open(os.path.join(path, 'version.py'), 'w') as stream:
74+
stream.write('__version__ = "{}"\n'.format(cache_version_id()))
75+
76+
def run(self):
77+
self.execute(self.pin_version, ())
78+
build_py.run(self)
79+
80+
5481
data_files = []
5582

5683
data_files += find_data_files('typeshed', ['*.py', '*.pyi'])
@@ -93,4 +120,5 @@ def find_data_files(base, globs):
93120
scripts=scripts,
94121
data_files=data_files,
95122
classifiers=classifiers,
123+
cmdclass={'build_py': CustomPythonBuild},
96124
)

0 commit comments

Comments
 (0)