Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package cookiecutter for openSUSE:Factory 
checked in at 2023-09-07 21:13:17
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/cookiecutter (Old)
 and      /work/SRC/openSUSE:Factory/.cookiecutter.new.1766 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "cookiecutter"

Thu Sep  7 21:13:17 2023 rev:13 rq:1109385 version:2.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/cookiecutter/cookiecutter.changes        
2023-07-26 13:24:02.812036938 +0200
+++ /work/SRC/openSUSE:Factory/.cookiecutter.new.1766/cookiecutter.changes      
2023-09-07 21:14:33.157190960 +0200
@@ -1,0 +2,9 @@
+Thu Sep  7 05:19:46 UTC 2023 - Steve Kowalik <steven.kowa...@suse.com>
+
+- Update to 2.3.0:
+  * Improve style of prompts using `rich` (#1901) @vemonet
+  * Fix replay (#1904) @vemonet
+  * Support multichoice overwrite (#1903) @Meepit
+- Switch to pyproject macro.
+
+-------------------------------------------------------------------

Old:
----
  cookiecutter-2.2.3.tar.gz

New:
----
  cookiecutter-2.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ cookiecutter.spec ++++++
--- /var/tmp/diff_new_pack.efQPoZ/_old  2023-09-07 21:14:34.269230713 +0200
+++ /var/tmp/diff_new_pack.efQPoZ/_new  2023-09-07 21:14:34.269230713 +0200
@@ -19,37 +19,34 @@
 
 %{?sle15_python_module_pythons}
 Name:           cookiecutter
-Version:        2.2.3
+Version:        2.3.0
 Release:        0
 Summary:        A command-line utility that creates projects from project 
templates
 License:        BSD-3-Clause
-Group:          Development/Languages/Python
 URL:            https://github.com/audreyr/cookiecutter
 Source:         
https://github.com/cookiecutter/cookiecutter/archive/refs/tags/%{version}.tar.gz#/%{name}-%{version}.tar.gz
 # recent versions are not published on PyPI: 
https://github.com/cookiecutter/cookiecutter/issues/1636
 #Source:         
https://files.pythonhosted.org/packages/source/c/cookiecutter/cookiecutter-%%{version}.tar.gz
 Source1:        ccext.py
 BuildRequires:  %{python_module Jinja2 >= 2.7 with %python-Jinja2 < 4}
+BuildRequires:  %{python_module arrow}
 BuildRequires:  %{python_module binaryornot >= 0.2.0}
 BuildRequires:  %{python_module click >= 7 with %python-click < 9}
-BuildRequires:  %{python_module future >= 0.15.2}
-BuildRequires:  %{python_module jinja2-time >= 0.1.0}
-BuildRequires:  %{python_module poyo >= 0.1.0}
+BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module python-slugify}
+BuildRequires:  %{python_module rich}
 BuildRequires:  %{python_module setuptools}
-BuildRequires:  %{python_module whichcraft >= 0.4.0}
+BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  git-core
 BuildRequires:  python-rpm-macros
 Requires:       git-core
 Requires:       python-PyYAML
+Requires:       python-arrow
 Requires:       python-binaryornot >= 0.2.0
-Requires:       python-future >= 0.15.2
-Requires:       python-jinja2-time >= 0.1.0
-Requires:       python-poyo >= 0.1.0
 Requires:       python-python-slugify
 Requires:       python-requests >= 2.18.0
-Requires:       python-whichcraft >= 0.4.0
+Requires:       python-rich
 Requires:       (python-Jinja2 >= 2.7 with python-Jinja2 < 4)
 Requires:       (python-click >= 7 with python-click < 9)
 Requires(post): update-alternatives
@@ -105,14 +102,14 @@
 rm setup.cfg
 
 %build
-%python_build
+%pyproject_wheel
 pushd docs
 sphinx-build -b html -d .build/doctrees . _build/html
 rm _build/html/.buildinfo
 popd
 
 %install
-%python_install
+%pyproject_install
 %python_clone -a %{buildroot}%{_bindir}/cookiecutter
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 # the doc directive in the files section cannot deduplicate, so do it manually
@@ -140,7 +137,7 @@
 %license LICENSE
 %python_alternative cookiecutter
 %{python_sitelib}/cookiecutter
-%{python_sitelib}/cookiecutter-%{version}*-info
+%{python_sitelib}/cookiecutter-%{version}.dist-info
 
 %files -n cookiecutter-doc
 %license LICENSE

++++++ cookiecutter-2.2.3.tar.gz -> cookiecutter-2.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/.github/workflows/tests.yml 
new/cookiecutter-2.3.0/.github/workflows/tests.yml
--- old/cookiecutter-2.2.3/.github/workflows/tests.yml  2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/.github/workflows/tests.yml  2023-08-03 
22:32:14.000000000 +0200
@@ -61,7 +61,7 @@
         run: "tox -e safety"
       - name: Send coverage report to codeclimate
         continue-on-error: true
-        uses: paambaati/codeclimate-action@v4.0.0
+        uses: paambaati/codeclimate-action@v5.0.0
         with:
           coverageCommand: echo "Ignore rerun"
           coverageLocations: ${{github.workspace}}/coverage.xml:coverage.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/.gitignore 
new/cookiecutter-2.3.0/.gitignore
--- old/cookiecutter-2.2.3/.gitignore   2023-07-11 17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/.gitignore   2023-08-03 22:32:14.000000000 +0200
@@ -1,5 +1,7 @@
 # Adapted from https://github.com/github/gitignore/blob/main/Python.gitignore
 
+tests/tmp/
+
 # Byte-compiled / optimized / DLL files
 __pycache__/
 *.py[cod]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/.pre-commit-config.yaml 
new/cookiecutter-2.3.0/.pre-commit-config.yaml
--- old/cookiecutter-2.2.3/.pre-commit-config.yaml      2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/.pre-commit-config.yaml      2023-08-03 
22:32:14.000000000 +0200
@@ -51,7 +51,7 @@
       - id: check-yaml
         exclude: "not_rendered.yml|invalid-config.yaml"
   - repo: https://github.com/PyCQA/flake8
-    rev: 6.0.0
+    rev: 6.1.0
     hooks:
       - id: flake8
         additional_dependencies:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/HISTORY.md 
new/cookiecutter-2.3.0/HISTORY.md
--- old/cookiecutter-2.2.3/HISTORY.md   2023-07-11 17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/HISTORY.md   2023-08-03 22:32:14.000000000 +0200
@@ -2,6 +2,26 @@
 
 History is important, but our current roadmap can be found 
[here](https://github.com/cookiecutter/cookiecutter/projects)
 
+## 2.3.0 (2023-08-03)
+
+### Minor Changes
+
+* Improve style of prompts using `rich` (#1901) @vemonet
+
+### CI/CD and QA changes
+
+* Bump paambaati/codeclimate-action from 4.0.0 to 5.0.0 (#1908) @dependabot
+* [pre-commit.ci] pre-commit autoupdate (#1907) @pre-commit-ci
+
+### Bugfixes
+
+* Fix replay (#1904) @vemonet
+* Support multichoice overwrite (#1903) @Meepit
+
+### This release is made by wonderful contributors:
+
+@Meepit, @dependabot, @dependabot[bot], @ericof, @pre-commit-ci, 
@pre-commit-ci[bot] and @vemonet
+
 ## 2.2.3 (2023-07-11)
 ### Changes
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/README.md 
new/cookiecutter-2.3.0/README.md
--- old/cookiecutter-2.2.3/README.md    2023-07-11 17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/README.md    2023-08-03 22:32:14.000000000 +0200
@@ -113,7 +113,7 @@
   ```py
   
{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}.py
   ```
-- Simply define your template variables in a `cookiecutter.json` file. You can 
also add human-readable questions and choices that will be prompted to the user 
for each variable using the `__prompts__` key.
+- Simply define your template variables in a `cookiecutter.json` file. You can 
also add human-readable questions and choices that will be prompted to the user 
for each variable using the `__prompts__` key. Those human-readable questions 
supports [`rich` markup](https://rich.readthedocs.io/en/stable/markup.html) 
such as `[bold yellow]this is bold and yellow[/]`
   For example:
 
   ```json
@@ -128,10 +128,10 @@
     "version": "0.1.1",
     "linting": ["ruff", "flake8", "none"],
     "__prompts__": {
-      "full_name": "Provide your full name",
-      "email": "Provide your email",
+      "full_name": "Provide your [bold yellow]full name[/]",
+      "email": "Provide your [bold yellow]email[/]",
       "linting": {
-        "__prompt__": "Which linting tool do you want to use?",
+        "__prompt__": "Which [bold yellow]linting tool[/] do you want to use?",
         "ruff": "Ruff",
         "flake8": "Flake8",
         "none": "No linting tool"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/cookiecutter/VERSION.txt 
new/cookiecutter-2.3.0/cookiecutter/VERSION.txt
--- old/cookiecutter-2.2.3/cookiecutter/VERSION.txt     2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/cookiecutter/VERSION.txt     2023-08-03 
22:32:14.000000000 +0200
@@ -1 +1 @@
-2.2.3
+2.3.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/cookiecutter/generate.py 
new/cookiecutter-2.3.0/cookiecutter/generate.py
--- old/cookiecutter-2.2.3/cookiecutter/generate.py     2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/cookiecutter/generate.py     2023-08-03 
22:32:14.000000000 +0200
@@ -55,7 +55,17 @@
 
         context_value = context[variable]
 
-        if isinstance(context_value, list):
+        if isinstance(context_value, list) and isinstance(overwrite, list):
+            # We are dealing with a multichoice variable
+            # Let's confirm all choices are valid for the given context
+            if set(overwrite).issubset(set(context_value)):
+                context[variable] = overwrite
+            else:
+                raise ValueError(
+                    f"{overwrite} provided for multi-choice variable 
{variable}, "
+                    f"but valid choices are {context_value}"
+                )
+        elif 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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/cookiecutter/main.py 
new/cookiecutter-2.3.0/cookiecutter/main.py
--- old/cookiecutter-2.2.3/cookiecutter/main.py 2023-07-11 17:56:39.000000000 
+0200
+++ new/cookiecutter-2.3.0/cookiecutter/main.py 2023-08-03 22:32:14.000000000 
+0200
@@ -110,6 +110,7 @@
         }
         context_for_prompting = {}
         context_for_prompting['cookiecutter'] = items_for_prompting
+        context = context_from_replayfile
         logger.debug('prompting context: %s', context_for_prompting)
     else:
         context = generate_context(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/cookiecutter/prompt.py 
new/cookiecutter-2.3.0/cookiecutter/prompt.py
--- old/cookiecutter-2.2.3/cookiecutter/prompt.py       2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/cookiecutter/prompt.py       2023-08-03 
22:32:14.000000000 +0200
@@ -1,16 +1,15 @@
 """Functions for prompting the user for project info."""
-import functools
 import json
 from collections import OrderedDict
 
-import click
+from rich.prompt import Prompt, Confirm, PromptBase, InvalidResponse
 from jinja2.exceptions import UndefinedError
 
 from cookiecutter.environment import StrictEnvironment
 from cookiecutter.exceptions import UndefinedVariableInTemplate
 
 
-def read_user_variable(var_name, default_value, prompts=None):
+def read_user_variable(var_name, default_value, prompts=None, prefix=""):
     """Prompt user for variable and return the entered value or given default.
 
     :param str var_name: Variable of the context to query the user
@@ -21,10 +20,27 @@
         if prompts and var_name in prompts.keys() and prompts[var_name]
         else var_name
     )
-    return click.prompt(question, default=default_value)
+    return Prompt.ask(f"{prefix}{question}", default=default_value)
 
 
-def read_user_yes_no(var_name, default_value, prompts=None):
+class YesNoPrompt(Confirm):
+    """A prompt that returns a boolean for yes/no questions."""
+
+    yes_choices = ["1", "true", "t", "yes", "y", "on"]
+    no_choices = ["0", "false", "f", "no", "n", "off"]
+
+    def process_response(self, value: str) -> bool:
+        """Convert choices to a bool."""
+        value = value.strip().lower()
+        if value in self.yes_choices:
+            return True
+        elif value in self.no_choices:
+            return False
+        else:
+            raise InvalidResponse(self.validate_error_message)
+
+
+def read_user_yes_no(var_name, default_value, prompts=None, prefix=""):
     """Prompt the user to reply with 'yes' or 'no' (or equivalent values).
 
     - These input values will be converted to ``True``:
@@ -32,7 +48,7 @@
     - These input values will be converted to ``False``:
       "0", "false", "f", "no", "n", "off"
 
-    Actual parsing done by :func:`click.prompt`; Check this function codebase 
change in
+    Actual parsing done by :func:`prompt`; Check this function codebase change 
in
     case of unexpected behaviour.
 
     :param str question: Question to the user
@@ -43,7 +59,7 @@
         if prompts and var_name in prompts.keys() and prompts[var_name]
         else var_name
     )
-    return click.prompt(question, default=default_value, type=click.BOOL)
+    return YesNoPrompt.ask(f"{prefix}{question}", default=default_value)
 
 
 def read_repo_password(question):
@@ -51,10 +67,10 @@
 
     :param str question: Question to the user
     """
-    return click.prompt(question, hide_input=True)
+    return Prompt.ask(question, password=True)
 
 
-def read_user_choice(var_name, options, prompts=None):
+def read_user_choice(var_name, options, prompts=None, prefix=""):
     """Prompt the user to choose from several options for the given variable.
 
     The first item will be returned if no input happens.
@@ -71,9 +87,11 @@
 
     choice_map = OrderedDict((f'{i}', value) for i, value in 
enumerate(options, 1))
     choices = choice_map.keys()
-    default = '1'
+
     question = f"Select {var_name}"
-    choice_lines = ['{} - {}'.format(*c) for c in choice_map.items()]
+    choice_lines = [
+        '    [bold magenta]{}[/] - [bold]{}[/]'.format(*c) for c in 
choice_map.items()
+    ]
 
     # Handle if human-readable prompt is provided
     if prompts and var_name in prompts.keys():
@@ -83,23 +101,21 @@
             if "__prompt__" in prompts[var_name]:
                 question = prompts[var_name]["__prompt__"]
             choice_lines = [
-                f"{i} - {prompts[var_name][p]}"
+                f"    [bold magenta]{i}[/] - [bold]{prompts[var_name][p]}[/]"
                 if p in prompts[var_name]
-                else f"{i} - {p}"
+                else f"    [bold magenta]{i}[/] - [bold]{p}[/]"
                 for i, p in choice_map.items()
             ]
 
     prompt = '\n'.join(
         (
-            f"{question}:",
+            f"{prefix}{question}",
             "\n".join(choice_lines),
-            f"Choose from {', '.join(choices)}",
+            "    Choose from",
         )
     )
 
-    user_choice = click.prompt(
-        prompt, type=click.Choice(choices), default=default, show_choices=False
-    )
+    user_choice = Prompt.ask(prompt, choices=list(choices), 
default=list(choices)[0])
     return choice_map[user_choice]
 
 
@@ -111,24 +127,32 @@
 
     :param str user_value: User-supplied value to load as a JSON dict
     """
-    if user_value == DEFAULT_DISPLAY:
-        # Return the given default w/o any processing
-        return default_value
-
     try:
         user_dict = json.loads(user_value, object_pairs_hook=OrderedDict)
     except Exception as error:
         # Leave it up to click to ask the user again
-        raise click.UsageError('Unable to decode to JSON.') from error
+        raise InvalidResponse('Unable to decode to JSON.') from error
 
     if not isinstance(user_dict, dict):
         # Leave it up to click to ask the user again
-        raise click.UsageError('Requires JSON dict.')
+        raise InvalidResponse('Requires JSON dict.')
 
     return user_dict
 
 
-def read_user_dict(var_name, default_value, prompts=None):
+class JsonPrompt(PromptBase[dict]):
+    """A prompt that returns a dict from JSON string."""
+
+    default = None
+    response_type = dict
+    validate_error_message = "[prompt.invalid]  Please enter a valid JSON 
string"
+
+    def process_response(self, value: str) -> dict:
+        """Convert choices to a dict."""
+        return process_json(value, self.default)
+
+
+def read_user_dict(var_name, default_value, prompts=None, prefix=""):
     """Prompt the user to provide a dictionary of data.
 
     :param str var_name: Variable as specified in the context
@@ -143,16 +167,11 @@
         if prompts and var_name in prompts.keys() and prompts[var_name]
         else var_name
     )
-    user_value = click.prompt(
-        question,
-        default=DEFAULT_DISPLAY,
-        type=click.STRING,
-        value_proc=functools.partial(process_json, 
default_value=default_value),
+    user_value = JsonPrompt.ask(
+        f"{prefix}{question} [cyan bold]({DEFAULT_DISPLAY})[/]",
+        default=default_value,
+        show_default=False,
     )
-
-    if click.__version__.startswith("7.") and user_value == DEFAULT_DISPLAY:
-        # click 7.x does not invoke value_proc on the default value.
-        return default_value  # pragma: no cover
     return user_value
 
 
@@ -193,7 +212,7 @@
 
 
 def prompt_choice_for_config(
-    cookiecutter_dict, env, key, options, no_input, prompts=None
+    cookiecutter_dict, env, key, options, no_input, prompts=None, prefix=""
 ):
     """Prompt user with a set of options to choose from.
 
@@ -202,7 +221,7 @@
     rendered_options = [render_variable(env, raw, cookiecutter_dict) for raw 
in options]
     if no_input:
         return rendered_options[0]
-    return read_user_choice(key, rendered_options, prompts)
+    return read_user_choice(key, rendered_options, prompts, prefix)
 
 
 def prompt_for_config(context, no_input=False):
@@ -222,6 +241,9 @@
     # First pass: Handle simple and raw variables, plus choices.
     # These must be done first because the dictionaries keys and
     # values might refer to them.
+
+    count = 0
+    size = len(context['cookiecutter'].items())
     for key, raw in context['cookiecutter'].items():
         if key.startswith('_') and not key.startswith('__'):
             cookiecutter_dict[key] = raw
@@ -230,11 +252,15 @@
             cookiecutter_dict[key] = render_variable(env, raw, 
cookiecutter_dict)
             continue
 
+        if not isinstance(raw, dict):
+            count += 1
+            prefix = f"  [dim][{count}/{size}][/] "
+
         try:
             if isinstance(raw, list):
                 # We are dealing with a choice variable
                 val = prompt_choice_for_config(
-                    cookiecutter_dict, env, key, raw, no_input, prompts
+                    cookiecutter_dict, env, key, raw, no_input, prompts, prefix
                 )
                 cookiecutter_dict[key] = val
             elif isinstance(raw, bool):
@@ -244,13 +270,13 @@
                         env, raw, cookiecutter_dict
                     )
                 else:
-                    cookiecutter_dict[key] = read_user_yes_no(key, raw, 
prompts)
+                    cookiecutter_dict[key] = read_user_yes_no(key, raw, 
prompts, prefix)
             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, prompts)
+                    val = read_user_variable(key, val, prompts, prefix)
 
                 cookiecutter_dict[key] = val
         except UndefinedError as err:
@@ -266,10 +292,12 @@
         try:
             if isinstance(raw, dict):
                 # We are dealing with a dict variable
+                count += 1
+                prefix = f"  [dim][{count}/{size}][/] "
                 val = render_variable(env, raw, cookiecutter_dict)
 
                 if not no_input and not key.startswith('__'):
-                    val = read_user_dict(key, val, prompts)
+                    val = read_user_dict(key, val, prompts, prefix)
 
                 cookiecutter_dict[key] = val
         except UndefinedError as err:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/setup.py 
new/cookiecutter-2.3.0/setup.py
--- old/cookiecutter-2.2.3/setup.py     2023-07-11 17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/setup.py     2023-08-03 22:32:14.000000000 +0200
@@ -25,6 +25,7 @@
     'python-slugify>=4.0.0',
     'requests>=2.23.0',
     'arrow',
+    'rich',
 ]
 
 setup(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cookiecutter-2.2.3/tests/fake-repo-replay/cookiecutter.json 
new/cookiecutter-2.3.0/tests/fake-repo-replay/cookiecutter.json
--- old/cookiecutter-2.2.3/tests/fake-repo-replay/cookiecutter.json     
1970-01-01 01:00:00.000000000 +0100
+++ new/cookiecutter-2.3.0/tests/fake-repo-replay/cookiecutter.json     
2023-08-03 22:32:14.000000000 +0200
@@ -0,0 +1,11 @@
+{
+  "full_name": "Audrey Roy",
+  "email": "audr...@gmail.com",
+  "github_username": "audreyr",
+  "project_name": "Replay Project",
+  "repo_name": "{{ cookiecutter.project_name|lower|replace(' ', '-') }}",
+  "description": "original",
+  "release_date": "2013-07-28",
+  "year": "2013",
+  "version": "0.1"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cookiecutter-2.2.3/tests/fake-repo-replay/{{cookiecutter.repo_name}}/README.md
 
new/cookiecutter-2.3.0/tests/fake-repo-replay/{{cookiecutter.repo_name}}/README.md
--- 
old/cookiecutter-2.2.3/tests/fake-repo-replay/{{cookiecutter.repo_name}}/README.md
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/cookiecutter-2.3.0/tests/fake-repo-replay/{{cookiecutter.repo_name}}/README.md
  2023-08-03 22:32:14.000000000 +0200
@@ -0,0 +1 @@
+{{cookiecutter.description}}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cookiecutter-2.2.3/tests/test-replay/valid_replay.json 
new/cookiecutter-2.3.0/tests/test-replay/valid_replay.json
--- old/cookiecutter-2.2.3/tests/test-replay/valid_replay.json  1970-01-01 
01:00:00.000000000 +0100
+++ new/cookiecutter-2.3.0/tests/test-replay/valid_replay.json  2023-08-03 
22:32:14.000000000 +0200
@@ -0,0 +1,13 @@
+{
+  "cookiecutter": {
+    "version": "0.1.0",
+    "email": "raph...@hackebrot.de",
+    "full_name": "Raphael Pierzina",
+    "github_username": "hackebrot",
+    "project_name": "Replay Project",
+    "repo_name": "replay-project",
+    "release_date": "2013-07-28",
+    "year": "2013",
+    "description": "replayed"
+  }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_cli.py 
new/cookiecutter-2.3.0/tests/test_cli.py
--- old/cookiecutter-2.2.3/tests/test_cli.py    2023-07-11 17:56:39.000000000 
+0200
+++ new/cookiecutter-2.3.0/tests/test_cli.py    2023-08-03 22:32:14.000000000 
+0200
@@ -38,6 +38,19 @@
 
 
 @pytest.fixture
+def remove_tmp_dir(request):
+    """Remove the fake project directory created during the tests."""
+    if os.path.isdir('tests/tmp'):
+        utils.rmtree('tests/tmp')
+
+    def fin_remove_tmp_dir():
+        if os.path.isdir('tests/tmp'):
+            utils.rmtree('tests/tmp')
+
+    request.addfinalizer(fin_remove_tmp_dir)
+
+
+@pytest.fixture
 def make_fake_project_dir(request):
     """Create a fake project to be overwritten in the according tests."""
     os.makedirs('fake-project')
@@ -139,6 +152,22 @@
     )
 
 
+@pytest.mark.usefixtures('remove_tmp_dir')
+def test_cli_replay_generated(mocker, cli_runner):
+    """Test cli invocation correctly generates a project with replay."""
+    template_path = 'tests/fake-repo-replay/'
+    result = cli_runner(
+        template_path,
+        '--replay-file',
+        'tests/test-replay/valid_replay.json',
+        '-o',
+        'tests/tmp/',
+        '-v',
+    )
+    assert result.exit_code == 0
+    assert open('tests/tmp/replay-project/README.md').read().strip() == 
'replayed'
+
+
 @pytest.mark.usefixtures('remove_fake_project_dir')
 def test_cli_exit_on_noinput_and_replay(mocker, cli_runner):
     """Test cli invocation fail if both `no-input` and `replay` flags 
passed."""
@@ -656,13 +685,3 @@
     # this point.
     path = os.path.sep.join(['tests', 'fake-repo-bad-json', 
'cookiecutter.json'])
     assert path in result.output
-
-
-@pytest.mark.usefixtures('remove_fake_project_dir')
-def test_prompt_when_replyfile_not_full(mocker):
-    """Test execute prompt when replayfile not full."""
-    mock_prompt = mocker.patch('cookiecutter.main.prompt_for_config')
-    cookiecutter(
-        'tests/fake-repo-pre/', 
replay='tests/test-replay/cookiedozer_load.json'
-    )
-    assert mock_prompt.called
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/cookiecutter-2.2.3/tests/test_cookiecutter_local_with_input.py 
new/cookiecutter-2.3.0/tests/test_cookiecutter_local_with_input.py
--- old/cookiecutter-2.2.3/tests/test_cookiecutter_local_with_input.py  
2023-07-11 17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/tests/test_cookiecutter_local_with_input.py  
2023-08-03 22:32:14.000000000 +0200
@@ -21,7 +21,7 @@
     """Verify simple cookiecutter run results, without extra_context 
provided."""
     monkeypatch.setattr(
         'cookiecutter.prompt.read_user_variable',
-        lambda var, default, prompts: default,
+        lambda var, default, prompts, prefix: default,
     )
     main.cookiecutter('tests/fake-repo-pre/', no_input=False)
     assert os.path.isdir('tests/fake-repo-pre/{{cookiecutter.repo_name}}')
@@ -36,7 +36,7 @@
     """Verify simple cookiecutter run results, with extra_context provided."""
     monkeypatch.setattr(
         'cookiecutter.prompt.read_user_variable',
-        lambda var, default, prompts: default,
+        lambda var, default, prompts, prefix: default,
     )
     main.cookiecutter(
         'tests/fake-repo-pre',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_generate_context.py 
new/cookiecutter-2.3.0/tests/test_generate_context.py
--- old/cookiecutter-2.2.3/tests/test_generate_context.py       2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/tests/test_generate_context.py       2023-08-03 
22:32:14.000000000 +0200
@@ -135,6 +135,13 @@
             ('project_name', 'Kivy Project'),
             ('repo_name', '{{cookiecutter.project_name|lower}}'),
             ('orientation', ['all', 'landscape', 'portrait']),
+            (
+                'deployments',
+                {
+                    'preprod': ['eu', 'us', 'ap'],
+                    'prod': ['eu', 'us', 'ap'],
+                },
+            ),
         ]
     )
 
@@ -196,6 +203,36 @@
         )
 
 
+def test_apply_overwrites_sets_multichoice_values(template_context):
+    """Verify variable overwrite for list given multiple valid values."""
+    generate.apply_overwrites_to_context(
+        context=template_context,
+        overwrite_context={'deployments': {'preprod': ['eu'], 'prod': ['eu']}},
+    )
+    assert template_context['deployments']['preprod'] == ['eu']
+    assert template_context['deployments']['prod'] == ['eu']
+
+
+def test_apply_overwrites_invalid_multichoice_values(template_context):
+    """Verify variable overwrite for list given invalid list entries not 
ignored."""
+    with pytest.raises(ValueError):
+        generate.apply_overwrites_to_context(
+            context=template_context,
+            overwrite_context={'deployments': {'preprod': ['na'], 'prod': 
['na']}},
+        )
+
+
+def test_apply_overwrites_error_additional_values(template_context):
+    """Verify variable overwrite for list given additional entries not 
ignored."""
+    with pytest.raises(ValueError):
+        generate.apply_overwrites_to_context(
+            context=template_context,
+            overwrite_context={
+                'deployments': {'preprod': ['eu', 'na'], 'prod': ['eu', 'na']}
+            },
+        )
+
+
 def test_apply_overwrites_sets_default_for_choice_variable(template_context):
     """Verify overwritten list member became a default value."""
     generate.apply_overwrites_to_context(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_main.py 
new/cookiecutter-2.3.0/tests/test_main.py
--- old/cookiecutter-2.2.3/tests/test_main.py   2023-07-11 17:56:39.000000000 
+0200
+++ new/cookiecutter-2.3.0/tests/test_main.py   2023-08-03 22:32:14.000000000 
+0200
@@ -78,6 +78,7 @@
         'cookiecutter': {}
     }
     mocker.patch('cookiecutter.main.generate_files')
+    mocker.patch('cookiecutter.main.dump')
 
     cookiecutter(
         '.',
@@ -100,6 +101,7 @@
         'cookiecutter': {}
     }
     mocker.patch('cookiecutter.main.generate_files')
+    mocker.patch('cookiecutter.main.dump')
 
     cookiecutter(
         '.',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_prompt.py 
new/cookiecutter-2.3.0/tests/test_prompt.py
--- old/cookiecutter-2.2.3/tests/test_prompt.py 2023-07-11 17:56:39.000000000 
+0200
+++ new/cookiecutter-2.3.0/tests/test_prompt.py 2023-08-03 22:32:14.000000000 
+0200
@@ -82,7 +82,7 @@
         """Verify `prompt_for_config` call `read_user_variable` on text 
request."""
         monkeypatch.setattr(
             'cookiecutter.prompt.read_user_variable',
-            lambda var, default, prompts: default,
+            lambda var, default, prompts, prefix: default,
         )
 
         cookiecutter_dict = prompt.prompt_for_config(context)
@@ -109,15 +109,15 @@
         """Verify call `read_user_variable` on request when human-readable 
prompts."""
         monkeypatch.setattr(
             'cookiecutter.prompt.read_user_variable',
-            lambda var, default, prompts: default,
+            lambda var, default, prompts, prefix: default,
         )
         monkeypatch.setattr(
             'cookiecutter.prompt.read_user_yes_no',
-            lambda var, default, prompts: default,
+            lambda var, default, prompts, prefix: default,
         )
         monkeypatch.setattr(
             'cookiecutter.prompt.read_user_choice',
-            lambda var, default, prompts: default,
+            lambda var, default, prompts, prefix: default,
         )
 
         cookiecutter_dict = prompt.prompt_for_config(context)
@@ -169,7 +169,7 @@
         """Verify `prompt_for_config` call `read_user_variable` on dict 
request."""
         monkeypatch.setattr(
             'cookiecutter.prompt.read_user_dict',
-            lambda var, default, prompts: {"key": "value", "integer": 37},
+            lambda var, default, prompts, prefix: {"key": "value", "integer": 
37},
         )
         context = {'cookiecutter': {'details': {}}}
 
@@ -284,7 +284,7 @@
         """Verify Jinja2 templating works in unicode prompts."""
         monkeypatch.setattr(
             'cookiecutter.prompt.read_user_variable',
-            lambda var, default, prompts: default,
+            lambda var, default, prompts, prefix: default,
         )
         context = {
             'cookiecutter': OrderedDict(
@@ -373,6 +373,9 @@
         assert cookiecutter_dict == context['cookiecutter']
 
 
+DEFAULT_PREFIX = '  [dim][1/1][/] '
+
+
 class TestReadUserChoice:
     """Class to unite choices prompt related tests."""
 
@@ -395,7 +398,9 @@
 
         assert not read_user_variable.called
         assert prompt_choice.called
-        read_user_choice.assert_called_once_with('orientation', choices, {})
+        read_user_choice.assert_called_once_with(
+            'orientation', choices, {}, DEFAULT_PREFIX
+        )
         assert cookiecutter_dict == {'orientation': 'all'}
 
     def test_should_invoke_read_user_variable(self, mocker):
@@ -413,7 +418,9 @@
 
         assert not prompt_choice.called
         assert not read_user_choice.called
-        read_user_variable.assert_called_once_with('full_name', 'Your Name', 
{})
+        read_user_variable.assert_called_once_with(
+            'full_name', 'Your Name', {}, DEFAULT_PREFIX
+        )
         assert cookiecutter_dict == {'full_name': 'Audrey Roy'}
 
     def test_should_render_choices(self, mocker):
@@ -448,8 +455,12 @@
         }
         cookiecutter_dict = prompt.prompt_for_config(context)
 
-        read_user_variable.assert_called_once_with('project_name', 'A New 
Project', {})
-        read_user_choice.assert_called_once_with('pkg_name', rendered_choices, 
{})
+        read_user_variable.assert_called_once_with(
+            'project_name', 'A New Project', {}, '  [dim][1/2][/] '
+        )
+        read_user_choice.assert_called_once_with(
+            'pkg_name', rendered_choices, {}, '  [dim][2/2][/] '
+        )
         assert cookiecutter_dict == expected
 
 
@@ -497,7 +508,7 @@
             options=choices,
             no_input=False,  # Ask the user for input
         )
-        read_user_choice.assert_called_once_with('orientation', choices, None)
+        read_user_choice.assert_called_once_with('orientation', choices, None, 
'')
         assert expected_choice == actual_choice
 
 
@@ -523,7 +534,9 @@
         cookiecutter_dict = prompt.prompt_for_config(context)
 
         assert not read_user_variable.called
-        read_user_yes_no.assert_called_once_with('run_as_docker', 
run_as_docker, {})
+        read_user_yes_no.assert_called_once_with(
+            'run_as_docker', run_as_docker, {}, DEFAULT_PREFIX
+        )
         assert cookiecutter_dict == {'run_as_docker': run_as_docker}
 
     def test_boolean_parameter_no_input(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_read_repo_password.py 
new/cookiecutter-2.3.0/tests/test_read_repo_password.py
--- old/cookiecutter-2.2.3/tests/test_read_repo_password.py     2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/tests/test_read_repo_password.py     2023-08-03 
22:32:14.000000000 +0200
@@ -7,9 +7,9 @@
 
     Test for password (hidden input) type invocation.
     """
-    prompt = mocker.patch('click.prompt')
+    prompt = mocker.patch('rich.prompt.Prompt.ask')
     prompt.return_value = 'sekrit'
 
     assert read_repo_password('Password') == 'sekrit'
 
-    prompt.assert_called_once_with('Password', hide_input=True)
+    prompt.assert_called_once_with('Password', password=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_read_user_choice.py 
new/cookiecutter-2.3.0/tests/test_read_user_choice.py
--- old/cookiecutter-2.2.3/tests/test_read_user_choice.py       2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/tests/test_read_user_choice.py       2023-08-03 
22:32:14.000000000 +0200
@@ -1,17 +1,17 @@
 """Tests around prompting for and handling of choice variables."""
-import click
 import pytest
 
 from cookiecutter.prompt import read_user_choice
 
 OPTIONS = ['hello', 'world', 'foo', 'bar']
+OPTIONS_INDEX = ['1', '2', '3', '4']
 
-EXPECTED_PROMPT = """Select varname:
-1 - hello
-2 - world
-3 - foo
-4 - bar
-Choose from 1, 2, 3, 4"""
+EXPECTED_PROMPT = """Select varname
+    [bold magenta]1[/] - [bold]hello[/]
+    [bold magenta]2[/] - [bold]world[/]
+    [bold magenta]3[/] - [bold]foo[/]
+    [bold magenta]4[/] - [bold]bar[/]
+    Choose from"""
 
 
 @pytest.mark.parametrize('user_choice, expected_value', enumerate(OPTIONS, 1))
@@ -20,17 +20,12 @@
 
     Test for choice type invocation.
     """
-    choice = mocker.patch('click.Choice')
-    choice.return_value = click.Choice(OPTIONS)
-
-    prompt = mocker.patch('click.prompt')
+    prompt = mocker.patch('rich.prompt.Prompt.ask')
     prompt.return_value = f'{user_choice}'
 
     assert read_user_choice('varname', OPTIONS) == expected_value
 
-    prompt.assert_called_once_with(
-        EXPECTED_PROMPT, type=click.Choice(OPTIONS), default='1', 
show_choices=False
-    )
+    prompt.assert_called_once_with(EXPECTED_PROMPT, choices=OPTIONS_INDEX, 
default='1')
 
 
 def test_raise_if_options_is_not_a_non_empty_list():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_read_user_dict.py 
new/cookiecutter-2.3.0/tests/test_read_user_dict.py
--- old/cookiecutter-2.2.3/tests/test_read_user_dict.py 2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/tests/test_read_user_dict.py 2023-08-03 
22:32:14.000000000 +0200
@@ -2,15 +2,13 @@
 import click
 import pytest
 
-from cookiecutter.prompt import (
-    process_json,
-    read_user_dict,
-)
+from cookiecutter.prompt import process_json, read_user_dict, JsonPrompt
+from rich.prompt import InvalidResponse
 
 
 def test_process_json_invalid_json():
     """Test `process_json` for correct error on malformed input."""
-    with pytest.raises(click.UsageError) as exc_info:
+    with pytest.raises(InvalidResponse) as exc_info:
         process_json('nope]')
 
     assert str(exc_info.value) == 'Unable to decode to JSON.'
@@ -18,7 +16,7 @@
 
 def test_process_json_non_dict():
     """Test `process_json` for correct error on non-JSON input."""
-    with pytest.raises(click.UsageError) as exc_info:
+    with pytest.raises(InvalidResponse) as exc_info:
         process_json('[1, 2]')
 
     assert str(exc_info.value) == 'Requires JSON dict.'
@@ -75,11 +73,10 @@
 
 def test_should_raise_type_error(mocker):
     """Test `default_value` arg verification in `read_user_dict` function."""
-    prompt = mocker.patch('cookiecutter.prompt.click.prompt')
+    prompt = mocker.patch('cookiecutter.prompt.JsonPrompt.ask')
 
     with pytest.raises(TypeError):
         read_user_dict('name', 'russell')
-
     assert not prompt.called
 
 
@@ -88,16 +85,14 @@
 
     Verifies generation of a processor for the user input.
     """
-    mock_prompt = mocker.patch('cookiecutter.prompt.click.prompt', 
autospec=True)
+    mock_prompt = mocker.patch('cookiecutter.prompt.JsonPrompt.ask', 
autospec=True)
 
     read_user_dict('name', {'project_slug': 'pytest-plugin'})
-
+    print(mock_prompt.call_args)
     args, kwargs = mock_prompt.call_args
 
-    assert args == ('name',)
-    assert kwargs['type'] == click.STRING
-    assert kwargs['default'] == 'default'
-    assert kwargs['value_proc'].func == process_json
+    assert args == ('name [cyan bold](default)[/]',)
+    assert kwargs['default'] == {'project_slug': 'pytest-plugin'}
 
 
 def test_should_not_load_json_from_sentinel(mocker):
@@ -113,7 +108,7 @@
     mock_json_loads.assert_not_called()
 
 
-@pytest.mark.parametrize("input", ["\n", "default\n"])
+@pytest.mark.parametrize("input", ["\n", "\ndefault\n"])
 def test_read_user_dict_default_value(mocker, input):
     """Make sure that `read_user_dict` returns the default value.
 
@@ -124,3 +119,11 @@
         val = read_user_dict('name', {'project_slug': 'pytest-plugin'})
 
     assert val == {'project_slug': 'pytest-plugin'}
+
+
+def test_json_prompt_process_response():
+    """Test `JsonPrompt` process_response to convert str to json."""
+    jp = JsonPrompt()
+    assert jp.process_response('{"project_slug": "something"}') == {
+        'project_slug': 'something'
+    }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_read_user_variable.py 
new/cookiecutter-2.3.0/tests/test_read_user_variable.py
--- old/cookiecutter-2.2.3/tests/test_read_user_variable.py     2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/tests/test_read_user_variable.py     2023-08-03 
22:32:14.000000000 +0200
@@ -1,6 +1,4 @@
 """test_read_user_variable."""
-import click
-
 from cookiecutter.prompt import read_user_variable
 
 VARIABLE = 'project_name'
@@ -12,9 +10,9 @@
 
     Test for string type invocation.
     """
-    prompt = mocker.patch('click.prompt')
+    prompt = mocker.patch('rich.prompt.Prompt.ask')
     prompt.return_value = DEFAULT
 
     assert read_user_variable(VARIABLE, DEFAULT) == DEFAULT
 
-    click.prompt.assert_called_once_with(VARIABLE, default=DEFAULT)
+    prompt.assert_called_once_with(VARIABLE, default=DEFAULT)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cookiecutter-2.2.3/tests/test_read_user_yes_no.py 
new/cookiecutter-2.3.0/tests/test_read_user_yes_no.py
--- old/cookiecutter-2.2.3/tests/test_read_user_yes_no.py       2023-07-11 
17:56:39.000000000 +0200
+++ new/cookiecutter-2.3.0/tests/test_read_user_yes_no.py       2023-08-03 
22:32:14.000000000 +0200
@@ -1,7 +1,9 @@
 """test_read_user_yes_no."""
-import click
+import pytest
 
-from cookiecutter.prompt import read_user_yes_no
+from rich.prompt import InvalidResponse
+
+from cookiecutter.prompt import read_user_yes_no, YesNoPrompt
 
 QUESTION = 'Is it okay to delete and re-clone it?'
 DEFAULT = 'y'
@@ -12,9 +14,18 @@
 
     Test for boolean type invocation.
     """
-    prompt = mocker.patch('click.prompt')
+    prompt = mocker.patch('cookiecutter.prompt.YesNoPrompt.ask')
     prompt.return_value = DEFAULT
 
     assert read_user_yes_no(QUESTION, DEFAULT) == DEFAULT
 
-    click.prompt.assert_called_once_with(QUESTION, default=DEFAULT, 
type=click.BOOL)
+    prompt.assert_called_once_with(QUESTION, default=DEFAULT)
+
+
+def test_yesno_prompt_process_response():
+    """Test `YesNoPrompt` process_response to convert str to bool."""
+    ynp = YesNoPrompt()
+    with pytest.raises(InvalidResponse):
+        ynp.process_response('wrong')
+    assert ynp.process_response('t') is True
+    assert ynp.process_response('f') is False

Reply via email to