Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

copier update reformats YAML from answers file #1888

Open
lkubb opened this issue Dec 13, 2024 · 0 comments
Open

copier update reformats YAML from answers file #1888

lkubb opened this issue Dec 13, 2024 · 0 comments
Labels
bug triage Trying to make sure if this is valid or not

Comments

@lkubb
Copy link
Contributor

lkubb commented Dec 13, 2024

Describe the problem

One of my templates has a type: yaml question for configuration + defaults of the generated project. Since this input is used to render documentation, its order is somewhat significant (more important keys first).

I wrote a custom yaml filter to keep the input order (+ some other tweaks) when dumping .copier-answers.yml, which works perfectly for recopy and update with --skip-answered. However, when omitting --skip-answered during updates, the dumped YAML for the question prompt is sorted automatically, destroying the order in the answers file.

Template

set -exo pipefail

mkdir repro
cd repro

<< 'EOF' > copier.yml
settings:
  type: yaml
  multiline: true

_jinja_extensions:
  - copier_templates_extensions.TemplateExtensionLoader
  - dumper.py:YamlDumper
EOF

<< 'EOF' > dumper.py
import yaml
from jinja2.ext import Extension


def represent_str(dumper, data):
    """
    Represent multiline strings using "|"
    """
    if len(data.splitlines()) > 1:
        return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
    return dumper.represent_scalar("tag:yaml.org,2002:str", data)


class OpinionatedYamlDumper(yaml.SafeDumper):
    """
    Indent lists by two spaces
    """

    def increase_indent(self, flow=False, indentless=False):
        return super().increase_indent(flow=flow, indentless=False)


OpinionatedYamlDumper.add_representer(str, represent_str)


class YamlDumper(Extension):
    def __init__(self, environment):
        super().__init__(environment)
        environment.filters["yaml"] = dump_yaml


def dump_yaml(data, flow_style=False, indent=0, sort_keys=False):
    ret = yaml.dump(
        data,
        Dumper=OpinionatedYamlDumper,
        indent=indent,
        default_flow_style=flow_style,
        canonical=False,
        sort_keys=sort_keys,
    )
    if ret.endswith("...\n"):
        ret = ret[:-4]
    return ret.strip()
EOF

<< 'EOF' > doc.md.jinja
{%- for key, val in settings.items() %}
* {{ key }}: {{ val }}
{%- endfor %}
EOF

<< 'EOF' > "{{ _copier_conf.answers_file }}.jinja"
# Autogenerated. Do not edit this by hand, use 'copier update'.
---
{{ _copier_answers | yaml }}
EOF

git init
git add .
git commit -m init
git tag 1.0.0

To Reproduce

From where we left off above:

cd ..
copier copy --trust --data settings='foo: foo
bar: bar' $(pwd)/repro foo
cd foo
git init
git add .
git commit -m init
cat doc.md
# * foo: foo
# * bar: bar
copier update --trust
# just accept the default via alt+enter / esc,enter
git diff -p

Even though I accepted the previous value verbatim, this prints:

diff --git a/.copier-answers.yml b/.copier-answers.yml
index a8a6d42..7434558 100644
--- a/.copier-answers.yml
+++ b/.copier-answers.yml
@@ -3,5 +3,5 @@
 _commit: 1.0.0
 _src_path: /Users/jeanluc/tmp/reprotemp/repro
 settings:
-  foo: foo
   bar: bar
+  foo: foo
diff --git a/doc.md b/doc.md
index 98bf876..c64a7c6 100644
--- a/doc.md
+++ b/doc.md
@@ -1,3 +1,3 @@
 
-* foo: foo
 * bar: bar
+* foo: foo

Logs

No response

Expected behavior

The default value in the question should not modify the order that is found in the answers file.

I would prefer if Copier kept the formatting exactly as dumped into the answers file, but this would be a bit more involved than just setting sort_keys=False in yaml.dumps and can be recovered.

Screenshots/screencasts/logs

No response

Operating system

macOS

Operating system distribution and version

Sequoia 15.1.1

Copier version

copier 9.4.1

Python version

CPython 3.13

Installation method

pipx+pypi (actually uv+pypi)

Additional context

As mentioned, this could be solved by just setting sort_keys=False here:

copier/copier/user_data.py

Lines 283 to 285 in 2dc1687

return yaml.safe_dump(
default, default_flow_style=not self.get_multiline(), width=2147483647
).strip()

I think this should be the default behavior since the current one encodes information in a lossy way.

@lkubb lkubb added bug triage Trying to make sure if this is valid or not labels Dec 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug triage Trying to make sure if this is valid or not
Projects
None yet
Development

No branches or pull requests

1 participant