Skip to content

Commit

Permalink
Fixed[cookiecutter#824] - Cannot use local imports in hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
Theo committed May 23, 2019
1 parent 3bc7b98 commit 5472420
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 0 deletions.
21 changes: 21 additions & 0 deletions cookiecutter/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def run_script(script_path, cwd='.'):
try:
proc = subprocess.Popen(
script_command,
env=get_hooks_env(cwd),
shell=run_thru_shell,
cwd=cwd
)
Expand Down Expand Up @@ -142,3 +143,23 @@ def run_hook(hook_name, project_dir, context):
return
logger.debug('Running hook {}'.format(hook_name))
run_script_with_context(script, project_dir, context)


def get_hooks_env(cwd='.', hooks_dir='hooks'):
"""
Create and return a copy of the environmental variables including
the current hooks path appended to the PYTHONPATH. This is used for
Popen to enable local imports when running hooks.
:param cwd: The directory to run the script from.
:param hooks_dir: The hook directory in the template.
:return: A copy of the environmental variables with the addition of
the current hooks path appended to the PYTHONPATH.
"""
hooks_env = os.environ.copy()

PYTHONPATH = hooks_env.get("PYTHONPATH", "")
hooks_env["PYTHONPATH"] = \
os.path.join(os.path.dirname(cwd), hooks_dir) + ":" + PYTHONPATH

return hooks_env
5 changes: 5 additions & 0 deletions tests/test-hook-imports/hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from utils import which

which()
5 changes: 5 additions & 0 deletions tests/test-hook-imports/hooks/pre_gen_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from utils import which

which()
7 changes: 7 additions & 0 deletions tests/test-hook-imports/hooks/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys


def which():
sys.exit(0)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Empty, just like the hooks we are testing.
40 changes: 40 additions & 0 deletions tests/test_generate_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def fin_remove_additional_folders():
'inputhooks',
'tests/test-shellhooks',
'tests/test-hooks',
'tests/test-hook-imports/inputpyhooks'
]
for directory in directories_to_delete:
if os.path.exists(directory):
Expand All @@ -56,6 +57,19 @@ def test_ignore_hooks_dirs():
assert not os.path.exists('tests/test-pyhooks/inputpyhooks/hooks')


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
def test_imports_in_hooks():
try:
generate.generate_files(
context={
'cookiecutter': {'pyhooks': 'pyhooks'}
},
repo_dir='tests/test-hook-imports/',
output_dir='tests/test-hook-imports/')
except FailedHookException:
pytest.fail("Unexpected FailedHookException...")


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
def test_run_python_hooks():
generate.generate_files(
Expand Down Expand Up @@ -119,6 +133,32 @@ def test_oserror_hooks(mocker):
assert message in str(excinfo.value)


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
def test_popen_includes_hooks_in_python_path(mocker):
err = OSError()
prompt = mocker.patch('subprocess.Popen')
prompt.side_effect = err

original_env = os.environ.copy()
original_python_path = original_env.get("PYTHONPATH", "")

with pytest.raises(FailedHookException):
generate.generate_files(
context={
'cookiecutter': {'pyhooks': 'pyhooks'}
},
repo_dir='tests/test-hook-imports/',
output_dir='tests/test-hook-imports/')

hooks_env = prompt.call_args[1]["env"]
hooks_env_python_path = hooks_env["PYTHONPATH"]

assert original_python_path in hooks_env_python_path
assert os.path.join("tests",
"test-hook-imports",
"hooks") in hooks_env_python_path


@pytest.mark.usefixtures('clean_system', 'remove_additional_folders')
def test_run_failing_hook_removes_output_directory():
repo_path = os.path.abspath('tests/test-hooks/')
Expand Down

0 comments on commit 5472420

Please sign in to comment.