Repository: audreyr/cookiecutter (python)

Issue:
Accepting default dict parameter value with no user input causes string "default" to be put into dict parameter
* Cookiecutter version: 1.5
* Template project url: private repo, minimal example is attached
* Python version: 2.7.10
* Operating System: Mac OS X 10.10.5

### Description:

I'm using dict parameter and trying to let it use default value. When prompted, `[default]` is shown as dict parameter default value. Then, if I respond to prompt with:

1. Just enter (i.e. no user input) - project cutting fails due to an error, as cookiecutter puts `default` into dict parameter (i.e. it becomes string instead of dict).
2. String "default" and enter - project is cooked successfully with default value.

In two words, just accepting default value for dict parameter by providing no user input causes string "default" to be put into a dict parameter instead of default value provided in cookiecutter.json.

If you allow some speculation, this happens because `click.prompt` [does not call `value_proc` if no user input is given](https://github.com/pallets/click/blob/master/click/termui.py#L103-L104)

See console session logs below.

### What I've run:

Failure:
```
cookiecutter minimal-example/
app_name [dict_default_bug]: 
dict [default]: 
Unable to create file 'placeholder.md'
Error message: 'str object' has no attribute 'foo'
Context: {
    "cookiecutter": {
        "app_name": "dict_default_bug", 
        "dict": "default"
    }
}
```

Success:
```
cookiecutter minimal-example/
app_name [dict_default_bug]: 
dict [default]: default
```
[minimal-example.zip](https://github.com/audreyr/cookiecutter/files/686416/minimal-example.zip)


Pull request:
Fix dict prompt for default value
Resolve #882 

Patch:
diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py
index 7f4d12f..41e8964 100644
--- a/cookiecutter/prompt.py
+++ b/cookiecutter/prompt.py
@@ -84,28 +84,21 @@ def read_user_choice(var_name, options):
     return choice_map[user_choice]
 
 
-def create_value_proc(default_display, default_value):
-    def process_json(user_value):
-        if user_value == default_display:
-            # Return the given default w/o any processing
-            return default_value
+def process_json(user_value):
+    try:
+        user_dict = json.loads(
+            user_value,
+            object_pairs_hook=OrderedDict,
+        )
+    except Exception:
+        # Leave it up to click to ask the user again
+        raise click.UsageError('Unable to decode to JSON.')
 
-        try:
-            user_dict = json.loads(
-                user_value,
-                object_pairs_hook=OrderedDict,
-            )
-        except Exception:
-            # Leave it up to click to ask the user again
-            raise click.UsageError('Unable to decode to JSON.')
-
-        if not isinstance(user_dict, dict):
-            # Leave it up to click to ask the user again
-            raise click.UsageError('Requires JSON dict.')
-
-        return user_dict
+    if not isinstance(user_dict, dict):
+        # Leave it up to click to ask the user again
+        raise click.UsageError('Requires JSON dict.')
 
-    return process_json
+    return user_dict
 
 
 def read_user_dict(var_name, default_value):
@@ -121,16 +114,18 @@ def read_user_dict(var_name, default_value):
 
     default_display = 'default'
 
-    return click.prompt(
+    user_value = click.prompt(
         var_name,
         default=default_display,
         type=click.STRING,
-        value_proc=create_value_proc(
-            default_display,
-            default_value,
-        ),
+        value_proc=process_json,
     )
 
+    if user_value == default_display:
+        # Return the given default w/o any processing
+        return default_value
+    return user_value
+
 
 def render_variable(env, raw, cookiecutter_dict):
     """Inside the prompting taken from the cookiecutter.json file, this renders


Test patch:
diff --git a/tests/test_read_user_dict.py b/tests/test_read_user_dict.py
index fabd82f..a434b8d 100644
--- a/tests/test_read_user_dict.py
+++ b/tests/test_read_user_dict.py
@@ -11,49 +11,35 @@ import click
 import pytest
 
 from cookiecutter.prompt import (
-    create_value_proc,
+    process_json,
     read_user_dict,
 )
 
 
-@pytest.fixture
-def value_proc():
-    return create_value_proc(
-        'default123',
-        {
-            'key': 1,
-        },
-    )
-
-
-def test_value_proc_default_display(value_proc):
-    assert value_proc('default123') == {'key': 1}
-
-
-def test_value_proc_invalid_json(value_proc):
+def test_process_json_invalid_json():
     with pytest.raises(click.UsageError) as exc_info:
-        value_proc('nope]')
+        process_json('nope]')
 
     assert str(exc_info.value) == 'Unable to decode to JSON.'
 
 
-def test_value_proc_non_dict(value_proc):
+def test_process_json_non_dict():
     with pytest.raises(click.UsageError) as exc_info:
-        value_proc('[1, 2]')
+        process_json('[1, 2]')
 
     assert str(exc_info.value) == 'Requires JSON dict.'
 
 
-def test_value_proc_valid_json(value_proc):
+def test_process_json_valid_json():
     user_value = '{"name": "foobar", "bla": ["a", 1, "b", false]}'
 
-    assert value_proc(user_value) == {
+    assert process_json(user_value) == {
         'name': 'foobar',
         'bla': ['a', 1, 'b', False],
     }
 
 
-def test_value_proc_deep_dict(value_proc):
+def test_process_json_deep_dict():
     user_value = '''{
         "key": "value",
         "integer_key": 37,
@@ -73,7 +59,7 @@ def test_value_proc_deep_dict(value_proc):
         ]
     }'''
 
-    assert value_proc(user_value) == {
+    assert process_json(user_value) == {
         "key": "value",
         "integer_key": 37,
         "dict_key": {
@@ -102,28 +88,42 @@ def test_should_raise_type_error(mocker):
     assert not prompt.called
 
 
-def test_should_call_prompt_with_value_proc(mocker):
-    """Test to make sure that create_value_proc is actually being used
+def test_should_call_prompt_with_process_json(mocker):
+    """Test to make sure that process_jon is actually being used
     to generate a processer for the user input."""
-    prompt = mocker.patch('click.prompt')
 
-    def process_json(user_value):
-        return user_value
-
-    create_value_proc = mocker.patch(
-        'cookiecutter.prompt.create_value_proc',
-        return_value=process_json,
+    mock_prompt = mocker.patch(
+        'cookiecutter.prompt.click.prompt',
+        autospec=True,
     )
 
     read_user_dict('name', {'project_slug': 'pytest-plugin'})
 
-    assert create_value_proc.call_args == mocker.call(
-        'default',
-        {'project_slug': 'pytest-plugin'},
+    assert mock_prompt.call_args == mocker.call(
+        'name',
+        type=click.STRING,
+        default='default',
+        value_proc=process_json,
+    )
+
+
+def test_read_user_dict_default_value(mocker):
+    """Test to make sure that read_user_dict returns the default value for a
+    dict variable rather than the display value.
+    """
+    mock_prompt = mocker.patch(
+        'cookiecutter.prompt.click.prompt',
+        autospec=True,
+        return_value='default',
     )
-    assert prompt.call_args == mocker.call(
+
+    val = read_user_dict('name', {'project_slug': 'pytest-plugin'})
+
+    assert mock_prompt.call_args == mocker.call(
         'name',
         type=click.STRING,
         default='default',
         value_proc=process_json,
     )
+
+    assert val == {'project_slug': 'pytest-plugin'}