def install_hooks(term: Terminal, args: Namespace) -> None: pre_commit_hook = PreCommitHook() pyproject_toml = get_pyproject_toml_path() config = load_config_from_pyproject_toml(pyproject_toml) if pre_commit_hook.exists() and not args.force: term.ok('autohooks pre-commit hook is already installed at {}.'.format( str(pre_commit_hook))) with term.indent(): term.print() term.info( "Run 'autohooks activate --force' to override the current " "installed pre-commit hook.") term.info( "Run 'autohooks check' to validate the current status of " "the installed pre-commit hook.") else: if not config.is_autohooks_enabled(): term.warning('autohooks is not enabled in your {} file. ' 'Run \'autohooks check\' for more ' 'details.'.format(str(pyproject_toml))) if args.mode: mode = Mode.from_string(args.mode) else: mode = config.get_mode() pre_commit_hook.write(mode=mode) term.ok( 'autohooks pre-commit hook installed at {} using {} mode.'.format( str(pre_commit_hook), str(mode.get_effective_mode())))
def run() -> int: term = Terminal() _set_terminal(term) config = load_config_from_pyproject_toml() pre_commit_hook = PreCommitHook() check_hook_is_current(term, pre_commit_hook) if config.has_autohooks_config(): check_hook_mode(term, config.get_mode(), pre_commit_hook.read_mode()) plugins = get_project_autohooks_plugins_path() plugins_dir_name = str(plugins) if plugins.is_dir(): sys.path.append(plugins_dir_name) term.bold_info('autohooks => pre-commit') with autohooks_module_path(), term.indent(): for name in config.get_pre_commit_script_names(): term.info('Running {}'.format(name)) with term.indent(): try: plugin = load_plugin(name) if not has_precommit_function(plugin): term.fail( 'No precommit function found in plugin {}. ' 'Your autohooks settings may be invalid.'.format( name)) return 1 if has_precommit_parameters(plugin): retval = plugin.precommit(config=config.get_config()) else: term.warning( 'precommit function without kwargs is deprecated. ' 'Please update {} to a newer version.'.format( name)) retval = plugin.precommit() if retval: return retval except ImportError as e: term.error('An error occurred while importing pre-commit ' 'hook {}. {}.'.format(name, e)) return 1 except Exception as e: # pylint: disable=broad-except term.error('An error occurred while running pre-commit ' 'hook {}. {}.'.format(name, e)) return 1 return 0
def test_info(self): term = Terminal() term.info('foo bar') # width has been calculated self.width_mock.assert_called_with() # 71 == 80 - 5 - len('info') self.terminal_mock.move_x.assert_called_with(71) # info has been printed in cyan self.terminal_mock.cyan.assert_called_with('info') # an actual output has been generated self.assertCalled(self.print_mock)
def check_config( term: Terminal, pyproject_toml: Path, pre_commit_hook: PreCommitHook, ) -> None: if not pyproject_toml.exists(): term.error( f'Missing {str(pyproject_toml)} file. Please add a pyproject.toml ' f'file and include a "{AUTOHOOKS_SECTION}" section.' ) else: config = load_config_from_pyproject_toml(pyproject_toml) if not config.is_autohooks_enabled(): term.error( f'autohooks is not enabled in your {str(pyproject_toml)} file.' f' Please add a "{AUTOHOOKS_SECTION}" section.' ) elif pre_commit_hook.exists(): config_mode = config.get_mode() hook_mode = pre_commit_hook.read_mode() if config_mode == Mode.UNDEFINED: term.warning( f'autohooks mode is not defined in {str(pyproject_toml)}.' ) elif config_mode == Mode.UNKNOWN: term.warning( f'Unknown autohooks mode in {str(pyproject_toml)}.' ) if ( config_mode.get_effective_mode() != hook_mode.get_effective_mode() ): term.warning( f'autohooks mode "{str(hook_mode)}" in pre-commit ' f'hook {str(pre_commit_hook)} differs from ' f'mode "{str(config_mode)}" in {str(pyproject_toml)}.' ) term.info( f'Using autohooks mode "{str(hook_mode.get_effective_mode())}".' ) plugins = config.get_pre_commit_script_names() if not plugins: term.error( 'No autohooks plugin is activated in ' f'{str(pyproject_toml)} for your pre commit hook. Please ' 'add a "pre-commit = [plugin1, plugin2]" setting.' ) else: with autohooks_module_path(): for name in plugins: try: plugin = load_plugin(name) if not has_precommit_function(plugin): term.error( f'Plugin "{name}" has no precommit ' 'function. The function is required to run' ' the plugin as git pre commit hook.' ) elif not has_precommit_parameters(plugin): term.warning( f'Plugin "{name}" uses a deprecated ' 'signature for its precommit function. It ' 'is missing the **kwargs parameter.' ) else: term.ok(f'Plugin "{name}" active and loadable.') except ImportError as e: term.error( f'"{name}" is not a valid autohooks ' f'plugin. {e}' )
def check_config( term: Terminal, pyproject_toml: Path, pre_commit_hook: PreCommitHook, ) -> None: if not pyproject_toml.exists(): term.error( 'Missing {} file. Please add a pyproject.toml file and include ' 'a "{}" section.'.format(str(pyproject_toml), AUTOHOOKS_SECTION) ) else: config = load_config_from_pyproject_toml(pyproject_toml) if not config.is_autohooks_enabled(): term.error( 'autohooks is not enabled in your {} file. Please add ' 'a "{}" section.'.format(str(pyproject_toml), AUTOHOOKS_SECTION) ) elif pre_commit_hook.exists(): config_mode = config.get_mode() hook_mode = pre_commit_hook.read_mode() if config_mode == Mode.UNDEFINED: term.warning( 'autohooks mode is not defined in {}.'.format( str(pyproject_toml) ) ) elif config_mode == Mode.UNKNOWN: term.warning( 'Unknown autohooks mode in {}.'.format(str(pyproject_toml)) ) if ( config_mode.get_effective_mode() != hook_mode.get_effective_mode() ): term.warning( 'autohooks mode "{}" in pre-commit hook {} differs from ' 'mode "{}" in {}.'.format( str(hook_mode), str(pre_commit_hook), str(config_mode), str(pyproject_toml), ) ) term.info( 'Using autohooks mode "{}".'.format( str(hook_mode.get_effective_mode()) ) ) plugins = config.get_pre_commit_script_names() if not plugins: term.error( 'No autohooks plugin is activated in {} for your pre ' 'commit hook. Please add a ' '"pre-commit = [plugin1, plugin2]" ' 'setting.'.format(str(pyproject_toml)) ) else: with autohooks_module_path(): for name in plugins: try: plugin = load_plugin(name) if not has_precommit_function(plugin): term.error( 'Plugin "{}" has no precommit function. ' 'The function is required to run the ' 'plugin as git pre commit hook.'.format( name ) ) elif not has_precommit_parameters(plugin): term.warning( 'Plugin "{}" uses a deprecated signature ' 'for its precommit function. It is missing ' 'the **kwargs parameter.'.format(name) ) else: term.ok( 'Plugin "{}" active and loadable.'.format( name ) ) except ImportError as e: term.error( '"{}" is not a valid autohooks ' 'plugin. {}'.format(name, e) )
class TerminalTestCase(unittest.TestCase): def setUp(self): self.maxDiff = 180 # getting the bash-color-codes from the colorful module self.red = cf.red self.green = cf.green self.yellow = cf.yellow self.cyan = cf.cyan self.reset = cf.reset self.bold = cf.bold # every colors second value is the reset value ... self.term = Terminal() self.term.get_width = MagicMock(return_value=80) @patch('sys.stdout', new_callable=StringIO) def test_error(self, mock_stdout): status = '{} '.format(self.red(Signs.ERROR)) msg = 'foo bar' expected_msg = (self.reset('{}{}'.format(status, msg)).styled_string + '\n') expected_len = len(expected_msg) self.term.error(msg) ret = mock_stdout.getvalue() self.assertEqual(ret, expected_msg) self.assertEqual(len(ret), expected_len) @patch('sys.stdout', new_callable=StringIO) def test_fail(self, mock_stdout): status = '{} '.format(self.red(Signs.FAIL)) msg = 'foo bar baz' expected_msg = (self.reset('{}{}'.format(status, msg)).styled_string + '\n') expected_len = len(expected_msg) self.term.fail(msg) ret = mock_stdout.getvalue() self.assertEqual(ret, expected_msg) self.assertEqual(len(ret), expected_len) @patch('sys.stdout', new_callable=StringIO) def test_info(self, mock_stdout): status = '{} '.format(self.cyan(Signs.INFO)) msg = 'foo bar' expected_msg = (self.reset('{}{}'.format(status, msg)).styled_string + '\n') expected_len = len(expected_msg) self.term.info(msg) ret = mock_stdout.getvalue() self.assertEqual(ret, expected_msg) self.assertEqual(len(ret), expected_len) @patch('sys.stdout', new_callable=StringIO) def test_bold_info(self, mock_stdout): status = '{} '.format(self.cyan(Signs.INFO)) msg = 'bold foo bar' expected_msg = (self.bold('{}{}'.format(status, msg)).styled_string + '\n') expected_len = len(expected_msg) self.term.bold_info(msg) ret = mock_stdout.getvalue() self.assertEqual(ret, expected_msg) self.assertEqual(len(ret), expected_len) @patch('sys.stdout', new_callable=StringIO) def test_ok(self, mock_stdout): status = '{} '.format(self.green(Signs.OK)) msg = 'foo bar' expected_msg = (self.reset('{}{}'.format(status, msg)).styled_string + '\n') expected_len = len(expected_msg) self.term.ok(msg) ret = mock_stdout.getvalue() self.assertEqual(ret, expected_msg) self.assertEqual(len(ret), expected_len) @patch('sys.stdout', new_callable=StringIO) def test_warning(self, mock_stdout): msg = 'foo bar' status = '{} '.format(self.yellow(Signs.WARNING)) expected_msg = (self.reset('{}{}'.format(status, msg)).styled_string + '\n') expected_len = len(expected_msg) self.term.warning(msg) ret = mock_stdout.getvalue() self.assertEqual(ret, expected_msg) self.assertEqual(len(ret), expected_len) @patch('sys.stdout', new_callable=StringIO) def test_print(self, mock_stdout): expected_msg = self.reset(' foo bar').styled_string + '\n' self.term.print('foo bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_add_indent(self, mock_stdout): i = 6 expected_msg = self.reset(' ' * i + 'foo').styled_string + '\n' self.term.add_indent(i - 2) self.term.print('foo') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) # clear the buffer mock_stdout.truncate(0) mock_stdout.seek(0) j = 4 expected_msg = self.reset(' ' * (i + j) + 'bar').styled_string + '\n' self.term.add_indent(j) self.term.print('bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) # clear the buffer mock_stdout.truncate(0) mock_stdout.seek(0) @patch('sys.stdout', new_callable=StringIO) def test_reset_indent(self, mock_stdout): i = 6 expected_msg = self.reset(' ' * i + 'foo').styled_string + '\n' self.term.add_indent(i - 2) self.term.print('foo') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) # clear the buffer mock_stdout.truncate(0) mock_stdout.seek(0) expected_msg = self.reset(' bar').styled_string + '\n' self.term.reset_indent() self.term.print('bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_with_indent(self, mock_stdout): expected_msg = self.reset(' foo').styled_string + '\n' with self.term.indent(2): self.term.print('foo') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) # clear the buffer mock_stdout.truncate(0) mock_stdout.seek(0) expected_msg = self.reset(' bar').styled_string + '\n' self.term.print('bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_long_msg(self, mock_stdout): long_msg = ( 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, ' 'sed diam nonumy eirmod tempor invidunt ut labore et dolore magna' ' aliquyam erat, sed diam voluptua.') expected_msg = (self.reset( ' Lorem ipsum dolor sit amet, consetetur sadipscing elitr, ' 'sed diam nonumy eirmo\n d tempor invidunt ut labore et' ' dolore magna aliquyam erat, sed diam voluptua.').styled_string + '\n') expected_len = len(expected_msg) self.term.print(long_msg) ret = mock_stdout.getvalue() self.assertEqual(ret, expected_msg) self.assertEqual(len(ret), expected_len)
class TerminalTestCase(unittest.TestCase): def setUp(self): self.maxDiff = None # getting the bash-color-codes from the colorful module self.red = cf.red.style[0] self.green = cf.green.style[0] self.yellow = cf.yellow.style[0] self.cyan = cf.cyan.style[0] self.reset = cf.black.style[ 1] # every colors second value is the reset value ... self.term = Terminal() self.term.get_width = MagicMock(return_value=80) @patch('sys.stdout', new_callable=StringIO) def test_error(self, mock_stdout): msg = 'foo bar' width = self.term.get_width() expected_len = width + len(self.red) + len(self.reset) + 1 status = '[ {}error{} ]\n'.format(self.red, self.reset) sep = ' ' * (expected_len - len(msg) - len(status)) expected_msg = msg + sep + status self.term.error(msg) ret = mock_stdout.getvalue() self.assertEqual(len(ret), expected_len) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_fail(self, mock_stdout): width = self.term.get_width() expected_len = width + len(self.red) + len(self.reset) + 1 status = '[ {}fail{} ]\n'.format(self.red, self.reset) msg = 'foo bar baz' sep = ' ' * (expected_len - len(msg) - len(status)) expected_msg = msg + sep + status self.term.fail('foo bar baz') ret = mock_stdout.getvalue() self.assertEqual(len(ret), expected_len) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_info(self, mock_stdout): width = self.term.get_width() expected_len = width + len(self.cyan) + len(self.reset) + 1 status = '[ {}info{} ]\n'.format(self.cyan, self.reset) msg = 'foo bar' sep = ' ' * (expected_len - len(msg) - len(status)) expected_msg = msg + sep + status self.term.info('foo bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), expected_len) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_ok(self, mock_stdout): width = self.term.get_width() expected_len = width + len(self.green) + len(self.reset) + 1 status = '[ {}ok{} ]\n'.format(self.green, self.reset) msg = 'foo bar' sep = ' ' * (expected_len - len(msg) - len(status)) expected_msg = msg + sep + status self.term.ok('foo bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), expected_len) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_warning(self, mock_stdout): width = self.term.get_width() expected_len = width + len(self.yellow) + len(self.reset) + 1 msg = 'foo bar' status = '[ {}warning{} ]\n'.format(self.yellow, self.reset) sep = ' ' * (expected_len - len(msg) - len(status)) expected_msg = msg + sep + status self.term.warning(msg) ret = mock_stdout.getvalue() self.assertEqual(len(ret), expected_len) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_print(self, mock_stdout): term = Terminal() expected_msg = 'foo bar\n' term.print('foo bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_add_indent(self, mock_stdout): term = Terminal() i = 6 expected_msg = ' ' * i + 'foo\n' term.add_indent(i) term.print('foo') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) # clear the buffer mock_stdout.truncate(0) mock_stdout.seek(0) j = 4 expected_msg = ' ' * (i + j) + 'bar\n' term.add_indent(j) term.print('bar') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) @patch('sys.stdout', new_callable=StringIO) def test_with_indent(self, mock_stdout): term = Terminal() expected_msg = ' foo\n' with term.indent(2): term.print('foo') ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg) # clear the buffer mock_stdout.truncate(0) mock_stdout.seek(0) term.print('bar') expected_msg = 'bar\n' ret = mock_stdout.getvalue() self.assertEqual(len(ret), len(expected_msg)) self.assertEqual(ret, expected_msg)