@@ -717,28 +717,6 @@ def parse_file(self, id: str, path: str, source: str, ignore_errors: bool) -> My
717
717
self .errors .set_file_ignored_lines (path , tree .ignored_lines , ignore_errors )
718
718
return tree
719
719
720
- def module_not_found (self , path : str , source : str , line : int , target : str ) -> None :
721
- self .errors .set_file (path , source )
722
- stub_msg = "(Stub files are from https://github.com/python/typeshed)"
723
- if target == 'builtins' :
724
- self .errors .report (line , 0 , "Cannot find 'builtins' module. Typeshed appears broken!" ,
725
- blocker = True )
726
- self .errors .raise_error ()
727
- elif ((self .options .python_version [0 ] == 2 and moduleinfo .is_py2_std_lib_module (target ))
728
- or (self .options .python_version [0 ] >= 3
729
- and moduleinfo .is_py3_std_lib_module (target ))):
730
- self .errors .report (
731
- line , 0 , "No library stub file for standard library module '{}'" .format (target ))
732
- self .errors .report (line , 0 , stub_msg , severity = 'note' , only_once = True )
733
- elif moduleinfo .is_third_party_module (target ):
734
- self .errors .report (line , 0 , "No library stub file for module '{}'" .format (target ))
735
- self .errors .report (line , 0 , stub_msg , severity = 'note' , only_once = True )
736
- else :
737
- self .errors .report (line , 0 , "Cannot find module named '{}'" .format (target ))
738
- self .errors .report (line , 0 , '(Perhaps setting MYPYPATH '
739
- 'or using the "--ignore-missing-imports" flag would help)' ,
740
- severity = 'note' , only_once = True )
741
-
742
720
def report_file (self ,
743
721
file : MypyFile ,
744
722
type_map : Dict [Expression , Type ],
@@ -1512,63 +1490,15 @@ def __init__(self,
1512
1490
self .fine_grained_deps = {}
1513
1491
if not path and source is None :
1514
1492
assert id is not None
1515
- file_id = id
1516
- if id == 'builtins' and self .options .python_version [0 ] == 2 :
1517
- # The __builtin__ module is called internally by mypy
1518
- # 'builtins' in Python 2 mode (similar to Python 3),
1519
- # but the stub file is __builtin__.pyi. The reason is
1520
- # that a lot of code hard-codes 'builtins.x' and it's
1521
- # easier to work it around like this. It also means
1522
- # that the implementation can mostly ignore the
1523
- # difference and just assume 'builtins' everywhere,
1524
- # which simplifies code.
1525
- file_id = '__builtin__'
1526
- path = manager .find_module_cache .find_module (file_id , manager .lib_path )
1527
- if path :
1528
- # For non-stubs, look at options.follow_imports:
1529
- # - normal (default) -> fully analyze
1530
- # - silent -> analyze but silence errors
1531
- # - skip -> don't analyze, make the type Any
1532
- follow_imports = self .options .follow_imports
1533
- if (follow_imports != 'normal'
1534
- and not root_source # Honor top-level modules
1535
- and (path .endswith ('.py' ) # Stubs are always normal
1536
- or self .options .follow_imports_for_stubs ) # except when they aren't
1537
- and id != 'builtins' ): # Builtins is always normal
1538
- if follow_imports == 'silent' :
1539
- # Still import it, but silence non-blocker errors.
1540
- manager .log ("Silencing %s (%s)" % (path , id ))
1541
- self .ignore_all = True
1542
- else :
1543
- # In 'error' mode, produce special error messages.
1544
- if id not in manager .missing_modules :
1545
- manager .log ("Skipping %s (%s)" % (path , id ))
1546
- if follow_imports == 'error' :
1547
- if ancestor_for :
1548
- self .skipping_ancestor (id , path , ancestor_for )
1549
- else :
1550
- self .skipping_module (id , path )
1551
- path = None
1552
- manager .missing_modules .add (id )
1553
- raise ModuleNotFound
1554
- else :
1555
- # Could not find a module. Typically the reason is a
1556
- # misspelled module name, missing stub, module not in
1557
- # search path or the module has not been installed.
1558
- if caller_state :
1559
- if not self .options .ignore_missing_imports :
1560
- save_import_context = manager .errors .import_context ()
1561
- manager .errors .set_import_context (caller_state .import_context )
1562
- manager .module_not_found (caller_state .xpath , caller_state .id ,
1563
- caller_line , id )
1564
- manager .errors .set_import_context (save_import_context )
1565
- manager .missing_modules .add (id )
1566
- raise ModuleNotFound
1567
- else :
1568
- # If we can't find a root source it's always fatal.
1569
- # TODO: This might hide non-fatal errors from
1570
- # root sources processed earlier.
1571
- raise CompileError (["mypy: can't find module '%s'" % id ])
1493
+ try :
1494
+ path , follow_imports = find_module_and_diagnose (
1495
+ manager , id , self .options , caller_state , caller_line ,
1496
+ ancestor_for , root_source )
1497
+ except ModuleNotFound :
1498
+ manager .missing_modules .add (id )
1499
+ raise
1500
+ if follow_imports == 'silent' :
1501
+ self .ignore_all = True
1572
1502
self .path = path
1573
1503
self .xpath = path or '<string>'
1574
1504
self .source = source
@@ -1604,35 +1534,6 @@ def __init__(self,
1604
1534
self .compute_dependencies ()
1605
1535
self .child_modules = set ()
1606
1536
1607
- def skipping_ancestor (self , id : str , path : str , ancestor_for : 'State' ) -> None :
1608
- # TODO: Read the path (the __init__.py file) and return
1609
- # immediately if it's empty or only contains comments.
1610
- # But beware, some package may be the ancestor of many modules,
1611
- # so we'd need to cache the decision.
1612
- manager = self .manager
1613
- manager .errors .set_import_context ([])
1614
- manager .errors .set_file (ancestor_for .xpath , ancestor_for .id )
1615
- manager .errors .report (- 1 , - 1 , "Ancestor package '%s' ignored" % (id ,),
1616
- severity = 'note' , only_once = True )
1617
- manager .errors .report (- 1 , - 1 ,
1618
- "(Using --follow-imports=error, submodule passed on command line)" ,
1619
- severity = 'note' , only_once = True )
1620
-
1621
- def skipping_module (self , id : str , path : str ) -> None :
1622
- assert self .caller_state , (id , path )
1623
- manager = self .manager
1624
- save_import_context = manager .errors .import_context ()
1625
- manager .errors .set_import_context (self .caller_state .import_context )
1626
- manager .errors .set_file (self .caller_state .xpath , self .caller_state .id )
1627
- line = self .caller_line
1628
- manager .errors .report (line , 0 ,
1629
- "Import of '%s' ignored" % (id ,),
1630
- severity = 'note' )
1631
- manager .errors .report (line , 0 ,
1632
- "(Using --follow-imports=error, module not passed on command line)" ,
1633
- severity = 'note' , only_once = True )
1634
- manager .errors .set_import_context (save_import_context )
1635
-
1636
1537
def add_ancestors (self ) -> None :
1637
1538
if self .path is not None :
1638
1539
_ , name = os .path .split (self .path )
@@ -2008,6 +1909,35 @@ def write_cache(self) -> None:
2008
1909
self .mark_interface_stale ()
2009
1910
self .interface_hash = new_interface_hash
2010
1911
1912
+ def verify_dependencies (self ) -> None :
1913
+ """Report errors for import targets in modules that don't exist."""
1914
+ # Strip out indirect dependencies. See comment in build.load_graph().
1915
+ manager = self .manager
1916
+ dependencies = [dep for dep in self .dependencies
1917
+ if self .priorities .get (dep ) != PRI_INDIRECT ]
1918
+ assert self .ancestors is not None
1919
+ for dep in dependencies + self .suppressed + self .ancestors :
1920
+ options = manager .options .clone_for_module (dep )
1921
+ if dep not in manager .modules and not options .ignore_missing_imports :
1922
+ line = self .dep_line_map .get (dep , 1 )
1923
+ try :
1924
+ if dep in self .ancestors :
1925
+ state , ancestor = None , self # type: (Optional[State], Optional[State])
1926
+ else :
1927
+ state , ancestor = self , None
1928
+ # Called just for its side effects of producing diagnostics.
1929
+ find_module_and_diagnose (
1930
+ manager , dep , options ,
1931
+ caller_state = state , caller_line = line ,
1932
+ ancestor_for = ancestor )
1933
+ except (ModuleNotFound , CompileError ):
1934
+ # Swallow up any ModuleNotFounds or CompilerErrors while generating
1935
+ # a diagnostic. CompileErrors may get generated in
1936
+ # fine-grained mode when an __init__.py is deleted, if a module
1937
+ # that was in that package has targets reprocessed before
1938
+ # it is renamed.
1939
+ pass
1940
+
2011
1941
def dependency_priorities (self ) -> List [int ]:
2012
1942
return [self .priorities .get (dep , PRI_HIGH ) for dep in self .dependencies ]
2013
1943
@@ -2019,6 +1949,149 @@ def generate_unused_ignore_notes(self) -> None:
2019
1949
self .manager .errors .generate_unused_ignore_notes (self .xpath )
2020
1950
2021
1951
1952
+ # Module import and diagnostic glue
1953
+
1954
+
1955
+ def find_module_and_diagnose (manager : BuildManager ,
1956
+ id : str ,
1957
+ options : Options ,
1958
+ caller_state : 'Optional[State]' = None ,
1959
+ caller_line : int = 0 ,
1960
+ ancestor_for : 'Optional[State]' = None ,
1961
+ root_source : bool = False ) -> Tuple [str , str ]:
1962
+ """Find a module by name, respecting follow_imports and producing diagnostics.
1963
+
1964
+ Args:
1965
+ id: module to find
1966
+ options: the options for the module being loaded
1967
+ caller_state: the state of the importing module, if applicable
1968
+ caller_line: the line number of the import
1969
+ ancestor_for: the child module this is an ancestor of, if applicable
1970
+ root_source: whether this source was specified on the command line
1971
+
1972
+ The specified value of follow_imports for a module can be overridden
1973
+ if the module is specified on the command line or if it is a stub,
1974
+ so we compute and return the "effective" follow_imports of the module.
1975
+
1976
+ Returns a tuple containing (file path, target's effective follow_imports setting)
1977
+ """
1978
+ file_id = id
1979
+ if id == 'builtins' and options .python_version [0 ] == 2 :
1980
+ # The __builtin__ module is called internally by mypy
1981
+ # 'builtins' in Python 2 mode (similar to Python 3),
1982
+ # but the stub file is __builtin__.pyi. The reason is
1983
+ # that a lot of code hard-codes 'builtins.x' and it's
1984
+ # easier to work it around like this. It also means
1985
+ # that the implementation can mostly ignore the
1986
+ # difference and just assume 'builtins' everywhere,
1987
+ # which simplifies code.
1988
+ file_id = '__builtin__'
1989
+ path = manager .find_module_cache .find_module (file_id , manager .lib_path )
1990
+ if path :
1991
+ # For non-stubs, look at options.follow_imports:
1992
+ # - normal (default) -> fully analyze
1993
+ # - silent -> analyze but silence errors
1994
+ # - skip -> don't analyze, make the type Any
1995
+ follow_imports = options .follow_imports
1996
+ if (root_source # Honor top-level modules
1997
+ or (not path .endswith ('.py' ) # Stubs are always normal
1998
+ and not options .follow_imports_for_stubs ) # except when they aren't
1999
+ or id == 'builtins' ): # Builtins is always normal
2000
+ follow_imports = 'normal'
2001
+
2002
+ if follow_imports == 'silent' :
2003
+ # Still import it, but silence non-blocker errors.
2004
+ manager .log ("Silencing %s (%s)" % (path , id ))
2005
+ elif follow_imports == 'skip' or follow_imports == 'error' :
2006
+ # In 'error' mode, produce special error messages.
2007
+ if id not in manager .missing_modules :
2008
+ manager .log ("Skipping %s (%s)" % (path , id ))
2009
+ if follow_imports == 'error' :
2010
+ if ancestor_for :
2011
+ skipping_ancestor (manager , id , path , ancestor_for )
2012
+ else :
2013
+ skipping_module (manager , caller_line , caller_state ,
2014
+ id , path )
2015
+ raise ModuleNotFound
2016
+
2017
+ return (path , follow_imports )
2018
+ else :
2019
+ # Could not find a module. Typically the reason is a
2020
+ # misspelled module name, missing stub, module not in
2021
+ # search path or the module has not been installed.
2022
+ if caller_state :
2023
+ if not options .ignore_missing_imports :
2024
+ module_not_found (manager , caller_line , caller_state , id )
2025
+ raise ModuleNotFound
2026
+ else :
2027
+ # If we can't find a root source it's always fatal.
2028
+ # TODO: This might hide non-fatal errors from
2029
+ # root sources processed earlier.
2030
+ raise CompileError (["mypy: can't find module '%s'" % id ])
2031
+
2032
+
2033
+ def module_not_found (manager : BuildManager , line : int , caller_state : State ,
2034
+ target : str ) -> None :
2035
+ errors = manager .errors
2036
+ save_import_context = errors .import_context ()
2037
+ errors .set_import_context (caller_state .import_context )
2038
+ errors .set_file (caller_state .xpath , caller_state .id )
2039
+ stub_msg = "(Stub files are from https://github.com/python/typeshed)"
2040
+ if target == 'builtins' :
2041
+ errors .report (line , 0 , "Cannot find 'builtins' module. Typeshed appears broken!" ,
2042
+ blocker = True )
2043
+ errors .raise_error ()
2044
+ elif ((manager .options .python_version [0 ] == 2 and moduleinfo .is_py2_std_lib_module (target ))
2045
+ or (manager .options .python_version [0 ] >= 3
2046
+ and moduleinfo .is_py3_std_lib_module (target ))):
2047
+ errors .report (
2048
+ line , 0 , "No library stub file for standard library module '{}'" .format (target ))
2049
+ errors .report (line , 0 , stub_msg , severity = 'note' , only_once = True )
2050
+ elif moduleinfo .is_third_party_module (target ):
2051
+ errors .report (line , 0 , "No library stub file for module '{}'" .format (target ))
2052
+ errors .report (line , 0 , stub_msg , severity = 'note' , only_once = True )
2053
+ else :
2054
+ errors .report (line , 0 , "Cannot find module named '{}'" .format (target ))
2055
+ errors .report (line , 0 , '(Perhaps setting MYPYPATH '
2056
+ 'or using the "--ignore-missing-imports" flag would help)' ,
2057
+ severity = 'note' , only_once = True )
2058
+ errors .set_import_context (save_import_context )
2059
+
2060
+
2061
+ def skipping_module (manager : BuildManager , line : int , caller_state : Optional [State ],
2062
+ id : str , path : str ) -> None :
2063
+ """Produce an error for an import ignored due to --follow_imports=error"""
2064
+ assert caller_state , (id , path )
2065
+ save_import_context = manager .errors .import_context ()
2066
+ manager .errors .set_import_context (caller_state .import_context )
2067
+ manager .errors .set_file (caller_state .xpath , caller_state .id )
2068
+ manager .errors .report (line , 0 ,
2069
+ "Import of '%s' ignored" % (id ,),
2070
+ severity = 'note' )
2071
+ manager .errors .report (line , 0 ,
2072
+ "(Using --follow-imports=error, module not passed on command line)" ,
2073
+ severity = 'note' , only_once = True )
2074
+ manager .errors .set_import_context (save_import_context )
2075
+
2076
+
2077
+ def skipping_ancestor (manager : BuildManager , id : str , path : str , ancestor_for : 'State' ) -> None :
2078
+ """Produce an error for an ancestor ignored due to --follow_imports=error"""
2079
+ # TODO: Read the path (the __init__.py file) and return
2080
+ # immediately if it's empty or only contains comments.
2081
+ # But beware, some package may be the ancestor of many modules,
2082
+ # so we'd need to cache the decision.
2083
+ manager .errors .set_import_context ([])
2084
+ manager .errors .set_file (ancestor_for .xpath , ancestor_for .id )
2085
+ manager .errors .report (- 1 , - 1 , "Ancestor package '%s' ignored" % (id ,),
2086
+ severity = 'note' , only_once = True )
2087
+ manager .errors .report (- 1 , - 1 ,
2088
+ "(Using --follow-imports=error, submodule passed on command line)" ,
2089
+ severity = 'note' , only_once = True )
2090
+
2091
+
2092
+ # The driver
2093
+
2094
+
2022
2095
def dispatch (sources : List [BuildSource ], manager : BuildManager ) -> Graph :
2023
2096
manager .log ()
2024
2097
manager .log ("Mypy version %s" % __version__ )
0 commit comments