Repository: cookiecutter/cookiecutter

Issue:
pre_gen hook doesn't appear to be loading custom extensions
Latest version of cookiecutter from master. Last commit [ceafa1dc1b5eb70835ffba5dc7457df269d77ba8](https://github.com/audreyr/cookiecutter/commit/ceafa1dc1b5eb70835ffba5dc7457df269d77ba8)

In my pre_gen_project hook, I have this code: `{{cookiecutter.should_be_list | to_sequence}}` which turns a string into a list but cookiecutter raises a TemplateAssertionError `jinja2.exceptions.TemplateAssertionError: no filter named 'to_sequence'`. I know that my to_sequence filter isn't the issue because when I use it in other parts of my template and remove it from the pre_gen hook, everything executes successfully.

Are the custom extensions intended to only be loaded after the pre_gen hook is finished executing?



Discussion:
Hi @cheungnj,

Thanks for the issue!

This looks like an oversight, we should be using the same Jinja environment to render hooks as we do elsewhere.
This looks like a relatively easy fix to implement- would you like to submit a PR?

Essentially, we should be using [cookiecutter.environment.StrictEnvironment](https://github.com/audreyr/cookiecutter/blob/master/cookiecutter/generate.py#L253) instead of just [jinja2.Template](https://github.com/audreyr/cookiecutter/blob/master/cookiecutter/hooks.py#L94) in `cookiecutter.hooks.run_script_with_context`.
Hey @cheungnj! 👋 

I've just pushed a PR adding two tests that replicate this bug. As @michaeljoseph said, we would happily review a PR from you to address this issue, feel free to base your branch off of #856.

Please let us know in case of any questions!
@michaeljoseph @hackebrot 

I'd be happy to submit a PR for this. Thanks for the opportunity to contribute!
Hey @cheungnj! 👋 

FYI The tests have been merged into master!

Resolving patch:
diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py
--- a/cookiecutter/generate.py
+++ b/cookiecutter/generate.py
@@ -221,7 +221,13 @@ def ensure_dir_is_templated(dirname):
 
 
 def _run_hook_from_repo_dir(repo_dir, hook_name, project_dir, context):
-    """Run hook from repo directory, clean project directory if hook fails."""
+    """Run hook from repo directory, clean project directory if hook fails.
+
+    :param repo_dir: Project template input directory.
+    :param hook_name: The hook to execute.
+    :param project_dir: The directory to execute the script from.
+    :param context: Cookiecutter project context.
+    """
     with work_in(repo_dir):
         try:
             run_hook(hook_name, project_dir, context)
diff --git a/cookiecutter/hooks.py b/cookiecutter/hooks.py
--- a/cookiecutter/hooks.py
+++ b/cookiecutter/hooks.py
@@ -9,9 +9,8 @@
 import sys
 import tempfile
 
-from jinja2 import Template
-
 from cookiecutter import utils
+from cookiecutter.environment import StrictEnvironment
 from .exceptions import FailedHookException
 
 logger = logging.getLogger(__name__)
@@ -106,7 +105,12 @@ def run_script_with_context(script_path, cwd, context):
         mode='wb',
         suffix=extension
     ) as temp:
-        output = Template(contents).render(**context)
+        env = StrictEnvironment(
+            context=context,
+            keep_trailing_newline=True,
+        )
+        template = env.from_string(contents)
+        output = template.render(**context)
         temp.write(output.encode('utf-8'))
 
     run_script(temp.name, cwd)


Test patch:
diff --git a/tests/test-extensions/custom-extension-post/hooks/post_gen_project.py b/tests/test-extensions/custom-extension-post/hooks/post_gen_project.py
--- a/tests/test-extensions/custom-extension-post/hooks/post_gen_project.py
+++ b/tests/test-extensions/custom-extension-post/hooks/post_gen_project.py
@@ -1,4 +1,4 @@
 # -*- coding: utf-8 -*-
 # flake8: noqa
 
-print({% hello cookiecutter.name %})
+print('{% hello cookiecutter.name %}')
diff --git a/tests/test-extensions/custom-extension-pre/hooks/pre_gen_project.py b/tests/test-extensions/custom-extension-pre/hooks/pre_gen_project.py
--- a/tests/test-extensions/custom-extension-pre/hooks/pre_gen_project.py
+++ b/tests/test-extensions/custom-extension-pre/hooks/pre_gen_project.py
@@ -1,4 +1,4 @@
 # -*- coding: utf-8 -*-
 # flake8: noqa
 
-print({% hello cookiecutter.name %})
+print('{% hello cookiecutter.name %}')
diff --git a/tests/test_custom_extensions_in_hooks.py b/tests/test_custom_extensions_in_hooks.py
--- a/tests/test_custom_extensions_in_hooks.py
+++ b/tests/test_custom_extensions_in_hooks.py
@@ -32,10 +32,6 @@ def modify_syspath(monkeypatch):
     )
 
 
-@pytest.mark.xfail(
-    reason='issue #850',
-    strict=True,
-)
 def test_hook_with_extension(template, output_dir):
     project_dir = main.cookiecutter(
         template,