1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
"""Getting specific information for ModuleNotFoundError"""
import re
import sys
from .. import debug_helper
from ..ft_gettext import current_lang
from ..message_parser import get_parser
from ..tb_data import TracebackData # for type checking only
from ..typing_info import CauseInfo # for type checking only
from ..utils import get_similar_words, list_to_string
from . import stdlib_modules
parser = get_parser(ModuleNotFoundError)
_ = current_lang.translate
@parser._add
def is_not_a_package(message: str, _tb_data: TracebackData) -> CauseInfo:
# Python 3.12.0a1 has two spaces after 'named'
pattern = re.compile(r"No module named\s*'(.*)'; '(.*)' is not a package")
match = re.search(pattern, message)
if not match:
return {}
hint = ""
dotted_path = match[1]
name = match[2]
rest = dotted_path.replace(name + ".", "")
# This specific exception should not have been raised if name was not a module.
# Still, when dealing with imports, better safe than sorry.
try:
module = __import__(name)
except ImportError as e: # pragma: no cover
cause = _(
"No additional information available since `{name}` cannot be imported.\n"
).format(name=name)
debug_helper.log("Problem in is_not_a_package()")
debug_helper.log(str(e))
debug_helper.log(cause)
return {"cause": cause}
attributes = dir(module)
if rest in attributes:
hint = _("Did you mean `from {name} import {rest}`?\n").format(
name=name, rest=rest
)
cause = _(
"`{rest}` is not a separate module but an object that is part of `{name}`.\n"
).format(name=name, rest=rest)
return {"cause": cause, "suggest": hint}
similar = get_similar_words(rest, attributes)
if similar:
for attr in similar:
obj = getattr(module, attr)
if isinstance(obj, type(module)):
hint = _("Did you mean `import {name}.{attr}`?\n").format(
name=name, attr=attr
)
cause = _(
"Perhaps you meant `import {name}.{attr}`.\n"
"`{attr}` is a name similar to `{rest}` and is a module that\n"
"can be imported from `{name}`.\n"
).format(name=name, attr=attr, rest=rest)
break
else:
attr = similar[0]
hint = _("Did you mean `from {name} import {attr}`?\n").format(
name=name, attr=attr
)
cause = _(
"Perhaps you meant `from {name} import {attr}`.\n"
"`{attr}` is a name similar to `{rest}` and is an object that\n"
"can be imported from `{name}`.\n"
).format(name=name, attr=attr, rest=rest)
if len(similar) > 1:
cause += _(
"Other objects with similar names that are part of\n"
" `{name}` include `{others}`.\n"
).format(name=name, others=list_to_string(similar[1:]))
return {"cause": cause, "suggest": hint}
else:
cause = _("`{rest}` cannot be imported from `{name}`.\n").format(
rest=rest, name=name
)
return {"cause": cause, "suggest": hint} if hint else {"cause": cause}
def curses_no_found() -> CauseInfo:
if sys.platform.startswith("win"):
hint = _("The curses module is rarely installed with Python on Windows.\n")
else: # pragma: no cover
hint = _("The curses module is often not installed with Python.\n")
cause = _("You have tried to import the curses module.\n")
return {"cause": cause + hint, "suggest": hint}
@parser._add
def no_module_named(message: str, _tb_data: TracebackData) -> CauseInfo:
pattern = re.compile(r"No module named '(.*)'$")
match = re.search(pattern, message)
if not match: # pragma: no cover
return {}
name = match[1]
if name == "_curses":
return curses_no_found()
if name in stdlib_modules.names and not stdlib_modules.module_exists(name):
return {
"cause": _(
"No module named `{name}` can be imported.\n\n"
"Note that there is a module named `{name}` that is part of the\n"
"Python standard library. However, it might not be available\n"
"for your operating system, or it may to be installed separately.\n"
).format(name=name)
}
similar = get_similar_words(name, stdlib_modules.names)
similar = [
mod_name for mod_name in similar if stdlib_modules.module_exists(mod_name)
]
cause = _(
"No module named `{name}` can be imported.\n"
"Perhaps you need to install it.\n"
).format(name=name)
if not similar:
return {"cause": cause}
hint = _("Did you mean `{name}`?\n").format(name=similar[0])
if len(similar) > 1:
cause += _(
"The following existing modules have names that are similar \n"
"to the module you tried to import: `{names}`\n"
).format(names=", ".join(similar))
else:
cause += _("`{name}` is an existing module that has a similar name.\n").format(
name=similar[0]
)
return {"cause": cause, "suggest": hint}
|