mirror of
https://github.com/copier-org/copier.git
synced 2025-05-05 15:32:54 +00:00
Merge pull request #54 from pykong/cleanup
Cleanup and move to Python 3.6
This commit is contained in:
commit
9b8ce8ad17
@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||
|
||||
|
||||
### Version 3.x (2019-xx)
|
||||
- Dropped support for Python 3.5.
|
||||
- Dropped support for deprecated `voodoo.json`.
|
||||
- Type annotated entire code base.
|
||||
|
||||
|
||||
### Version 2.5 (2019-06)
|
||||
- Expanduser on all paths (so "~/foo/bar" is expanded to "<YOUR_HOME_FOLDER>/foo/bar").
|
||||
- Improve the output when running tasks.
|
||||
|
@ -1,8 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import sys
|
||||
from hashlib import sha512
|
||||
from os import urandom
|
||||
|
||||
try:
|
||||
from .main import copy
|
||||
@ -73,20 +70,9 @@ parser.add_argument(
|
||||
|
||||
|
||||
def run() -> None: # pragma:no cover
|
||||
if len(sys.argv) == 1 or sys.argv[1] == "help":
|
||||
parser.print_help(sys.stderr)
|
||||
print()
|
||||
sys.exit(1)
|
||||
|
||||
if sys.argv[1] == "version":
|
||||
sys.stdout.write(__version__)
|
||||
print()
|
||||
sys.exit(1)
|
||||
|
||||
args = parser.parse_args()
|
||||
kwargs = vars(args)
|
||||
data = {"make_secret": lambda: sha512(urandom(48)).hexdigest()}
|
||||
copy(kwargs.pop("source"), kwargs.pop("dest"), data=data, **kwargs)
|
||||
copy(kwargs.pop("source"), kwargs.pop("dest"), **kwargs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -4,15 +4,26 @@ import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from hashlib import sha512
|
||||
from os import urandom
|
||||
from pathlib import Path
|
||||
from typing import Callable, Dict, List, Optional, Tuple
|
||||
|
||||
from . import vcs
|
||||
from .tools import (STYLE_DANGER, STYLE_IGNORE, STYLE_OK, STYLE_WARNING,
|
||||
Renderer, copy_file, get_jinja_renderer, get_name_filters,
|
||||
make_folder, printf, prompt_bool)
|
||||
from .types import (AnyByStrDict, CheckPathFunc, OptStrOrPathSeq, OptStrSeq,
|
||||
StrOrPath)
|
||||
from .tools import (
|
||||
STYLE_DANGER,
|
||||
STYLE_IGNORE,
|
||||
STYLE_OK,
|
||||
STYLE_WARNING,
|
||||
Renderer,
|
||||
copy_file,
|
||||
get_jinja_renderer,
|
||||
get_name_filters,
|
||||
make_folder,
|
||||
printf,
|
||||
prompt_bool,
|
||||
)
|
||||
from .types import AnyByStrDict, CheckPathFunc, OptStrOrPathSeq, OptStrSeq, StrOrPath
|
||||
from .user_data import load_config_data, query_user_data
|
||||
|
||||
__all__ = ("copy", "copy_local")
|
||||
@ -24,7 +35,6 @@ DEFAULT_EXCLUDE: Tuple[str, ...] = (
|
||||
"copier.yml",
|
||||
"copier.toml",
|
||||
"copier.json",
|
||||
"voodoo.json",
|
||||
"~*",
|
||||
"*.py[co]",
|
||||
"__pycache__",
|
||||
@ -36,7 +46,10 @@ DEFAULT_EXCLUDE: Tuple[str, ...] = (
|
||||
)
|
||||
|
||||
DEFAULT_INCLUDE: Tuple[str, ...] = ()
|
||||
DEFAULT_DATA: AnyByStrDict = {"now": datetime.datetime.utcnow}
|
||||
DEFAULT_DATA: AnyByStrDict = {
|
||||
"now": datetime.datetime.utcnow,
|
||||
"make_secret": lambda: sha512(urandom(48)).hexdigest(),
|
||||
}
|
||||
|
||||
|
||||
def copy(
|
||||
@ -54,7 +67,7 @@ def copy(
|
||||
force: bool = False,
|
||||
skip: bool = False,
|
||||
quiet: bool = False,
|
||||
cleanup_on_error: bool = True
|
||||
cleanup_on_error: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Uses the template in src_path to generate a new project at dst_path.
|
||||
@ -144,12 +157,11 @@ def copy(
|
||||
except Exception:
|
||||
if cleanup_on_error:
|
||||
print("Something went wrong. Removing destination folder.")
|
||||
# Python3.5 shutil methods doesn't wok with `pathlib.Path`
|
||||
shutil.rmtree(str(dst_path), ignore_errors=True)
|
||||
shutil.rmtree(dst_path, ignore_errors=True)
|
||||
raise
|
||||
finally:
|
||||
if repo:
|
||||
shutil.rmtree(str(src_path))
|
||||
shutil.rmtree(src_path)
|
||||
|
||||
|
||||
RE_TMPL = re.compile(r"\.tmpl$", re.IGNORECASE)
|
||||
@ -190,7 +202,7 @@ def copy_local(
|
||||
skip_if_exists: OptStrOrPathSeq = None,
|
||||
tasks: OptStrSeq = None,
|
||||
envops: Optional[AnyByStrDict] = None,
|
||||
**flags: bool
|
||||
**flags: bool,
|
||||
) -> None:
|
||||
src_path, dst_path, extra_paths = resolve_paths(src_path, dst_path, extra_paths)
|
||||
config_data = load_config_data(src_path, quiet=flags["quiet"])
|
||||
@ -347,7 +359,6 @@ def file_is_identical(
|
||||
) -> bool:
|
||||
if content is None:
|
||||
return files_are_identical(source_path, final_path)
|
||||
|
||||
return file_has_this_content(final_path, content)
|
||||
|
||||
|
||||
@ -369,7 +380,7 @@ def overwrite_file(
|
||||
if flags["skip"]:
|
||||
return False
|
||||
|
||||
msg = " Overwrite {}?".format(final_path) # pragma:no cover
|
||||
msg = f" Overwrite {final_path}?" # pragma:no cover
|
||||
return prompt_bool(msg, default=True) # pragma:no cover
|
||||
|
||||
|
||||
@ -377,7 +388,5 @@ def run_tasks(dst_path: StrOrPath, engine: Renderer, tasks) -> None:
|
||||
dst_path = str(dst_path)
|
||||
for i, task in enumerate(tasks):
|
||||
task = engine.string(task)
|
||||
printf(
|
||||
" > Running task {} of {}".format(i + 1, len(tasks)), task, style=STYLE_OK
|
||||
)
|
||||
printf(f" > Running task {i + 1} of {len(tasks)}", task, style=STYLE_OK)
|
||||
subprocess.run(task, shell=True, check=True, cwd=dst_path)
|
||||
|
@ -31,6 +31,9 @@ STYLE_WARNING: List[int] = [Fore.YELLOW, Style.BRIGHT]
|
||||
STYLE_IGNORE: List[int] = [Fore.CYAN]
|
||||
STYLE_DANGER: List[int] = [Fore.RED, Style.BRIGHT]
|
||||
|
||||
INDENT = " " * 2
|
||||
HLINE = "-" * 42
|
||||
|
||||
|
||||
def printf(
|
||||
action: str, msg: str = "", style: Optional[List[int]] = None, indent: int = 10
|
||||
@ -39,7 +42,7 @@ def printf(
|
||||
if not style:
|
||||
return action + msg
|
||||
|
||||
out = style + [action, Fore.RESET, Style.RESET_ALL, " ", msg] # type: ignore
|
||||
out = style + [action, Fore.RESET, Style.RESET_ALL, INDENT, msg] # type: ignore
|
||||
print(*out, sep="")
|
||||
return None # HACK: Satisfy MyPy
|
||||
|
||||
@ -55,9 +58,9 @@ def printf_block(
|
||||
if not quiet:
|
||||
print("")
|
||||
printf(action, msg=msg, style=style, indent=indent)
|
||||
print("-" * 42)
|
||||
print(HLINE)
|
||||
print(e)
|
||||
print("-" * 42)
|
||||
print(HLINE)
|
||||
|
||||
|
||||
no_value: object = object()
|
||||
@ -74,7 +77,7 @@ def prompt(
|
||||
default: Optional[Any] = no_value,
|
||||
default_show: Optional[Any] = None,
|
||||
validator: Callable = required,
|
||||
**kwargs: AnyByStrDict
|
||||
**kwargs: AnyByStrDict,
|
||||
) -> Optional[Any]:
|
||||
"""
|
||||
Prompt for a value from the command line. A default value can be provided,
|
||||
@ -85,9 +88,9 @@ def prompt(
|
||||
printed and the user asked to supply another value.
|
||||
"""
|
||||
if default_show:
|
||||
question += " [{}] ".format(default_show)
|
||||
question += f" [{default_show}] "
|
||||
elif default and default is not no_value:
|
||||
question += " [{}] ".format(default)
|
||||
question += f" [{default}] "
|
||||
else:
|
||||
question += " "
|
||||
|
||||
@ -114,13 +117,13 @@ def prompt_bool(
|
||||
yes_choices: Optional[List[str]] = None,
|
||||
no_choices: Optional[List[str]] = None,
|
||||
) -> Optional[bool]:
|
||||
# Backwards compatibility. Remove for version 3.0
|
||||
# TODO: Backwards compatibility. Remove for version 3.0
|
||||
if yes_choices:
|
||||
yes = yes_choices[0]
|
||||
if no_choices:
|
||||
no = no_choices[0]
|
||||
|
||||
please_answer = ' Please answer "{}" or "{}"'.format(yes, no)
|
||||
please_answer = f' Please answer "{yes}" or "{no}"'
|
||||
|
||||
def validator(value: Union[str, bool], **kwargs) -> Union[str, bool]:
|
||||
if value:
|
||||
@ -134,13 +137,13 @@ def prompt_bool(
|
||||
|
||||
if default is None:
|
||||
default = no_value
|
||||
default_show = yes + "/" + no
|
||||
default_show = f"{yes}/{no}"
|
||||
elif default:
|
||||
default = yes
|
||||
default_show = yes.upper() + "/" + no
|
||||
default_show = f"{yes.upper()}/{no}"
|
||||
else:
|
||||
default = no
|
||||
default_show = yes + "/" + no.upper()
|
||||
default_show = f"{yes}/{no.upper()}"
|
||||
|
||||
return prompt(
|
||||
question, default=default, default_show=default_show, validator=validator
|
||||
@ -173,14 +176,14 @@ DEFAULT_ENV_OPTIONS: AnyByStrDict = {
|
||||
|
||||
class Renderer:
|
||||
def __init__(
|
||||
self, env: SandboxedEnvironment, src_path: str, data: AnyByStrDict
|
||||
self, env: SandboxedEnvironment, src_path: Path, data: AnyByStrDict
|
||||
) -> None:
|
||||
self.env = env
|
||||
self.src_path = src_path
|
||||
self.data = data
|
||||
|
||||
def __call__(self, fullpath: StrOrPath) -> str:
|
||||
relpath = str(fullpath).replace(self.src_path, "", 1).lstrip(os.path.sep)
|
||||
relpath = str(fullpath).replace(str(self.src_path), "", 1).lstrip(os.path.sep)
|
||||
tmpl = self.env.get_template(relpath)
|
||||
return tmpl.render(**self.data)
|
||||
|
||||
@ -198,11 +201,10 @@ def get_jinja_renderer(
|
||||
"""Returns a function that can render a Jinja template.
|
||||
"""
|
||||
# Jinja <= 2.10 does not work with `pathlib.Path`s
|
||||
_src_path: str = str(src_path)
|
||||
_envops = DEFAULT_ENV_OPTIONS.copy()
|
||||
_envops.update(envops or {})
|
||||
|
||||
paths = [_src_path] + [str(p) for p in extra_paths or []]
|
||||
paths = [src_path] + [Path(p) for p in extra_paths or []]
|
||||
_envops.setdefault("loader", FileSystemLoader(paths)) # type: ignore
|
||||
|
||||
# We want to minimize the risk of hidden malware in the templates
|
||||
@ -210,7 +212,7 @@ def get_jinja_renderer(
|
||||
# Of couse we still have the post-copy tasks to worry about, but at least
|
||||
# they are more visible to the final user.
|
||||
env = SandboxedEnvironment(**_envops)
|
||||
return Renderer(env=env, src_path=_src_path, data=data)
|
||||
return Renderer(env=env, src_path=src_path, data=data)
|
||||
|
||||
|
||||
def normalize_str(text: StrOrPath, form: str = "NFD") -> str:
|
||||
|
@ -1,7 +1,6 @@
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, Optional, Sequence, TypeVar, Union
|
||||
|
||||
IntOrStr = Union[int, str]
|
||||
StrOrPath = Union[str, Path]
|
||||
|
||||
AnyByStrDict = Dict[str, Any]
|
||||
|
@ -1,12 +1,10 @@
|
||||
from pathlib import Path
|
||||
|
||||
from .tools import STYLE_WARNING, printf, printf_block, prompt
|
||||
from .tools import HLINE, INDENT, printf_block, prompt
|
||||
from .types import AnyByStrDict, StrOrPath
|
||||
|
||||
__all__ = ("load_config_data", "query_user_data")
|
||||
|
||||
INDENT = " "
|
||||
|
||||
|
||||
def load_toml_data(src_path: StrOrPath, quiet: bool = False) -> AnyByStrDict:
|
||||
toml_path = Path(src_path) / "copier.toml"
|
||||
@ -46,36 +44,8 @@ def load_json_data(
|
||||
) -> AnyByStrDict:
|
||||
json_path = Path(src_path) / "copier.json"
|
||||
if not json_path.exists():
|
||||
return load_old_json_data(src_path, quiet=quiet, _warning=_warning)
|
||||
|
||||
import json
|
||||
|
||||
json_src = json_path.read_text()
|
||||
try:
|
||||
return json.loads(json_src)
|
||||
except ValueError as e:
|
||||
printf_block(e, "INVALID", msg=str(json_path), quiet=quiet)
|
||||
return {}
|
||||
|
||||
|
||||
def load_old_json_data(
|
||||
src_path: StrOrPath, quiet: bool = False, _warning: bool = True
|
||||
) -> AnyByStrDict:
|
||||
# TODO: Remove on version 3.0
|
||||
json_path = Path(src_path) / "voodoo.json"
|
||||
if not json_path.exists():
|
||||
return {}
|
||||
|
||||
if _warning and not quiet:
|
||||
print("")
|
||||
printf(
|
||||
"WARNING",
|
||||
msg="`voodoo.json` is deprecated. "
|
||||
+ "Replace it with a `copier.yaml`, `copier.toml`, or `copier.json`.",
|
||||
style=STYLE_WARNING,
|
||||
indent=10,
|
||||
)
|
||||
|
||||
import json
|
||||
|
||||
json_src = json_path.read_text()
|
||||
@ -110,7 +80,7 @@ def query_user_data(default_user_data: AnyByStrDict) -> AnyByStrDict: # pragma:
|
||||
user_data = {}
|
||||
for key in default_user_data:
|
||||
default = default_user_data[key]
|
||||
user_data[key] = prompt(INDENT + " {0}?".format(key), default)
|
||||
user_data[key] = prompt(INDENT + f" {key}?", default)
|
||||
|
||||
print("\n" + INDENT + "-" * 42)
|
||||
print(f"\n {INDENT} {HLINE}")
|
||||
return user_data
|
||||
|
@ -1,4 +0,0 @@
|
||||
A string: [[ a_string ]]
|
||||
A number: [[ a_number ]]
|
||||
A boolean: [[ a_boolean ]]
|
||||
A list: [[ ", ".join(a_list) ]]
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"a_string": "lorem ipsum",
|
||||
"a_number": 12345,
|
||||
"a_boolean": true,
|
||||
"a_list": ["one", "two", "three"]
|
||||
}
|
@ -8,7 +8,6 @@ from copier.user_data import (
|
||||
load_yaml_data,
|
||||
load_toml_data,
|
||||
load_json_data,
|
||||
load_old_json_data,
|
||||
load_config_data,
|
||||
)
|
||||
|
||||
@ -24,20 +23,13 @@ def test_config_data_is_loaded_from_file():
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template",
|
||||
[
|
||||
"tests/demo_toml",
|
||||
"tests/demo_yaml",
|
||||
"tests/demo_yml",
|
||||
"tests/demo_json",
|
||||
"tests/demo_json_old",
|
||||
],
|
||||
["tests/demo_toml", "tests/demo_yaml", "tests/demo_yml", "tests/demo_json"],
|
||||
)
|
||||
def test_read_data(dst, template):
|
||||
copier.copy(template, dst, force=True)
|
||||
|
||||
gen_file = dst / "user_data.txt"
|
||||
result = gen_file.read_text()
|
||||
print(result)
|
||||
expected = Path("tests/user_data.ref.txt").read_text()
|
||||
assert result == expected
|
||||
|
||||
@ -48,38 +40,22 @@ def test_bad_toml(capsys):
|
||||
|
||||
def test_invalid_toml(capsys):
|
||||
assert {} == load_yaml_data("tests/demo_invalid")
|
||||
out, err = capsys.readouterr()
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"INVALID.*tests/demo_invalid/copier\.yml", out)
|
||||
|
||||
assert {} == load_toml_data("tests/demo_invalid")
|
||||
out, err = capsys.readouterr()
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"INVALID.*tests/demo_invalid/copier\.toml", out)
|
||||
|
||||
assert {} == load_json_data("tests/demo_invalid")
|
||||
out, err = capsys.readouterr()
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"INVALID.*tests/demo_invalid/copier\.json", out)
|
||||
|
||||
# TODO: Remove on version 3.0
|
||||
assert {} == load_old_json_data("tests/demo_invalid", _warning=False)
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r"INVALID.*tests/demo_invalid/voodoo\.json", out)
|
||||
|
||||
assert {} == load_config_data("tests/demo_invalid", _warning=False)
|
||||
assert re.search(r"INVALID", out)
|
||||
|
||||
|
||||
def test_invalid_quiet(capsys):
|
||||
assert {} == load_config_data("tests/demo_invalid", quiet=True)
|
||||
out, err = capsys.readouterr()
|
||||
out, _ = capsys.readouterr()
|
||||
assert out == ""
|
||||
|
||||
assert {} == load_old_json_data("tests/demo_invalid", quiet=True)
|
||||
out, err = capsys.readouterr()
|
||||
assert out == ""
|
||||
|
||||
|
||||
def test_deprecated_msg(capsys):
|
||||
# TODO: Remove on version 3.0
|
||||
load_old_json_data("tests/demo_json_old")
|
||||
out, err = capsys.readouterr()
|
||||
assert re.search(r"`voodoo\.json` is deprecated", out)
|
||||
|
@ -26,7 +26,6 @@ def test_copy_with_extra_paths(dst):
|
||||
|
||||
gen_file = dst / "child.txt"
|
||||
result = gen_file.read_text()
|
||||
print(result)
|
||||
expected = Path(PARENT_DIR + "/parent.txt").read_text()
|
||||
assert result == expected
|
||||
|
||||
@ -36,6 +35,5 @@ def test_copy_with_extra_paths_from_config(dst):
|
||||
|
||||
gen_file = dst / "child.txt"
|
||||
result = gen_file.read_text()
|
||||
print(result)
|
||||
expected = Path(PARENT_DIR + "/parent.txt").read_text()
|
||||
assert result == expected
|
||||
|
@ -5,8 +5,7 @@ from .helpers import render
|
||||
|
||||
def test_output(capsys, dst):
|
||||
render(dst, quiet=False)
|
||||
out, err = capsys.readouterr()
|
||||
print(out)
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"create[^\s]* config\.py", out)
|
||||
assert re.search(r"create[^\s]* pyproject\.toml", out)
|
||||
assert re.search(r"create[^\s]* doc/images/nslogo\.gif", out)
|
||||
@ -14,8 +13,7 @@ def test_output(capsys, dst):
|
||||
|
||||
def test_output_pretend(capsys, dst):
|
||||
render(dst, quiet=False, pretend=True)
|
||||
out, err = capsys.readouterr()
|
||||
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"create[^\s]* config\.py", out)
|
||||
assert re.search(r"create[^\s]* pyproject\.toml", out)
|
||||
assert re.search(r"create[^\s]* doc/images/nslogo\.gif", out)
|
||||
@ -23,11 +21,9 @@ def test_output_pretend(capsys, dst):
|
||||
|
||||
def test_output_force(capsys, dst):
|
||||
render(dst)
|
||||
out, err = capsys.readouterr()
|
||||
out, _ = capsys.readouterr()
|
||||
render(dst, quiet=False, force=True)
|
||||
out, err = capsys.readouterr()
|
||||
print(out)
|
||||
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"conflict[^\s]* config\.py", out)
|
||||
assert re.search(r"force[^\s]* config\.py", out)
|
||||
assert re.search(r"identical[^\s]* pyproject\.toml", out)
|
||||
@ -36,11 +32,9 @@ def test_output_force(capsys, dst):
|
||||
|
||||
def test_output_skip(capsys, dst):
|
||||
render(dst)
|
||||
out, err = capsys.readouterr()
|
||||
out, _ = capsys.readouterr()
|
||||
render(dst, quiet=False, skip=True)
|
||||
out, err = capsys.readouterr()
|
||||
print(out)
|
||||
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"conflict[^\s]* config\.py", out)
|
||||
assert re.search(r"skip[^\s]* config\.py", out)
|
||||
assert re.search(r"identical[^\s]* pyproject\.toml", out)
|
||||
@ -49,5 +43,5 @@ def test_output_skip(capsys, dst):
|
||||
|
||||
def test_output_quiet(capsys, dst):
|
||||
render(dst, quiet=True)
|
||||
out, err = capsys.readouterr()
|
||||
out, _ = capsys.readouterr()
|
||||
assert out == ""
|
||||
|
@ -50,7 +50,7 @@ def test_prompt_default_no_input(stdin, capsys):
|
||||
|
||||
out, _ = capsys.readouterr()
|
||||
assert response == default
|
||||
assert out == "{} [{}] ".format(question, default)
|
||||
assert out == f"{question} [{default}] "
|
||||
|
||||
|
||||
def test_prompt_default_overridden(stdin, capsys):
|
||||
@ -63,7 +63,7 @@ def test_prompt_default_overridden(stdin, capsys):
|
||||
|
||||
out, _ = capsys.readouterr()
|
||||
assert response == name
|
||||
assert out == "{} [{}] ".format(question, default)
|
||||
assert out == f"{question} [{default}] "
|
||||
|
||||
|
||||
def test_prompt_error_message(stdin, capsys):
|
||||
@ -79,9 +79,8 @@ def test_prompt_error_message(stdin, capsys):
|
||||
stdin.append("yes\n")
|
||||
response = prompt(question, validator=validator)
|
||||
out, _ = capsys.readouterr()
|
||||
print(out)
|
||||
assert response is True
|
||||
assert out == "{0} {1}\n{0} ".format(question, error)
|
||||
assert out == f"{question} {error}\n{question} "
|
||||
|
||||
|
||||
def test_prompt_bool(stdin, capsys):
|
||||
@ -90,7 +89,7 @@ def test_prompt_bool(stdin, capsys):
|
||||
response = prompt_bool(question)
|
||||
stdout, _ = capsys.readouterr()
|
||||
assert response is True
|
||||
assert stdout == "{} [y/N] ".format(question)
|
||||
assert stdout == f"{question} [y/N] "
|
||||
|
||||
|
||||
def test_prompt_bool_false(stdin, capsys):
|
||||
@ -99,7 +98,7 @@ def test_prompt_bool_false(stdin, capsys):
|
||||
response = prompt_bool(question)
|
||||
stdout, _ = capsys.readouterr()
|
||||
assert response is False
|
||||
assert stdout == "{} [y/N] ".format(question)
|
||||
assert stdout == f"{question} [y/N] "
|
||||
|
||||
|
||||
def test_prompt_bool_default_true(stdin, capsys):
|
||||
@ -108,7 +107,7 @@ def test_prompt_bool_default_true(stdin, capsys):
|
||||
response = prompt_bool(question, default=True)
|
||||
stdout, _ = capsys.readouterr()
|
||||
assert response is True
|
||||
assert stdout == "{} [Y/n] ".format(question)
|
||||
assert stdout == f"{question} [Y/n] "
|
||||
|
||||
|
||||
def test_prompt_bool_default_false(stdin, capsys):
|
||||
@ -117,7 +116,7 @@ def test_prompt_bool_default_false(stdin, capsys):
|
||||
response = prompt_bool(question, default=False)
|
||||
stdout, _ = capsys.readouterr()
|
||||
assert response is False
|
||||
assert stdout == "{} [y/N] ".format(question)
|
||||
assert stdout == f"{question} [y/N] "
|
||||
|
||||
|
||||
def test_prompt_bool_no_default(stdin, capsys):
|
||||
@ -125,7 +124,7 @@ def test_prompt_bool_no_default(stdin, capsys):
|
||||
stdin.append("\ny\n")
|
||||
prompt_bool(question, default=None)
|
||||
stdout, _ = capsys.readouterr()
|
||||
assert "{} [y/n] ".format(question) in stdout
|
||||
assert f"{question} [y/n] " in stdout
|
||||
assert 'Please answer "y" or "n"' in stdout
|
||||
|
||||
|
||||
|
@ -14,5 +14,4 @@ def test_render(dst):
|
||||
sourcepath = PROJECT_TEMPLATE / "pyproject.toml.tmpl"
|
||||
result = render(sourcepath)
|
||||
expected = Path("./tests/pyproject.toml.ref").read_text()
|
||||
print(result)
|
||||
assert result == expected
|
||||
|
@ -45,4 +45,4 @@ def test_clone():
|
||||
tmp = vcs.clone("https://github.com/jpscaletti/siht.git")
|
||||
assert tmp
|
||||
assert exists(join(tmp, "setup.py"))
|
||||
shutil.rmtree(str(tmp))
|
||||
shutil.rmtree(tmp)
|
||||
|
Loading…
x
Reference in New Issue
Block a user