mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-11-04 00:52:04 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/nvbn/thefuck into conda
This commit is contained in:
		
							
								
								
									
										49
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
name: Tests
 | 
			
		||||
 | 
			
		||||
on: [push, pull_request]
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  PYTHON_LATEST: 3.9
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  test:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ubuntu-latest, macos-latest, windows-latest]
 | 
			
		||||
        python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10-dev]
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v2
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ matrix.python-version }}
 | 
			
		||||
      - name: Cache dependencies
 | 
			
		||||
        id: cache-deps
 | 
			
		||||
        uses: actions/cache@v2
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
            ${{ env.pythonLocation }}/bin/*
 | 
			
		||||
            ${{ env.pythonLocation }}/lib/*
 | 
			
		||||
            ${{ env.pythonLocation }}/scripts/*
 | 
			
		||||
          key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py', 'requirements.txt') }}
 | 
			
		||||
      - name: Install The Fuck with all dependencies
 | 
			
		||||
        if: steps.cache-deps.outputs.cache-hit != 'true'
 | 
			
		||||
        run: |
 | 
			
		||||
          pip install -Ur requirements.txt coveralls
 | 
			
		||||
          python setup.py develop
 | 
			
		||||
      - name: Lint
 | 
			
		||||
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == env.PYTHON_LATEST
 | 
			
		||||
        run: flake8
 | 
			
		||||
      - name: Run tests
 | 
			
		||||
        if: matrix.os != 'ubuntu-latest' || matrix.python-version != env.PYTHON_LATEST
 | 
			
		||||
        run: coverage run --source=thefuck,tests -m pytest -v --capture=sys tests
 | 
			
		||||
      - name: Run tests (including functional)
 | 
			
		||||
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == env.PYTHON_LATEST
 | 
			
		||||
        run: coverage run --source=thefuck,tests -m pytest -v --capture=sys tests --enable-functional
 | 
			
		||||
      - name: Post coverage results
 | 
			
		||||
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == env.PYTHON_LATEST
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        run: coveralls --service=github
 | 
			
		||||
							
								
								
									
										51
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,51 +0,0 @@
 | 
			
		||||
language: python
 | 
			
		||||
sudo: false
 | 
			
		||||
os: linux
 | 
			
		||||
dist: xenial
 | 
			
		||||
matrix:
 | 
			
		||||
  include:
 | 
			
		||||
    - python: "nightly"
 | 
			
		||||
    - python: "3.8-dev"
 | 
			
		||||
    - python: "3.8"
 | 
			
		||||
    - python: "3.7-dev"
 | 
			
		||||
    - python: "3.7"
 | 
			
		||||
    - python: "3.6-dev"
 | 
			
		||||
    - python: "3.6"
 | 
			
		||||
    - python: "3.5"
 | 
			
		||||
    - python: "2.7"
 | 
			
		||||
    - os: osx
 | 
			
		||||
      language: generic
 | 
			
		||||
  allow_failures:
 | 
			
		||||
    - python: nightly
 | 
			
		||||
    - python: 3.8-dev
 | 
			
		||||
    - python: 3.7-dev
 | 
			
		||||
    - python: 3.6-dev
 | 
			
		||||
services:
 | 
			
		||||
  - docker
 | 
			
		||||
addons:
 | 
			
		||||
  apt:
 | 
			
		||||
    packages:
 | 
			
		||||
      - python-commandnotfound
 | 
			
		||||
      - python3-commandnotfound
 | 
			
		||||
before_install:
 | 
			
		||||
  - if [[ $TRAVIS_OS_NAME == "osx" ]]; then rm -rf /usr/local/include/c++; fi
 | 
			
		||||
  - if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update; fi
 | 
			
		||||
  - if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew unlink python@2; fi
 | 
			
		||||
  - if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew upgrade python; fi
 | 
			
		||||
  - if [[ $TRAVIS_OS_NAME == "osx" ]]; then pip3 install virtualenv; fi
 | 
			
		||||
  - if [[ $TRAVIS_OS_NAME == "osx" ]]; then virtualenv venv -p python3; fi
 | 
			
		||||
  - if [[ $TRAVIS_OS_NAME == "osx" ]]; then source venv/bin/activate; fi
 | 
			
		||||
  - pip install -U pip
 | 
			
		||||
  - pip install -U coveralls
 | 
			
		||||
install:
 | 
			
		||||
  - pip install -Ur requirements.txt
 | 
			
		||||
  - python setup.py develop
 | 
			
		||||
  - rm -rf build
 | 
			
		||||
script:
 | 
			
		||||
  - flake8
 | 
			
		||||
  - export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
 | 
			
		||||
  - export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys tests"
 | 
			
		||||
  - if [[ $TRAVIS_PYTHON_VERSION == 3.8 && $TRAVIS_OS_NAME != "osx" ]]; then $RUN_TESTS --enable-functional; fi
 | 
			
		||||
  - if [[ $TRAVIS_PYTHON_VERSION != 3.8 || $TRAVIS_OS_NAME == "osx" ]]; then $RUN_TESTS; fi
 | 
			
		||||
after_success:
 | 
			
		||||
  - if [[ $TRAVIS_PYTHON_VERSION == 3.8 && $TRAVIS_OS_NAME != "osx" ]]; then coveralls; fi
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# The Fuck [![Version][version-badge]][version-link] [![Build Status][travis-badge]][travis-link] [![Windows Build Status][appveyor-badge]][appveyor-link] [![Coverage][coverage-badge]][coverage-link] [![MIT License][license-badge]](LICENSE.md)
 | 
			
		||||
# The Fuck [![Version][version-badge]][version-link] [![Build Status][workflow-badge]][workflow-link] [![Coverage][coverage-badge]][coverage-link] [![MIT License][license-badge]](LICENSE.md)
 | 
			
		||||
 | 
			
		||||
*The Fuck* is a magnificent app, inspired by a [@liamosaur](https://twitter.com/liamosaur/)
 | 
			
		||||
[tweet](https://twitter.com/liamosaur/status/506975850596536320),
 | 
			
		||||
@@ -282,6 +282,7 @@ following rules are enabled by default:
 | 
			
		||||
* `pyenv_no_such_command` – fixes wrong pyenv commands like `pyenv isntall` or `pyenv list`;
 | 
			
		||||
* `python_command` – prepends `python` when you try to run non-executable/without `./` python script;
 | 
			
		||||
* `python_execute` – appends missing `.py` when executing Python files;
 | 
			
		||||
* `python_module_error` – fixes ModuleNotFoundError by trying to `pip install` that module;
 | 
			
		||||
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
 | 
			
		||||
* `path_from_history` – replaces not found path with similar absolute path from history;
 | 
			
		||||
* `react_native_command_unrecognized` – fixes unrecognized `react-native` commands;
 | 
			
		||||
@@ -503,10 +504,8 @@ Project License can be found [here](LICENSE.md).
 | 
			
		||||
 | 
			
		||||
[version-badge]:   https://img.shields.io/pypi/v/thefuck.svg?label=version
 | 
			
		||||
[version-link]:    https://pypi.python.org/pypi/thefuck/
 | 
			
		||||
[travis-badge]:    https://travis-ci.org/nvbn/thefuck.svg?branch=master
 | 
			
		||||
[travis-link]:     https://travis-ci.org/nvbn/thefuck
 | 
			
		||||
[appveyor-badge]:  https://ci.appveyor.com/api/projects/status/1sskj4imj02um0gu/branch/master?svg=true
 | 
			
		||||
[appveyor-link]:   https://ci.appveyor.com/project/nvbn/thefuck
 | 
			
		||||
[workflow-badge]:  https://github.com/divykj/thefuck/workflows/Tests/badge.svg
 | 
			
		||||
[workflow-link]:   https://github.com/divykj/thefuck/actions?query=workflow%3ATests
 | 
			
		||||
[coverage-badge]:  https://img.shields.io/coveralls/nvbn/thefuck.svg
 | 
			
		||||
[coverage-link]:   https://coveralls.io/github/nvbn/thefuck
 | 
			
		||||
[license-badge]:   https://img.shields.io/badge/license-MIT-007EC7.svg
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								appveyor.yml
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								appveyor.yml
									
									
									
									
									
								
							@@ -1,23 +0,0 @@
 | 
			
		||||
build: false
 | 
			
		||||
 | 
			
		||||
environment:
 | 
			
		||||
  matrix:
 | 
			
		||||
    - PYTHON: "C:/Python27"
 | 
			
		||||
    - PYTHON: "C:/Python35"
 | 
			
		||||
    - PYTHON: "C:/Python36"
 | 
			
		||||
    - PYTHON: "C:/Python37"
 | 
			
		||||
 | 
			
		||||
init:
 | 
			
		||||
  - "ECHO %PYTHON%"
 | 
			
		||||
  - ps: "ls C:/Python*"
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
  - "curl -fsS -o C:/get-pip.py https://bootstrap.pypa.io/get-pip.py"
 | 
			
		||||
  - "%PYTHON%/python.exe C:/get-pip.py"
 | 
			
		||||
  - "%PYTHON%/Scripts/pip.exe install -U setuptools"
 | 
			
		||||
  - "%PYTHON%/python.exe setup.py develop"
 | 
			
		||||
  - "%PYTHON%/Scripts/pip.exe install -U -r requirements.txt"
 | 
			
		||||
 | 
			
		||||
test_script:
 | 
			
		||||
  - "%PYTHON%/python.exe -m flake8"
 | 
			
		||||
  - "%PYTHON%/Scripts/py.test.exe -sv"
 | 
			
		||||
@@ -7,6 +7,10 @@ from thefuck.system import Path
 | 
			
		||||
shells.shell = shells.Generic()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pytest_configure(config):
 | 
			
		||||
    config.addinivalue_line("markers", "functional: mark test as functional")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pytest_addoption(parser):
 | 
			
		||||
    """Adds `--enable-functional` argument."""
 | 
			
		||||
    group = parser.getgroup("thefuck")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.go_unknown_command import match, get_new_command
 | 
			
		||||
from thefuck.types import Command
 | 
			
		||||
from thefuck.utils import which
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
@@ -17,5 +18,6 @@ def test_not_match():
 | 
			
		||||
    assert not match(Command('go run', 'go run: no go files listed'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.skipif(not which('go'), reason='Skip if go executable not found')
 | 
			
		||||
def test_get_new_command(build_misspelled_output):
 | 
			
		||||
    assert get_new_command(Command('go bulid', build_misspelled_output)) == 'go build'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								tests/rules/test_python_module_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								tests/rules/test_python_module_error.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
from thefuck.rules.python_module_error import get_new_command, match
 | 
			
		||||
from thefuck.types import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def module_error_output(filename, module_name):
 | 
			
		||||
    return """Traceback (most recent call last):
 | 
			
		||||
  File "{0}", line 1, in <module>
 | 
			
		||||
    import {1}
 | 
			
		||||
ModuleNotFoundError: No module named '{1}'""".format(
 | 
			
		||||
        filename, module_name
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    "test",
 | 
			
		||||
    [
 | 
			
		||||
        Command("python hello_world.py", "Hello World"),
 | 
			
		||||
        Command(
 | 
			
		||||
            "./hello_world.py",
 | 
			
		||||
            """Traceback (most recent call last):
 | 
			
		||||
  File "hello_world.py", line 1, in <module>
 | 
			
		||||
    pritn("Hello World")
 | 
			
		||||
NameError: name 'pritn' is not defined""",
 | 
			
		||||
        ),
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
def test_not_match(test):
 | 
			
		||||
    assert not match(test)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
positive_tests = [
 | 
			
		||||
    (
 | 
			
		||||
        "python some_script.py",
 | 
			
		||||
        "some_script.py",
 | 
			
		||||
        "more_itertools",
 | 
			
		||||
        "pip install more_itertools && python some_script.py",
 | 
			
		||||
    ),
 | 
			
		||||
    (
 | 
			
		||||
        "./some_other_script.py",
 | 
			
		||||
        "some_other_script.py",
 | 
			
		||||
        "a_module",
 | 
			
		||||
        "pip install a_module && ./some_other_script.py",
 | 
			
		||||
    ),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    "script, filename, module_name, corrected_script", positive_tests
 | 
			
		||||
)
 | 
			
		||||
def test_match(script, filename, module_name, corrected_script, module_error_output):
 | 
			
		||||
    assert match(Command(script, module_error_output))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    "script, filename, module_name, corrected_script", positive_tests
 | 
			
		||||
)
 | 
			
		||||
def test_get_new_command(
 | 
			
		||||
    script, filename, module_name, corrected_script, module_error_output
 | 
			
		||||
):
 | 
			
		||||
    assert get_new_command(Command(script, module_error_output)) == corrected_script
 | 
			
		||||
@@ -43,7 +43,7 @@ class TestSettingsFromFile(object):
 | 
			
		||||
        assert settings.rules == const.DEFAULT_RULES + ['test']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.usefixture('load_source')
 | 
			
		||||
@pytest.mark.usefixtures('load_source')
 | 
			
		||||
class TestSettingsFromEnv(object):
 | 
			
		||||
    def test_from_env(self, os_environ, settings):
 | 
			
		||||
        os_environ.update({'THEFUCK_RULES': 'bash:lisp',
 | 
			
		||||
 
 | 
			
		||||
@@ -8,14 +8,15 @@ from thefuck.types import Command
 | 
			
		||||
from thefuck.corrector import get_corrected_commands, organize_commands
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestGetRules(object):
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def glob(self, mocker):
 | 
			
		||||
        results = {}
 | 
			
		||||
        mocker.patch('thefuck.system.Path.glob',
 | 
			
		||||
                     new_callable=lambda: lambda *_: results.pop('value', []))
 | 
			
		||||
        return lambda value: results.update({'value': value})
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def glob(mocker):
 | 
			
		||||
    results = {}
 | 
			
		||||
    mocker.patch('thefuck.system.Path.glob',
 | 
			
		||||
                 new_callable=lambda: lambda *_: results.pop('value', []))
 | 
			
		||||
    return lambda value: results.update({'value': value})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestGetRules(object):
 | 
			
		||||
    @pytest.fixture(autouse=True)
 | 
			
		||||
    def load_source(self, monkeypatch):
 | 
			
		||||
        monkeypatch.setattr('thefuck.types.load_source',
 | 
			
		||||
@@ -39,6 +40,14 @@ class TestGetRules(object):
 | 
			
		||||
        self._compare_names(rules, loaded_rules)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_rules_rule_exception(mocker, glob):
 | 
			
		||||
    load_source = mocker.patch('thefuck.types.load_source',
 | 
			
		||||
                               side_effect=ImportError("No module named foo..."))
 | 
			
		||||
    glob([Path('git.py')])
 | 
			
		||||
    assert not corrector.get_rules()
 | 
			
		||||
    load_source.assert_called_once_with('git', 'git.py')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_corrected_commands(mocker):
 | 
			
		||||
    command = Command('test', 'test')
 | 
			
		||||
    rules = [Rule(match=lambda _: False),
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,12 @@ class TestCorrectedCommand(object):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestRule(object):
 | 
			
		||||
    def test_from_path_rule_exception(self, mocker):
 | 
			
		||||
        load_source = mocker.patch('thefuck.types.load_source',
 | 
			
		||||
                                   side_effect=ImportError("No module named foo..."))
 | 
			
		||||
        assert Rule.from_path(Path('git.py')) is None
 | 
			
		||||
        load_source.assert_called_once_with('git', 'git.py')
 | 
			
		||||
 | 
			
		||||
    def test_from_path(self, mocker):
 | 
			
		||||
        match = object()
 | 
			
		||||
        get_new_command = object()
 | 
			
		||||
@@ -60,20 +66,22 @@ class TestRule(object):
 | 
			
		||||
                == Rule('bash', match, get_new_command, priority=900))
 | 
			
		||||
        load_source.assert_called_once_with('bash', rule_path)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.parametrize('rules, exclude_rules, rule, is_enabled', [
 | 
			
		||||
        (const.DEFAULT_RULES, [], Rule('git', enabled_by_default=True), True),
 | 
			
		||||
        (const.DEFAULT_RULES, [], Rule('git', enabled_by_default=False), False),
 | 
			
		||||
        ([], [], Rule('git', enabled_by_default=False), False),
 | 
			
		||||
        ([], [], Rule('git', enabled_by_default=True), False),
 | 
			
		||||
        (const.DEFAULT_RULES + ['git'], [], Rule('git', enabled_by_default=False), True),
 | 
			
		||||
        (['git'], [], Rule('git', enabled_by_default=False), True),
 | 
			
		||||
        (const.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=True), False),
 | 
			
		||||
        (const.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=False), False),
 | 
			
		||||
        ([], ['git'], Rule('git', enabled_by_default=True), False),
 | 
			
		||||
        ([], ['git'], Rule('git', enabled_by_default=False), False)])
 | 
			
		||||
    def test_is_enabled(self, settings, rules, exclude_rules, rule, is_enabled):
 | 
			
		||||
        settings.update(rules=rules,
 | 
			
		||||
                        exclude_rules=exclude_rules)
 | 
			
		||||
    def test_from_path_excluded_rule(self, mocker, settings):
 | 
			
		||||
        load_source = mocker.patch('thefuck.types.load_source')
 | 
			
		||||
        settings.update(exclude_rules=['git'])
 | 
			
		||||
        rule_path = os.path.join(os.sep, 'rules', 'git.py')
 | 
			
		||||
        assert Rule.from_path(Path(rule_path)) is None
 | 
			
		||||
        assert not load_source.called
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.parametrize('rules, rule, is_enabled', [
 | 
			
		||||
        (const.DEFAULT_RULES, Rule('git', enabled_by_default=True), True),
 | 
			
		||||
        (const.DEFAULT_RULES, Rule('git', enabled_by_default=False), False),
 | 
			
		||||
        ([], Rule('git', enabled_by_default=False), False),
 | 
			
		||||
        ([], Rule('git', enabled_by_default=True), False),
 | 
			
		||||
        (const.DEFAULT_RULES + ['git'], Rule('git', enabled_by_default=False), True),
 | 
			
		||||
        (['git'], Rule('git', enabled_by_default=False), True)])
 | 
			
		||||
    def test_is_enabled(self, settings, rules, rule, is_enabled):
 | 
			
		||||
        settings.update(rules=rules)
 | 
			
		||||
        assert rule.is_enabled == is_enabled
 | 
			
		||||
 | 
			
		||||
    def test_isnt_match(self):
 | 
			
		||||
@@ -131,6 +139,7 @@ class TestCommand(object):
 | 
			
		||||
                                      env=os_environ)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.parametrize('script, result', [
 | 
			
		||||
        ([], None),
 | 
			
		||||
        ([''], None),
 | 
			
		||||
        (['', ''], None),
 | 
			
		||||
        (['ls', '-la'], 'ls -la'),
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ def get_loaded_rules(rules_paths):
 | 
			
		||||
    for path in rules_paths:
 | 
			
		||||
        if path.name != '__init__.py':
 | 
			
		||||
            rule = Rule.from_path(path)
 | 
			
		||||
            if rule.is_enabled:
 | 
			
		||||
            if rule and rule.is_enabled:
 | 
			
		||||
                yield rule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ def _get_raw_command(known_args):
 | 
			
		||||
            diff = SequenceMatcher(a=alias, b=command).ratio()
 | 
			
		||||
            if diff < const.DIFF_WITH_ALIAS or command in executables:
 | 
			
		||||
                return [command]
 | 
			
		||||
    return []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fix_command(known_args):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								thefuck/rules/python_module_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								thefuck/rules/python_module_error.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import re
 | 
			
		||||
from thefuck.shells import shell
 | 
			
		||||
 | 
			
		||||
MISSING_MODULE = r"ModuleNotFoundError: No module named '([^']+)'"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def match(command):
 | 
			
		||||
    return "ModuleNotFoundError: No module named '" in command.output
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_new_command(command):
 | 
			
		||||
    missing_module = re.findall(MISSING_MODULE, command.output)[0]
 | 
			
		||||
    return shell.and_("pip install {}".format(missing_module), command.script)
 | 
			
		||||
@@ -136,9 +136,16 @@ class Rule(object):
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        name = path.name[:-3]
 | 
			
		||||
        if name in settings.exclude_rules:
 | 
			
		||||
            logs.debug(u'Ignoring excluded rule: {}'.format(name))
 | 
			
		||||
            return
 | 
			
		||||
        with logs.debug_time(u'Importing rule: {};'.format(name)):
 | 
			
		||||
            rule_module = load_source(name, str(path))
 | 
			
		||||
            priority = getattr(rule_module, 'priority', DEFAULT_PRIORITY)
 | 
			
		||||
            try:
 | 
			
		||||
                rule_module = load_source(name, str(path))
 | 
			
		||||
            except Exception:
 | 
			
		||||
                logs.exception(u"Rule {} failed to load".format(name), sys.exc_info())
 | 
			
		||||
                return
 | 
			
		||||
        priority = getattr(rule_module, 'priority', DEFAULT_PRIORITY)
 | 
			
		||||
        return cls(name, rule_module.match,
 | 
			
		||||
                   rule_module.get_new_command,
 | 
			
		||||
                   getattr(rule_module, 'enabled_by_default', True),
 | 
			
		||||
@@ -153,14 +160,11 @@ class Rule(object):
 | 
			
		||||
        :rtype: bool
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        if self.name in settings.exclude_rules:
 | 
			
		||||
            return False
 | 
			
		||||
        elif self.name in settings.rules:
 | 
			
		||||
            return True
 | 
			
		||||
        elif self.enabled_by_default and ALL_ENABLED in settings.rules:
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
        return (
 | 
			
		||||
            self.name in settings.rules
 | 
			
		||||
            or self.enabled_by_default
 | 
			
		||||
            and ALL_ENABLED in settings.rules
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def is_match(self, command):
 | 
			
		||||
        """Returns `True` if rule matches the command.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user