prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Empty hook file causes cryptic error
  If you have a pre_gen_project.sh or a post_gen_project.sh file with no data in it, cookiecutter fails with an unhelpful traceback.
  
  ```
  Traceback (most recent call last):
    File "/usr/local/bin/cookiecutter", line 11, in <module>
      sys.exit(main())
    File "/usr/local/lib/python2.7/site-packages/click/core.py", line 716, in __call__
      return self.main(*args, **kwargs)
    File "/usr/local/lib/python2.7/site-packages/click/core.py", line 696, in main
      rv = self.invoke(ctx)
    File "/usr/local/lib/python2.7/site-packages/click/core.py", line 889, in invoke
      return ctx.invoke(self.callback, **ctx.params)
    File "/usr/local/lib/python2.7/site-packages/click/core.py", line 534, in invoke
      return callback(*args, **kwargs)
    File "/usr/local/lib/python2.7/site-packages/cookiecutter/cli.py", line 100, in main
      config_file=user_config
    File "/usr/local/lib/python2.7/site-packages/cookiecutter/main.py", line 140, in cookiecutter
      output_dir=output_dir
    File "/usr/local/lib/python2.7/site-packages/cookiecutter/generate.py", line 273, in generate_files
      _run_hook_from_repo_dir(repo_dir, 'pre_gen_project', project_dir, context)
    File "/usr/local/lib/python2.7/site-packages/cookiecutter/generate.py", line 232, in _run_hook_from_repo_dir
      run_hook(hook_name, project_dir, context)
    File "/usr/local/lib/python2.7/site-packages/cookiecutter/hooks.py", line 116, in run_hook
      run_script_with_context(script, project_dir, context)
    File "/usr/local/lib/python2.7/site-packages/cookiecutter/hooks.py", line 101, in run_script_with_context
      run_script(temp.name, cwd)
    File "/usr/local/lib/python2.7/site-packages/cookiecutter/hooks.py", line 73, in run_script
      cwd=cwd
    File "/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 656, in __init__
      _cleanup()
    File "/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1335, in _execute_child
      raise child_exception
  OSError: [Errno 8] Exec format error
  ```
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `cookiecutter/hooks.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 
  3 """Functions for discovering and executing various cookiecutter hooks."""
  4 
  5 import io
  6 import logging
  7 import os
  8 import subprocess
  9 import sys
  10 import tempfile
  11 
  12 from jinja2 import Template
  13 
  14 from cookiecutter import utils
  15 from .exceptions import FailedHookException
  16 
  17 logger = logging.getLogger(__name__)
  18 
  19 _HOOKS = [
  20     'pre_gen_project',
  21     'post_gen_project',
  22 ]
  23 EXIT_SUCCESS = 0
  24 
  25 
  26 def valid_hook(hook_file, hook_name):
  27     """Determine if a hook file is valid.
  28 
  29     :param hook_file: The hook file to consider for validity
  30     :param hook_name: The hook to find
  31     :return: The hook file validity
  32     """
  33     filename = os.path.basename(hook_file)
  34     basename = os.path.splitext(filename)[0]
  35 
  36     matching_hook = basename == hook_name
  37     supported_hook = basename in _HOOKS
  38     backup_file = filename.endswith('~')
  39 
  40     return matching_hook and supported_hook and not backup_file
  41 
  42 
  43 def find_hook(hook_name, hooks_dir='hooks'):
  44     """Return a dict of all hook scripts provided.
  45 
  46     Must be called with the project template as the current working directory.
  47     Dict's key will be the hook/script's name, without extension, while values
  48     will be the absolute path to the script. Missing scripts will not be
  49     included in the returned dict.
  50 
  51     :param hook_name: The hook to find
  52     :param hooks_dir: The hook directory in the template
  53     :return: The absolute path to the hook script or None
  54     """
  55     logger.debug('hooks_dir is {}'.format(os.path.abspath(hooks_dir)))
  56 
  57     if not os.path.isdir(hooks_dir):
  58         logger.debug('No hooks/ dir in template_dir')
  59         return None
  60 
  61     for hook_file in os.listdir(hooks_dir):
  62         if valid_hook(hook_file, hook_name):
  63             return os.path.abspath(os.path.join(hooks_dir, hook_file))
  64 
  65     return None
  66 
  67 
  68 def run_script(script_path, cwd='.'):
  69     """Execute a script from a working directory.
  70 
  71     :param script_path: Absolute path to the script to run.
  72     :param cwd: The directory to run the script from.
  73     """
  74     run_thru_shell = sys.platform.startswith('win')
  75     if script_path.endswith('.py'):
  76         script_command = [sys.executable, script_path]
  77     else:
  78         script_command = [script_path]
  79 
  80     utils.make_executable(script_path)
  81 
  82     proc = subprocess.Popen(
  83         script_command,
  84         shell=run_thru_shell,
  85         cwd=cwd
  86     )
  87     exit_status = proc.wait()
  88     if exit_status != EXIT_SUCCESS:
  89         raise FailedHookException(
  90             "Hook script failed (exit status: %d)" % exit_status)
  91 
  92 
  93 def run_script_with_context(script_path, cwd, context):
  94     """Execute a script after rendering it with Jinja.
  95 
  96     :param script_path: Absolute path to the script to run.
  97     :param cwd: The directory to run the script from.
  98     :param context: Cookiecutter project template context.
  99     """
  100     _, extension = os.path.splitext(script_path)
  101 
  102     contents = io.open(script_path, 'r', encoding='utf-8').read()
  103 
  104     with tempfile.NamedTemporaryFile(
  105         delete=False,
  106         mode='wb',
  107         suffix=extension
  108     ) as temp:
  109         output = Template(contents).render(**context)
  110         temp.write(output.encode('utf-8'))
  111 
  112     run_script(temp.name, cwd)
  113 
  114 
  115 def run_hook(hook_name, project_dir, context):
  116     """
  117     Try to find and execute a hook from the specified project directory.
  118 
  119     :param hook_name: The hook to execute.
  120     :param project_dir: The directory to execute the script from.
  121     :param context: Cookiecutter project context.
  122     """
  123     script = find_hook(hook_name)
  124     if script is None:
  125         logger.debug('No hooks found')
  126         return
  127     logger.debug('Running hook {}'.format(hook_name))
  128     run_script_with_context(script, project_dir, context)
  129
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/cookiecutter/hooks.py b/cookiecutter/hooks.py
  --- a/cookiecutter/hooks.py
  +++ b/cookiecutter/hooks.py
  @@ -2,6 +2,7 @@
   
   """Functions for discovering and executing various cookiecutter hooks."""
   
  +import errno
   import io
   import logging
   import os
  @@ -79,15 +80,26 @@
   
       utils.make_executable(script_path)
   
  -    proc = subprocess.Popen(
  -        script_command,
  -        shell=run_thru_shell,
  -        cwd=cwd
  -    )
  -    exit_status = proc.wait()
  -    if exit_status != EXIT_SUCCESS:
  +    try:
  +        proc = subprocess.Popen(
  +            script_command,
  +            shell=run_thru_shell,
  +            cwd=cwd
  +        )
  +        exit_status = proc.wait()
  +        if exit_status != EXIT_SUCCESS:
  +            raise FailedHookException(
  +                'Hook script failed (exit status: {})'.format(exit_status)
  +            )
  +    except OSError as os_error:
  +        if os_error.errno == errno.ENOEXEC:
  +            raise FailedHookException(
  +                'Hook script failed, might be an '
  +                'empty file or missing a shebang'
  +            )
           raise FailedHookException(
  -            "Hook script failed (exit status: %d)" % exit_status)
  +            'Hook script failed (error: {})'.format(os_error)
  +        )
   
   
   def run_script_with_context(script_path, cwd, context):
  
verification_info: {"golden_diff": "diff --git a/cookiecutter/hooks.py b/cookiecutter/hooks.py\n--- a/cookiecutter/hooks.py\n+++ b/cookiecutter/hooks.py\n@@ -2,6 +2,7 @@\n \n \"\"\"Functions for discovering and executing various cookiecutter hooks.\"\"\"\n \n+import errno\n import io\n import logging\n import os\n@@ -79,15 +80,26 @@\n \n     utils.make_executable(script_path)\n \n-    proc = subprocess.Popen(\n-        script_command,\n-        shell=run_thru_shell,\n-        cwd=cwd\n-    )\n-    exit_status = proc.wait()\n-    if exit_status != EXIT_SUCCESS:\n+    try:\n+        proc = subprocess.Popen(\n+            script_command,\n+            shell=run_thru_shell,\n+            cwd=cwd\n+        )\n+        exit_status = proc.wait()\n+        if exit_status != EXIT_SUCCESS:\n+            raise FailedHookException(\n+                'Hook script failed (exit status: {})'.format(exit_status)\n+            )\n+    except OSError as os_error:\n+        if os_error.errno == errno.ENOEXEC:\n+            raise FailedHookException(\n+                'Hook script failed, might be an '\n+                'empty file or missing a shebang'\n+            )\n         raise FailedHookException(\n-            \"Hook script failed (exit status: %d)\" % exit_status)\n+            'Hook script failed (error: {})'.format(os_error)\n+        )\n \n \n def run_script_with_context(script_path, cwd, context):\n", "issue": "Empty hook file causes cryptic error\nIf you have a pre_gen_project.sh or a post_gen_project.sh file with no data in it, cookiecutter fails with an unhelpful traceback.\n\n```\nTraceback (most recent call last):\n  File \"/usr/local/bin/cookiecutter\", line 11, in <module>\n    sys.exit(main())\n  File \"/usr/local/lib/python2.7/site-packages/click/core.py\", line 716, in __call__\n    return self.main(*args, **kwargs)\n  File \"/usr/local/lib/python2.7/site-packages/click/core.py\", line 696, in main\n    rv = self.invoke(ctx)\n  File \"/usr/local/lib/python2.7/site-packages/click/core.py\", line 889, in invoke\n    return ctx.invoke(self.callback, **ctx.params)\n  File \"/usr/local/lib/python2.7/site-packages/click/core.py\", line 534, in invoke\n    return callback(*args, **kwargs)\n  File \"/usr/local/lib/python2.7/site-packages/cookiecutter/cli.py\", line 100, in main\n    config_file=user_config\n  File \"/usr/local/lib/python2.7/site-packages/cookiecutter/main.py\", line 140, in cookiecutter\n    output_dir=output_dir\n  File \"/usr/local/lib/python2.7/site-packages/cookiecutter/generate.py\", line 273, in generate_files\n    _run_hook_from_repo_dir(repo_dir, 'pre_gen_project', project_dir, context)\n  File \"/usr/local/lib/python2.7/site-packages/cookiecutter/generate.py\", line 232, in _run_hook_from_repo_dir\n    run_hook(hook_name, project_dir, context)\n  File \"/usr/local/lib/python2.7/site-packages/cookiecutter/hooks.py\", line 116, in run_hook\n    run_script_with_context(script, project_dir, context)\n  File \"/usr/local/lib/python2.7/site-packages/cookiecutter/hooks.py\", line 101, in run_script_with_context\n    run_script(temp.name, cwd)\n  File \"/usr/local/lib/python2.7/site-packages/cookiecutter/hooks.py\", line 73, in run_script\n    cwd=cwd\n  File \"/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py\", line 656, in __init__\n    _cleanup()\n  File \"/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py\", line 1335, in _execute_child\n    raise child_exception\nOSError: [Errno 8] Exec format error\n```\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"Functions for discovering and executing various cookiecutter hooks.\"\"\"\n\nimport io\nimport logging\nimport os\nimport subprocess\nimport sys\nimport tempfile\n\nfrom jinja2 import Template\n\nfrom cookiecutter import utils\nfrom .exceptions import FailedHookException\n\nlogger = logging.getLogger(__name__)\n\n_HOOKS = [\n    'pre_gen_project',\n    'post_gen_project',\n]\nEXIT_SUCCESS = 0\n\n\ndef valid_hook(hook_file, hook_name):\n    \"\"\"Determine if a hook file is valid.\n\n    :param hook_file: The hook file to consider for validity\n    :param hook_name: The hook to find\n    :return: The hook file validity\n    \"\"\"\n    filename = os.path.basename(hook_file)\n    basename = os.path.splitext(filename)[0]\n\n    matching_hook = basename == hook_name\n    supported_hook = basename in _HOOKS\n    backup_file = filename.endswith('~')\n\n    return matching_hook and supported_hook and not backup_file\n\n\ndef find_hook(hook_name, hooks_dir='hooks'):\n    \"\"\"Return a dict of all hook scripts provided.\n\n    Must be called with the project template as the current working directory.\n    Dict's key will be the hook/script's name, without extension, while values\n    will be the absolute path to the script. Missing scripts will not be\n    included in the returned dict.\n\n    :param hook_name: The hook to find\n    :param hooks_dir: The hook directory in the template\n    :return: The absolute path to the hook script or None\n    \"\"\"\n    logger.debug('hooks_dir is {}'.format(os.path.abspath(hooks_dir)))\n\n    if not os.path.isdir(hooks_dir):\n        logger.debug('No hooks/ dir in template_dir')\n        return None\n\n    for hook_file in os.listdir(hooks_dir):\n        if valid_hook(hook_file, hook_name):\n            return os.path.abspath(os.path.join(hooks_dir, hook_file))\n\n    return None\n\n\ndef run_script(script_path, cwd='.'):\n    \"\"\"Execute a script from a working directory.\n\n    :param script_path: Absolute path to the script to run.\n    :param cwd: The directory to run the script from.\n    \"\"\"\n    run_thru_shell = sys.platform.startswith('win')\n    if script_path.endswith('.py'):\n        script_command = [sys.executable, script_path]\n    else:\n        script_command = [script_path]\n\n    utils.make_executable(script_path)\n\n    proc = subprocess.Popen(\n        script_command,\n        shell=run_thru_shell,\n        cwd=cwd\n    )\n    exit_status = proc.wait()\n    if exit_status != EXIT_SUCCESS:\n        raise FailedHookException(\n            \"Hook script failed (exit status: %d)\" % exit_status)\n\n\ndef run_script_with_context(script_path, cwd, context):\n    \"\"\"Execute a script after rendering it with Jinja.\n\n    :param script_path: Absolute path to the script to run.\n    :param cwd: The directory to run the script from.\n    :param context: Cookiecutter project template context.\n    \"\"\"\n    _, extension = os.path.splitext(script_path)\n\n    contents = io.open(script_path, 'r', encoding='utf-8').read()\n\n    with tempfile.NamedTemporaryFile(\n        delete=False,\n        mode='wb',\n        suffix=extension\n    ) as temp:\n        output = Template(contents).render(**context)\n        temp.write(output.encode('utf-8'))\n\n    run_script(temp.name, cwd)\n\n\ndef run_hook(hook_name, project_dir, context):\n    \"\"\"\n    Try to find and execute a hook from the specified project directory.\n\n    :param hook_name: The hook to execute.\n    :param project_dir: The directory to execute the script from.\n    :param context: Cookiecutter project context.\n    \"\"\"\n    script = find_hook(hook_name)\n    if script is None:\n        logger.debug('No hooks found')\n        return\n    logger.debug('Running hook {}'.format(hook_name))\n    run_script_with_context(script, project_dir, context)\n", "path": "cookiecutter/hooks.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"Functions for discovering and executing various cookiecutter hooks.\"\"\"\n\nimport errno\nimport io\nimport logging\nimport os\nimport subprocess\nimport sys\nimport tempfile\n\nfrom jinja2 import Template\n\nfrom cookiecutter import utils\nfrom .exceptions import FailedHookException\n\nlogger = logging.getLogger(__name__)\n\n_HOOKS = [\n    'pre_gen_project',\n    'post_gen_project',\n]\nEXIT_SUCCESS = 0\n\n\ndef valid_hook(hook_file, hook_name):\n    \"\"\"Determine if a hook file is valid.\n\n    :param hook_file: The hook file to consider for validity\n    :param hook_name: The hook to find\n    :return: The hook file validity\n    \"\"\"\n    filename = os.path.basename(hook_file)\n    basename = os.path.splitext(filename)[0]\n\n    matching_hook = basename == hook_name\n    supported_hook = basename in _HOOKS\n    backup_file = filename.endswith('~')\n\n    return matching_hook and supported_hook and not backup_file\n\n\ndef find_hook(hook_name, hooks_dir='hooks'):\n    \"\"\"Return a dict of all hook scripts provided.\n\n    Must be called with the project template as the current working directory.\n    Dict's key will be the hook/script's name, without extension, while values\n    will be the absolute path to the script. Missing scripts will not be\n    included in the returned dict.\n\n    :param hook_name: The hook to find\n    :param hooks_dir: The hook directory in the template\n    :return: The absolute path to the hook script or None\n    \"\"\"\n    logger.debug('hooks_dir is {}'.format(os.path.abspath(hooks_dir)))\n\n    if not os.path.isdir(hooks_dir):\n        logger.debug('No hooks/ dir in template_dir')\n        return None\n\n    for hook_file in os.listdir(hooks_dir):\n        if valid_hook(hook_file, hook_name):\n            return os.path.abspath(os.path.join(hooks_dir, hook_file))\n\n    return None\n\n\ndef run_script(script_path, cwd='.'):\n    \"\"\"Execute a script from a working directory.\n\n    :param script_path: Absolute path to the script to run.\n    :param cwd: The directory to run the script from.\n    \"\"\"\n    run_thru_shell = sys.platform.startswith('win')\n    if script_path.endswith('.py'):\n        script_command = [sys.executable, script_path]\n    else:\n        script_command = [script_path]\n\n    utils.make_executable(script_path)\n\n    try:\n        proc = subprocess.Popen(\n            script_command,\n            shell=run_thru_shell,\n            cwd=cwd\n        )\n        exit_status = proc.wait()\n        if exit_status != EXIT_SUCCESS:\n            raise FailedHookException(\n                'Hook script failed (exit status: {})'.format(exit_status)\n            )\n    except OSError as os_error:\n        if os_error.errno == errno.ENOEXEC:\n            raise FailedHookException(\n                'Hook script failed, might be an '\n                'empty file or missing a shebang'\n            )\n        raise FailedHookException(\n            'Hook script failed (error: {})'.format(os_error)\n        )\n\n\ndef run_script_with_context(script_path, cwd, context):\n    \"\"\"Execute a script after rendering it with Jinja.\n\n    :param script_path: Absolute path to the script to run.\n    :param cwd: The directory to run the script from.\n    :param context: Cookiecutter project template context.\n    \"\"\"\n    _, extension = os.path.splitext(script_path)\n\n    contents = io.open(script_path, 'r', encoding='utf-8').read()\n\n    with tempfile.NamedTemporaryFile(\n        delete=False,\n        mode='wb',\n        suffix=extension\n    ) as temp:\n        output = Template(contents).render(**context)\n        temp.write(output.encode('utf-8'))\n\n    run_script(temp.name, cwd)\n\n\ndef run_hook(hook_name, project_dir, context):\n    \"\"\"\n    Try to find and execute a hook from the specified project directory.\n\n    :param hook_name: The hook to execute.\n    :param project_dir: The directory to execute the script from.\n    :param context: Cookiecutter project context.\n    \"\"\"\n    script = find_hook(hook_name)\n    if script is None:\n        logger.debug('No hooks found')\n        return\n    logger.debug('Running hook {}'.format(hook_name))\n    run_script_with_context(script, project_dir, context)\n", "path": "cookiecutter/hooks.py"}]}