Repository: audreyr/cookiecutter (python)

Issue:
Choice prompts are disabled if there's a ~/.cookiecutterrc
Seems I don't get the nice prompts from https://github.com/audreyr/cookiecutter/pull/455 if I have a `~/.cookiecutterrc`




Pull request description:
Choice with default in user config
This PR makes sure to factor in choice variables in templates when using a user config or extra context.

Instead of replacing the list, it will set the *overwrite* value as the new default for the choice variable.

Please note that 8f08298 slightly changes the current behavior as it ignores variables which are not used in the template as opposed to ``dict.update({})``.

Resolve #536 

Patch:
diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py
index 536aa1e..91e97ef 100755
--- a/cookiecutter/generate.py
+++ b/cookiecutter/generate.py
@@ -50,6 +50,28 @@ def copy_without_render(path, context):
     return False
 
 
+def apply_overwrites_to_context(context, overwrite_context):
+    """Modify the given context in place based on the overwrite_context."""
+    for variable, overwrite in overwrite_context.items():
+        if variable not in context:
+            # Do not include variables which are not used in the template
+            continue
+
+        context_value = context[variable]
+
+        if isinstance(context_value, list):
+            # We are dealing with a choice variable
+            if overwrite in context_value:
+                # This overwrite is actually valid for the given context
+                # Let's set it as default (by definition first item in list)
+                # see ``cookiecutter.prompt.prompt_choice_for_config``
+                context_value.remove(overwrite)
+                context_value.insert(0, overwrite)
+        else:
+            # Simply overwrite the value for this variable
+            context[variable] = overwrite
+
+
 def generate_context(context_file='cookiecutter.json', default_context=None,
                      extra_context=None):
     """
@@ -85,9 +107,9 @@ def generate_context(context_file='cookiecutter.json', default_context=None,
     # Overwrite context variable defaults with the default context from the
     # user's global config, if available
     if default_context:
-        obj.update(default_context)
+        apply_overwrites_to_context(obj, default_context)
     if extra_context:
-        obj.update(extra_context)
+        apply_overwrites_to_context(obj, extra_context)
 
     logging.debug('Context generated is {0}'.format(context))
     return context


Test patch:
diff --git a/tests/test-generate-context/choices_template.json b/tests/test-generate-context/choices_template.json
new file mode 100644
index 0000000..7862aff
--- /dev/null
+++ b/tests/test-generate-context/choices_template.json
@@ -0,0 +1,7 @@
+{
+    "full_name": "Raphael Pierzina",
+    "github_username": "hackebrot",
+    "project_name": "Kivy Project",
+    "repo_name": "{{cookiecutter.project_name|lower",
+    "orientation": ["all", "landscape", "portrait"]
+}
diff --git a/tests/test_generate_context.py b/tests/test_generate_context.py
index 41d33e9..8effd73 100644
--- a/tests/test_generate_context.py
+++ b/tests/test_generate_context.py
@@ -16,6 +16,8 @@ from __future__ import unicode_literals
 import pytest
 import os
 import re
+from collections import OrderedDict
+
 from cookiecutter import generate
 from cookiecutter.exceptions import ContextDecodingException
 
@@ -97,3 +99,94 @@ def test_generate_context_with_json_decoding_error():
         ['tests', 'test-generate-context', 'invalid-syntax.json']
     )
     assert path in str(excinfo.value)
+
+
+@pytest.fixture
+def default_context():
+    return {
+        'not_in_template': 'foobar',
+        'project_name': 'Kivy Project',
+        'orientation': 'landscape'
+    }
+
+
+@pytest.fixture
+def extra_context():
+    return {
+        'also_not_in_template': 'foobar2',
+        'github_username': 'hackebrot',
+    }
+
+
+@pytest.fixture
+def context_file():
+    return 'tests/test-generate-context/choices_template.json'
+
+
+def test_choices(context_file, default_context, extra_context):
+    """Make sure that the default for list variables is based on the user
+    config and the list as such is not changed to a single value.
+    """
+    expected_context = {
+        'choices_template': OrderedDict([
+            ('full_name', 'Raphael Pierzina'),
+            ('github_username', 'hackebrot'),
+            ('project_name', 'Kivy Project'),
+            ('repo_name', '{{cookiecutter.project_name|lower'),
+            ('orientation', ['landscape', 'all', 'portrait']),
+        ])
+    }
+
+    generated_context = generate.generate_context(
+        context_file, default_context, extra_context
+    )
+
+    assert generated_context == expected_context
+
+
+@pytest.fixture
+def template_context():
+    return OrderedDict([
+        ('full_name', 'Raphael Pierzina'),
+        ('github_username', 'hackebrot'),
+        ('project_name', 'Kivy Project'),
+        ('repo_name', '{{cookiecutter.project_name|lower'),
+        ('orientation', ['all', 'landscape', 'portrait']),
+    ])
+
+
+def test_apply_overwrites_does_include_unused_variables(template_context):
+    generate.apply_overwrites_to_context(
+        template_context,
+        {'not in template': 'foobar'}
+    )
+
+    assert 'not in template' not in template_context
+
+
+def test_apply_overwrites_sets_non_list_value(template_context):
+    generate.apply_overwrites_to_context(
+        template_context,
+        {'repo_name': 'foobar'}
+    )
+
+    assert template_context['repo_name'] == 'foobar'
+
+
+def test_apply_overwrites_does_not_modify_choices_for_invalid_overwrite(
+        template_context):
+    generate.apply_overwrites_to_context(
+        template_context,
+        {'orientation': 'foobar'}
+    )
+
+    assert template_context['orientation'] == ['all', 'landscape', 'portrait']
+
+
+def test_apply_overwrites_sets_default_for_choice_variable(template_context):
+    generate.apply_overwrites_to_context(
+        template_context,
+        {'orientation': 'landscape'}
+    )
+
+    assert template_context['orientation'] == ['landscape', 'all', 'portrait']