1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-03-14 06:38:32 +00:00

Merge 838c4227769abf5c73f1229243a7d3962433548e into 2ced7a7f33ae0bec3ffc7a43ce95330bdf6cfcb9

This commit is contained in:
Chester Koh 2020-01-14 00:51:15 +01:00 committed by GitHub
commit 68f507edb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 366 additions and 56 deletions

View File

@ -184,6 +184,7 @@ following rules are enabled by default:
* `chmod_x` – add execution bit;
* `choco_install` – append common suffixes for chocolatey packages;
* `composer_not_command` – fixes composer command name;
* `composer_not_package` – fixes composer misspelled package names;
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
* `cpp11` – adds missing `-std=c++11` to `g++` or `clang++`;
* `dirty_untar` – fixes `tar x` command that untarred in the current directory;

View File

@ -2,55 +2,90 @@ import pytest
from thefuck.rules.composer_not_command import match, get_new_command
from thefuck.types import Command
# tested with composer version 1.9.0
@pytest.fixture
def composer_not_command():
# that weird spacing is part of the actual command output
return (
'\n'
'\n'
' \n'
' [InvalidArgumentException] \n'
' Command "udpate" is not defined. \n'
' Did you mean this? \n'
' update \n'
' \n'
'\n'
'\n'
)
# command: composer udpate
case_single_command_with_one_suggestion = (
"composer udpate",
r"\n"
" "
"\n"
r" [Symfony\Component\Console\Exception\CommandNotFoundException] "
"\n"
' Command "udpate" is not defined. '
"\n"
" "
"\n"
" Did you mean this? "
"\n"
" update "
"\n"
" "
"\n"
"\n",
)
case_single_command_with_one_suggestion_expected = "composer update"
# command: composer selupdate
case_single_command_with_many_suggestions = (
"composer selupdate",
r"\n"
" "
"\n"
r" [Symfony\Component\Console\Exception\CommandNotFoundException] "
"\n"
' Command "selupdate" is not defined. '
"\n"
" "
"\n"
" Did you mean one of these? "
"\n"
" update "
"\n"
" self-update "
"\n"
" selfupdate "
"\n"
" "
"\n"
"\n",
)
case_single_command_with_many_suggesitons_expected = [
"composer update",
"composer self-update",
"composer selfupdate",
]
@pytest.fixture
def composer_not_command_one_of_this():
# that weird spacing is part of the actual command output
return (
'\n'
'\n'
' \n'
' [InvalidArgumentException] \n'
' Command "pdate" is not defined. \n'
' Did you mean one of these? \n'
' selfupdate \n'
' self-update \n'
' update \n'
' \n'
'\n'
'\n'
)
@pytest.mark.parametrize(
"command",
[
Command(*v)
for v in [
case_single_command_with_one_suggestion,
case_single_command_with_many_suggestions,
]
],
)
def test_match(command):
assert match(command)
def test_match(composer_not_command, composer_not_command_one_of_this):
assert match(Command('composer udpate',
composer_not_command))
assert match(Command('composer pdate',
composer_not_command_one_of_this))
assert not match(Command('ls update', composer_not_command))
def test_get_new_command(composer_not_command, composer_not_command_one_of_this):
assert (get_new_command(Command('composer udpate',
composer_not_command))
== 'composer update')
assert (get_new_command(Command('composer pdate',
composer_not_command_one_of_this))
== 'composer selfupdate')
@pytest.mark.parametrize(
"command, new_command",
[
(Command(*t[0]), t[1])
for t in [
(
case_single_command_with_one_suggestion,
case_single_command_with_one_suggestion_expected,
),
(
case_single_command_with_many_suggestions,
case_single_command_with_many_suggesitons_expected,
),
]
],
)
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@ -0,0 +1,202 @@
import pytest
from thefuck.rules.composer_not_package import match, get_new_command
from thefuck.types import Command
# tested with composer version 1.9.0
# command:
# composer require laravel-nova-csv-import
# the one that started it all
case_original = (
"composer require laravel-nova-csv-import",
"\n"
" "
"\n"
" [InvalidArgumentException] "
"\n"
" Could not find package laravel-nova-csv-import. "
"\n"
" "
"\n"
" Did you mean this? "
"\n"
" simonhamp/laravel-nova-csv-import "
"\n"
" "
"\n"
"require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]..."
"\n"
"\n",
)
case_original_expected = "composer require simonhamp/laravel-nova-csv-import"
# command:
# composer require datrack/elasticroute
case_single_suggestion = (
"composer require datrack/elasticroute",
"\n"
" "
"\n"
" [InvalidArgumentException] "
"\n"
" Could not find package datrack/elasticroute. "
"\n"
" "
"\n"
" Did you mean this? "
"\n"
" detrack/elasticroute "
"\n"
" "
"\n"
"require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]..."
"\n"
"\n",
)
case_single_suggestion_expected = "composer require detrack/elasticroute"
# command:
# composer require potato
case_many_suggestions = (
"composer require potato",
"\n"
" "
"\n"
" [InvalidArgumentException] "
"\n"
" Could not find package potato. "
"\n"
" "
"\n"
" Did you mean one of these? "
"\n"
" drteam/potato "
"\n"
" florence/potato "
"\n"
" kola/potato-orm "
"\n"
" jsyqw/potato-bot "
"\n"
" vundi/potato-orm "
"\n"
" "
"\n\n"
"require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]..."
"\n"
"\n",
)
case_many_suggestions_expected = [
"composer require drteam/potato",
"composer require florence/potato",
"composer require kola/potato-orm",
"composer require jsyqw/potato-bot",
"composer require vundi/potato-orm",
]
# command:
# composer require datrack/elasticroute:*
case_single_package_with_version_constraint = (
"composer require datrack/elasticroute:*",
"\n"
" "
"\n"
" [InvalidArgumentException] "
"\n"
" Could not find package datrack/elasticroute. "
"\n"
" "
"\n"
" Did you mean this? "
"\n"
" detrack/elasticroute "
"\n"
" "
"\n\n"
"require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]..."
"\n"
"\n",
)
case_single_package_with_version_constraint_expected = (
"composer require detrack/elasticroute:*"
)
# command:
# composer require potato:*
case_single_package_with_version_constraint_many_suggestions = (
"composer require potato:1.2.3",
"\n"
" "
"\n"
" [InvalidArgumentException] "
"\n"
" Could not find package potato. "
"\n"
" "
"\n"
" Did you mean one of these? "
"\n"
" drteam/potato "
"\n"
" florence/potato "
"\n"
" kola/potato-orm "
"\n"
" jsyqw/potato-bot "
"\n"
" vundi/potato-orm "
"\n"
" "
"\n\n"
"require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]..."
"\n"
"\n",
)
case_single_package_with_version_constraint_many_suggestions_expected = [
"composer require drteam/potato:1.2.3",
"composer require florence/potato:1.2.3",
"composer require kola/potato-orm:1.2.3",
"composer require jsyqw/potato-bot:1.2.3",
"composer require vundi/potato-orm:1.2.3",
]
@pytest.mark.parametrize(
"command",
[
Command(*v)
for v in [
case_original,
case_single_suggestion,
case_many_suggestions,
case_single_package_with_version_constraint,
case_single_package_with_version_constraint_many_suggestions,
]
],
)
def test_match(command):
assert match(command)
@pytest.mark.parametrize(
"command, new_command",
[
(Command(*t[0]), t[1])
for t in [
(case_original, case_original_expected),
(case_single_suggestion, case_single_suggestion_expected),
(case_many_suggestions, case_many_suggestions_expected),
(
case_single_package_with_version_constraint,
case_single_package_with_version_constraint_expected,
),
(
case_single_package_with_version_constraint_many_suggestions,
case_single_package_with_version_constraint_many_suggestions_expected,
),
]
],
)
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@ -1,16 +1,35 @@
import re
from thefuck.utils import replace_argument, for_app
from thefuck.utils import for_app
@for_app('composer')
@for_app("composer")
def match(command):
return (('did you mean this?' in command.output.lower()
or 'did you mean one of these?' in command.output.lower()))
# determine error type
# matching "did you mean this" is not enough as composer also gives spelling suggestions for mistakes other than mispelled commands
is_undefined_command_error = "CommandNotFoundException" in command.output
suggestions_present = "Did you mean" in command.output
return is_undefined_command_error and suggestions_present
def get_new_command(command):
broken_cmd = re.findall(r"Command \"([^']*)\" is not defined", command.output)[0]
new_cmd = re.findall(r'Did you mean this\?[^\n]*\n\s*([^\n]*)', command.output)
if not new_cmd:
new_cmd = re.findall(r'Did you mean one of these\?[^\n]*\n\s*([^\n]*)', command.output)
return replace_argument(command.script, broken_cmd, new_cmd[0].strip())
# since the command class already tells us the original argument, we need not resort to regex
broken_cmd = command.script_parts[1]
one_suggestion_only = "Did you mean this?" in command.output
if one_suggestion_only:
new_cmd = (
re.search(r"Did you mean this\?[^\n]*\n\s*([^\n]*)", command.output)
.group(1)
.strip()
)
return command.script.replace(broken_cmd, new_cmd)
# else there are multiple suggestions
# trim output text to make it more digestable by regex
trim_start_index = command.output.find("Did you mean")
short_output = command.output[trim_start_index:]
stripped_lines = [line.strip() for line in short_output.split("\n")]
# each of the suggested commands can be found from index 1 to the first occurrence of a blank string
end_index = stripped_lines.index("")
suggested_commands = stripped_lines[1:end_index]
return [
command.script.replace(broken_cmd, cmd.strip()) for cmd in suggested_commands
]

View File

@ -0,0 +1,53 @@
import re
from thefuck.utils import for_app
@for_app("composer")
def match(command):
# determine error type
# matching "did you mean this" is not enough as composer also gives spelling suggestions for mistakes other than mispelled commands
is_invalid_argument_exception = r"[InvalidArgumentException]" in command.output
is_unable_to_find_package = (
re.search(r"Could not find package (.*)\.", command.output) is not None
)
suggestions_present = "Did you mean" in command.output
return (
is_invalid_argument_exception
and is_unable_to_find_package
and suggestions_present
)
def get_new_command(command):
# because composer lets you install many packages at once, must look at output to determine the erroneous package name
wrong_package_name = re.search(
r"Could not find package (.*)\.", command.output
).group(1)
offending_script_param = (
wrong_package_name
if (wrong_package_name in command.script_parts)
else re.findall(r"{}:[^ ]+".format(wrong_package_name), command.script)[0]
)
version_constraint = offending_script_param[len(wrong_package_name) :]
one_suggestion_only = "Did you mean this?" in command.output
if one_suggestion_only:
new_pkg = (
re.search(r"Did you mean this\?[^\n]*\n\s*([^\n]*)", command.output)
.group(1)
.strip()
)
return command.script.replace(
offending_script_param, new_pkg + version_constraint
)
# else there are multiple suggestions
# trim output text to make it more digestable by regex
trim_start_index = command.output.find("Did you mean")
short_output = command.output[trim_start_index:]
stripped_lines = [line.strip() for line in short_output.split("\n")]
# each of the suggested packages can be found from index 1 to the first occurrence of a blank string
end_index = stripped_lines.index("")
suggested_packages = stripped_lines[1:end_index]
return [
command.script.replace(offending_script_param, pkg + version_constraint)
for pkg in suggested_packages
]