Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion copier/_user_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ class Question:
Text that appears if there's nothing written in the input field,
but disappears as soon as the user writes anything. Can be templated.

qmark:
Custom emoji or mark to display before the question. If not specified,
defaults to 🎤 for regular questions and 🕵️ for secret questions.

secret:
Indicates if the question should be removed from the answers file.
If the question type is str, it will hide user input on the screen
Expand Down Expand Up @@ -212,6 +216,7 @@ class Question:
help: str = ""
multiline: str | bool = False
placeholder: str = ""
qmark: str | None = None
secret: bool = False
type: str = Field(default="", validate_default=True)
validator: str = ""
Expand Down Expand Up @@ -399,7 +404,7 @@ def _validate(answer: str) -> str | Literal[True]:
"message": self.get_message(),
"mouse_support": True,
"name": self.var_name,
"qmark": "🕵️" if self.secret else "🎤",
"qmark": self.qmark or ("🕵️" if self.secret else "🎤"),
"when": lambda _: self.get_when(),
}
default = self.get_default_rendered()
Expand Down
13 changes: 13 additions & 0 deletions docs/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ Supported keys:
Multiline placeholders are not supported currently, due to
[this upstream bug](https://github.com/prompt-toolkit/python-prompt-toolkit/issues/1267).

- **qmark**: Custom emoji or mark to display before the question. If not specified,
defaults to 🎤 for regular questions and 🕵️ for secret questions. This is useful for
customizing the visual appearance of your prompts.

!!! example

```yaml title="copier.yml"
love_copier:
type: bool
qmark: "❤️ "
help: Do you love Copier?
```

- **multiline**: When set to `true`, it allows multiline input. This is especially
useful when `type` is `json` or `yaml`.

Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def _spawn(
# instead. However, it's working fine and it seems easier to fix in the
# future to work on Windows (where, this way, spawning actually works; it's just
# python-prompt-toolkit that rejects displaying a TUI)
return PopenSpawn(cmd, timeout, logfile=sys.stderr.buffer)
return PopenSpawn(cmd, timeout, logfile=sys.stderr, encoding="utf-8")

return _spawn

Expand Down
85 changes: 85 additions & 0 deletions tests/test_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,91 @@ def test_placeholder(tmp_path_factory: pytest.TempPathFactory, spawn: Spawn) ->
}


def test_qmark(tmp_path_factory: pytest.TempPathFactory, spawn: Spawn) -> None:
"""Test that custom qmark parameter is displayed for questions."""
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): yaml.dump(
{
"_envops": BRACKET_ENVOPS,
"_templates_suffix": SUFFIX_TMPL,
"favorite_color": {
"type": "str",
"default": "blue",
"qmark": "❤️",
},
"api_key": {
"type": "str",
"default": "secret123",
"secret": True,
"qmark": "🔐",
},
},
sort_keys=False,
),
(src / "[[ _copier_conf.answers_file ]].tmpl"): (
"[[ _copier_answers|to_nice_yaml ]]"
),
}
)
tui = spawn(COPIER_PATH + ("copy", str(src), str(dst)))
tui.expect_exact("❤️")
tui.expect_exact("favorite_color")
tui.expect_exact("blue")
tui.sendline()
tui.expect_exact("🔐")
tui.expect_exact("api_key")
tui.sendline()
tui.expect_exact(pexpect.EOF)
answers = load_answersfile_data(dst)
assert answers == {
"_src_path": str(src),
"favorite_color": "blue",
}


def test_qmark_default(tmp_path_factory: pytest.TempPathFactory, spawn: Spawn) -> None:
"""Test that default qmark emojis are used when qmark is not specified."""
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): yaml.dump(
{
"_envops": BRACKET_ENVOPS,
"_templates_suffix": SUFFIX_TMPL,
"name": {
"type": "str",
"default": "John",
},
"password": {
"type": "str",
"default": "secret",
"secret": True,
},
}
),
(src / "[[ _copier_conf.answers_file ]].tmpl"): (
"[[ _copier_answers|to_nice_yaml ]]"
),
}
)
tui = spawn(COPIER_PATH + ("copy", str(src), str(dst)))
tui.expect_exact("🎤")
tui.expect_exact("name")
tui.expect_exact("John")
tui.sendline()
tui.expect_exact("🕵️")
tui.expect_exact("password")
tui.sendline()
tui.expect_exact(pexpect.EOF)
answers = load_answersfile_data(dst)
assert answers == {
"_src_path": str(src),
"name": "John",
}


@pytest.mark.parametrize("type_", ["str", "yaml", "json"])
def test_multiline(
tmp_path_factory: pytest.TempPathFactory, spawn: Spawn, type_: str
Expand Down
Loading