From 3e4c043cccc8f5294feff05a755dcecd1a40af0e Mon Sep 17 00:00:00 2001 From: nvbn Date: Wed, 15 Jul 2015 07:47:54 +0300 Subject: [PATCH] #280: Add debug output --- README.md | 7 +++++-- tests/test_logs.py | 7 +++++++ tests/test_main.py | 4 ++-- thefuck/conf.py | 6 ++++-- thefuck/logs.py | 10 ++++++++++ thefuck/main.py | 12 ++++++++++++ 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7c943900..65b5f402 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,8 @@ The Fuck has a few settings parameters which can be changed in `~/.thefuck/setti * `require_confirmation` – requires confirmation before running new command, by default `False`; * `wait_command` – max amount of time in seconds for getting previous command output; * `no_colors` – disable colored output; -* `priority` – dict with rules priorities, rule with lower `priority` will be matched first. +* `priority` – dict with rules priorities, rule with lower `priority` will be matched first; +* `debug` – enabled debug output, by default `False`; Example of `settings.py`: @@ -263,6 +264,7 @@ require_confirmation = True wait_command = 10 no_colors = False priority = {'sudo': 100, 'no_command': 9999} +debug = False ``` Or via environment variables: @@ -272,7 +274,8 @@ Or via environment variables: * `THEFUCK_WAIT_COMMAND` – max amount of time in seconds for getting previous command output; * `THEFUCK_NO_COLORS` – disable colored output, `true/false`; * `THEFUCK_PRIORITY` – priority of the rules, like `no_command=9999:apt_get=100`, -rule with lower `priority` will be matched first. +rule with lower `priority` will be matched first; +* `THEFUCK_DEBUG` – enables debug output, `true/false`. For example: diff --git a/tests/test_logs.py b/tests/test_logs.py index bf3780e1..6e87fe8b 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -5,3 +5,10 @@ from thefuck import logs def test_color(): assert logs.color('red', Mock(no_colors=False)) == 'red' assert logs.color('red', Mock(no_colors=True)) == '' + + +def test_debug(capsys): + logs.debug('test', Mock(no_colors=True, debug=True)) + assert capsys.readouterr() == ('', 'DEBUG: test\n') + logs.debug('test', Mock(no_colors=True, debug=False)) + assert capsys.readouterr() == ('', '') diff --git a/tests/test_main.py b/tests/test_main.py index fe82cfaa..7d07150d 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -110,7 +110,7 @@ class TestGetMatchedRule(object): def test_when_rule_failed(self, capsys): main.get_matched_rule( Command('ls'), [Rule('test', Mock(side_effect=OSError('Denied')))], - Mock(no_colors=True)) + Mock(no_colors=True, debug=False)) assert capsys.readouterr()[1].split('\n')[0] == '[WARN] Rule test:' @@ -126,7 +126,7 @@ class TestRunRule(object): def test_run_rule_with_side_effect(self, capsys): side_effect = Mock() - settings = Mock() + settings = Mock(debug=False) command = Command() main.run_rule(Rule(get_new_command=lambda *_: 'new-command', side_effect=side_effect), diff --git a/thefuck/conf.py b/thefuck/conf.py index 916d216f..14d33206 100644 --- a/thefuck/conf.py +++ b/thefuck/conf.py @@ -29,13 +29,15 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, 'wait_command': 3, 'require_confirmation': False, 'no_colors': False, + 'debug': False, 'priority': {}} ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', 'THEFUCK_WAIT_COMMAND': 'wait_command', 'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation', 'THEFUCK_NO_COLORS': 'no_colors', - 'THEFUCK_PRIORITY': 'priority'} + 'THEFUCK_PRIORITY': 'priority', + 'THEFUCK_DEBUG': 'debug'} SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file @@ -87,7 +89,7 @@ def _val_from_env(env, attr): return dict(_priority_from_env(val)) elif attr == 'wait_command': return int(val) - elif attr in ('require_confirmation', 'no_colors'): + elif attr in ('require_confirmation', 'no_colors', 'debug'): return val.lower() == 'true' else: return val diff --git a/thefuck/logs.py b/thefuck/logs.py index 3d765f69..7e98b2da 100644 --- a/thefuck/logs.py +++ b/thefuck/logs.py @@ -1,3 +1,4 @@ +from pprint import pformat import sys from traceback import format_exception import colorama @@ -52,3 +53,12 @@ def failed(msg, settings): msg=msg, red=color(colorama.Fore.RED, settings), reset=color(colorama.Style.RESET_ALL, settings))) + + +def debug(msg, settings): + if settings.debug: + sys.stderr.write(u'{blue}{bold}DEBUG:{reset} {msg}\n'.format( + msg=msg, + reset=color(colorama.Style.RESET_ALL, settings), + blue=color(colorama.Fore.BLUE, settings), + bold=color(colorama.Style.BRIGHT, settings))) diff --git a/thefuck/main.py b/thefuck/main.py index eebb6688..8d1a3dac 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -1,6 +1,7 @@ from imp import load_source from pathlib import Path from os.path import expanduser +from pprint import pformat from subprocess import Popen, PIPE import os import sys @@ -79,6 +80,7 @@ def get_command(settings, args): return script = shells.from_shell(script) + logs.debug('Call: {}'.format(script), settings) result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE, env=dict(os.environ, LANG='C')) if wait_output(settings, result): @@ -90,6 +92,7 @@ def get_matched_rule(command, rules, settings): """Returns first matched rule for command.""" for rule in rules: try: + logs.debug(u'Trying rule: {}'.format(rule.name), settings) if rule.match(command, settings): return rule except Exception: @@ -125,12 +128,21 @@ def main(): colorama.init() user_dir = setup_user_dir() settings = conf.get_settings(user_dir) + logs.debug('Run with settings: {}'.format(pformat(settings)), settings) command = get_command(settings, sys.argv) if command: + logs.debug('Received stdout: {}'.format(command.stdout), settings) + logs.debug('Received stderr: {}'.format(command.stderr), settings) + rules = get_rules(user_dir, settings) + logs.debug( + 'Loaded rules: {}'.format(', '.join(rule.name for rule in rules)), + settings) + matched_rule = get_matched_rule(command, rules, settings) if matched_rule: + logs.debug('Matched rule: {}'.format(matched_rule.name), settings) run_rule(matched_rule, command, settings) return