prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  passing list to template
  I would like to be able to pass a list to the templates. See the two code blocks at the end of this post for an example. Right now it appears that if you pass a list in the config object, it's read as a list of options for that key.
  
  I know that you can use `str.split()` in the jinja2 template, but that's not a real solution, since it's impossible to "escape" the character that's used as the delimiter. What's the best solution here? I would prefer to be able to pass a list in the json object and call it a day, but obviously that doesn't work for the user input prompts.
  - `cookiecutter.json`:
  
  ``` json
  {
    "build_steps": [
       "do_something",
       "do_something_else"
    ]
  }
  ```
  - `Dockerfile`:
  
  ``` jinja2
  FROM something
  
  {% for step in cookiecutter.build_steps %}
  RUN {{ step }}
  {% endfor %}
  ```
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `cookiecutter/prompt.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 
  3 """
  4 cookiecutter.prompt
  5 ---------------------
  6 
  7 Functions for prompting the user for project info.
  8 """
  9 
  10 from collections import OrderedDict
  11 
  12 import click
  13 from past.builtins import basestring
  14 
  15 from future.utils import iteritems
  16 
  17 from jinja2.exceptions import UndefinedError
  18 
  19 from .exceptions import UndefinedVariableInTemplate
  20 from .environment import StrictEnvironment
  21 
  22 
  23 def read_user_variable(var_name, default_value):
  24     """Prompt the user for the given variable and return the entered value
  25     or the given default.
  26 
  27     :param str var_name: Variable of the context to query the user
  28     :param default_value: Value that will be returned if no input happens
  29     """
  30     # Please see http://click.pocoo.org/4/api/#click.prompt
  31     return click.prompt(var_name, default=default_value)
  32 
  33 
  34 def read_user_yes_no(question, default_value):
  35     """Prompt the user to reply with 'yes' or 'no' (or equivalent values).
  36 
  37     Note:
  38       Possible choices are 'true', '1', 'yes', 'y' or 'false', '0', 'no', 'n'
  39 
  40     :param str question: Question to the user
  41     :param default_value: Value that will be returned if no input happens
  42     """
  43     # Please see http://click.pocoo.org/4/api/#click.prompt
  44     return click.prompt(
  45         question,
  46         default=default_value,
  47         type=click.BOOL
  48     )
  49 
  50 
  51 def read_user_choice(var_name, options):
  52     """Prompt the user to choose from several options for the given variable.
  53 
  54     The first item will be returned if no input happens.
  55 
  56     :param str var_name: Variable as specified in the context
  57     :param list options: Sequence of options that are available to select from
  58     :return: Exactly one item of ``options`` that has been chosen by the user
  59     """
  60     # Please see http://click.pocoo.org/4/api/#click.prompt
  61     if not isinstance(options, list):
  62         raise TypeError
  63 
  64     if not options:
  65         raise ValueError
  66 
  67     choice_map = OrderedDict(
  68         (u'{}'.format(i), value) for i, value in enumerate(options, 1)
  69     )
  70     choices = choice_map.keys()
  71     default = u'1'
  72 
  73     choice_lines = [u'{} - {}'.format(*c) for c in choice_map.items()]
  74     prompt = u'\n'.join((
  75         u'Select {}:'.format(var_name),
  76         u'\n'.join(choice_lines),
  77         u'Choose from {}'.format(u', '.join(choices))
  78     ))
  79 
  80     user_choice = click.prompt(
  81         prompt, type=click.Choice(choices), default=default
  82     )
  83     return choice_map[user_choice]
  84 
  85 
  86 def render_variable(env, raw, cookiecutter_dict):
  87     if raw is None:
  88         return None
  89     if not isinstance(raw, basestring):
  90         raw = str(raw)
  91     template = env.from_string(raw)
  92 
  93     rendered_template = template.render(cookiecutter=cookiecutter_dict)
  94     return rendered_template
  95 
  96 
  97 def prompt_choice_for_config(cookiecutter_dict, env, key, options, no_input):
  98     """Prompt the user which option to choose from the given. Each of the
  99     possible choices is rendered beforehand.
  100     """
  101     rendered_options = [
  102         render_variable(env, raw, cookiecutter_dict) for raw in options
  103     ]
  104 
  105     if no_input:
  106         return rendered_options[0]
  107     return read_user_choice(key, rendered_options)
  108 
  109 
  110 def prompt_for_config(context, no_input=False):
  111     """
  112     Prompts the user to enter new config, using context as a source for the
  113     field names and sample values.
  114 
  115     :param no_input: Prompt the user at command line for manual configuration?
  116     """
  117     cookiecutter_dict = {}
  118     env = StrictEnvironment(context=context)
  119 
  120     for key, raw in iteritems(context[u'cookiecutter']):
  121         if key.startswith(u'_'):
  122             cookiecutter_dict[key] = raw
  123             continue
  124 
  125         try:
  126             if isinstance(raw, list):
  127                 # We are dealing with a choice variable
  128                 val = prompt_choice_for_config(
  129                     cookiecutter_dict, env, key, raw, no_input
  130                 )
  131             else:
  132                 # We are dealing with a regular variable
  133                 val = render_variable(env, raw, cookiecutter_dict)
  134 
  135                 if not no_input:
  136                     val = read_user_variable(key, val)
  137         except UndefinedError as err:
  138             msg = "Unable to render variable '{}'".format(key)
  139             raise UndefinedVariableInTemplate(msg, err, context)
  140 
  141         cookiecutter_dict[key] = val
  142     return cookiecutter_dict
  143
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py
  --- a/cookiecutter/prompt.py
  +++ b/cookiecutter/prompt.py
  @@ -8,6 +8,7 @@
   """
   
   from collections import OrderedDict
  +import json
   
   import click
   from past.builtins import basestring
  @@ -83,11 +84,43 @@
       return choice_map[user_choice]
   
   
  +def read_user_dict(var_name, default_value):
  +    """Prompt the user to provide a dictionary of data.
  +
  +    :param str var_name: Variable as specified in the context
  +    :param default_value: Value that will be returned if no input is provided
  +    :return: A Python dictionary to use in the context.
  +    """
  +    # Please see http://click.pocoo.org/4/api/#click.prompt
  +    if not isinstance(default_value, dict):
  +        raise TypeError
  +
  +    raw = click.prompt(var_name, default='default')
  +    if raw != 'default':
  +        value = json.loads(raw, object_hook=OrderedDict)
  +    else:
  +        value = default_value
  +
  +    return value
  +
  +
   def render_variable(env, raw, cookiecutter_dict):
       if raw is None:
           return None
  -    if not isinstance(raw, basestring):
  +    elif isinstance(raw, dict):
  +        return {
  +            render_variable(env, k, cookiecutter_dict):
  +                render_variable(env, v, cookiecutter_dict)
  +            for k, v in raw.items()
  +        }
  +    elif isinstance(raw, list):
  +        return [
  +            render_variable(env, v, cookiecutter_dict)
  +            for v in raw
  +        ]
  +    elif not isinstance(raw, basestring):
           raw = str(raw)
  +
       template = env.from_string(raw)
   
       rendered_template = template.render(cookiecutter=cookiecutter_dict)
  @@ -117,6 +150,9 @@
       cookiecutter_dict = {}
       env = StrictEnvironment(context=context)
   
  +    # First pass: Handle simple and raw variables, plus choices.
  +    # These must be done first because the dictionaries keys and
  +    # values might refer to them.
       for key, raw in iteritems(context[u'cookiecutter']):
           if key.startswith(u'_'):
               cookiecutter_dict[key] = raw
  @@ -128,15 +164,33 @@
                   val = prompt_choice_for_config(
                       cookiecutter_dict, env, key, raw, no_input
                   )
  -            else:
  +                cookiecutter_dict[key] = val
  +            elif not isinstance(raw, dict):
                   # We are dealing with a regular variable
                   val = render_variable(env, raw, cookiecutter_dict)
   
                   if not no_input:
                       val = read_user_variable(key, val)
  +
  +                cookiecutter_dict[key] = val
  +        except UndefinedError as err:
  +            msg = "Unable to render variable '{}'".format(key)
  +            raise UndefinedVariableInTemplate(msg, err, context)
  +
  +    # Second pass; handle the dictionaries.
  +    for key, raw in iteritems(context[u'cookiecutter']):
  +
  +        try:
  +            if isinstance(raw, dict):
  +                # We are dealing with a dict variable
  +                val = render_variable(env, raw, cookiecutter_dict)
  +
  +                if not no_input:
  +                    val = read_user_dict(key, val)
  +
  +                cookiecutter_dict[key] = val
           except UndefinedError as err:
               msg = "Unable to render variable '{}'".format(key)
               raise UndefinedVariableInTemplate(msg, err, context)
   
  -        cookiecutter_dict[key] = val
       return cookiecutter_dict
  
verification_info: {"golden_diff": "diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py\n--- a/cookiecutter/prompt.py\n+++ b/cookiecutter/prompt.py\n@@ -8,6 +8,7 @@\n \"\"\"\n \n from collections import OrderedDict\n+import json\n \n import click\n from past.builtins import basestring\n@@ -83,11 +84,43 @@\n     return choice_map[user_choice]\n \n \n+def read_user_dict(var_name, default_value):\n+    \"\"\"Prompt the user to provide a dictionary of data.\n+\n+    :param str var_name: Variable as specified in the context\n+    :param default_value: Value that will be returned if no input is provided\n+    :return: A Python dictionary to use in the context.\n+    \"\"\"\n+    # Please see http://click.pocoo.org/4/api/#click.prompt\n+    if not isinstance(default_value, dict):\n+        raise TypeError\n+\n+    raw = click.prompt(var_name, default='default')\n+    if raw != 'default':\n+        value = json.loads(raw, object_hook=OrderedDict)\n+    else:\n+        value = default_value\n+\n+    return value\n+\n+\n def render_variable(env, raw, cookiecutter_dict):\n     if raw is None:\n         return None\n-    if not isinstance(raw, basestring):\n+    elif isinstance(raw, dict):\n+        return {\n+            render_variable(env, k, cookiecutter_dict):\n+                render_variable(env, v, cookiecutter_dict)\n+            for k, v in raw.items()\n+        }\n+    elif isinstance(raw, list):\n+        return [\n+            render_variable(env, v, cookiecutter_dict)\n+            for v in raw\n+        ]\n+    elif not isinstance(raw, basestring):\n         raw = str(raw)\n+\n     template = env.from_string(raw)\n \n     rendered_template = template.render(cookiecutter=cookiecutter_dict)\n@@ -117,6 +150,9 @@\n     cookiecutter_dict = {}\n     env = StrictEnvironment(context=context)\n \n+    # First pass: Handle simple and raw variables, plus choices.\n+    # These must be done first because the dictionaries keys and\n+    # values might refer to them.\n     for key, raw in iteritems(context[u'cookiecutter']):\n         if key.startswith(u'_'):\n             cookiecutter_dict[key] = raw\n@@ -128,15 +164,33 @@\n                 val = prompt_choice_for_config(\n                     cookiecutter_dict, env, key, raw, no_input\n                 )\n-            else:\n+                cookiecutter_dict[key] = val\n+            elif not isinstance(raw, dict):\n                 # We are dealing with a regular variable\n                 val = render_variable(env, raw, cookiecutter_dict)\n \n                 if not no_input:\n                     val = read_user_variable(key, val)\n+\n+                cookiecutter_dict[key] = val\n+        except UndefinedError as err:\n+            msg = \"Unable to render variable '{}'\".format(key)\n+            raise UndefinedVariableInTemplate(msg, err, context)\n+\n+    # Second pass; handle the dictionaries.\n+    for key, raw in iteritems(context[u'cookiecutter']):\n+\n+        try:\n+            if isinstance(raw, dict):\n+                # We are dealing with a dict variable\n+                val = render_variable(env, raw, cookiecutter_dict)\n+\n+                if not no_input:\n+                    val = read_user_dict(key, val)\n+\n+                cookiecutter_dict[key] = val\n         except UndefinedError as err:\n             msg = \"Unable to render variable '{}'\".format(key)\n             raise UndefinedVariableInTemplate(msg, err, context)\n \n-        cookiecutter_dict[key] = val\n     return cookiecutter_dict\n", "issue": "passing list to template\nI would like to be able to pass a list to the templates. See the two code blocks at the end of this post for an example. Right now it appears that if you pass a list in the config object, it's read as a list of options for that key.\n\nI know that you can use `str.split()` in the jinja2 template, but that's not a real solution, since it's impossible to \"escape\" the character that's used as the delimiter. What's the best solution here? I would prefer to be able to pass a list in the json object and call it a day, but obviously that doesn't work for the user input prompts.\n- `cookiecutter.json`:\n\n``` json\n{\n  \"build_steps\": [\n     \"do_something\",\n     \"do_something_else\"\n  ]\n}\n```\n- `Dockerfile`:\n\n``` jinja2\nFROM something\n\n{% for step in cookiecutter.build_steps %}\nRUN {{ step }}\n{% endfor %}\n```\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"\ncookiecutter.prompt\n---------------------\n\nFunctions for prompting the user for project info.\n\"\"\"\n\nfrom collections import OrderedDict\n\nimport click\nfrom past.builtins import basestring\n\nfrom future.utils import iteritems\n\nfrom jinja2.exceptions import UndefinedError\n\nfrom .exceptions import UndefinedVariableInTemplate\nfrom .environment import StrictEnvironment\n\n\ndef read_user_variable(var_name, default_value):\n    \"\"\"Prompt the user for the given variable and return the entered value\n    or the given default.\n\n    :param str var_name: Variable of the context to query the user\n    :param default_value: Value that will be returned if no input happens\n    \"\"\"\n    # Please see http://click.pocoo.org/4/api/#click.prompt\n    return click.prompt(var_name, default=default_value)\n\n\ndef read_user_yes_no(question, default_value):\n    \"\"\"Prompt the user to reply with 'yes' or 'no' (or equivalent values).\n\n    Note:\n      Possible choices are 'true', '1', 'yes', 'y' or 'false', '0', 'no', 'n'\n\n    :param str question: Question to the user\n    :param default_value: Value that will be returned if no input happens\n    \"\"\"\n    # Please see http://click.pocoo.org/4/api/#click.prompt\n    return click.prompt(\n        question,\n        default=default_value,\n        type=click.BOOL\n    )\n\n\ndef read_user_choice(var_name, options):\n    \"\"\"Prompt the user to choose from several options for the given variable.\n\n    The first item will be returned if no input happens.\n\n    :param str var_name: Variable as specified in the context\n    :param list options: Sequence of options that are available to select from\n    :return: Exactly one item of ``options`` that has been chosen by the user\n    \"\"\"\n    # Please see http://click.pocoo.org/4/api/#click.prompt\n    if not isinstance(options, list):\n        raise TypeError\n\n    if not options:\n        raise ValueError\n\n    choice_map = OrderedDict(\n        (u'{}'.format(i), value) for i, value in enumerate(options, 1)\n    )\n    choices = choice_map.keys()\n    default = u'1'\n\n    choice_lines = [u'{} - {}'.format(*c) for c in choice_map.items()]\n    prompt = u'\\n'.join((\n        u'Select {}:'.format(var_name),\n        u'\\n'.join(choice_lines),\n        u'Choose from {}'.format(u', '.join(choices))\n    ))\n\n    user_choice = click.prompt(\n        prompt, type=click.Choice(choices), default=default\n    )\n    return choice_map[user_choice]\n\n\ndef render_variable(env, raw, cookiecutter_dict):\n    if raw is None:\n        return None\n    if not isinstance(raw, basestring):\n        raw = str(raw)\n    template = env.from_string(raw)\n\n    rendered_template = template.render(cookiecutter=cookiecutter_dict)\n    return rendered_template\n\n\ndef prompt_choice_for_config(cookiecutter_dict, env, key, options, no_input):\n    \"\"\"Prompt the user which option to choose from the given. Each of the\n    possible choices is rendered beforehand.\n    \"\"\"\n    rendered_options = [\n        render_variable(env, raw, cookiecutter_dict) for raw in options\n    ]\n\n    if no_input:\n        return rendered_options[0]\n    return read_user_choice(key, rendered_options)\n\n\ndef prompt_for_config(context, no_input=False):\n    \"\"\"\n    Prompts the user to enter new config, using context as a source for the\n    field names and sample values.\n\n    :param no_input: Prompt the user at command line for manual configuration?\n    \"\"\"\n    cookiecutter_dict = {}\n    env = StrictEnvironment(context=context)\n\n    for key, raw in iteritems(context[u'cookiecutter']):\n        if key.startswith(u'_'):\n            cookiecutter_dict[key] = raw\n            continue\n\n        try:\n            if isinstance(raw, list):\n                # We are dealing with a choice variable\n                val = prompt_choice_for_config(\n                    cookiecutter_dict, env, key, raw, no_input\n                )\n            else:\n                # We are dealing with a regular variable\n                val = render_variable(env, raw, cookiecutter_dict)\n\n                if not no_input:\n                    val = read_user_variable(key, val)\n        except UndefinedError as err:\n            msg = \"Unable to render variable '{}'\".format(key)\n            raise UndefinedVariableInTemplate(msg, err, context)\n\n        cookiecutter_dict[key] = val\n    return cookiecutter_dict\n", "path": "cookiecutter/prompt.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"\ncookiecutter.prompt\n---------------------\n\nFunctions for prompting the user for project info.\n\"\"\"\n\nfrom collections import OrderedDict\nimport json\n\nimport click\nfrom past.builtins import basestring\n\nfrom future.utils import iteritems\n\nfrom jinja2.exceptions import UndefinedError\n\nfrom .exceptions import UndefinedVariableInTemplate\nfrom .environment import StrictEnvironment\n\n\ndef read_user_variable(var_name, default_value):\n    \"\"\"Prompt the user for the given variable and return the entered value\n    or the given default.\n\n    :param str var_name: Variable of the context to query the user\n    :param default_value: Value that will be returned if no input happens\n    \"\"\"\n    # Please see http://click.pocoo.org/4/api/#click.prompt\n    return click.prompt(var_name, default=default_value)\n\n\ndef read_user_yes_no(question, default_value):\n    \"\"\"Prompt the user to reply with 'yes' or 'no' (or equivalent values).\n\n    Note:\n      Possible choices are 'true', '1', 'yes', 'y' or 'false', '0', 'no', 'n'\n\n    :param str question: Question to the user\n    :param default_value: Value that will be returned if no input happens\n    \"\"\"\n    # Please see http://click.pocoo.org/4/api/#click.prompt\n    return click.prompt(\n        question,\n        default=default_value,\n        type=click.BOOL\n    )\n\n\ndef read_user_choice(var_name, options):\n    \"\"\"Prompt the user to choose from several options for the given variable.\n\n    The first item will be returned if no input happens.\n\n    :param str var_name: Variable as specified in the context\n    :param list options: Sequence of options that are available to select from\n    :return: Exactly one item of ``options`` that has been chosen by the user\n    \"\"\"\n    # Please see http://click.pocoo.org/4/api/#click.prompt\n    if not isinstance(options, list):\n        raise TypeError\n\n    if not options:\n        raise ValueError\n\n    choice_map = OrderedDict(\n        (u'{}'.format(i), value) for i, value in enumerate(options, 1)\n    )\n    choices = choice_map.keys()\n    default = u'1'\n\n    choice_lines = [u'{} - {}'.format(*c) for c in choice_map.items()]\n    prompt = u'\\n'.join((\n        u'Select {}:'.format(var_name),\n        u'\\n'.join(choice_lines),\n        u'Choose from {}'.format(u', '.join(choices))\n    ))\n\n    user_choice = click.prompt(\n        prompt, type=click.Choice(choices), default=default\n    )\n    return choice_map[user_choice]\n\n\ndef read_user_dict(var_name, default_value):\n    \"\"\"Prompt the user to provide a dictionary of data.\n\n    :param str var_name: Variable as specified in the context\n    :param default_value: Value that will be returned if no input is provided\n    :return: A Python dictionary to use in the context.\n    \"\"\"\n    # Please see http://click.pocoo.org/4/api/#click.prompt\n    if not isinstance(default_value, dict):\n        raise TypeError\n\n    raw = click.prompt(var_name, default='default')\n    if raw != 'default':\n        value = json.loads(raw, object_hook=OrderedDict)\n    else:\n        value = default_value\n\n    return value\n\n\ndef render_variable(env, raw, cookiecutter_dict):\n    if raw is None:\n        return None\n    elif isinstance(raw, dict):\n        return {\n            render_variable(env, k, cookiecutter_dict):\n                render_variable(env, v, cookiecutter_dict)\n            for k, v in raw.items()\n        }\n    elif isinstance(raw, list):\n        return [\n            render_variable(env, v, cookiecutter_dict)\n            for v in raw\n        ]\n    elif not isinstance(raw, basestring):\n        raw = str(raw)\n\n    template = env.from_string(raw)\n\n    rendered_template = template.render(cookiecutter=cookiecutter_dict)\n    return rendered_template\n\n\ndef prompt_choice_for_config(cookiecutter_dict, env, key, options, no_input):\n    \"\"\"Prompt the user which option to choose from the given. Each of the\n    possible choices is rendered beforehand.\n    \"\"\"\n    rendered_options = [\n        render_variable(env, raw, cookiecutter_dict) for raw in options\n    ]\n\n    if no_input:\n        return rendered_options[0]\n    return read_user_choice(key, rendered_options)\n\n\ndef prompt_for_config(context, no_input=False):\n    \"\"\"\n    Prompts the user to enter new config, using context as a source for the\n    field names and sample values.\n\n    :param no_input: Prompt the user at command line for manual configuration?\n    \"\"\"\n    cookiecutter_dict = {}\n    env = StrictEnvironment(context=context)\n\n    # First pass: Handle simple and raw variables, plus choices.\n    # These must be done first because the dictionaries keys and\n    # values might refer to them.\n    for key, raw in iteritems(context[u'cookiecutter']):\n        if key.startswith(u'_'):\n            cookiecutter_dict[key] = raw\n            continue\n\n        try:\n            if isinstance(raw, list):\n                # We are dealing with a choice variable\n                val = prompt_choice_for_config(\n                    cookiecutter_dict, env, key, raw, no_input\n                )\n                cookiecutter_dict[key] = val\n            elif not isinstance(raw, dict):\n                # We are dealing with a regular variable\n                val = render_variable(env, raw, cookiecutter_dict)\n\n                if not no_input:\n                    val = read_user_variable(key, val)\n\n                cookiecutter_dict[key] = val\n        except UndefinedError as err:\n            msg = \"Unable to render variable '{}'\".format(key)\n            raise UndefinedVariableInTemplate(msg, err, context)\n\n    # Second pass; handle the dictionaries.\n    for key, raw in iteritems(context[u'cookiecutter']):\n\n        try:\n            if isinstance(raw, dict):\n                # We are dealing with a dict variable\n                val = render_variable(env, raw, cookiecutter_dict)\n\n                if not no_input:\n                    val = read_user_dict(key, val)\n\n                cookiecutter_dict[key] = val\n        except UndefinedError as err:\n            msg = \"Unable to render variable '{}'\".format(key)\n            raise UndefinedVariableInTemplate(msg, err, context)\n\n    return cookiecutter_dict\n", "path": "cookiecutter/prompt.py"}]}