Skip to content

Commit 62dddd5

Browse files
authored
#1149: Add python_module_error rule (#1151)
1 parent 40dd659 commit 62dddd5

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ following rules are enabled by default:
281281
* `pyenv_no_such_command` – fixes wrong pyenv commands like `pyenv isntall` or `pyenv list`;
282282
* `python_command` – prepends `python` when you try to run non-executable/without `./` python script;
283283
* `python_execute` – appends missing `.py` when executing Python files;
284+
* `python_module_error` – fixes ModuleNotFoundError by trying to `pip install` that module;
284285
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
285286
* `path_from_history` – replaces not found path with similar absolute path from history;
286287
* `react_native_command_unrecognized` – fixes unrecognized `react-native` commands;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import pytest
2+
3+
from thefuck.rules.python_module_error import get_new_command, match
4+
from thefuck.types import Command
5+
6+
7+
@pytest.fixture
8+
def module_error_output(filename, module_name):
9+
return """Traceback (most recent call last):
10+
File "{0}", line 1, in <module>
11+
import {1}
12+
ModuleNotFoundError: No module named '{1}'""".format(
13+
filename, module_name
14+
)
15+
16+
17+
@pytest.mark.parametrize(
18+
"test",
19+
[
20+
Command("python hello_world.py", "Hello World"),
21+
Command(
22+
"./hello_world.py",
23+
"""Traceback (most recent call last):
24+
File "hello_world.py", line 1, in <module>
25+
pritn("Hello World")
26+
NameError: name 'pritn' is not defined""",
27+
),
28+
],
29+
)
30+
def test_not_match(test):
31+
assert not match(test)
32+
33+
34+
positive_tests = [
35+
(
36+
"python some_script.py",
37+
"some_script.py",
38+
"more_itertools",
39+
"pip install more_itertools && python some_script.py",
40+
),
41+
(
42+
"./some_other_script.py",
43+
"some_other_script.py",
44+
"a_module",
45+
"pip install a_module && ./some_other_script.py",
46+
),
47+
]
48+
49+
50+
@pytest.mark.parametrize(
51+
"script, filename, module_name, corrected_script", positive_tests
52+
)
53+
def test_match(script, filename, module_name, corrected_script, module_error_output):
54+
assert match(Command(script, module_error_output))
55+
56+
57+
@pytest.mark.parametrize(
58+
"script, filename, module_name, corrected_script", positive_tests
59+
)
60+
def test_get_new_command(
61+
script, filename, module_name, corrected_script, module_error_output
62+
):
63+
assert get_new_command(Command(script, module_error_output)) == corrected_script
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import re
2+
from thefuck.shells import shell
3+
4+
MISSING_MODULE = r"ModuleNotFoundError: No module named '([^']+)'"
5+
6+
7+
def match(command):
8+
return "ModuleNotFoundError: No module named '" in command.output
9+
10+
11+
def get_new_command(command):
12+
missing_module = re.findall(MISSING_MODULE, command.output)[0]
13+
return shell.and_("pip install {}".format(missing_module), command.script)

0 commit comments

Comments
 (0)