We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Variable with null default no longer being required
* Cookiecutter version: 2.3.0
* Template project url: internal
* Python version: Python 3.8.12
* Operating System: Debian GNU/Linux 11

### Description:

After updating from 2.1.1 to 2.3.0, setting a field's default value in `cookiecutter.json` to `null` no longer makes that field required. It instead passes `None` as the value of that variable. Previously, it would repeat that prompt until you gave it a value.

It looks like this happened in the switch from `click` to `rich` in 2.3.0 - `click` doesn't allow a prompt without a default value to be empty (there's [a `while True` loop](https://github.com/pallets/click/blob/388b0e14093355e30b17ffaff0c7588533d9dbc8/src/click/termui.py#L163)), while `rich` does ([it just casts the input and makes sure it doesn't cause a ValueError](https://github.com/Textualize/rich/blob/720800e6930d85ad027b1e9bd0cbb96b5e994ce3/rich/prompt.py#L230) -- which it doesn't, as `str(None)=="None"`). It seems like, with `rich`, you have to handle that logic yourself.

### What I've run:

An example (`component_name`'s value is `null`, `service_name`'s is `test`):

```
Template 1:

  [1/8] component_name: 
  [2/8] service_name (test): ^C
Aborted!
```

Compare to output with version 2.1.1:

```
Template 1:

component_name: 
component_name: 
component_name: test
service_name [test]: ^CAborted!
```

--- 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 """Functions for prompting the user for project info."""
2 import json
3 from collections import OrderedDict
4 
5 from rich.prompt import Prompt, Confirm, PromptBase, InvalidResponse
6 from jinja2.exceptions import UndefinedError
7 
8 from cookiecutter.environment import StrictEnvironment
9 from cookiecutter.exceptions import UndefinedVariableInTemplate
10 
11 
12 def read_user_variable(var_name, default_value, prompts=None, prefix=""):
13     """Prompt user for variable and return the entered value or given default.
14 
15     :param str var_name: Variable of the context to query the user
16     :param default_value: Value that will be returned if no input happens
17     """
18     question = (
19         prompts[var_name]
20         if prompts and var_name in prompts.keys() and prompts[var_name]
21         else var_name
22     )
23     return Prompt.ask(f"{prefix}{question}", default=default_value)
24 
25 
26 class YesNoPrompt(Confirm):
27     """A prompt that returns a boolean for yes/no questions."""
28 
29     yes_choices = ["1", "true", "t", "yes", "y", "on"]
30     no_choices = ["0", "false", "f", "no", "n", "off"]
31 
32     def process_response(self, value: str) -> bool:
33         """Convert choices to a bool."""
34         value = value.strip().lower()
35         if value in self.yes_choices:
36             return True
37         elif value in self.no_choices:
38             return False
39         else:
40             raise InvalidResponse(self.validate_error_message)
41 
42 
43 def read_user_yes_no(var_name, default_value, prompts=None, prefix=""):
44     """Prompt the user to reply with 'yes' or 'no' (or equivalent values).
45 
46     - These input values will be converted to ``True``:
47       "1", "true", "t", "yes", "y", "on"
48     - These input values will be converted to ``False``:
49       "0", "false", "f", "no", "n", "off"
50 
51     Actual parsing done by :func:`prompt`; Check this function codebase change in
52     case of unexpected behaviour.
53 
54     :param str question: Question to the user
55     :param default_value: Value that will be returned if no input happens
56     """
57     question = (
58         prompts[var_name]
59         if prompts and var_name in prompts.keys() and prompts[var_name]
60         else var_name
61     )
62     return YesNoPrompt.ask(f"{prefix}{question}", default=default_value)
63 
64 
65 def read_repo_password(question):
66     """Prompt the user to enter a password.
67 
68     :param str question: Question to the user
69     """
70     return Prompt.ask(question, password=True)
71 
72 
73 def read_user_choice(var_name, options, prompts=None, prefix=""):
74     """Prompt the user to choose from several options for the given variable.
75 
76     The first item will be returned if no input happens.
77 
78     :param str var_name: Variable as specified in the context
79     :param list options: Sequence of options that are available to select from
80     :return: Exactly one item of ``options`` that has been chosen by the user
81     """
82     if not isinstance(options, list):
83         raise TypeError
84 
85     if not options:
86         raise ValueError
87 
88     choice_map = OrderedDict((f'{i}', value) for i, value in enumerate(options, 1))
89     choices = choice_map.keys()
90 
91     question = f"Select {var_name}"
92     choice_lines = [
93         '    [bold magenta]{}[/] - [bold]{}[/]'.format(*c) for c in choice_map.items()
94     ]
95 
96     # Handle if human-readable prompt is provided
97     if prompts and var_name in prompts.keys():
98         if isinstance(prompts[var_name], str):
99             question = prompts[var_name]
100         else:
101             if "__prompt__" in prompts[var_name]:
102                 question = prompts[var_name]["__prompt__"]
103             choice_lines = [
104                 f"    [bold magenta]{i}[/] - [bold]{prompts[var_name][p]}[/]"
105                 if p in prompts[var_name]
106                 else f"    [bold magenta]{i}[/] - [bold]{p}[/]"
107                 for i, p in choice_map.items()
108             ]
109 
110     prompt = '\n'.join(
111         (
112             f"{prefix}{question}",
113             "\n".join(choice_lines),
114             "    Choose from",
115         )
116     )
117 
118     user_choice = Prompt.ask(prompt, choices=list(choices), default=list(choices)[0])
119     return choice_map[user_choice]
120 
121 
122 DEFAULT_DISPLAY = 'default'
123 
124 
125 def process_json(user_value, default_value=None):
126     """Load user-supplied value as a JSON dict.
127 
128     :param str user_value: User-supplied value to load as a JSON dict
129     """
130     try:
131         user_dict = json.loads(user_value, object_pairs_hook=OrderedDict)
132     except Exception as error:
133         # Leave it up to click to ask the user again
134         raise InvalidResponse('Unable to decode to JSON.') from error
135 
136     if not isinstance(user_dict, dict):
137         # Leave it up to click to ask the user again
138         raise InvalidResponse('Requires JSON dict.')
139 
140     return user_dict
141 
142 
143 class JsonPrompt(PromptBase[dict]):
144     """A prompt that returns a dict from JSON string."""
145 
146     default = None
147     response_type = dict
148     validate_error_message = "[prompt.invalid]  Please enter a valid JSON string"
149 
150     def process_response(self, value: str) -> dict:
151         """Convert choices to a dict."""
152         return process_json(value, self.default)
153 
154 
155 def read_user_dict(var_name, default_value, prompts=None, prefix=""):
156     """Prompt the user to provide a dictionary of data.
157 
158     :param str var_name: Variable as specified in the context
159     :param default_value: Value that will be returned if no input is provided
160     :return: A Python dictionary to use in the context.
161     """
162     if not isinstance(default_value, dict):
163         raise TypeError
164 
165     question = (
166         prompts[var_name]
167         if prompts and var_name in prompts.keys() and prompts[var_name]
168         else var_name
169     )
170     user_value = JsonPrompt.ask(
171         f"{prefix}{question} [cyan bold]({DEFAULT_DISPLAY})[/]",
172         default=default_value,
173         show_default=False,
174     )
175     return user_value
176 
177 
178 def render_variable(env, raw, cookiecutter_dict):
179     """Render the next variable to be displayed in the user prompt.
180 
181     Inside the prompting taken from the cookiecutter.json file, this renders
182     the next variable. For example, if a project_name is "Peanut Butter
183     Cookie", the repo_name could be be rendered with:
184 
185         `{{ cookiecutter.project_name.replace(" ", "_") }}`.
186 
187     This is then presented to the user as the default.
188 
189     :param Environment env: A Jinja2 Environment object.
190     :param raw: The next value to be prompted for by the user.
191     :param dict cookiecutter_dict: The current context as it's gradually
192         being populated with variables.
193     :return: The rendered value for the default variable.
194     """
195     if raw is None or isinstance(raw, bool):
196         return raw
197     elif isinstance(raw, dict):
198         return {
199             render_variable(env, k, cookiecutter_dict): render_variable(
200                 env, v, cookiecutter_dict
201             )
202             for k, v in raw.items()
203         }
204     elif isinstance(raw, list):
205         return [render_variable(env, v, cookiecutter_dict) for v in raw]
206     elif not isinstance(raw, str):
207         raw = str(raw)
208 
209     template = env.from_string(raw)
210 
211     return template.render(cookiecutter=cookiecutter_dict)
212 
213 
214 def prompt_choice_for_config(
215     cookiecutter_dict, env, key, options, no_input, prompts=None, prefix=""
216 ):
217     """Prompt user with a set of options to choose from.
218 
219     :param no_input: Do not prompt for user input and return the first available option.
220     """
221     rendered_options = [render_variable(env, raw, cookiecutter_dict) for raw in options]
222     if no_input:
223         return rendered_options[0]
224     return read_user_choice(key, rendered_options, prompts, prefix)
225 
226 
227 def prompt_for_config(context, no_input=False):
228     """Prompt user to enter a new config.
229 
230     :param dict context: Source for field names and sample values.
231     :param no_input: Do not prompt for user input and use only values from context.
232     """
233     cookiecutter_dict = OrderedDict([])
234     env = StrictEnvironment(context=context)
235 
236     prompts = {}
237     if '__prompts__' in context['cookiecutter'].keys():
238         prompts = context['cookiecutter']['__prompts__']
239         del context['cookiecutter']['__prompts__']
240 
241     # First pass: Handle simple and raw variables, plus choices.
242     # These must be done first because the dictionaries keys and
243     # values might refer to them.
244 
245     count = 0
246     size = len(context['cookiecutter'].items())
247     for key, raw in context['cookiecutter'].items():
248         if key.startswith('_') and not key.startswith('__'):
249             cookiecutter_dict[key] = raw
250             continue
251         elif key.startswith('__'):
252             cookiecutter_dict[key] = render_variable(env, raw, cookiecutter_dict)
253             continue
254 
255         if not isinstance(raw, dict):
256             count += 1
257             prefix = f"  [dim][{count}/{size}][/] "
258 
259         try:
260             if isinstance(raw, list):
261                 # We are dealing with a choice variable
262                 val = prompt_choice_for_config(
263                     cookiecutter_dict, env, key, raw, no_input, prompts, prefix
264                 )
265                 cookiecutter_dict[key] = val
266             elif isinstance(raw, bool):
267                 # We are dealing with a boolean variable
268                 if no_input:
269                     cookiecutter_dict[key] = render_variable(
270                         env, raw, cookiecutter_dict
271                     )
272                 else:
273                     cookiecutter_dict[key] = read_user_yes_no(key, raw, prompts, prefix)
274             elif not isinstance(raw, dict):
275                 # We are dealing with a regular variable
276                 val = render_variable(env, raw, cookiecutter_dict)
277 
278                 if not no_input:
279                     val = read_user_variable(key, val, prompts, prefix)
280 
281                 cookiecutter_dict[key] = val
282         except UndefinedError as err:
283             msg = f"Unable to render variable '{key}'"
284             raise UndefinedVariableInTemplate(msg, err, context) from err
285 
286     # Second pass; handle the dictionaries.
287     for key, raw in context['cookiecutter'].items():
288         # Skip private type dicts not to be rendered.
289         if key.startswith('_') and not key.startswith('__'):
290             continue
291 
292         try:
293             if isinstance(raw, dict):
294                 # We are dealing with a dict variable
295                 count += 1
296                 prefix = f"  [dim][{count}/{size}][/] "
297                 val = render_variable(env, raw, cookiecutter_dict)
298 
299                 if not no_input and not key.startswith('__'):
300                     val = read_user_dict(key, val, prompts, prefix)
301 
302                 cookiecutter_dict[key] = val
303         except UndefinedError as err:
304             msg = f"Unable to render variable '{key}'"
305             raise UndefinedVariableInTemplate(msg, err, context) from err
306 
307     return cookiecutter_dict
308
```
--- 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()

```

diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py
--- a/cookiecutter/prompt.py
+++ b/cookiecutter/prompt.py
@@ -20,7 +20,13 @@
         if prompts and var_name in prompts.keys() and prompts[var_name]
         else var_name
     )
-    return Prompt.ask(f"{prefix}{question}", default=default_value)
+
+    while True:
+        variable = Prompt.ask(f"{prefix}{question}", default=default_value)
+        if variable is not None:
+            break
+
+    return variable
 
 
 class YesNoPrompt(Confirm):

{"golden_diff": "diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py\n--- a/cookiecutter/prompt.py\n+++ b/cookiecutter/prompt.py\n@@ -20,7 +20,13 @@\n         if prompts and var_name in prompts.keys() and prompts[var_name]\n         else var_name\n     )\n-    return Prompt.ask(f\"{prefix}{question}\", default=default_value)\n+\n+    while True:\n+        variable = Prompt.ask(f\"{prefix}{question}\", default=default_value)\n+        if variable is not None:\n+            break\n+\n+    return variable\n \n \n class YesNoPrompt(Confirm):\n", "issue": "Variable with null default no longer being required\n* Cookiecutter version: 2.3.0\r\n* Template project url: internal\r\n* Python version: Python 3.8.12\r\n* Operating System: Debian GNU/Linux 11\r\n\r\n### Description:\r\n\r\nAfter updating from 2.1.1 to 2.3.0, setting a field's default value in `cookiecutter.json` to `null` no longer makes that field required. It instead passes `None` as the value of that variable. Previously, it would repeat that prompt until you gave it a value.\r\n\r\nIt looks like this happened in the switch from `click` to `rich` in 2.3.0 - `click` doesn't allow a prompt without a default value to be empty (there's [a `while True` loop](https://github.com/pallets/click/blob/388b0e14093355e30b17ffaff0c7588533d9dbc8/src/click/termui.py#L163)), while `rich` does ([it just casts the input and makes sure it doesn't cause a ValueError](https://github.com/Textualize/rich/blob/720800e6930d85ad027b1e9bd0cbb96b5e994ce3/rich/prompt.py#L230) -- which it doesn't, as `str(None)==\"None\"`). It seems like, with `rich`, you have to handle that logic yourself.\r\n\r\n### What I've run:\r\n\r\nAn example (`component_name`'s value is `null`, `service_name`'s is `test`):\r\n\r\n```\r\nTemplate 1:\r\n\r\n  [1/8] component_name: \r\n  [2/8] service_name (test): ^C\r\nAborted!\r\n```\r\n\r\nCompare to output with version 2.1.1:\r\n\r\n```\r\nTemplate 1:\r\n\r\ncomponent_name: \r\ncomponent_name: \r\ncomponent_name: test\r\nservice_name [test]: ^CAborted!\r\n```\n", "before_files": [{"content": "\"\"\"Functions for prompting the user for project info.\"\"\"\nimport json\nfrom collections import OrderedDict\n\nfrom rich.prompt import Prompt, Confirm, PromptBase, InvalidResponse\nfrom jinja2.exceptions import UndefinedError\n\nfrom cookiecutter.environment import StrictEnvironment\nfrom cookiecutter.exceptions import UndefinedVariableInTemplate\n\n\ndef read_user_variable(var_name, default_value, prompts=None, prefix=\"\"):\n    \"\"\"Prompt user for variable and return the entered value or 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    question = (\n        prompts[var_name]\n        if prompts and var_name in prompts.keys() and prompts[var_name]\n        else var_name\n    )\n    return Prompt.ask(f\"{prefix}{question}\", default=default_value)\n\n\nclass YesNoPrompt(Confirm):\n    \"\"\"A prompt that returns a boolean for yes/no questions.\"\"\"\n\n    yes_choices = [\"1\", \"true\", \"t\", \"yes\", \"y\", \"on\"]\n    no_choices = [\"0\", \"false\", \"f\", \"no\", \"n\", \"off\"]\n\n    def process_response(self, value: str) -> bool:\n        \"\"\"Convert choices to a bool.\"\"\"\n        value = value.strip().lower()\n        if value in self.yes_choices:\n            return True\n        elif value in self.no_choices:\n            return False\n        else:\n            raise InvalidResponse(self.validate_error_message)\n\n\ndef read_user_yes_no(var_name, default_value, prompts=None, prefix=\"\"):\n    \"\"\"Prompt the user to reply with 'yes' or 'no' (or equivalent values).\n\n    - These input values will be converted to ``True``:\n      \"1\", \"true\", \"t\", \"yes\", \"y\", \"on\"\n    - These input values will be converted to ``False``:\n      \"0\", \"false\", \"f\", \"no\", \"n\", \"off\"\n\n    Actual parsing done by :func:`prompt`; Check this function codebase change in\n    case of unexpected behaviour.\n\n    :param str question: Question to the user\n    :param default_value: Value that will be returned if no input happens\n    \"\"\"\n    question = (\n        prompts[var_name]\n        if prompts and var_name in prompts.keys() and prompts[var_name]\n        else var_name\n    )\n    return YesNoPrompt.ask(f\"{prefix}{question}\", default=default_value)\n\n\ndef read_repo_password(question):\n    \"\"\"Prompt the user to enter a password.\n\n    :param str question: Question to the user\n    \"\"\"\n    return Prompt.ask(question, password=True)\n\n\ndef read_user_choice(var_name, options, prompts=None, prefix=\"\"):\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    if not isinstance(options, list):\n        raise TypeError\n\n    if not options:\n        raise ValueError\n\n    choice_map = OrderedDict((f'{i}', value) for i, value in enumerate(options, 1))\n    choices = choice_map.keys()\n\n    question = f\"Select {var_name}\"\n    choice_lines = [\n        '    [bold magenta]{}[/] - [bold]{}[/]'.format(*c) for c in choice_map.items()\n    ]\n\n    # Handle if human-readable prompt is provided\n    if prompts and var_name in prompts.keys():\n        if isinstance(prompts[var_name], str):\n            question = prompts[var_name]\n        else:\n            if \"__prompt__\" in prompts[var_name]:\n                question = prompts[var_name][\"__prompt__\"]\n            choice_lines = [\n                f\"    [bold magenta]{i}[/] - [bold]{prompts[var_name][p]}[/]\"\n                if p in prompts[var_name]\n                else f\"    [bold magenta]{i}[/] - [bold]{p}[/]\"\n                for i, p in choice_map.items()\n            ]\n\n    prompt = '\\n'.join(\n        (\n            f\"{prefix}{question}\",\n            \"\\n\".join(choice_lines),\n            \"    Choose from\",\n        )\n    )\n\n    user_choice = Prompt.ask(prompt, choices=list(choices), default=list(choices)[0])\n    return choice_map[user_choice]\n\n\nDEFAULT_DISPLAY = 'default'\n\n\ndef process_json(user_value, default_value=None):\n    \"\"\"Load user-supplied value as a JSON dict.\n\n    :param str user_value: User-supplied value to load as a JSON dict\n    \"\"\"\n    try:\n        user_dict = json.loads(user_value, object_pairs_hook=OrderedDict)\n    except Exception as error:\n        # Leave it up to click to ask the user again\n        raise InvalidResponse('Unable to decode to JSON.') from error\n\n    if not isinstance(user_dict, dict):\n        # Leave it up to click to ask the user again\n        raise InvalidResponse('Requires JSON dict.')\n\n    return user_dict\n\n\nclass JsonPrompt(PromptBase[dict]):\n    \"\"\"A prompt that returns a dict from JSON string.\"\"\"\n\n    default = None\n    response_type = dict\n    validate_error_message = \"[prompt.invalid]  Please enter a valid JSON string\"\n\n    def process_response(self, value: str) -> dict:\n        \"\"\"Convert choices to a dict.\"\"\"\n        return process_json(value, self.default)\n\n\ndef read_user_dict(var_name, default_value, prompts=None, prefix=\"\"):\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    if not isinstance(default_value, dict):\n        raise TypeError\n\n    question = (\n        prompts[var_name]\n        if prompts and var_name in prompts.keys() and prompts[var_name]\n        else var_name\n    )\n    user_value = JsonPrompt.ask(\n        f\"{prefix}{question} [cyan bold]({DEFAULT_DISPLAY})[/]\",\n        default=default_value,\n        show_default=False,\n    )\n    return user_value\n\n\ndef render_variable(env, raw, cookiecutter_dict):\n    \"\"\"Render the next variable to be displayed in the user prompt.\n\n    Inside the prompting taken from the cookiecutter.json file, this renders\n    the next variable. For example, if a project_name is \"Peanut Butter\n    Cookie\", the repo_name could be be rendered with:\n\n        `{{ cookiecutter.project_name.replace(\" \", \"_\") }}`.\n\n    This is then presented to the user as the default.\n\n    :param Environment env: A Jinja2 Environment object.\n    :param raw: The next value to be prompted for by the user.\n    :param dict cookiecutter_dict: The current context as it's gradually\n        being populated with variables.\n    :return: The rendered value for the default variable.\n    \"\"\"\n    if raw is None or isinstance(raw, bool):\n        return raw\n    elif isinstance(raw, dict):\n        return {\n            render_variable(env, k, cookiecutter_dict): render_variable(\n                env, v, cookiecutter_dict\n            )\n            for k, v in raw.items()\n        }\n    elif isinstance(raw, list):\n        return [render_variable(env, v, cookiecutter_dict) for v in raw]\n    elif not isinstance(raw, str):\n        raw = str(raw)\n\n    template = env.from_string(raw)\n\n    return template.render(cookiecutter=cookiecutter_dict)\n\n\ndef prompt_choice_for_config(\n    cookiecutter_dict, env, key, options, no_input, prompts=None, prefix=\"\"\n):\n    \"\"\"Prompt user with a set of options to choose from.\n\n    :param no_input: Do not prompt for user input and return the first available option.\n    \"\"\"\n    rendered_options = [render_variable(env, raw, cookiecutter_dict) for raw in options]\n    if no_input:\n        return rendered_options[0]\n    return read_user_choice(key, rendered_options, prompts, prefix)\n\n\ndef prompt_for_config(context, no_input=False):\n    \"\"\"Prompt user to enter a new config.\n\n    :param dict context: Source for field names and sample values.\n    :param no_input: Do not prompt for user input and use only values from context.\n    \"\"\"\n    cookiecutter_dict = OrderedDict([])\n    env = StrictEnvironment(context=context)\n\n    prompts = {}\n    if '__prompts__' in context['cookiecutter'].keys():\n        prompts = context['cookiecutter']['__prompts__']\n        del context['cookiecutter']['__prompts__']\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\n    count = 0\n    size = len(context['cookiecutter'].items())\n    for key, raw in context['cookiecutter'].items():\n        if key.startswith('_') and not key.startswith('__'):\n            cookiecutter_dict[key] = raw\n            continue\n        elif key.startswith('__'):\n            cookiecutter_dict[key] = render_variable(env, raw, cookiecutter_dict)\n            continue\n\n        if not isinstance(raw, dict):\n            count += 1\n            prefix = f\"  [dim][{count}/{size}][/] \"\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, prompts, prefix\n                )\n                cookiecutter_dict[key] = val\n            elif isinstance(raw, bool):\n                # We are dealing with a boolean variable\n                if no_input:\n                    cookiecutter_dict[key] = render_variable(\n                        env, raw, cookiecutter_dict\n                    )\n                else:\n                    cookiecutter_dict[key] = read_user_yes_no(key, raw, prompts, prefix)\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, prompts, prefix)\n\n                cookiecutter_dict[key] = val\n        except UndefinedError as err:\n            msg = f\"Unable to render variable '{key}'\"\n            raise UndefinedVariableInTemplate(msg, err, context) from err\n\n    # Second pass; handle the dictionaries.\n    for key, raw in context['cookiecutter'].items():\n        # Skip private type dicts not to be rendered.\n        if key.startswith('_') and not key.startswith('__'):\n            continue\n\n        try:\n            if isinstance(raw, dict):\n                # We are dealing with a dict variable\n                count += 1\n                prefix = f\"  [dim][{count}/{size}][/] \"\n                val = render_variable(env, raw, cookiecutter_dict)\n\n                if not no_input and not key.startswith('__'):\n                    val = read_user_dict(key, val, prompts, prefix)\n\n                cookiecutter_dict[key] = val\n        except UndefinedError as err:\n            msg = f\"Unable to render variable '{key}'\"\n            raise UndefinedVariableInTemplate(msg, err, context) from err\n\n    return cookiecutter_dict\n", "path": "cookiecutter/prompt.py"}], "after_files": [{"content": "\"\"\"Functions for prompting the user for project info.\"\"\"\nimport json\nfrom collections import OrderedDict\n\nfrom rich.prompt import Prompt, Confirm, PromptBase, InvalidResponse\nfrom jinja2.exceptions import UndefinedError\n\nfrom cookiecutter.environment import StrictEnvironment\nfrom cookiecutter.exceptions import UndefinedVariableInTemplate\n\n\ndef read_user_variable(var_name, default_value, prompts=None, prefix=\"\"):\n    \"\"\"Prompt user for variable and return the entered value or 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    question = (\n        prompts[var_name]\n        if prompts and var_name in prompts.keys() and prompts[var_name]\n        else var_name\n    )\n\n    while True:\n        variable = Prompt.ask(f\"{prefix}{question}\", default=default_value)\n        if variable is not None:\n            break\n\n    return variable\n\n\nclass YesNoPrompt(Confirm):\n    \"\"\"A prompt that returns a boolean for yes/no questions.\"\"\"\n\n    yes_choices = [\"1\", \"true\", \"t\", \"yes\", \"y\", \"on\"]\n    no_choices = [\"0\", \"false\", \"f\", \"no\", \"n\", \"off\"]\n\n    def process_response(self, value: str) -> bool:\n        \"\"\"Convert choices to a bool.\"\"\"\n        value = value.strip().lower()\n        if value in self.yes_choices:\n            return True\n        elif value in self.no_choices:\n            return False\n        else:\n            raise InvalidResponse(self.validate_error_message)\n\n\ndef read_user_yes_no(var_name, default_value, prompts=None, prefix=\"\"):\n    \"\"\"Prompt the user to reply with 'yes' or 'no' (or equivalent values).\n\n    - These input values will be converted to ``True``:\n      \"1\", \"true\", \"t\", \"yes\", \"y\", \"on\"\n    - These input values will be converted to ``False``:\n      \"0\", \"false\", \"f\", \"no\", \"n\", \"off\"\n\n    Actual parsing done by :func:`prompt`; Check this function codebase change in\n    case of unexpected behaviour.\n\n    :param str question: Question to the user\n    :param default_value: Value that will be returned if no input happens\n    \"\"\"\n    question = (\n        prompts[var_name]\n        if prompts and var_name in prompts.keys() and prompts[var_name]\n        else var_name\n    )\n    return YesNoPrompt.ask(f\"{prefix}{question}\", default=default_value)\n\n\ndef read_repo_password(question):\n    \"\"\"Prompt the user to enter a password.\n\n    :param str question: Question to the user\n    \"\"\"\n    return Prompt.ask(question, password=True)\n\n\ndef read_user_choice(var_name, options, prompts=None, prefix=\"\"):\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    if not isinstance(options, list):\n        raise TypeError\n\n    if not options:\n        raise ValueError\n\n    choice_map = OrderedDict((f'{i}', value) for i, value in enumerate(options, 1))\n    choices = choice_map.keys()\n\n    question = f\"Select {var_name}\"\n    choice_lines = [\n        '    [bold magenta]{}[/] - [bold]{}[/]'.format(*c) for c in choice_map.items()\n    ]\n\n    # Handle if human-readable prompt is provided\n    if prompts and var_name in prompts.keys():\n        if isinstance(prompts[var_name], str):\n            question = prompts[var_name]\n        else:\n            if \"__prompt__\" in prompts[var_name]:\n                question = prompts[var_name][\"__prompt__\"]\n            choice_lines = [\n                f\"    [bold magenta]{i}[/] - [bold]{prompts[var_name][p]}[/]\"\n                if p in prompts[var_name]\n                else f\"    [bold magenta]{i}[/] - [bold]{p}[/]\"\n                for i, p in choice_map.items()\n            ]\n\n    prompt = '\\n'.join(\n        (\n            f\"{prefix}{question}\",\n            \"\\n\".join(choice_lines),\n            \"    Choose from\",\n        )\n    )\n\n    user_choice = Prompt.ask(prompt, choices=list(choices), default=list(choices)[0])\n    return choice_map[user_choice]\n\n\nDEFAULT_DISPLAY = 'default'\n\n\ndef process_json(user_value, default_value=None):\n    \"\"\"Load user-supplied value as a JSON dict.\n\n    :param str user_value: User-supplied value to load as a JSON dict\n    \"\"\"\n    try:\n        user_dict = json.loads(user_value, object_pairs_hook=OrderedDict)\n    except Exception as error:\n        # Leave it up to click to ask the user again\n        raise InvalidResponse('Unable to decode to JSON.') from error\n\n    if not isinstance(user_dict, dict):\n        # Leave it up to click to ask the user again\n        raise InvalidResponse('Requires JSON dict.')\n\n    return user_dict\n\n\nclass JsonPrompt(PromptBase[dict]):\n    \"\"\"A prompt that returns a dict from JSON string.\"\"\"\n\n    default = None\n    response_type = dict\n    validate_error_message = \"[prompt.invalid]  Please enter a valid JSON string\"\n\n    def process_response(self, value: str) -> dict:\n        \"\"\"Convert choices to a dict.\"\"\"\n        return process_json(value, self.default)\n\n\ndef read_user_dict(var_name, default_value, prompts=None, prefix=\"\"):\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    if not isinstance(default_value, dict):\n        raise TypeError\n\n    question = (\n        prompts[var_name]\n        if prompts and var_name in prompts.keys() and prompts[var_name]\n        else var_name\n    )\n    user_value = JsonPrompt.ask(\n        f\"{prefix}{question} [cyan bold]({DEFAULT_DISPLAY})[/]\",\n        default=default_value,\n        show_default=False,\n    )\n    return user_value\n\n\ndef render_variable(env, raw, cookiecutter_dict):\n    \"\"\"Render the next variable to be displayed in the user prompt.\n\n    Inside the prompting taken from the cookiecutter.json file, this renders\n    the next variable. For example, if a project_name is \"Peanut Butter\n    Cookie\", the repo_name could be be rendered with:\n\n        `{{ cookiecutter.project_name.replace(\" \", \"_\") }}`.\n\n    This is then presented to the user as the default.\n\n    :param Environment env: A Jinja2 Environment object.\n    :param raw: The next value to be prompted for by the user.\n    :param dict cookiecutter_dict: The current context as it's gradually\n        being populated with variables.\n    :return: The rendered value for the default variable.\n    \"\"\"\n    if raw is None or isinstance(raw, bool):\n        return raw\n    elif isinstance(raw, dict):\n        return {\n            render_variable(env, k, cookiecutter_dict): render_variable(\n                env, v, cookiecutter_dict\n            )\n            for k, v in raw.items()\n        }\n    elif isinstance(raw, list):\n        return [render_variable(env, v, cookiecutter_dict) for v in raw]\n    elif not isinstance(raw, str):\n        raw = str(raw)\n\n    template = env.from_string(raw)\n\n    return template.render(cookiecutter=cookiecutter_dict)\n\n\ndef prompt_choice_for_config(\n    cookiecutter_dict, env, key, options, no_input, prompts=None, prefix=\"\"\n):\n    \"\"\"Prompt user with a set of options to choose from.\n\n    :param no_input: Do not prompt for user input and return the first available option.\n    \"\"\"\n    rendered_options = [render_variable(env, raw, cookiecutter_dict) for raw in options]\n    if no_input:\n        return rendered_options[0]\n    return read_user_choice(key, rendered_options, prompts, prefix)\n\n\ndef prompt_for_config(context, no_input=False):\n    \"\"\"Prompt user to enter a new config.\n\n    :param dict context: Source for field names and sample values.\n    :param no_input: Do not prompt for user input and use only values from context.\n    \"\"\"\n    cookiecutter_dict = OrderedDict([])\n    env = StrictEnvironment(context=context)\n\n    prompts = {}\n    if '__prompts__' in context['cookiecutter'].keys():\n        prompts = context['cookiecutter']['__prompts__']\n        del context['cookiecutter']['__prompts__']\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\n    count = 0\n    size = len(context['cookiecutter'].items())\n    for key, raw in context['cookiecutter'].items():\n        if key.startswith('_') and not key.startswith('__'):\n            cookiecutter_dict[key] = raw\n            continue\n        elif key.startswith('__'):\n            cookiecutter_dict[key] = render_variable(env, raw, cookiecutter_dict)\n            continue\n\n        if not isinstance(raw, dict):\n            count += 1\n            prefix = f\"  [dim][{count}/{size}][/] \"\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, prompts, prefix\n                )\n                cookiecutter_dict[key] = val\n            elif isinstance(raw, bool):\n                # We are dealing with a boolean variable\n                if no_input:\n                    cookiecutter_dict[key] = render_variable(\n                        env, raw, cookiecutter_dict\n                    )\n                else:\n                    cookiecutter_dict[key] = read_user_yes_no(key, raw, prompts, prefix)\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, prompts, prefix)\n\n                cookiecutter_dict[key] = val\n        except UndefinedError as err:\n            msg = f\"Unable to render variable '{key}'\"\n            raise UndefinedVariableInTemplate(msg, err, context) from err\n\n    # Second pass; handle the dictionaries.\n    for key, raw in context['cookiecutter'].items():\n        # Skip private type dicts not to be rendered.\n        if key.startswith('_') and not key.startswith('__'):\n            continue\n\n        try:\n            if isinstance(raw, dict):\n                # We are dealing with a dict variable\n                count += 1\n                prefix = f\"  [dim][{count}/{size}][/] \"\n                val = render_variable(env, raw, cookiecutter_dict)\n\n                if not no_input and not key.startswith('__'):\n                    val = read_user_dict(key, val, prompts, prefix)\n\n                cookiecutter_dict[key] = val\n        except UndefinedError as err:\n            msg = f\"Unable to render variable '{key}'\"\n            raise UndefinedVariableInTemplate(msg, err, context) from err\n\n    return cookiecutter_dict\n", "path": "cookiecutter/prompt.py"}]}