@@ -690,8 +690,7 @@ def propagate_changes_using_dependencies(
690
690
if num_iter > MAX_ITER :
691
691
raise RuntimeError ('Max number of iterations (%d) reached (endless loop?)' % MAX_ITER )
692
692
693
- todo = find_targets_recursive (manager , triggered , deps ,
694
- manager .modules , up_to_date_modules )
693
+ todo = find_targets_recursive (manager , triggered , deps , up_to_date_modules )
695
694
# Also process targets that used to have errors, as otherwise some
696
695
# errors might be lost.
697
696
for target in targets_with_errors :
@@ -700,7 +699,7 @@ def propagate_changes_using_dependencies(
700
699
if id not in todo :
701
700
todo [id ] = set ()
702
701
manager .log_fine_grained ('process target with error: %s' % target )
703
- todo [id ].update (lookup_target (manager . modules , target ))
702
+ todo [id ].update (lookup_target (manager , target ))
704
703
triggered = set ()
705
704
# TODO: Preserve order (set is not optimal)
706
705
for id , nodes in sorted (todo .items (), key = lambda x : x [0 ]):
@@ -727,7 +726,6 @@ def find_targets_recursive(
727
726
manager : BuildManager ,
728
727
triggers : Set [str ],
729
728
deps : Dict [str , Set [str ]],
730
- modules : Dict [str , MypyFile ],
731
729
up_to_date_modules : Set [str ]) -> Dict [str , Set [DeferredNode ]]:
732
730
"""Find names of all targets that need to reprocessed, given some triggers.
733
731
@@ -748,7 +746,7 @@ def find_targets_recursive(
748
746
if target .startswith ('<' ):
749
747
worklist |= deps .get (target , set ()) - processed
750
748
else :
751
- module_id = module_prefix (modules , target )
749
+ module_id = module_prefix (manager . modules , target )
752
750
if module_id is None :
753
751
# Deleted module.
754
752
continue
@@ -758,7 +756,7 @@ def find_targets_recursive(
758
756
if module_id not in result :
759
757
result [module_id ] = set ()
760
758
manager .log_fine_grained ('process: %s' % target )
761
- deferred = lookup_target (modules , target )
759
+ deferred = lookup_target (manager , target )
762
760
result [module_id ].update (deferred )
763
761
764
762
return result
@@ -794,8 +792,11 @@ def key(node: DeferredNode) -> int:
794
792
# TODO: ignore_all argument to set_file_ignored_lines
795
793
manager .errors .set_file_ignored_lines (file_node .path , file_node .ignored_lines )
796
794
797
- targets = {target_from_node (module_id , node .node )
798
- for node in nodes }
795
+ targets = set ()
796
+ for node in nodes :
797
+ target = target_from_node (module_id , node .node )
798
+ if target is not None :
799
+ targets .add (target )
799
800
manager .errors .clear_errors_in_targets (file_node .path , targets )
800
801
801
802
# Strip semantic analysis information.
@@ -897,11 +898,18 @@ def update_deps(module_id: str,
897
898
deps .setdefault (trigger , set ()).update (targets )
898
899
899
900
900
- def lookup_target (modules : Dict [str , MypyFile ], target : str ) -> List [DeferredNode ]:
901
+ def lookup_target (manager : BuildManager ,
902
+ target : str ) -> List [DeferredNode ]:
901
903
"""Look up a target by fully-qualified name."""
904
+
905
+ def not_found () -> None :
906
+ manager .log_fine_grained (
907
+ "Can't find matching target for %s (stale dependency?)" % target )
908
+
909
+ modules = manager .modules
902
910
items = split_target (modules , target )
903
911
if items is None :
904
- # Deleted target
912
+ not_found () # Stale dependency
905
913
return []
906
914
module , rest = items
907
915
if rest :
@@ -916,12 +924,11 @@ def lookup_target(modules: Dict[str, MypyFile], target: str) -> List[DeferredNod
916
924
if isinstance (node , TypeInfo ):
917
925
active_class = node
918
926
active_class_name = node .name ()
919
- # TODO: Is it possible for the assertion to fail?
920
927
if isinstance (node , MypyFile ):
921
928
file = node
922
- assert isinstance (node , (MypyFile , TypeInfo ))
923
- if c not in node .names :
924
- # Deleted target
929
+ if ( not isinstance (node , (MypyFile , TypeInfo ))
930
+ or c not in node .names ) :
931
+ not_found () # Stale dependency
925
932
return []
926
933
node = node .names [c ].node
927
934
if isinstance (node , TypeInfo ):
@@ -930,18 +937,33 @@ def lookup_target(modules: Dict[str, MypyFile], target: str) -> List[DeferredNod
930
937
# typically a module top-level, since we don't support processing class
931
938
# bodies as separate entitites for simplicity.
932
939
assert file is not None
940
+ if node .fullname () != target :
941
+ # This is a reference to a different TypeInfo, likely due to a stale dependency.
942
+ # Processing them would spell trouble -- for example, we could be refreshing
943
+ # a deserialized TypeInfo with missing attributes.
944
+ not_found ()
945
+ return []
933
946
result = [DeferredNode (file , None , None )]
934
947
for name , symnode in node .names .items ():
935
948
node = symnode .node
936
949
if isinstance (node , FuncDef ):
937
- result .extend (lookup_target (modules , target + '.' + name ))
950
+ result .extend (lookup_target (manager , target + '.' + name ))
938
951
return result
939
952
if isinstance (node , Decorator ):
940
953
# Decorator targets actually refer to the function definition only.
941
954
node = node .func
942
- assert isinstance (node , (FuncDef ,
955
+ if not isinstance (node , (FuncDef ,
943
956
MypyFile ,
944
- OverloadedFuncDef )), 'unexpected type: %s' % type (node )
957
+ OverloadedFuncDef )):
958
+ # The target can't be refreshed. It's possible that the target was
959
+ # changed to another type and we have a stale dependency pointing to it.
960
+ not_found ()
961
+ return []
962
+ if node .fullname () != target :
963
+ # Stale reference points to something unexpected. We shouldn't process since the
964
+ # context will be wrong and it could be a partially initialized deserialized node.
965
+ not_found ()
966
+ return []
945
967
return [DeferredNode (node , active_class_name , active_class )]
946
968
947
969
@@ -950,14 +972,20 @@ def is_verbose(manager: BuildManager) -> bool:
950
972
951
973
952
974
def target_from_node (module : str ,
953
- node : Union [FuncDef , MypyFile , OverloadedFuncDef , LambdaExpr ]) -> str :
975
+ node : Union [FuncDef , MypyFile , OverloadedFuncDef , LambdaExpr ]
976
+ ) -> Optional [str ]:
954
977
"""Return the target name corresponding to a deferred node.
955
978
956
979
Args:
957
980
module: Must be module id of the module that defines 'node'
981
+
982
+ Returns the target name, or None if the node is not a valid target in the given
983
+ module (for example, if it's actually defined in another module).
958
984
"""
959
985
if isinstance (node , MypyFile ):
960
- assert module == node .fullname ()
986
+ if module != node .fullname ():
987
+ # Actually a reference to another module -- likely a stale dependency.
988
+ return None
961
989
return module
962
990
elif isinstance (node , (OverloadedFuncDef , FuncDef )):
963
991
if node .info is not None :
0 commit comments