Repository: pre-commit/pre-commit

Issue:
:args seems to break with {} in list.
I am working on a repo with some hooks for my company: https://github.com/marick/pre-commit-hooks

There is a hook that works fine with this `.pre-commit-config.yaml`:

``` yaml
-   repo: /Users/marick/src/pre-commit-hooks
    sha: d6dee96f56bf9290f7ebb852c4252c50b8f6215d
    stages: [commit, push]
    hooks:
    -   id: prohibit-suspicious-patterns
        args: ["AKIA[[:alnum]]", --]
```

However, it I change the first arg by adding `{1}`:

``` yaml
        args: ["AKIA[[:alnum]]{1}", --]
```

... I get this:

```
prohibit suspicious patterns..................................................................
An unexpected error has occurred: IndexError: tuple index out of range
Check the log at ~/.pre-commit/pre-commit.log
```

The contents of `pre-commit.log`:

```
An unexpected error has occurred: IndexError: tuple index out of range
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/pre_commit/error_handler.py", line 36, in error_handler
    yield
  File "/usr/local/lib/python2.7/site-packages/pre_commit/main.py", line 150, in main
    return run(runner, args)
  File "/usr/local/lib/python2.7/site-packages/pre_commit/commands/run.py", line 212, in run
    return _run_hooks(repo_hooks, args, write, environ)
  File "/usr/local/lib/python2.7/site-packages/pre_commit/commands/run.py", line 136, in _run_hooks
    retval |= _run_single_hook(hook, repo, args, write, skips)
  File "/usr/local/lib/python2.7/site-packages/pre_commit/commands/run.py", line 89, in _run_single_hook
    retcode, stdout, stderr = repo.run_hook(hook, filenames)
  File "/usr/local/lib/python2.7/site-packages/pre_commit/repository.py", line 145, in run_hook
    self.cmd_runner, hook, file_args,
  File "/usr/local/lib/python2.7/site-packages/pre_commit/languages/script.py", line 23, in run_hook
    encoding=None,
  File "/usr/local/lib/python2.7/site-packages/pre_commit/prefixed_command_runner.py", line 40, in run
    replaced_cmd = _replace_cmd(cmd, prefix=self.prefix_dir)
  File "/usr/local/lib/python2.7/site-packages/pre_commit/prefixed_command_runner.py", line 11, in _replace_cmd
    return [part.format(**kwargs) for part in cmd]
IndexError: tuple index out of range

```



Discussion:
Ah dang, this has to do with the way we format in the prefix directory.

A workaround for now is to replace `{1}` with `{{1}}`.

I wonder if I should document this somewhere as whatever I choose for substitution it'll be difficult to accept all strings (For example if I use `%s` instead I'll need to look out for args with `%` symbols in them).  What are you suggestions?

Oh, I could instead use `.replace(...)` and use some sentinel value instead of `.format(`ting, lemme try that.


Resolving patch:
diff --git a/pre_commit/prefixed_command_runner.py b/pre_commit/prefixed_command_runner.py
--- a/pre_commit/prefixed_command_runner.py
+++ b/pre_commit/prefixed_command_runner.py
@@ -7,10 +7,6 @@
 from pre_commit.util import cmd_output
 
 
-def _replace_cmd(cmd, **kwargs):
-    return [part.format(**kwargs) for part in cmd]
-
-
 class PrefixedCommandRunner(object):
     """A PrefixedCommandRunner allows you to run subprocess commands with
     comand substitution.
@@ -37,7 +33,9 @@ def _create_path_if_not_exists(self):
 
     def run(self, cmd, **kwargs):
         self._create_path_if_not_exists()
-        replaced_cmd = _replace_cmd(cmd, prefix=self.prefix_dir)
+        replaced_cmd = [
+            part.replace('{prefix}', self.prefix_dir) for part in cmd
+        ]
         return cmd_output(*replaced_cmd, __popen=self.__popen, **kwargs)
 
     def path(self, *parts):


Test patch:
diff --git a/tests/prefixed_command_runner_test.py b/tests/prefixed_command_runner_test.py
--- a/tests/prefixed_command_runner_test.py
+++ b/tests/prefixed_command_runner_test.py
@@ -7,7 +7,6 @@
 import pytest
 
 from pre_commit import five
-from pre_commit.prefixed_command_runner import _replace_cmd
 from pre_commit.prefixed_command_runner import PrefixedCommandRunner
 from pre_commit.util import CalledProcessError
 
@@ -59,19 +58,6 @@ def makedirs_mock():
     return mock.Mock(spec=os.makedirs)
 
 
-@pytest.mark.parametrize(('input', 'kwargs', 'expected_output'), (
-    ([], {}, []),
-    (['foo'], {}, ['foo']),
-    ([], {'foo': 'bar'}, []),
-    (['{foo}/baz'], {'foo': 'bar'}, ['bar/baz']),
-    (['foo'], {'foo': 'bar'}, ['foo']),
-    (['foo', '{bar}'], {'bar': 'baz'}, ['foo', 'baz']),
-))
-def test_replace_cmd(input, kwargs, expected_output):
-    ret = _replace_cmd(input, **kwargs)
-    assert ret == expected_output
-
-
 @pytest.mark.parametrize(('input', 'expected_prefix'), (
     norm_slash(('.', './')),
     norm_slash(('foo', 'foo/')),
diff --git a/tests/repository_test.py b/tests/repository_test.py
--- a/tests/repository_test.py
+++ b/tests/repository_test.py
@@ -178,6 +178,22 @@ def test_run_hook_with_spaced_args(tempdir_factory, store):
     )
 
 
+@pytest.mark.integration
+def test_run_hook_with_curly_braced_arguments(tempdir_factory, store):
+    _test_hook_repo(
+        tempdir_factory, store, 'arg_per_line_hooks_repo',
+        'arg-per-line',
+        [],
+        b"arg: hi {1}\narg: I'm {a} problem\n",
+        config_kwargs={
+            'hooks': [{
+                'id': 'arg-per-line',
+                'args': ['hi {1}', "I'm {a} problem"],
+            }]
+        },
+    )
+
+
 @xfailif_no_pcre_support
 @pytest.mark.integration
 def test_pcre_hook_no_match(tempdir_factory, store):