@@ -1107,11 +1107,20 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
11071107 suggestion = _compute_suggestion_error (exc_value , exc_traceback , wrong_name )
11081108 if suggestion :
11091109 self ._str += f". Did you mean: '{ suggestion } '?"
1110- elif exc_type and issubclass (exc_type , ModuleNotFoundError ) and \
1111- sys .flags .no_site and \
1112- getattr (exc_value , "name" , None ) not in sys .stdlib_module_names :
1113- self ._str += (". Site initialization is disabled, did you forget to "
1114- + "add the site-packages directory to sys.path?" )
1110+ elif exc_type and issubclass (exc_type , ModuleNotFoundError ) and \
1111+ getattr (exc_value , "name" , None ):
1112+ wrong_name = getattr (exc_value , "name" , None )
1113+ suggestion = _compute_suggestion_error (exc_value , exc_traceback , wrong_name )
1114+ self ._str = exc_value .msg
1115+ if suggestion :
1116+ self ._str += f". Did you mean: '{ suggestion } '?"
1117+ if sys .flags .no_site and getattr (exc_value , "name" , None ) not in sys .stdlib_module_names :
1118+ if not suggestion :
1119+ self ._str += (". Site initialization is disabled, did you forget to "
1120+ + "add the site-packages directory to sys.path?" )
1121+ else :
1122+ self ._str += ("Or did you forget to add the site-packages directory to sys.path? The "
1123+ + "site initialization is disabled" )
11151124 elif exc_type and issubclass (exc_type , (NameError , AttributeError )) and \
11161125 getattr (exc_value , "name" , None ) is not None :
11171126 wrong_name = getattr (exc_value , "name" , None )
@@ -1634,7 +1643,100 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
16341643 if wrong_name [:1 ] != '_' :
16351644 d = [x for x in d if x [:1 ] != '_' ]
16361645 except Exception :
1637- return None
1646+ scan_dir , find_all_packages = _find_all_packages ()
1647+ import os
1648+ list_d = find_all_packages ()
1649+ _module_name = exc_value .name
1650+ wrong_name_list = _module_name .split ("." )
1651+ module_name = wrong_name_list [0 ]
1652+ if module_name not in sys .modules :
1653+ wrong_name = module_name
1654+ exc_value .msg = f"no module named '{ module_name } '"
1655+ if len (wrong_name_list ) == 1 :
1656+ if (_closed_name := _calculate_closed_name (module_name , sorted (sys .stdlib_module_names ))):
1657+ return _closed_name # stdlib first
1658+ _close_name_list = []
1659+ for i in list_d :
1660+ module_result = _calculate_closed_name (wrong_name , i )
1661+ if module_result :
1662+ _close_name_list .append (module_result )
1663+ _close_name_list .sorted ()
1664+ return _closed_name_list [0 ]
1665+ else :
1666+ if wrong_name in sum (list_d , []):
1667+ path = ""
1668+ for i in sys .path :
1669+ if i and isinstance (i , str ) and not i .endswith ("idlelib" ):
1670+ if wrong_name in scan_dir (i ):
1671+ path = f"{ i } /{ wrong_name } "
1672+ break
1673+ else :
1674+ if (_closed_name := _calculate_closed_name (module_name , sorted (sys .stdlib_module_names ))):
1675+ return _closed_name
1676+ _close_name_list = []
1677+ for i in list_d :
1678+ module_result = _calculate_closed_name (wrong_name , i )
1679+ if module_result :
1680+ _close_name_list .append (module_result )
1681+ _close_name_list .sorted ()
1682+ return _closed_name_list [0 ]
1683+ else :
1684+ if (_closed_name := _calculate_closed_name (module_name , sorted (sys .stdlib_module_names ))):
1685+ return _closed_name
1686+ _close_name_list = []
1687+ for i in list_d :
1688+ module_result = _calculate_closed_name (wrong_name , i )
1689+ if module_result :
1690+ _close_name_list .append (module_result )
1691+ _close_name_list .sorted ()
1692+ return _closed_name_list [0 ]
1693+
1694+ if not os .path .exists (path ) or not os .path .isdir (path ):
1695+ exc_value .msg = f"module '{ module_name } ' has no child module '{ wrong_name_list [1 ]} '; '{ module_name } ' is not a package"
1696+ return None
1697+ index = 0
1698+ for i in wrong_name_list [1 :]:
1699+ index += 1
1700+ _child_modules_d = scan_dir (path )
1701+ original_module_name = module_name
1702+ if wrong_name_list [index ] not in _child_modules_d :
1703+ exc_value .msg = f"module '{ module_name } ' has no child module '{ i } '"
1704+ wrong_name = i
1705+ d = _child_modules_d
1706+ break
1707+ path += f"/{ i } "
1708+ if not os .path .exists (path ) or not os .path .isdir (path ) and len (wrong_name_list ) > index + 1 :
1709+ module_name += "." + i
1710+ exc_value .msg = f"module '{ module_name } ' has no child module '{ wrong_name_list [index + 1 ]} '; '{ module_name } ' is not a package"
1711+ return None
1712+ module_name += "." + i
1713+ exc_value .args = (exc_value .msg ,)
1714+ else :
1715+ if hasattr (sys .modules [module_name ], '__path__' ) and len (wrong_name_list )> 1 :
1716+ index = 0
1717+ for i in wrong_name_list [1 :]:
1718+ index += 1
1719+ original_module_name = module_name
1720+ exc_value .msg = f"module '{ module_name } ' has no child module '{ i } '"
1721+ exc_value .args = (exc_value .msg ,)
1722+ module_name += "." + i
1723+ if module_name not in sys .modules :
1724+ wrong_name = i
1725+ d = scan_dir (sys .modules [original_module_name ].__path__ [0 ])
1726+ break
1727+ else :
1728+ if hasattr (sys .modules [module_name ], '__path__' ):
1729+ continue
1730+ else :
1731+ if len (wrong_name_list ) > index + 1 :
1732+ exc_value .msg = f"module '{ module_name } ' has no child module '{ wrong_name_list [index + 1 ]} '; '{ module_name } ' is not a package"
1733+ exc_value .args = (exc_value .msg ,)
1734+ return None
1735+ else :
1736+ if len (wrong_name_list ) > 1 :
1737+ exc_value .msg = f"module '{ module_name } ' has no child module '{ wrong_name_list [1 ]} '; '{ module_name } ' is not a package"
1738+ exc_value .args = (exc_value .msg ,)
1739+ return None
16381740 else :
16391741 assert isinstance (exc_value , NameError )
16401742 # find most recent frame
@@ -1661,13 +1763,14 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
16611763 if has_wrong_name :
16621764 return f"self.{ wrong_name } "
16631765
1766+ return _calculate_closed_name (wrong_name , d )
1767+
1768+ def _calculate_closed_name (wrong_name , d ):
16641769 try :
16651770 import _suggestions
1771+ return _suggestions ._generate_suggestions (d , wrong_name )
16661772 except ImportError :
16671773 pass
1668- else :
1669- return _suggestions ._generate_suggestions (d , wrong_name )
1670-
16711774 # Compute closest match
16721775
16731776 if len (d ) > _MAX_CANDIDATE_ITEMS :
@@ -1693,6 +1796,54 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
16931796 best_distance = current_distance
16941797 return suggestion
16951798
1799+ def _find_all_packages ():
1800+ import os
1801+ import sys
1802+ from importlib import machinery
1803+
1804+ def scan_dir (path ):
1805+ """
1806+ Return all of the packages in the path without import
1807+ contains:
1808+ - .py file
1809+ - directory with "__init__.py"
1810+ - the .pyd/so file that has right ABI
1811+ """
1812+ if not os .path .isdir (path ):
1813+ return []
1814+
1815+ suffixes = machinery .EXTENSION_SUFFIXES
1816+ result = []
1817+
1818+ for name in os .listdir (path ):
1819+ full_path = os .path .join (path , name )
1820+
1821+ # .py file
1822+ if name .endswith (".py" ) and os .path .isfile (full_path ):
1823+ modname = name [:- 3 ]
1824+ if modname .isidentifier ():
1825+ result .append (modname )
1826+
1827+ # directory with "__init__.py"
1828+ elif os .path .isdir (full_path ):
1829+ init_file = os .path .join (full_path , "__init__.py" )
1830+ if os .path .isfile (init_file ) and name .isidentifier ():
1831+ result .append (name )
1832+
1833+ # the .pyd/so file that has right ABI
1834+ elif os .path .isfile (full_path ):
1835+ for suf in suffixes :
1836+ if name .endswith (suf ):
1837+ modname = name [:- len (suf )]
1838+ if modname .isidentifier ():
1839+ result .append (modname )
1840+ break
1841+
1842+ return sorted (result )
1843+
1844+ def find_all_packages ():
1845+ return [scan_dir (i ) if i and isinstance (i , str ) and not i .endswith ("idlelib" ) else [] for i in sys .path ] + [sorted (sys .builtin_module_names )]
1846+ return scan_dir , find_all_packages
16961847
16971848def _levenshtein_distance (a , b , max_cost ):
16981849 # A Python implementation of Python/suggestions.c:levenshtein_distance.
0 commit comments