We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
documention regarding adding new python based hooks needs improvement
Apparently we need some kind of how-to or mini tutorial on how to add a new hook to pre-commit as the basic documentation does not help someone without previous pre-commit knowledge.

I wanted to add support for `bashate`, a shell script linter written in python, available on pypi and that installs a shell script with the same name that can be used just like other linters.

Initially I went to https://pre-commit.com/#new-hooks which didn't give me enough info. So, i looked for other linters based on python and I found yamllint, which pointed me to https://github.com/adrienverge/yamllint/blob/master/.pre-commit-hooks.yaml

So the idea was to add the hook definition directly to the linter package. In this case I had to fork bashate in order to test the new, hook. So I ended up creating https://github.com/ssbarnea/bashate/blob/master/.pre-commit-hooks.yaml -- folowing the same model used in yamllint.

Now, I wanted to add and test the hook on one of the repos I maintain so I did create https://github.com/pycontribs/jira/blob/feature/pre-commit/.pre-commit-config.yaml#L25

When I tried to run it using `pre-commit run bashate --all`, it failed with this error:
```
Bashate..................................................................An unexpected error has occurred: OSError: [Errno 2] No such file or directory
Check the log at /Users/ssbarnea/.cache/pre-commit/pre-commit.log

An unexpected error has occurred: OSError: [Errno 2] No such file or directory
Traceback (most recent call last):
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/error_handler.py", line 47, in error_handler
    yield
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/main.py", line 258, in main
    return run(runner, args)
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/commands/run.py", line 270, in run
    return _run_hooks(runner.config, repo_hooks, args, environ)
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/commands/run.py", line 199, in _run_hooks
    retval |= _run_single_hook(filenames, hook, repo, args, skips, cols)
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/commands/run.py", line 110, in _run_single_hook
    hook, tuple(filenames) if hook['pass_filenames'] else (),
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/repository.py", line 207, in run_hook
    return languages[language_name].run_hook(prefix, hook, file_args)
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/languages/script.py", line 16, in run_hook
    return xargs(cmd, file_args)
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/xargs.py", line 63, in xargs
    *run_cmd, encoding=None, retcode=None
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/util.py", line 167, in cmd_output
    proc = subprocess.Popen(cmd, **popen_kwargs)
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/subprocess.py", line 390, in __init__
    errread, errwrite)
  File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/subprocess.py", line 1025, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
```

At the moment I have no idea what I did wrong, probably something simple. Still, we need to make it easier to integrate new linters into pre-commit.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pre_commit/parse_shebang.py`
Content:
```
1 from __future__ import absolute_import
2 from __future__ import unicode_literals
3 
4 import os.path
5 
6 from identify.identify import parse_shebang_from_file
7 
8 
9 class ExecutableNotFoundError(OSError):
10     def to_output(self):
11         return (1, self.args[0].encode('UTF-8'), b'')
12 
13 
14 def parse_filename(filename):
15     if not os.path.exists(filename):
16         return ()
17     else:
18         return parse_shebang_from_file(filename)
19 
20 
21 def find_executable(exe, _environ=None):
22     exe = os.path.normpath(exe)
23     if os.sep in exe:
24         return exe
25 
26     environ = _environ if _environ is not None else os.environ
27 
28     if 'PATHEXT' in environ:
29         possible_exe_names = tuple(
30             exe + ext.lower() for ext in environ['PATHEXT'].split(os.pathsep)
31         ) + (exe,)
32 
33     else:
34         possible_exe_names = (exe,)
35 
36     for path in environ.get('PATH', '').split(os.pathsep):
37         for possible_exe_name in possible_exe_names:
38             joined = os.path.join(path, possible_exe_name)
39             if os.path.isfile(joined) and os.access(joined, os.X_OK):
40                 return joined
41     else:
42         return None
43 
44 
45 def normexe(orig_exe):
46     if os.sep not in orig_exe:
47         exe = find_executable(orig_exe)
48         if exe is None:
49             raise ExecutableNotFoundError(
50                 'Executable `{}` not found'.format(orig_exe),
51             )
52         return exe
53     else:
54         return orig_exe
55 
56 
57 def normalize_cmd(cmd):
58     """Fixes for the following issues on windows
59     - https://bugs.python.org/issue8557
60     - windows does not parse shebangs
61 
62     This function also makes deep-path shebangs work just fine
63     """
64     # Use PATH to determine the executable
65     exe = normexe(cmd[0])
66 
67     # Figure out the shebang from the resulting command
68     cmd = parse_filename(exe) + (exe,) + cmd[1:]
69 
70     # This could have given us back another bare executable
71     exe = normexe(cmd[0])
72 
73     return (exe,) + cmd[1:]
74
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pre_commit/parse_shebang.py b/pre_commit/parse_shebang.py
--- a/pre_commit/parse_shebang.py
+++ b/pre_commit/parse_shebang.py
@@ -42,16 +42,21 @@
         return None
 
 
-def normexe(orig_exe):
-    if os.sep not in orig_exe:
-        exe = find_executable(orig_exe)
+def normexe(orig):
+    def _error(msg):
+        raise ExecutableNotFoundError('Executable `{}` {}'.format(orig, msg))
+
+    if os.sep not in orig and (not os.altsep or os.altsep not in orig):
+        exe = find_executable(orig)
         if exe is None:
-            raise ExecutableNotFoundError(
-                'Executable `{}` not found'.format(orig_exe),
-            )
+            _error('not found')
         return exe
+    elif not os.access(orig, os.X_OK):
+        _error('not found')
+    elif os.path.isdir(orig):
+        _error('is a directory')
     else:
-        return orig_exe
+        return orig
 
 
 def normalize_cmd(cmd):

{"golden_diff": "diff --git a/pre_commit/parse_shebang.py b/pre_commit/parse_shebang.py\n--- a/pre_commit/parse_shebang.py\n+++ b/pre_commit/parse_shebang.py\n@@ -42,16 +42,21 @@\n         return None\n \n \n-def normexe(orig_exe):\n-    if os.sep not in orig_exe:\n-        exe = find_executable(orig_exe)\n+def normexe(orig):\n+    def _error(msg):\n+        raise ExecutableNotFoundError('Executable `{}` {}'.format(orig, msg))\n+\n+    if os.sep not in orig and (not os.altsep or os.altsep not in orig):\n+        exe = find_executable(orig)\n         if exe is None:\n-            raise ExecutableNotFoundError(\n-                'Executable `{}` not found'.format(orig_exe),\n-            )\n+            _error('not found')\n         return exe\n+    elif not os.access(orig, os.X_OK):\n+        _error('not found')\n+    elif os.path.isdir(orig):\n+        _error('is a directory')\n     else:\n-        return orig_exe\n+        return orig\n \n \n def normalize_cmd(cmd):\n", "issue": "documention regarding adding new python based hooks needs improvement\nApparently we need some kind of how-to or mini tutorial on how to add a new hook to pre-commit as the basic documentation does not help someone without previous pre-commit knowledge.\r\n\r\nI wanted to add support for `bashate`, a shell script linter written in python, available on pypi and that installs a shell script with the same name that can be used just like other linters.\r\n\r\nInitially I went to https://pre-commit.com/#new-hooks which didn't give me enough info. So, i looked for other linters based on python and I found yamllint, which pointed me to https://github.com/adrienverge/yamllint/blob/master/.pre-commit-hooks.yaml\r\n\r\nSo the idea was to add the hook definition directly to the linter package. In this case I had to fork bashate in order to test the new, hook. So I ended up creating https://github.com/ssbarnea/bashate/blob/master/.pre-commit-hooks.yaml -- folowing the same model used in yamllint.\r\n\r\nNow, I wanted to add and test the hook on one of the repos I maintain so I did create https://github.com/pycontribs/jira/blob/feature/pre-commit/.pre-commit-config.yaml#L25\r\n\r\nWhen I tried to run it using `pre-commit run bashate --all`, it failed with this error:\r\n```\r\nBashate..................................................................An unexpected error has occurred: OSError: [Errno 2] No such file or directory\r\nCheck the log at /Users/ssbarnea/.cache/pre-commit/pre-commit.log\r\n\r\nAn unexpected error has occurred: OSError: [Errno 2] No such file or directory\r\nTraceback (most recent call last):\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/error_handler.py\", line 47, in error_handler\r\n    yield\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/main.py\", line 258, in main\r\n    return run(runner, args)\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/commands/run.py\", line 270, in run\r\n    return _run_hooks(runner.config, repo_hooks, args, environ)\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/commands/run.py\", line 199, in _run_hooks\r\n    retval |= _run_single_hook(filenames, hook, repo, args, skips, cols)\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/commands/run.py\", line 110, in _run_single_hook\r\n    hook, tuple(filenames) if hook['pass_filenames'] else (),\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/repository.py\", line 207, in run_hook\r\n    return languages[language_name].run_hook(prefix, hook, file_args)\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/languages/script.py\", line 16, in run_hook\r\n    return xargs(cmd, file_args)\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/xargs.py\", line 63, in xargs\r\n    *run_cmd, encoding=None, retcode=None\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/pre_commit/util.py\", line 167, in cmd_output\r\n    proc = subprocess.Popen(cmd, **popen_kwargs)\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/subprocess.py\", line 390, in __init__\r\n    errread, errwrite)\r\n  File \"/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/subprocess.py\", line 1025, in _execute_child\r\n    raise child_exception\r\nOSError: [Errno 2] No such file or directory\r\n```\r\n\r\nAt the moment I have no idea what I did wrong, probably something simple. Still, we need to make it easier to integrate new linters into pre-commit.\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import unicode_literals\n\nimport os.path\n\nfrom identify.identify import parse_shebang_from_file\n\n\nclass ExecutableNotFoundError(OSError):\n    def to_output(self):\n        return (1, self.args[0].encode('UTF-8'), b'')\n\n\ndef parse_filename(filename):\n    if not os.path.exists(filename):\n        return ()\n    else:\n        return parse_shebang_from_file(filename)\n\n\ndef find_executable(exe, _environ=None):\n    exe = os.path.normpath(exe)\n    if os.sep in exe:\n        return exe\n\n    environ = _environ if _environ is not None else os.environ\n\n    if 'PATHEXT' in environ:\n        possible_exe_names = tuple(\n            exe + ext.lower() for ext in environ['PATHEXT'].split(os.pathsep)\n        ) + (exe,)\n\n    else:\n        possible_exe_names = (exe,)\n\n    for path in environ.get('PATH', '').split(os.pathsep):\n        for possible_exe_name in possible_exe_names:\n            joined = os.path.join(path, possible_exe_name)\n            if os.path.isfile(joined) and os.access(joined, os.X_OK):\n                return joined\n    else:\n        return None\n\n\ndef normexe(orig_exe):\n    if os.sep not in orig_exe:\n        exe = find_executable(orig_exe)\n        if exe is None:\n            raise ExecutableNotFoundError(\n                'Executable `{}` not found'.format(orig_exe),\n            )\n        return exe\n    else:\n        return orig_exe\n\n\ndef normalize_cmd(cmd):\n    \"\"\"Fixes for the following issues on windows\n    - https://bugs.python.org/issue8557\n    - windows does not parse shebangs\n\n    This function also makes deep-path shebangs work just fine\n    \"\"\"\n    # Use PATH to determine the executable\n    exe = normexe(cmd[0])\n\n    # Figure out the shebang from the resulting command\n    cmd = parse_filename(exe) + (exe,) + cmd[1:]\n\n    # This could have given us back another bare executable\n    exe = normexe(cmd[0])\n\n    return (exe,) + cmd[1:]\n", "path": "pre_commit/parse_shebang.py"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import unicode_literals\n\nimport os.path\n\nfrom identify.identify import parse_shebang_from_file\n\n\nclass ExecutableNotFoundError(OSError):\n    def to_output(self):\n        return (1, self.args[0].encode('UTF-8'), b'')\n\n\ndef parse_filename(filename):\n    if not os.path.exists(filename):\n        return ()\n    else:\n        return parse_shebang_from_file(filename)\n\n\ndef find_executable(exe, _environ=None):\n    exe = os.path.normpath(exe)\n    if os.sep in exe:\n        return exe\n\n    environ = _environ if _environ is not None else os.environ\n\n    if 'PATHEXT' in environ:\n        possible_exe_names = tuple(\n            exe + ext.lower() for ext in environ['PATHEXT'].split(os.pathsep)\n        ) + (exe,)\n\n    else:\n        possible_exe_names = (exe,)\n\n    for path in environ.get('PATH', '').split(os.pathsep):\n        for possible_exe_name in possible_exe_names:\n            joined = os.path.join(path, possible_exe_name)\n            if os.path.isfile(joined) and os.access(joined, os.X_OK):\n                return joined\n    else:\n        return None\n\n\ndef normexe(orig):\n    def _error(msg):\n        raise ExecutableNotFoundError('Executable `{}` {}'.format(orig, msg))\n\n    if os.sep not in orig and (not os.altsep or os.altsep not in orig):\n        exe = find_executable(orig)\n        if exe is None:\n            _error('not found')\n        return exe\n    elif not os.access(orig, os.X_OK):\n        _error('not found')\n    elif os.path.isdir(orig):\n        _error('is a directory')\n    else:\n        return orig\n\n\ndef normalize_cmd(cmd):\n    \"\"\"Fixes for the following issues on windows\n    - https://bugs.python.org/issue8557\n    - windows does not parse shebangs\n\n    This function also makes deep-path shebangs work just fine\n    \"\"\"\n    # Use PATH to determine the executable\n    exe = normexe(cmd[0])\n\n    # Figure out the shebang from the resulting command\n    cmd = parse_filename(exe) + (exe,) + cmd[1:]\n\n    # This could have given us back another bare executable\n    exe = normexe(cmd[0])\n\n    return (exe,) + cmd[1:]\n", "path": "pre_commit/parse_shebang.py"}]}