1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-01-31 02:01:13 +00:00

Move all app/os specific utils to specific package

This commit is contained in:
nvbn 2015-08-25 14:09:47 +03:00
parent 2e002f666b
commit b21c9ebb43
39 changed files with 214 additions and 182 deletions

View File

@ -29,7 +29,7 @@ def test_match(command):
@pytest.mark.parametrize('command, return_value', [
(Command(script='vim', stderr='vim: command not found'), PKGFILE_OUTPUT_VIM),
(Command(script='sudo vim', stderr='sudo: vim: command not found'), PKGFILE_OUTPUT_VIM)])
@patch('thefuck.utils.subprocess')
@patch('thefuck.specific.archlinux.subprocess')
@patch.multiple(pacman, create=True, pacman=pacman_cmd)
def test_match_mocked(subp_mock, command, return_value):
subp_mock.check_output.return_value = return_value
@ -75,7 +75,7 @@ def test_get_new_command(command, new_command, mocker):
(Command('convert'), ['{} -S extra/imagemagick && convert'.format(pacman_cmd)], PKGFILE_OUTPUT_CONVERT),
(Command('sudo'), ['{} -S core/sudo && sudo'.format(pacman_cmd)], PKGFILE_OUTPUT_SUDO),
(Command('sudo convert'), ['{} -S extra/imagemagick && sudo convert'.format(pacman_cmd)], PKGFILE_OUTPUT_CONVERT)])
@patch('thefuck.utils.subprocess')
@patch('thefuck.specific.archlinux.subprocess')
@patch.multiple(pacman, create=True, pacman=pacman_cmd)
def test_get_new_command_mocked(subp_mock, command, new_command, return_value):
subp_mock.check_output.return_value = return_value

View File

@ -22,7 +22,7 @@ def test_match(command):
Command(script='yaourt -S llc', stderr='error: target not found: llc'),
Command(script='pacman llc', stderr='error: target not found: llc'),
Command(script='sudo pacman llc', stderr='error: target not found: llc')])
@patch('thefuck.utils.subprocess')
@patch('thefuck.specific.archlinux.subprocess')
def test_match_mocked(subp_mock, command):
subp_mock.check_output.return_value = PKGFILE_OUTPUT_LLC
assert match(command, None)
@ -42,7 +42,7 @@ def test_get_new_command(command, fixed):
(Command(script='yaourt -S llc', stderr='error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
(Command(script='pacman -S llc', stderr='error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
(Command(script='sudo pacman -S llc', stderr='error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
@patch('thefuck.utils.subprocess')
@patch('thefuck.specific.archlinux.subprocess')
def test_get_new_command_mocked(subp_mock, command, fixed):
subp_mock.check_output.return_value = PKGFILE_OUTPUT_LLC
assert get_new_command(command, None) == fixed

View File

View File

@ -0,0 +1,29 @@
import pytest
from thefuck.specific.git import git_support
from tests.utils import Command
@pytest.mark.parametrize('called, command, stderr', [
('git co', 'git checkout', "19:22:36.299340 git.c:282 trace: alias expansion: co => 'checkout'"),
('git com file', 'git commit --verbose file',
"19:23:25.470911 git.c:282 trace: alias expansion: com => 'commit' '--verbose'")])
def test_git_support(called, command, stderr):
@git_support
def fn(command, settings): return command.script
assert fn(Command(script=called, stderr=stderr), None) == command
@pytest.mark.parametrize('command, is_git', [
('git pull', True),
('hub pull', True),
('git push --set-upstream origin foo', True),
('hub push --set-upstream origin foo', True),
('ls', False),
('cat git', False),
('cat hub', False)])
def test_git_support_match(command, is_git):
@git_support
def fn(command, settings): return True
assert fn(Command(script=command), None) == is_git

View File

@ -0,0 +1,18 @@
import pytest
from mock import Mock
from thefuck.specific.sudo import sudo_support
from tests.utils import Command
@pytest.mark.parametrize('return_value, command, called, result', [
('ls -lah', 'sudo ls', 'ls', 'sudo ls -lah'),
('ls -lah', 'ls', 'ls', 'ls -lah'),
(['ls -lah'], 'sudo ls', 'ls', ['sudo ls -lah']),
(True, 'sudo ls', 'ls', True),
(True, 'ls', 'ls', True),
(False, 'sudo ls', 'ls', False),
(False, 'ls', 'ls', False)])
def test_sudo_support(return_value, command, called, result):
fn = Mock(return_value=return_value, __name__='')
assert sudo_support(fn)(Command(command), None) == result
fn.assert_called_once_with(Command(called), None)

View File

@ -1,10 +1,9 @@
import pytest
from mock import Mock
from thefuck.utils import git_support, sudo_support, wrap_settings,\
from thefuck.utils import wrap_settings,\
memoize, get_closest, get_all_executables, replace_argument, \
get_all_matched_commands
from thefuck.types import Settings
from tests.utils import Command
@pytest.mark.parametrize('override, old, new', [
@ -16,43 +15,6 @@ def test_wrap_settings(override, old, new):
assert wrap_settings(override)(fn)(None, Settings(old)) == new
@pytest.mark.parametrize('return_value, command, called, result', [
('ls -lah', 'sudo ls', 'ls', 'sudo ls -lah'),
('ls -lah', 'ls', 'ls', 'ls -lah'),
(['ls -lah'], 'sudo ls', 'ls', ['sudo ls -lah']),
(True, 'sudo ls', 'ls', True),
(True, 'ls', 'ls', True),
(False, 'sudo ls', 'ls', False),
(False, 'ls', 'ls', False)])
def test_sudo_support(return_value, command, called, result):
fn = Mock(return_value=return_value, __name__='')
assert sudo_support(fn)(Command(command), None) == result
fn.assert_called_once_with(Command(called), None)
@pytest.mark.parametrize('called, command, stderr', [
('git co', 'git checkout', "19:22:36.299340 git.c:282 trace: alias expansion: co => 'checkout'"),
('git com file', 'git commit --verbose file', "19:23:25.470911 git.c:282 trace: alias expansion: com => 'commit' '--verbose'")])
def test_git_support(called, command, stderr):
@git_support
def fn(command, settings): return command.script
assert fn(Command(script=called, stderr=stderr), None) == command
@pytest.mark.parametrize('command, is_git', [
('git pull', True),
('hub pull', True),
('git push --set-upstream origin foo', True),
('hub push --set-upstream origin foo', True),
('ls', False),
('cat git', False),
('cat hub', False)])
def test_git_support_match(command, is_git):
@git_support
def fn(command, settings): return True
assert fn(Command(script=command), None) == is_git
def test_memoize():
fn = Mock(__name__='fn')
memoized = memoize(fn)

View File

@ -2,7 +2,7 @@
import os
from difflib import get_close_matches
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
from thefuck.rules import cd_mkdir
__author__ = "mmussomele"

View File

@ -1,6 +1,6 @@
import re
from thefuck import shells
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,5 +1,5 @@
import re
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,7 +1,8 @@
from itertools import dropwhile, takewhile, islice
import re
import subprocess
from thefuck.utils import sudo_support, replace_command
from thefuck.utils import replace_command
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
import re
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,14 +1,15 @@
import re
from thefuck import utils, shells
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('did not match any file(s) known to git.' in command.stderr
and "Did you forget to 'git add'?" in command.stderr)
@utils.git_support
@git_support
def get_new_command(command, settings):
missing_file = re.findall(
r"error: pathspec '([^']*)' "

View File

@ -1,13 +1,14 @@
from thefuck import utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('branch -d' in command.script
and 'If you are sure you want to delete it' in command.stderr)
@utils.git_support
@git_support
def get_new_command(command, settings):
return replace_argument(command.script, '-d', '-D')

View File

@ -1,12 +1,13 @@
from thefuck import utils, shells
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
# catches "git branch list" in place of "git branch"
return command.script.split()[1:] == 'branch list'.split()
@utils.git_support
@git_support
def get_new_command(command, settings):
return shells.and_('git branch --delete list', 'git branch')

View File

@ -2,9 +2,10 @@ import re
import subprocess
from thefuck import shells, utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('did not match any file(s) known to git.' in command.stderr
and "Did you forget to 'git add'?" not in command.stderr)
@ -23,7 +24,7 @@ def get_branches():
yield line.strip()
@utils.git_support
@git_support
def get_new_command(command, settings):
missing_file = re.findall(
r"error: pathspec '([^']*)' "

View File

@ -1,13 +1,14 @@
from thefuck import utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('diff' in command.script and
'--staged' not in command.script)
@utils.git_support
@git_support
def get_new_command(command, settings):
return replace_argument(command.script, 'diff', 'diff --staged')

View File

@ -1,8 +1,9 @@
from thefuck import utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return (command.script.split()[1] == 'stash'
and 'usage:' in command.stderr)
@ -19,7 +20,7 @@ stash_commands = (
'show')
@utils.git_support
@git_support
def get_new_command(command, settings):
stash_cmd = command.script.split()[2]
fixed = utils.get_closest(stash_cmd, stash_commands, fallback_to_first=False)

View File

@ -1,6 +1,6 @@
import re
from thefuck.utils import (git_support,
get_all_matched_commands, replace_command)
from thefuck.utils import get_all_matched_commands, replace_command
from thefuck.specific.git import git_support
@git_support

View File

@ -1,13 +1,14 @@
from thefuck import shells, utils
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('pull' in command.script
and 'set-upstream' in command.stderr)
@utils.git_support
@git_support
def get_new_command(command, settings):
line = command.stderr.split('\n')[-3].strip()
branch = line.split(' ')[-1]

View File

@ -1,13 +1,14 @@
from thefuck import utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('fatal: Not a git repository' in command.stderr
and "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)." in command.stderr)
@utils.git_support
@git_support
def get_new_command(command, settings):
return replace_argument(command.script, 'pull', 'clone')

View File

@ -1,12 +1,13 @@
from thefuck import utils
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('push' in command.script
and 'set-upstream' in command.stderr)
@utils.git_support
@git_support
def get_new_command(command, settings):
return command.stderr.split('\n')[-3].strip()

View File

@ -1,8 +1,9 @@
from thefuck import utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('push' in command.script
and '! [rejected]' in command.stderr
@ -10,7 +11,7 @@ def match(command, settings):
and 'Updates were rejected because the tip of your current branch is behind' in command.stderr)
@utils.git_support
@git_support
def get_new_command(command, settings):
return replace_argument(command.script, 'push', 'push --force')

View File

@ -1,8 +1,9 @@
from thefuck import utils, shells
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
return ('push' in command.script
and '! [rejected]' in command.stderr
@ -10,7 +11,7 @@ def match(command, settings):
and 'Updates were rejected because the tip of your current branch is behind' in command.stderr)
@utils.git_support
@git_support
def get_new_command(command, settings):
return shells.and_(replace_argument(command.script, 'push', 'pull'),
command.script)

View File

@ -1,14 +1,15 @@
from thefuck import shells, utils
from thefuck.specific.git import git_support
@utils.git_support
@git_support
def match(command, settings):
# catches "Please commit or stash them" and "Please, commit your changes or
# stash them before you can switch branches."
return 'or stash them' in command.stderr
@utils.git_support
@git_support
def get_new_command(command, settings):
formatme = shells.and_('git stash', '{}')
return formatme.format(command.script)

View File

@ -1,5 +1,5 @@
import os
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,6 +1,6 @@
import re
from thefuck.utils import sudo_support,\
replace_command, get_all_matched_commands
from thefuck.utils import replace_command, get_all_matched_commands
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,5 +1,5 @@
import re
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,5 +1,6 @@
from difflib import get_close_matches
from thefuck.utils import sudo_support, get_all_executables
from thefuck.utils import get_all_executables
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,4 +1,4 @@
from thefuck.utils import get_pkgfile, archlinux_env
from thefuck.specific.archlinux import get_pkgfile, archlinux_env
from thefuck import shells

View File

@ -6,7 +6,8 @@ should be:
yaourt -S llvm
"""
from thefuck.utils import replace_command, get_pkgfile, archlinux_env
from thefuck.utils import replace_command
from thefuck.specific.archlinux import get_pkgfile, archlinux_env
def match(command, settings):

View File

@ -1,4 +1,4 @@
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
# add 'python' suffix to the command if
# 1) The script does not have execute permission or
# 2) is interpreted as shell script

View File

@ -1,5 +1,5 @@
import re
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

@ -1,5 +1,4 @@
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
enabled_by_default = False

View File

@ -1,7 +1,7 @@
"""
The confusion in systemctl's param order is massive.
"""
from thefuck.utils import sudo_support
from thefuck.specific.sudo import sudo_support
@sudo_support

View File

View File

@ -0,0 +1,41 @@
""" This file provide some utility functions for Arch Linux specific rules."""
import thefuck.utils
import subprocess
@thefuck.utils.memoize
def get_pkgfile(command):
""" Gets the packages that provide the given command using `pkgfile`.
If the command is of the form `sudo foo`, searches for the `foo` command
instead.
"""
try:
command = command.strip()
if command.startswith('sudo '):
command = command[5:]
command = command.split(" ")[0]
packages = subprocess.check_output(
['pkgfile', '-b', '-v', command],
universal_newlines=True, stderr=thefuck.utils.DEVNULL
).splitlines()
return [package.split()[0] for package in packages]
except subprocess.CalledProcessError:
return None
def archlinux_env():
if thefuck.utils.which('yaourt'):
pacman = 'yaourt'
elif thefuck.utils.which('pacman'):
pacman = 'sudo pacman'
else:
return False, None
enabled_by_default = thefuck.utils.which('pkgfile')
return enabled_by_default, pacman

37
thefuck/specific/git.py Normal file
View File

@ -0,0 +1,37 @@
from functools import wraps
import re
from shlex import split
from ..types import Command
from ..utils import quote
def git_support(fn):
"""Resolves git aliases and supports testing for both git and hub."""
@wraps(fn)
def wrapper(command, settings):
# supports GitHub's `hub` command
# which is recommended to be used with `alias git=hub`
# but at this point, shell aliases have already been resolved
is_git_cmd = command.script.startswith(('git', 'hub'))
if not is_git_cmd:
return False
# perform git aliases expansion
if 'trace: alias expansion:' in command.stderr:
search = re.search("trace: alias expansion: ([^ ]*) => ([^\n]*)",
command.stderr)
alias = search.group(1)
# by default git quotes everything, for example:
# 'commit' '--amend'
# which is surprising and does not allow to easily test for
# eg. 'git commit'
expansion = ' '.join(map(quote, split(search.group(2))))
new_script = command.script.replace(alias, expansion)
command = Command._replace(command, script=new_script)
return fn(command, settings)
return wrapper

24
thefuck/specific/sudo.py Normal file
View File

@ -0,0 +1,24 @@
from functools import wraps
import six
from ..types import Command
def sudo_support(fn):
"""Removes sudo before calling fn and adds it after."""
@wraps(fn)
def wrapper(command, settings):
if not command.script.startswith('sudo '):
return fn(command, settings)
result = fn(Command(command.script[5:],
command.stdout,
command.stderr),
settings)
if result and isinstance(result, six.string_types):
return u'sudo {}'.format(result)
elif isinstance(result, list):
return [u'sudo {}'.format(x) for x in result]
else:
return result
return wrapper

View File

@ -1,14 +1,12 @@
from subprocess import CalledProcessError
import subprocess
from difflib import get_close_matches
from functools import wraps
from pathlib import Path
from shlex import split
import os
import pickle
import re
from pathlib import Path
import six
from .types import Command
DEVNULL = open(os.devnull, 'w')
@ -57,59 +55,6 @@ def wrap_settings(params):
return decorator
def sudo_support(fn):
"""Removes sudo before calling fn and adds it after."""
@wraps(fn)
def wrapper(command, settings):
if not command.script.startswith('sudo '):
return fn(command, settings)
result = fn(Command(command.script[5:],
command.stdout,
command.stderr),
settings)
if result and isinstance(result, six.string_types):
return u'sudo {}'.format(result)
elif isinstance(result, list):
return [u'sudo {}'.format(x) for x in result]
else:
return result
return wrapper
def git_support(fn):
"""Resolves git aliases and supports testing for both git and hub."""
@wraps(fn)
def wrapper(command, settings):
# supports GitHub's `hub` command
# which is recommended to be used with `alias git=hub`
# but at this point, shell aliases have already been resolved
is_git_cmd = command.script.startswith(('git', 'hub'))
if not is_git_cmd:
return False
# perform git aliases expansion
if 'trace: alias expansion:' in command.stderr:
search = re.search("trace: alias expansion: ([^ ]*) => ([^\n]*)",
command.stderr)
alias = search.group(1)
# by default git quotes everything, for example:
# 'commit' '--amend'
# which is surprising and does not allow to easily test for
# eg. 'git commit'
expansion = ' '.join(map(quote, split(search.group(2))))
new_script = command.script.replace(alias, expansion)
command = Command._replace(command, script=new_script)
return fn(command, settings)
return wrapper
def memoize(fn):
"""Caches previous calls to the function."""
memo = {}
@ -187,41 +132,3 @@ def replace_command(command, broken, matched):
new_cmds = get_close_matches(broken, matched, cutoff=0.1)
return [replace_argument(command.script, broken, new_cmd.strip())
for new_cmd in new_cmds]
@memoize
def get_pkgfile(command):
""" Gets the packages that provide the given command using `pkgfile`.
If the command is of the form `sudo foo`, searches for the `foo` command
instead.
"""
try:
command = command.strip()
if command.startswith('sudo '):
command = command[5:]
command = command.split(" ")[0]
packages = subprocess.check_output(
['pkgfile', '-b', '-v', command],
universal_newlines=True, stderr=DEVNULL
).splitlines()
return [package.split()[0] for package in packages]
except CalledProcessError:
return None
def archlinux_env():
if which('yaourt'):
pacman = 'yaourt'
elif which('pacman'):
pacman = 'sudo pacman'
else:
return False, None
enabled_by_default = which('pkgfile')
return enabled_by_default, pacman