From 1c710446cf0646e0ccbb3e980f9ca5fcf9ef2793 Mon Sep 17 00:00:00 2001 From: Lingjia Liu Date: Tue, 2 Nov 2021 22:22:41 +1100 Subject: [PATCH 1/3] Add `ping` rule --- README.md | 1 + tests/rules/test_ping.py | 41 ++++++++++++++++++++++++++++++++++++++++ thefuck/rules/ping.py | 19 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/rules/test_ping.py create mode 100644 thefuck/rules/ping.py diff --git a/README.md b/README.md index 182acce..456a7a3 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ following rules are enabled by default: * `no_such_file` – creates missing directories with `mv` and `cp` commands; * `omnienv_no_such_command` – fixes wrong commands for `goenv`, `nodenv`, `pyenv` and `rbenv` (eg.: `pyenv isntall` or `goenv list`); * `open` – either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`; +* `ping` – fixes cannot resolve host issues when `http(s)://` is prepended (eg. copy url from browser address bar); * `pip_install` – fixes permission issues with `pip install` commands by adding `--user` or prepending `sudo` if necessary; * `pip_unknown_command` – fixes wrong `pip` commands, for example `pip instatl/pip install`; * `php_s` – replaces `-s` by `-S` when trying to run a local php server; diff --git a/tests/rules/test_ping.py b/tests/rules/test_ping.py new file mode 100644 index 0000000..a94449e --- /dev/null +++ b/tests/rules/test_ping.py @@ -0,0 +1,41 @@ +import pytest +from thefuck.rules.ping import get_new_command, match +from thefuck.types import Command + + +cannot_resolve_output = ''' +ping: cannot resolve {}: Unknown host +'''.format + +resolve_output = ''' +PING {} ({}): 56 data bytes +64 bytes from {}: icmp_seq=0 ttl=64 time=0.050 ms +'''.format + +@pytest.mark.parametrize('command', [ + Command('ping https://google.com', cannot_resolve_output('https://google.com')), + Command('ping http://google.com', cannot_resolve_output('http://google.com')), + Command('ping http://google.com/', cannot_resolve_output('http://google.com/')), + Command('ping http://google.com/?q=test', cannot_resolve_output('http://google.com/?q=test')), + Command('ping ftp://192.168.0.1', cannot_resolve_output('ftp://192.168.0.1')), + Command('ping -c 10 ftp://192.168.0.1', cannot_resolve_output('ftp://192.168.0.1')), # allow options +]) +def test_match(command): + assert match(command) + + +@pytest.mark.parametrize('command', [ + Command('ping http:/google.com/', cannot_resolve_output('http:/google.com/')), # skip invalid url + Command('ping google.com', resolve_output('google.com', '142.250.70.174', '142.250.70.174')), + Command('ping 127.0.0.1', resolve_output('127.0.0.1', '127.0.0.1', '127.0.0.1')), +]) +def test_not_match(command): + assert not match(command) + + +@pytest.mark.parametrize('command, new_command', [ + (Command('ping https://google.com', cannot_resolve_output('https://google.com')), 'ping google.com'), + (Command('ping -c 10 https://google.com', cannot_resolve_output('https://google.com')), 'ping -c 10 google.com'), +]) +def test_get_new_command(command, new_command): + assert get_new_command(command) == new_command diff --git a/thefuck/rules/ping.py b/thefuck/rules/ping.py new file mode 100644 index 0000000..09a2a08 --- /dev/null +++ b/thefuck/rules/ping.py @@ -0,0 +1,19 @@ +# -*- encoding: utf-8 -*- +from six.moves.urllib.parse import urlparse +from thefuck.utils import for_app + + +@for_app('ping') +def match(command): + # It only fix if the target host is a valid url + try: + results = urlparse(command.script_parts[-1]) + is_valid_url = results.hostname is not None + except Exception: + is_valid_url = False + return 'cannot resolve' in command.output and is_valid_url + + +def get_new_command(command): + result = urlparse(command.script_parts[-1]) + return ' '.join(command.script_parts[:-1]) + ' ' + result.hostname From 2914021364760379575aed3cb558a53d62a540fe Mon Sep 17 00:00:00 2001 From: Lingjia Liu Date: Fri, 26 Nov 2021 10:02:00 +1100 Subject: [PATCH 2/3] refactor tests for `ping` with fixtures --- tests/rules/test_ping.py | 56 ++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/tests/rules/test_ping.py b/tests/rules/test_ping.py index a94449e..9308bdc 100644 --- a/tests/rules/test_ping.py +++ b/tests/rules/test_ping.py @@ -12,30 +12,46 @@ PING {} ({}): 56 data bytes 64 bytes from {}: icmp_seq=0 ttl=64 time=0.050 ms '''.format -@pytest.mark.parametrize('command', [ - Command('ping https://google.com', cannot_resolve_output('https://google.com')), - Command('ping http://google.com', cannot_resolve_output('http://google.com')), - Command('ping http://google.com/', cannot_resolve_output('http://google.com/')), - Command('ping http://google.com/?q=test', cannot_resolve_output('http://google.com/?q=test')), - Command('ping ftp://192.168.0.1', cannot_resolve_output('ftp://192.168.0.1')), - Command('ping -c 10 ftp://192.168.0.1', cannot_resolve_output('ftp://192.168.0.1')), # allow options +@pytest.fixture +def match_output(host): + return cannot_resolve_output(host) + +@pytest.mark.parametrize('script, host', [ + ('ping https://google.com', 'https://google.com'), + ('ping http://google.com', 'http://google.com'), + ('ping http://google.com/', 'http://google.com/'), + ('ping http://google.com/?q=test', 'http://google.com/?q=test'), + ('ping ftp://192.168.0.1', 'ftp://192.168.0.1'), + ('ping -c 10 ftp://192.168.0.1', 'ftp://192.168.0.1'), # allow options at the beginning + ('ping ftp://192.168.0.1 -c 10', 'ftp://192.168.0.1'), # allow options at the end + ('ping -c 10 ftp://192.168.0.1 -t 10', 'ftp://192.168.0.1'), # allow options in between ]) -def test_match(command): - assert match(command) +def test_match(match_output, script, host): + assert match(Command(script, match_output)) +@pytest.fixture +def no_match_output(resolvable, host, ip): + if resolvable: + return resolve_output(host, ip, ip) + else: + return cannot_resolve_output(host) -@pytest.mark.parametrize('command', [ - Command('ping http:/google.com/', cannot_resolve_output('http:/google.com/')), # skip invalid url - Command('ping google.com', resolve_output('google.com', '142.250.70.174', '142.250.70.174')), - Command('ping 127.0.0.1', resolve_output('127.0.0.1', '127.0.0.1', '127.0.0.1')), +@pytest.mark.parametrize('script, resolvable, host, ip', [ + ('ping http:/google.com/', False, 'http:/google.com/', None), # invalid url + ('ping google.com', True, 'google.com', '142.250.70.174'), + ('ping 127.0.0.1', True, '127.0.0.1', '127.0.0.1'), + ('ping -c 10 google.com', True, 'google.com', '142.250.70.174'), + ('ping google.com -c 10', True, 'google.com', '142.250.70.174'), ]) -def test_not_match(command): - assert not match(command) +def test_not_match(no_match_output, script, resolvable, host, ip): + assert not match(Command(script, no_match_output)) -@pytest.mark.parametrize('command, new_command', [ - (Command('ping https://google.com', cannot_resolve_output('https://google.com')), 'ping google.com'), - (Command('ping -c 10 https://google.com', cannot_resolve_output('https://google.com')), 'ping -c 10 google.com'), +@pytest.mark.parametrize('new_command, script, host', [ + ('ping google.com', 'ping https://google.com', 'https://google.com'), + ('ping -c 10 google.com', 'ping -c 10 https://google.com', 'https://google.com'), + ('ping google.com -c 10', 'ping https://google.com -c 10', 'https://google.com'), + ('ping -c 10 google.com -t 10', 'ping -c 10 https://google.com -t 10', 'https://google.com'), ]) -def test_get_new_command(command, new_command): - assert get_new_command(command) == new_command +def test_get_new_command(match_output, new_command, script, host): + assert get_new_command(Command(script, match_output)) == new_command From b026237b80cc4355b6820b48c7adc3ff716e790a Mon Sep 17 00:00:00 2001 From: Lingjia Liu Date: Fri, 26 Nov 2021 10:29:40 +1100 Subject: [PATCH 3/3] allow url be among options --- thefuck/rules/ping.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/thefuck/rules/ping.py b/thefuck/rules/ping.py index 09a2a08..ca3c81d 100644 --- a/thefuck/rules/ping.py +++ b/thefuck/rules/ping.py @@ -1,19 +1,37 @@ # -*- encoding: utf-8 -*- from six.moves.urllib.parse import urlparse -from thefuck.utils import for_app +from thefuck.utils import for_app, memoize + + +@memoize +def get_hostname_from_url(maybeUrl): + try: + results = urlparse(maybeUrl) + return results.hostname + except Exception: + return None + + +@memoize +def get_index_of_url(parts): + for i, part in enumerate(parts): + if get_hostname_from_url(part): + return i + return None @for_app('ping') def match(command): - # It only fix if the target host is a valid url - try: - results = urlparse(command.script_parts[-1]) - is_valid_url = results.hostname is not None - except Exception: - is_valid_url = False - return 'cannot resolve' in command.output and is_valid_url + if 'cannot resolve' not in command.output: + return False + + # It only fix if the command has a valid url + index_of_url = get_index_of_url(command.script_parts) + return index_of_url is not None def get_new_command(command): - result = urlparse(command.script_parts[-1]) - return ' '.join(command.script_parts[:-1]) + ' ' + result.hostname + index_of_url = get_index_of_url(command.script_parts) + url = command.script_parts[index_of_url] + hostname = get_hostname_from_url(url) + return ' '.join(command.script_parts[:index_of_url] + [hostname] + command.script_parts[index_of_url + 1:])