1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-03-14 22:58:31 +00:00
thefuck/thefuck/main.py

163 lines
4.9 KiB
Python
Raw Normal View History

2015-04-08 18:15:49 +02:00
from imp import load_source
from pathlib import Path
from os.path import expanduser
2015-07-15 07:47:54 +03:00
from pprint import pformat
2015-04-08 18:15:49 +02:00
from subprocess import Popen, PIPE
import os
2015-04-08 18:15:49 +02:00
import sys
from psutil import Process, TimeoutExpired
2015-04-22 06:03:06 +02:00
import colorama
2015-05-04 04:44:16 +02:00
import six
from . import logs, conf, types, shells
2015-04-08 18:15:49 +02:00
2015-04-17 16:24:03 +02:00
def setup_user_dir():
"""Returns user config dir, create it when it doesn't exist."""
2015-04-08 18:15:49 +02:00
user_dir = Path(expanduser('~/.thefuck'))
rules_dir = user_dir.joinpath('rules')
if not rules_dir.is_dir():
rules_dir.mkdir(parents=True)
conf.initialize_settings_file(user_dir)
2015-04-08 18:15:49 +02:00
return user_dir
2015-04-17 16:24:03 +02:00
def load_rule(rule):
2015-04-08 18:15:49 +02:00
"""Imports rule module and returns it."""
rule_module = load_source(rule.name[:-3], str(rule))
2015-04-22 23:04:22 +02:00
return types.Rule(rule.name[:-3], rule_module.match,
rule_module.get_new_command,
getattr(rule_module, 'enabled_by_default', True),
2015-05-06 13:57:09 +02:00
getattr(rule_module, 'side_effect', None),
getattr(rule_module, 'priority', conf.DEFAULT_PRIORITY))
def _get_loaded_rules(rules, settings):
"""Yields all available rules."""
for rule in rules:
if rule.name != '__init__.py':
loaded_rule = load_rule(rule)
if loaded_rule in settings.rules:
yield loaded_rule
2015-04-08 18:15:49 +02:00
2015-04-17 16:24:03 +02:00
def get_rules(user_dir, settings):
2015-04-08 18:15:49 +02:00
"""Returns all enabled rules."""
2015-04-22 23:04:22 +02:00
bundled = Path(__file__).parent \
.joinpath('rules') \
.glob('*.py')
2015-04-08 18:15:49 +02:00
user = user_dir.joinpath('rules').glob('*.py')
2015-05-06 13:57:09 +02:00
rules = _get_loaded_rules(sorted(bundled) + sorted(user), settings)
return sorted(rules, key=lambda rule: settings.priority.get(
rule.name, rule.priority))
2015-04-08 18:15:49 +02:00
def wait_output(settings, popen):
"""Returns `True` if we can get output of the command in the
`wait_command` time.
Command will be killed if it wasn't finished in the time.
"""
proc = Process(popen.pid)
try:
proc.wait(settings.wait_command)
return True
except TimeoutExpired:
for child in proc.children(recursive=True):
child.kill()
proc.kill()
return False
2015-05-04 04:44:16 +02:00
def get_command(settings, args):
2015-04-08 18:15:49 +02:00
"""Creates command from `args` and executes it."""
2015-05-04 04:44:16 +02:00
if six.PY2:
2015-04-21 06:33:51 +02:00
script = ' '.join(arg.decode('utf-8') for arg in args[1:])
else:
script = ' '.join(args[1:])
if not script:
return
script = shells.from_shell(script)
logs.debug(u'Call: {}'.format(script), settings)
env = dict(os.environ)
env.update(settings.env)
logs.debug(u'Executing with env: {}'.format(env), settings)
result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE, env=env)
if wait_output(settings, result):
2015-04-22 23:04:22 +02:00
return types.Command(script, result.stdout.read().decode('utf-8'),
result.stderr.read().decode('utf-8'))
2015-04-08 18:15:49 +02:00
2015-04-17 16:24:03 +02:00
def get_matched_rule(command, rules, settings):
2015-04-08 18:15:49 +02:00
"""Returns first matched rule for command."""
for rule in rules:
try:
2015-07-15 07:47:54 +03:00
logs.debug(u'Trying rule: {}'.format(rule.name), settings)
if rule.match(command, settings):
return rule
except Exception:
2015-04-22 06:03:06 +02:00
logs.rule_failed(rule, sys.exc_info(), settings)
2015-04-08 18:15:49 +02:00
def confirm(new_command, side_effect, settings):
2015-04-21 05:30:15 +02:00
"""Returns `True` when running of new command confirmed."""
if not settings.require_confirmation:
logs.show_command(new_command, side_effect, settings)
2015-04-21 05:30:15 +02:00
return True
logs.confirm_command(new_command, side_effect, settings)
2015-04-21 05:30:15 +02:00
try:
sys.stdin.read(1)
return True
except KeyboardInterrupt:
2015-04-22 06:03:06 +02:00
logs.failed('Aborted', settings)
2015-04-21 05:30:15 +02:00
return False
2015-05-04 04:44:16 +02:00
def run_rule(rule, command, settings):
2015-04-08 18:15:49 +02:00
"""Runs command from rule for passed command."""
new_command = shells.to_shell(rule.get_new_command(command, settings))
if confirm(new_command, rule.side_effect, settings):
if rule.side_effect:
rule.side_effect(command, settings)
2015-05-04 04:44:16 +02:00
shells.put_to_history(new_command)
2015-04-21 05:30:15 +02:00
print(new_command)
2015-04-08 18:15:49 +02:00
# Entry points:
2015-04-08 18:15:49 +02:00
def main():
2015-04-22 06:03:06 +02:00
colorama.init()
user_dir = setup_user_dir()
settings = conf.get_settings(user_dir)
logs.debug(u'Run with settings: {}'.format(pformat(settings)), settings)
2015-05-04 04:44:16 +02:00
command = get_command(settings, sys.argv)
if command:
logs.debug(u'Received stdout: {}'.format(command.stdout), settings)
logs.debug(u'Received stderr: {}'.format(command.stderr), settings)
2015-07-15 07:47:54 +03:00
2015-04-22 16:22:10 +02:00
rules = get_rules(user_dir, settings)
2015-07-15 07:47:54 +03:00
logs.debug(
u'Loaded rules: {}'.format(', '.join(rule.name for rule in rules)),
2015-07-15 07:47:54 +03:00
settings)
2015-04-17 22:09:46 +02:00
matched_rule = get_matched_rule(command, rules, settings)
if matched_rule:
logs.debug(u'Matched rule: {}'.format(matched_rule.name), settings)
2015-05-04 04:44:16 +02:00
run_rule(matched_rule, command, settings)
return
2015-04-22 06:03:06 +02:00
logs.failed('No fuck given', settings)
def print_alias():
alias = shells.thefuck_alias()
if len(sys.argv) > 1:
alias = sys.argv[1]
print(shells.app_alias(alias))