Repository: pre-commit/pre-commit

Issue:
System hooks with spaces in entry are not runnable
It's pretty reasonable to have a system hook that looks like this:

```
-   id: foo
    name: foo
    entry: python -m bar
    language: system
```

Currently this fails:

```
$ pre-commit run foo --all-files
foo...................................................Failed

xargs: python -m bar: No such file or directory
```



Discussion:

Resolving patch:
diff --git a/pre_commit/languages/system.py b/pre_commit/languages/system.py
--- a/pre_commit/languages/system.py
+++ b/pre_commit/languages/system.py
@@ -1,3 +1,6 @@
+import shlex
+
+
 ENVIRONMENT_DIR = None
 
 
@@ -7,7 +10,7 @@ def install_environment(repo_cmd_runner):
 
 def run_hook(repo_cmd_runner, hook, file_args):
     return repo_cmd_runner.run(
-        ['xargs', hook['entry']] + hook['args'],
+        ['xargs'] + shlex.split(hook['entry']) + hook['args'],
         # TODO: this is duplicated in pre_commit/languages/helpers.py
         stdin='\n'.join(list(file_args) + ['']),
         retcode=None,


Test patch:
diff --git a/testing/resources/prints_cwd_repo/setup.py b/testing/resources/prints_cwd_repo/setup.py
deleted file mode 100644
--- a/testing/resources/prints_cwd_repo/setup.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from setuptools import find_packages
-from setuptools import setup
-
-setup(
-    name='prints_cwd',
-    version='0.0.0',
-    packages=find_packages('.'),
-    entry_points={
-        'console_scripts': ['prints_cwd = prints_cwd.main:func'],
-    },
-)
diff --git a/testing/resources/system_hook_with_spaces_repo/hooks.yaml b/testing/resources/system_hook_with_spaces_repo/hooks.yaml
new file mode 100644
--- /dev/null
+++ b/testing/resources/system_hook_with_spaces_repo/hooks.yaml
@@ -0,0 +1,4 @@
+-   id: system-hook-with-spaces
+    name: System hook with spaces
+    entry: /usr/bin/python -c 'import sys; print("Hello World")'
+    language: system
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -102,6 +102,11 @@ def failing_hook_repo(dummy_git_repo):
     yield _make_repo(dummy_git_repo, 'failing_hook_repo')
 
 
+@pytest.yield_fixture
+def system_hook_with_spaces_repo(dummy_git_repo):
+    yield _make_repo(dummy_git_repo, 'system_hook_with_spaces_repo')
+
+
 def _make_config(path, hook_id, file_regex):
     config = {
         'repo': path,
@@ -138,6 +143,13 @@ def config_for_script_hooks_repo(script_hooks_repo):
     yield _make_config(script_hooks_repo, 'bash_hook', '')
 
 
+@pytest.yield_fixture
+def config_for_system_hook_with_spaces(system_hook_with_spaces_repo):
+    yield _make_config(
+        system_hook_with_spaces_repo, 'system-hook-with-spaces', '',
+    )
+
+
 def _make_repo_from_configs(*configs):
     with open(C.CONFIG_FILE, 'w') as config_file:
         yaml.dump(
diff --git a/tests/repository_test.py b/tests/repository_test.py
--- a/tests/repository_test.py
+++ b/tests/repository_test.py
@@ -40,6 +40,14 @@ def test_cwd_of_hook(config_for_prints_cwd_repo, store):
     assert ret[1] == repo.repo_url + '\n'
 
 
+@pytest.mark.integration
+def test_system_hook_with_spaces(config_for_system_hook_with_spaces, store):
+    repo = Repository.create(config_for_system_hook_with_spaces, store)
+    ret = repo.run_hook('system-hook-with-spaces', [])
+    assert ret[0] == 0
+    assert ret[1] == 'Hello World\n'
+
+
 @skipif_slowtests_false
 @pytest.mark.integration
 def test_run_a_node_hook(config_for_node_hooks_repo, store):