Repository: cookiecutter/cookiecutter

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`



Discussion:
Hi @ionelmc :wave: 

It works on my machine running Mac OS X. I'll see if it works on my Win 7 and Ubuntu... :confused:

Which template are you using and what are the contents of your rc?

```
(testchoices)[raphael@hackebook choicekivy]$ cat ~/.cookiecutterrc
default_context:
    full_name: "Raphael Pierzina"
    email: "raphael@hackebrot.de"
    github_username: "hackebrot"
abbreviations:
    cookiedozer: https://github.com/hackebrot/cookiedozer.git
    gh: https://github.com/{0}.git

(testchoices)[raphael@hackebook choicekivy]$ cookiecutter gh:hackebrot/cookiecutter-kivy
Cloning into 'cookiecutter-kivy'...
remote: Counting objects: 148, done.
remote: Total 148 (delta 0), reused 0 (delta 0), pack-reused 148
Receiving objects: 100% (148/148), 24.04 KiB | 0 bytes/s, done.
Resolving deltas: 100% (88/88), done.
Checking connectivity... done.
full_name [Raphael Pierzina]: 
email [raphael@hackebrot.de]: 
github_username [hackebrot]: 
project_name [Kivy Project]: 
repo_name [kivyproject]: 
project_short_description [Cookiecutter template for applications built upon the kivy-framework.]: 
release_date [2014-05-15]: 
year [2014]: 
version [0.1.0]: 
minimum_kivy_version [1.8.0]: 
Select orientation:
1 - all
2 - landscape
3 - portrait
Choose from 1, 2, 3 [1]: 4
Error: invalid choice: 4. (choose from 1, 2, 3)
Select orientation:
1 - all
2 - landscape
3 - portrait
Choose from 1, 2, 3 [1]: 2

(testchoices)[raphael@hackebook choicekivy]$ 
```

@hackebrot try adding `orientation` in your cookiecutterrc, the prompt will disappear then.

Yes, you are right. User config takes precedence over the context that is built from the template.

It works if you have a list in `~/.cookiecutterrc` though.

Hmmmm :no_mouth: 

Is that "Hmmmm :no_mouth:" short for "hard to fix, lots of painful refactoring"? :grin: 


Resolving patch:
diff --git a/cookiecutter/generate.py b/cookiecutter/generate.py
--- 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
--- /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
--- a/tests/test_generate_context.py
+++ b/tests/test_generate_context.py
@@ -16,6 +16,8 @@
 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']