@@ -860,16 +860,17 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache
860
860
"""
861
861
# TODO: May need to take more build options into account
862
862
meta_json, data_json = get_cache_names(id, path, manager)
863
- manager.trace('Looking for {} {}'.format(id, data_json ))
863
+ manager.trace('Looking for {} at {}'.format(id, meta_json ))
864
864
if not os.path.exists(meta_json):
865
- manager.trace ('Could not load cache for {}: could not find {}'.format(id, meta_json))
865
+ manager.log ('Could not load cache for {}: could not find {}'.format(id, meta_json))
866
866
return None
867
867
with open(meta_json, 'r') as f:
868
868
meta_str = f.read()
869
869
manager.trace('Meta {} {}'.format(id, meta_str.rstrip()))
870
870
meta = json.loads(meta_str) # TODO: Errors
871
871
if not isinstance(meta, dict):
872
- manager.trace('Could not load cache for {}: meta cache is not a dict'.format(id))
872
+ manager.log('Could not load cache for {}: meta cache is not a dict: {}'
873
+ .format(id, repr(meta)))
873
874
return None
874
875
m = CacheMeta(
875
876
meta.get('id'),
@@ -891,27 +892,36 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache
891
892
if (m.id != id or
892
893
m.mtime is None or m.size is None or
893
894
m.dependencies is None or m.data_mtime is None):
894
- manager.trace ('Metadata abandoned for {}: attributes are missing'.format(id))
895
+ manager.log ('Metadata abandoned for {}: attributes are missing'.format(id))
895
896
return None
896
897
897
898
# Ignore cache if generated by an older mypy version.
898
899
if ((m.version_id != manager.version_id and not manager.options.skip_version_check)
899
900
or m.options is None
900
901
or len(m.dependencies) != len(m.dep_prios)):
901
- manager.trace ('Metadata abandoned for {}: new attributes are missing'.format(id))
902
+ manager.log ('Metadata abandoned for {}: new attributes are missing'.format(id))
902
903
return None
903
904
904
905
# Ignore cache if (relevant) options aren't the same.
906
+ # Note that it's fine to mutilate cached_options since it's only used here.
905
907
cached_options = m.options
906
908
current_options = manager.options.clone_for_module(id).select_options_affecting_cache()
907
909
if manager.options.quick_and_dirty:
908
910
# In quick_and_dirty mode allow non-quick_and_dirty cache files.
909
911
cached_options['quick_and_dirty'] = True
910
- if not cached_options.get('platform') and manager.options.skip_version_check:
911
- # Older versions didn't write platform.
912
- cached_options['platform'] = manager.options.platform
912
+ if manager.options.skip_version_check:
913
+ # When we're lax about version we're also lax about platform.
914
+ cached_options['platform'] = current_options['platform']
915
+ if 'debug_cache' in cached_options:
916
+ # Older versions included debug_cache, but it's silly to compare it.
917
+ del cached_options['debug_cache']
913
918
if cached_options != current_options:
914
- manager.trace('Metadata abandoned for {}: options differ'.format(id))
919
+ manager.log('Metadata abandoned for {}: options differ'.format(id))
920
+ if manager.options.verbosity >= 2:
921
+ for key in sorted(set(cached_options) | set(current_options)):
922
+ if cached_options.get(key) != current_options.get(key):
923
+ manager.trace(' {}: {} != {}'
924
+ .format(key, cached_options.get(key), current_options.get(key)))
915
925
return None
916
926
917
927
return m
@@ -948,41 +958,63 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: str,
948
958
# we use cache data file mtime to propagate information about changes in the dependencies.
949
959
950
960
if meta is None:
961
+ manager.log('Metadata not found for {}'.format(id))
962
+ return None
963
+
964
+ # Check data_json; assume if its mtime matches it's good.
965
+ # TODO: stat() errors
966
+ data_mtime = getmtime(meta.data_json)
967
+ if data_mtime != meta.data_mtime:
968
+ manager.log('Metadata abandoned for {}: data cache is modified'.format(id))
951
969
return None
952
970
953
971
# TODO: Share stat() outcome with find_module()
954
972
path = os.path.abspath(path)
955
973
st = manager.get_stat(path) # TODO: Errors
956
- if st.st_size != meta.size:
974
+ size = st.st_size
975
+ if size != meta.size:
957
976
manager.log('Metadata abandoned for {}: file {} has different size'.format(id, path))
958
977
return None
959
978
960
- if int(st.st_mtime) != meta.mtime or path != meta.path:
979
+ mtime = int(st.st_mtime)
980
+ if mtime != meta.mtime or path != meta.path:
961
981
with open(path, 'rb') as f:
962
982
source_hash = hashlib.md5(f.read()).hexdigest()
963
983
if source_hash != meta.hash:
964
984
manager.log('Metadata abandoned for {}: file {} has different hash'.format(id, path))
965
985
return None
966
986
else:
967
- manager.log('Metadata ok for {}: file {} (match on path, size, hash)'.format(id, path))
968
987
# Optimization: update mtime and path (otherwise, this mismatch will reappear).
969
- meta = meta._replace(mtime=int(st.st_mtime), path=path)
988
+ meta = meta._replace(mtime=mtime, path=path)
989
+ # Construct a dict we can pass to json.dumps() (compare to write_cache()).
990
+ meta_dict = {
991
+ 'id': id,
992
+ 'path': path,
993
+ 'mtime': mtime,
994
+ 'size': size,
995
+ 'hash': source_hash,
996
+ 'data_mtime': data_mtime,
997
+ 'dependencies': meta.dependencies,
998
+ 'suppressed': meta.suppressed,
999
+ 'child_modules': meta.child_modules,
1000
+ 'options': (manager.options.clone_for_module(id)
1001
+ .select_options_affecting_cache()),
1002
+ 'dep_prios': meta.dep_prios,
1003
+ 'interface_hash': meta.interface_hash,
1004
+ 'version_id': manager.version_id,
1005
+ }
970
1006
if manager.options.debug_cache:
971
- meta_str = json.dumps(meta , indent=2, sort_keys=True)
1007
+ meta_str = json.dumps(meta_dict , indent=2, sort_keys=True)
972
1008
else:
973
- meta_str = json.dumps(meta )
1009
+ meta_str = json.dumps(meta_dict )
974
1010
meta_json, _ = get_cache_names(id, path, manager)
975
1011
manager.log('Updating mtime for {}: file {}, meta {}, mtime {}'
976
1012
.format(id, path, meta_json, meta.mtime))
977
- atomic_write(meta_json, meta_str) # Ignore errors, since this is just an optimization.
1013
+ atomic_write(meta_json, meta_str, '\n') # Ignore errors, it's just an optimization.
1014
+ return meta
978
1015
979
- # It's a match on (id, path, mtime/hash, size).
980
- # Check data_json; assume if its mtime matches it's good.
981
- # TODO: stat() errors
982
- if getmtime(meta.data_json) != meta.data_mtime:
983
- manager.log('Metadata abandoned for {}: data cache is modified'.format(id))
984
- return None
985
- manager.log('Found {} {} (metadata is fresh)'.format(id, meta.data_json))
1016
+ # It's a match on (id, path, size, hash, mtime).
1017
+ manager.log('Metadata fresh for {}: file {}'.format(id, path))
986
1018
return meta
987
1019
988
1020
@@ -1096,7 +1128,7 @@ def write_cache(id: str, path: str, tree: MypyFile,
1096
1128
meta_str = json.dumps(meta, indent=2, sort_keys=True)
1097
1129
else:
1098
1130
meta_str = json.dumps(meta)
1099
- if not atomic_write(meta_json, meta_str):
1131
+ if not atomic_write(meta_json, meta_str, '\n' ):
1100
1132
# Most likely the error is the replace() call
1101
1133
# (see https://github.com/python/mypy/issues/3215).
1102
1134
# The next run will simply find the cache entry out of date.
0 commit comments