From 53486a86d60f62d3ee2d7d520fa36fe1b8d2c8fe Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Mon, 4 Mar 2024 17:49:44 +0000 Subject: [PATCH 01/23] apply strict mypy config --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 7549f5335..9fcc04916 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -135,6 +135,7 @@ known-first-party = ["copier"] convention = "google" [tool.mypy] +strict = true ignore_missing_imports = true plugins = ["pydantic.mypy"] warn_no_return = false From be63dd645146ce744b6c9e1be20217b1c6a103f9 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Mon, 4 Mar 2024 18:40:05 +0000 Subject: [PATCH 02/23] address some errors --- copier/main.py | 21 +++++++++++---------- copier/subproject.py | 2 +- copier/template.py | 4 ++-- copier/tools.py | 2 +- copier/user_data.py | 4 ++-- tests/test_jinja2_extensions.py | 3 ++- tests/test_prompt.py | 7 ++++--- tests/test_tmpdir.py | 4 ++-- 8 files changed, 25 insertions(+), 22 deletions(-) diff --git a/copier/main.py b/copier/main.py index 3aad6b25c..acd3fdd09 100644 --- a/copier/main.py +++ b/copier/main.py @@ -1,4 +1,5 @@ """Main functions and classes, used to generate or update projects.""" + from __future__ import annotations import os @@ -13,7 +14,7 @@ from pathlib import Path from shutil import rmtree from tempfile import TemporaryDirectory -from typing import Callable, Iterable, Literal, Mapping, Sequence, get_args +from typing import Any, Callable, Iterable, Literal, Mapping, Sequence, get_args from unicodedata import normalize from jinja2.loaders import FileSystemLoader @@ -181,11 +182,11 @@ class Worker: answers: AnswersMap = field(default_factory=AnswersMap, init=False) _cleanup_hooks: list[Callable] = field(default_factory=list, init=False) - def __enter__(self): + def __enter__(self) -> Worker: """Allow using worker as a context manager.""" return self - def __exit__(self, type, value, traceback): + def __exit__(self, type, value, traceback) -> None: """Clean up garbage files after worker usage ends.""" if value is not None: # exception was raised from code inside context manager: @@ -196,7 +197,7 @@ def __exit__(self, type, value, traceback): # otherwise clean up and let any exception bubble up self._cleanup() - def _cleanup(self): + def _cleanup(self) -> None: """Execute all stored cleanup methods.""" for method in self._cleanup_hooks: method() @@ -226,7 +227,7 @@ def _print_message(self, message: str) -> None: if message and not self.quiet: print(self._render_string(message), file=sys.stderr) - def _answers_to_remember(self) -> Mapping: + def _answers_to_remember(self) -> Mapping[str, Any]: """Get only answers that will be remembered in the copier answers file.""" # All internal values must appear first answers: AnyByStrDict = {} @@ -273,7 +274,7 @@ def _execute_tasks(self, tasks: Sequence[Task]) -> None: with local.cwd(self.subproject.local_abspath), local.env(**task.extra_env): subprocess.run(task_cmd, shell=use_shell, check=True, env=local.env) - def _render_context(self) -> Mapping: + def _render_context(self) -> Mapping[str, Any]: """Produce render context for Jinja.""" # Backwards compatibility # FIXME Remove it? @@ -305,7 +306,7 @@ def _path_matcher(self, patterns: Iterable[str]) -> Callable[[Path], bool]: spec = PathSpec.from_lines("gitwildmatch", normalized_patterns) return spec.match_file - def _solve_render_conflict(self, dst_relpath: Path): + def _solve_render_conflict(self, dst_relpath: Path) -> bool: """Properly solve render conflicts. It can ask the user if running in interactive mode. @@ -766,7 +767,7 @@ def run_recopy(self) -> None: f"from `{self.subproject.answers_relpath}`." ) with replace(self, src_path=self.subproject.template.url) as new_worker: - return new_worker.run_copy() + new_worker.run_copy() def run_update(self) -> None: """Update a subproject that was already generated. @@ -818,7 +819,7 @@ def run_update(self) -> None: self._apply_update() self._print_message(self.template.message_after_update) - def _apply_update(self): # noqa: C901 + def _apply_update(self) -> None: # noqa: C901 git = get_git() subproject_top = Path( git( @@ -971,7 +972,7 @@ def _apply_update(self): # noqa: C901 self.template.migration_tasks("after", self.subproject.template) ) - def _git_initialize_repo(self): + def _git_initialize_repo(self) -> None: """Initialize a git repository in the current directory.""" git = get_git() git("init", retcode=None) diff --git a/copier/subproject.py b/copier/subproject.py index 3f30888d2..8691b873d 100644 --- a/copier/subproject.py +++ b/copier/subproject.py @@ -45,7 +45,7 @@ def is_dirty(self) -> bool: return bool(get_git()("status", "--porcelain").strip()) return False - def _cleanup(self): + def _cleanup(self) -> None: """Remove temporary files and folders created by the subproject.""" for method in self._cleanup_hooks: method() diff --git a/copier/template.py b/copier/template.py index 1a90660b7..abaff0a0c 100644 --- a/copier/template.py +++ b/copier/template.py @@ -28,7 +28,7 @@ UnsupportedVersionError, ) from .tools import copier_version, handle_remove_readonly -from .types import AnyByStrDict, Env, OptStr, StrSeq, Union, VCSTypes +from .types import AnyByStrDict, Env, OptStr, StrSeq, VCSTypes from .vcs import checkout_latest_tag, clone, get_git, get_repo # Default list of files in the template to exclude from the rendered project @@ -157,7 +157,7 @@ class Task: Additional environment variables to set while executing the command. """ - cmd: Union[str, Sequence[str]] + cmd: str | Sequence[str] extra_env: Env = field(default_factory=dict) diff --git a/copier/tools.py b/copier/tools.py index f4eb61c65..36673f00d 100644 --- a/copier/tools.py +++ b/copier/tools.py @@ -150,7 +150,7 @@ def force_str_end(original_str: str, end: str = "\n") -> str: def handle_remove_readonly( - func: Callable, + func: Callable[[str], None], path: str, # TODO: Change this union to simply `BaseException` when Python 3.11 support is dropped exc: BaseException | tuple[type[BaseException], BaseException, TracebackType], diff --git a/copier/user_data.py b/copier/user_data.py index 7995d9afa..bb7958169 100644 --- a/copier/user_data.py +++ b/copier/user_data.py @@ -190,14 +190,14 @@ class Question: @field_validator("var_name") @classmethod - def _check_var_name(cls, v: str): + def _check_var_name(cls, v: str) -> str: if v in DEFAULT_DATA: raise ValueError("Invalid question name") return v @field_validator("type") @classmethod - def _check_type(cls, v: str, info: ValidationInfo): + def _check_type(cls, v: str, info: ValidationInfo) -> str: if v == "": default_type_name = type(info.data.get("default")).__name__ v = default_type_name if default_type_name in CAST_STR_TO_NATIVE else "yaml" diff --git a/tests/test_jinja2_extensions.py b/tests/test_jinja2_extensions.py index bc72a6e1c..3e1bc7a93 100644 --- a/tests/test_jinja2_extensions.py +++ b/tests/test_jinja2_extensions.py @@ -3,7 +3,8 @@ from typing import Any import pytest -from jinja2.ext import Environment, Extension +from jinja2 import Environment +from jinja2.ext import Extension import copier diff --git a/tests/test_prompt.py b/tests/test_prompt.py index 1911e334f..7346cdb0c 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys from pathlib import Path from typing import Any, Dict, List, Mapping, Protocol, Union @@ -24,10 +25,10 @@ git_save, ) -try: - from typing import TypeAlias # type: ignore[attr-defined] -except ImportError: +if sys.version_info < (3, 10): from typing_extensions import TypeAlias +else: + from typing import TypeAlias MARIO_TREE: Mapping[StrOrPath, str | bytes] = { diff --git a/tests/test_tmpdir.py b/tests/test_tmpdir.py index 2ae8c0407..654facc5f 100644 --- a/tests/test_tmpdir.py +++ b/tests/test_tmpdir.py @@ -44,7 +44,7 @@ def test_api( template_path: str, tmp_path_factory: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch, -): +) -> None: tmp, dst = map(tmp_path_factory.mktemp, ["tmp", "dst"]) _git = git["-C", dst] # Mock tmp dir to assert it ends up clean @@ -73,7 +73,7 @@ def test_cli( template_path: str, tmp_path_factory: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch, -): +) -> False: tmp, dst = map(tmp_path_factory.mktemp, ["tmp", "dst"]) _git = git["-C", dst] # Mock tmp dir to assert it ends up clean From 4903582b14c19f94799aa41e9ed04904a0b04745 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Mon, 4 Mar 2024 18:52:44 +0000 Subject: [PATCH 03/23] address some errors --- copier/main.py | 31 ++++++++++++++++++++++++++++--- tests/helpers.py | 12 +++++++----- tests/test_copy.py | 4 ++-- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/copier/main.py b/copier/main.py index acd3fdd09..7494a283c 100644 --- a/copier/main.py +++ b/copier/main.py @@ -14,7 +14,17 @@ from pathlib import Path from shutil import rmtree from tempfile import TemporaryDirectory -from typing import Any, Callable, Iterable, Literal, Mapping, Sequence, get_args +from types import TracebackType +from typing import ( + Any, + Callable, + Iterable, + Literal, + Mapping, + Sequence, + get_args, + overload, +) from unicodedata import normalize from jinja2.loaders import FileSystemLoader @@ -180,13 +190,28 @@ class Worker: skip_answered: bool = False answers: AnswersMap = field(default_factory=AnswersMap, init=False) - _cleanup_hooks: list[Callable] = field(default_factory=list, init=False) + _cleanup_hooks: list[Callable[[], None]] = field(default_factory=list, init=False) def __enter__(self) -> Worker: """Allow using worker as a context manager.""" return self - def __exit__(self, type, value, traceback) -> None: + @overload + def __exit__(self, type: None, value: None, traceback: None) -> None: + ... + + @overload + def __exit__( + self, type: type[BaseException], value: BaseException, traceback: TracebackType + ) -> None: + ... + + def __exit__( + self, + type: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, + ) -> None: """Clean up garbage files after worker usage ends.""" if value is not None: # exception was raised from code inside context manager: diff --git a/tests/helpers.py b/tests/helpers.py index 848a46bcc..7fd710bcc 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -8,7 +8,7 @@ from enum import Enum from hashlib import sha1 from pathlib import Path -from typing import Mapping, Protocol +from typing import Any, Mapping, Protocol from pexpect.popen_spawn import PopenSpawn from plumbum import local @@ -85,7 +85,7 @@ class Keyboard(str, Enum): Backspace = ControlH -def render(tmp_path: Path, **kwargs) -> None: +def render(tmp_path: Path, **kwargs: Any) -> None: kwargs.setdefault("quiet", True) copier.run_copy(str(PROJECT_TEMPLATE), tmp_path, data=DATA, **kwargs) @@ -96,7 +96,9 @@ def assert_file(tmp_path: Path, *path: str) -> None: assert filecmp.cmp(p1, p2) -def build_file_tree(spec: Mapping[StrOrPath, str | bytes | Path], dedent: bool = True): +def build_file_tree( + spec: Mapping[StrOrPath, str | bytes | Path], dedent: bool = True +) -> None: """Builds a file tree based on the received spec. Params: @@ -135,7 +137,7 @@ def expect_prompt( def git_save( dst: StrOrPath = ".", message: str = "Test commit", tag: str | None = None -): +) -> None: """Save the current repo state in git. Args: @@ -151,7 +153,7 @@ def git_save( git("tag", tag) -def git_init(message="hello world") -> None: +def git_init(message: str = "hello world") -> None: """Initialize a Git repository with a first commit. Args: diff --git a/tests/test_copy.py b/tests/test_copy.py index 6b26f4309..2d93083dc 100644 --- a/tests/test_copy.py +++ b/tests/test_copy.py @@ -1,5 +1,6 @@ from __future__ import annotations +import filecmp import platform import stat import sys @@ -24,7 +25,6 @@ PROJECT_TEMPLATE, assert_file, build_file_tree, - filecmp, git_save, render, ) @@ -87,7 +87,7 @@ def test_tag_autodetect_v_prefix_optional( tmp_path_factory: pytest.TempPathFactory, tag2: str, use_prereleases: bool, - expected_version, + expected_version: str, prefix: str, ) -> None: """Tags with and without `v` prefix should work the same.""" From e7d2c8651e1baa79ef937c64d93bdb8629ea8fc1 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Mon, 4 Mar 2024 19:17:27 +0000 Subject: [PATCH 04/23] address some errors --- copier/cli.py | 23 +++++++++++------------ copier/main.py | 8 ++++---- copier/subproject.py | 2 +- copier/template.py | 2 +- copier/user_data.py | 6 ++++-- devtasks.py | 6 +++--- tests/test_config.py | 6 +++--- 7 files changed, 27 insertions(+), 26 deletions(-) diff --git a/copier/cli.py b/copier/cli.py index 09e596163..355c50b0f 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -52,7 +52,7 @@ from os import PathLike from pathlib import Path from textwrap import dedent -from typing import Callable +from typing import Any, Callable import yaml from plumbum import cli, colors @@ -84,21 +84,18 @@ class CopierApp(cli.Application): """The Copier CLI application.""" DESCRIPTION = "Create a new project from a template." - DESCRIPTION_MORE = ( - dedent( - """\ + DESCRIPTION_MORE = dedent( + """\ Docs in https://copier.readthedocs.io/ """ - ) - + ( - colors.yellow - | dedent( - """\ + ) + ( + colors.yellow + | dedent( + """\ WARNING! Use only trusted project templates, as they might execute code with the same level of access as your user.\n """ - ) ) ) VERSION = copier_version() @@ -108,7 +105,7 @@ class CopierApp(cli.Application): class _Subcommand(cli.Application): """Base class for Copier subcommands.""" - def __init__(self, executable: PathLike) -> None: + def __init__(self, executable: PathLike[str]) -> None: self.data: AnyByStrDict = {} super().__init__(executable) @@ -195,7 +192,9 @@ def data_file_switch(self, path: cli.ExistingFile) -> None: } self.data.update(updates_without_cli_overrides) - def _worker(self, src_path: OptStr = None, dst_path: str = ".", **kwargs) -> Worker: + def _worker( + self, src_path: OptStr = None, dst_path: str = ".", **kwargs: Any + ) -> Worker: """Run Copier's internal API using CLI switches. Arguments: diff --git a/copier/main.py b/copier/main.py index 7494a283c..1a32eb817 100644 --- a/copier/main.py +++ b/copier/main.py @@ -1030,7 +1030,7 @@ def run_copy( src_path: str, dst_path: StrOrPath = ".", data: AnyByStrDict | None = None, - **kwargs, + **kwargs: Any, ) -> Worker: """Copy a template to a destination, from zero. @@ -1046,7 +1046,7 @@ def run_copy( def run_recopy( - dst_path: StrOrPath = ".", data: AnyByStrDict | None = None, **kwargs + dst_path: StrOrPath = ".", data: AnyByStrDict | None = None, **kwargs: Any ) -> Worker: """Update a subproject from its template, discarding subproject evolution. @@ -1064,7 +1064,7 @@ def run_recopy( def run_update( dst_path: StrOrPath = ".", data: AnyByStrDict | None = None, - **kwargs, + **kwargs: Any, ) -> Worker: """Update a subproject, from its template. @@ -1079,7 +1079,7 @@ def run_update( return worker -def _remove_old_files(prefix: Path, cmp: dircmp, rm_common: bool = False) -> None: +def _remove_old_files(prefix: Path, cmp: dircmp[str], rm_common: bool = False) -> None: """Remove files and directories only found in "old" template. This is an internal helper method used to process a comparison of 2 diff --git a/copier/subproject.py b/copier/subproject.py index 8691b873d..6d0f47189 100644 --- a/copier/subproject.py +++ b/copier/subproject.py @@ -33,7 +33,7 @@ class Subproject: local_abspath: AbsolutePath answers_relpath: Path = Path(".copier-answers.yml") - _cleanup_hooks: list[Callable] = field(default_factory=list, init=False) + _cleanup_hooks: list[Callable[[], None]] = field(default_factory=list, init=False) def is_dirty(self) -> bool: """Indicate if the local template root is dirty. diff --git a/copier/template.py b/copier/template.py index abaff0a0c..cb3575a11 100644 --- a/copier/template.py +++ b/copier/template.py @@ -289,7 +289,7 @@ def config_data(self) -> AnyByStrDict: return result @cached_property - def envops(self) -> Mapping: + def envops(self) -> Mapping[str, Any]: """Get the Jinja configuration specified in the template, or default values. See [envops][]. diff --git a/copier/user_data.py b/copier/user_data.py index bb7958169..631b197b2 100644 --- a/copier/user_data.py +++ b/copier/user_data.py @@ -205,7 +205,9 @@ def _check_type(cls, v: str, info: ValidationInfo) -> str: @field_validator("secret") @classmethod - def _check_secret_question_default_value(cls, v: bool, info: ValidationInfo): + def _check_secret_question_default_value( + cls, v: bool, info: ValidationInfo + ) -> bool: if v and info.data["default"] is MISSING: raise ValueError("Secret question requires a default value") return v @@ -491,7 +493,7 @@ def load_answersfile_data( return {} -CAST_STR_TO_NATIVE: Mapping[str, Callable] = { +CAST_STR_TO_NATIVE: Mapping[str, Callable[[str], Any]] = { "bool": cast_to_bool, "float": float, "int": int, diff --git a/devtasks.py b/devtasks.py index d6ccbde60..e92ddfc96 100644 --- a/devtasks.py +++ b/devtasks.py @@ -9,7 +9,7 @@ HERE = Path(__file__).parent -def clean(): +def clean() -> None: """Clean build, test or other process artifacts from the project workspace.""" build_artefacts = ( "build/", @@ -35,14 +35,14 @@ def clean(): matching_path.unlink() -def dev_setup(): +def dev_setup() -> None: """Set up a development environment.""" with local.cwd(HERE): local["direnv"]("allow") local["poetry"]("install") -def lint(): +def lint() -> None: """Lint and format the project.""" args = [ "--extra-experimental-features", diff --git a/tests/test_config.py b/tests/test_config.py index 9105789b3..a5f00f7c8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -29,7 +29,7 @@ } -def git_init(message="hello world") -> None: +def git_init(message: str = "hello world") -> None: git("init") git("config", "user.name", "Copier Test") git("config", "user.email", "test@copier") @@ -126,7 +126,7 @@ def test_invalid_config_data( ) -> None: template = Template(conf_path) with pytest.raises(InvalidConfigFileError): - template.config_data # noqa: B018 + template.config_data # noqa: B018 if check_err: _, err = capsys.readouterr() assert check_err(err) @@ -261,7 +261,7 @@ def test_config_data_empty() -> None: def test_multiple_config_file_error() -> None: template = Template("tests/demo_multi_config") with pytest.raises(MultipleConfigFilesError): - template.config_data # noqa: B018 + template.config_data # noqa: B018 # ConfigData From 52a6b95e00af7c574021a72242ae5886ec822d77 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Mon, 4 Mar 2024 19:33:42 +0000 Subject: [PATCH 05/23] suppress some remaining errors --- copier/user_data.py | 1 + pyproject.toml | 14 ++++++++++++++ tests/test_cli.py | 2 +- tests/test_tmpdir.py | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/copier/user_data.py b/copier/user_data.py index 631b197b2..4426d497c 100644 --- a/copier/user_data.py +++ b/copier/user_data.py @@ -1,4 +1,5 @@ """Functions used to load user data.""" + from __future__ import annotations import json diff --git a/pyproject.toml b/pyproject.toml index 9fcc04916..6cf8162a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -140,6 +140,20 @@ ignore_missing_imports = true plugins = ["pydantic.mypy"] warn_no_return = false +[[tool.mypy.overrides]] +module = [ + "copier.subproject", + "copier.template", + "copier.tools", + "copier.user_data", + "copier.vcs", +] +warn_return_any = false + +[[tool.mypy.overrides]] +module = ["tests.test_cli", "tests.test_prompt"] +disable_error_code = ["no-untyped-def"] + [tool.pytest.ini_options] addopts = "-n auto -ra" markers = ["impure: needs network or is not 100% reproducible"] diff --git a/tests/test_cli.py b/tests/test_cli.py index fd56a58b1..eb9c74165 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -170,7 +170,7 @@ def test_data_file_parsed_by_question_type( def test_data_cli_takes_precedence_over_data_file( tmp_path_factory: pytest.TempPathFactory, -): +) -> None: src, dst, local = map(tmp_path_factory.mktemp, ("src", "dst", "local")) build_file_tree( { diff --git a/tests/test_tmpdir.py b/tests/test_tmpdir.py index 654facc5f..3dac75623 100644 --- a/tests/test_tmpdir.py +++ b/tests/test_tmpdir.py @@ -73,7 +73,7 @@ def test_cli( template_path: str, tmp_path_factory: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch, -) -> False: +) -> None: tmp, dst = map(tmp_path_factory.mktemp, ["tmp", "dst"]) _git = git["-C", dst] # Mock tmp dir to assert it ends up clean From c7b3c4f5cc97a58a9a7668ba7502ae934a67f173 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Thu, 14 Mar 2024 07:48:19 +0000 Subject: [PATCH 06/23] fixup --- copier/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copier/tools.py b/copier/tools.py index 36673f00d..3bed6e9c4 100644 --- a/copier/tools.py +++ b/copier/tools.py @@ -189,7 +189,7 @@ def readlink(link: Path) -> Path: _re_octal = re.compile(r"\\([0-9]{3})\\([0-9]{3})") -def _re_octal_replace(match: re.Match) -> str: +def _re_octal_replace(match: re.Match[str]) -> str: return bytes([int(match.group(1), 8), int(match.group(2), 8)]).decode("utf8") From 832ee0a08fda3e8ecf19999e0a8914e12adf0a8c Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Thu, 14 Mar 2024 07:58:48 +0000 Subject: [PATCH 07/23] suppress remaining errors --- copier/cli.py | 8 ++++---- copier/main.py | 12 ++++++------ copier/template.py | 2 +- tests/conftest.py | 2 +- tests/test_dirty_local.py | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/copier/cli.py b/copier/cli.py index 355c50b0f..ee2ea2724 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -80,7 +80,7 @@ def _handle_exceptions(method: Callable[[], None]) -> int: return 0 -class CopierApp(cli.Application): +class CopierApp(cli.Application): # type: ignore[misc] """The Copier CLI application.""" DESCRIPTION = "Create a new project from a template." @@ -102,7 +102,7 @@ class CopierApp(cli.Application): CALL_MAIN_IF_NESTED_COMMAND = False -class _Subcommand(cli.Application): +class _Subcommand(cli.Application): # type: ignore[misc] """Base class for Copier subcommands.""" def __init__(self, executable: PathLike[str]) -> None: @@ -155,7 +155,7 @@ def __init__(self, executable: PathLike[str]) -> None: ), ) - @cli.switch( + @cli.switch( # type: ignore[misc] ["-d", "--data"], str, "VARIABLE=VALUE", @@ -173,7 +173,7 @@ def data_switch(self, values: StrSeq) -> None: key, value = arg.split("=", 1) self.data[key] = value - @cli.switch( + @cli.switch( # type: ignore[misc] ["--data-file"], cli.ExistingFile, help="Load data from a YAML file", diff --git a/copier/main.py b/copier/main.py index 1a32eb817..ac7c1c28a 100644 --- a/copier/main.py +++ b/copier/main.py @@ -866,8 +866,8 @@ def _apply_update(self) -> None: # noqa: C901 data=self.subproject.last_answers, defaults=True, quiet=True, - src_path=self.subproject.template.url, - vcs_ref=self.subproject.template.commit, + src_path=self.subproject.template.url, # type: ignore[union-attr] + vcs_ref=self.subproject.template.commit, # type: ignore[union-attr] ) as old_worker: old_worker.run_copy() # Extract diff between temporary destination and real destination @@ -889,7 +889,7 @@ def _apply_update(self) -> None: # noqa: C901 diff = diff_cmd("--inter-hunk-context=0") # Run pre-migration tasks self._execute_tasks( - self.template.migration_tasks("before", self.subproject.template) + self.template.migration_tasks("before", self.subproject.template) # type: ignore[arg-type] ) # Clear last answers cache to load possible answers migration, if skip_answered flag is not set if self.skip_answered is False: @@ -911,10 +911,10 @@ def _apply_update(self) -> None: # noqa: C901 with replace( self, dst_path=new_copy / subproject_subdir, - data=self.answers.combined, + data=self.answers.combined, # type: ignore[arg-type] defaults=True, quiet=True, - src_path=self.subproject.template.url, + src_path=self.subproject.template.url, # type: ignore[union-attr] ) as new_worker: new_worker.run_copy() compared = dircmp(old_copy, new_copy) @@ -994,7 +994,7 @@ def _apply_update(self) -> None: # noqa: C901 # Run post-migration tasks self._execute_tasks( - self.template.migration_tasks("after", self.subproject.template) + self.template.migration_tasks("after", self.subproject.template) # type: ignore[arg-type] ) def _git_initialize_repo(self) -> None: diff --git a/copier/template.py b/copier/template.py index cb3575a11..8f2dddfe2 100644 --- a/copier/template.py +++ b/copier/template.py @@ -378,7 +378,7 @@ def migration_tasks( "VERSION_PEP440_FROM": str(from_template.version), "VERSION_PEP440_TO": str(self.version), } - migration: dict + migration: dict[str, Any] for migration in self._raw_config.get("_migrations", []): current = parse(migration["version"]) if self.version >= current > from_template.version: diff --git a/tests/conftest.py b/tests/conftest.py index 445c7f3d7..25b41e407 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ from coverage.tracer import CTracer from pexpect.popen_spawn import PopenSpawn from plumbum import local -from pytest_gitconfig import GitConfig +from pytest_gitconfig import GitConfig # type: ignore[attr-defined] from .helpers import Spawn diff --git a/tests/test_dirty_local.py b/tests/test_dirty_local.py index ff306ff18..b9f624398 100644 --- a/tests/test_dirty_local.py +++ b/tests/test_dirty_local.py @@ -4,7 +4,7 @@ import pytest from plumbum import local from plumbum.cmd import git -from pytest_gitconfig import GitConfig +from pytest_gitconfig import GitConfig # type: ignore[attr-defined] import copier from copier.errors import DirtyLocalWarning From 86f3ce757cd0360e3453b5715268aa1a6455989a Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Thu, 14 Mar 2024 08:08:01 +0000 Subject: [PATCH 08/23] fixup --- copier/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/copier/cli.py b/copier/cli.py index ee2ea2724..bf398e3a1 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -49,10 +49,9 @@ """ import sys -from os import PathLike from pathlib import Path from textwrap import dedent -from typing import Any, Callable +from typing import TYPE_CHECKING, Any, Callable import yaml from plumbum import cli, colors @@ -62,6 +61,9 @@ from .tools import copier_version from .types import AnyByStrDict, OptStr, StrSeq +if TYPE_CHECKING: + from os import PathLike + def _handle_exceptions(method: Callable[[], None]) -> int: """Handle keyboard interruption while running a method.""" From 895419646b74f4efed7c3fb33f60b9164e041825 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Thu, 14 Mar 2024 08:10:51 +0000 Subject: [PATCH 09/23] fixup --- copier/cli.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/copier/cli.py b/copier/cli.py index bf398e3a1..69961e992 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -49,9 +49,10 @@ """ import sys +from os import PathLike from pathlib import Path from textwrap import dedent -from typing import TYPE_CHECKING, Any, Callable +from typing import Any, Callable import yaml from plumbum import cli, colors @@ -61,9 +62,6 @@ from .tools import copier_version from .types import AnyByStrDict, OptStr, StrSeq -if TYPE_CHECKING: - from os import PathLike - def _handle_exceptions(method: Callable[[], None]) -> int: """Handle keyboard interruption while running a method.""" @@ -107,7 +105,7 @@ class CopierApp(cli.Application): # type: ignore[misc] class _Subcommand(cli.Application): # type: ignore[misc] """Base class for Copier subcommands.""" - def __init__(self, executable: PathLike[str]) -> None: + def __init__(self, executable: "PathLike[str]") -> None: self.data: AnyByStrDict = {} super().__init__(executable) From 830047bd02f170cf19048da2fed80e1f311b89f8 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sun, 17 Mar 2024 16:23:50 +0000 Subject: [PATCH 10/23] stricter config --- copier/subproject.py | 3 +++ copier/template.py | 6 +++-- poetry.lock | 64 ++++++++++++++++++++++++++++++++++---------- pyproject.toml | 14 ++++++++-- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/copier/subproject.py b/copier/subproject.py index 6d0f47189..b037baa97 100644 --- a/copier/subproject.py +++ b/copier/subproject.py @@ -2,6 +2,7 @@ A *subproject* is a project that gets rendered and/or updated with Copier. """ + from __future__ import annotations from dataclasses import field @@ -78,9 +79,11 @@ def template(self) -> Template | None: result = Template(url=last_url, ref=last_ref) self._cleanup_hooks.append(result._cleanup) return result + return None @cached_property def vcs(self) -> VCSTypes | None: """VCS type of the subproject.""" if is_in_git_repo(self.local_abspath): return "git" + return None diff --git a/copier/template.py b/copier/template.py index 8f2dddfe2..dc61c917c 100644 --- a/copier/template.py +++ b/copier/template.py @@ -1,4 +1,5 @@ """Tools related to template management.""" + from __future__ import annotations import re @@ -264,14 +265,14 @@ def answers_relpath(self) -> Path: return result @cached_property - def commit(self) -> OptStr: + def commit(self) -> OptStr: # type: ignore[return] """If the template is VCS-tracked, get its commit description.""" if self.vcs == "git": with local.cwd(self.local_abspath): return get_git()("describe", "--tags", "--always").strip() @cached_property - def commit_hash(self) -> OptStr: + def commit_hash(self) -> OptStr: # type: ignore[return] """If the template is VCS-tracked, get its commit full hash.""" if self.vcs == "git": return get_git()("-C", self.local_abspath, "rev-parse", "HEAD").strip() @@ -543,3 +544,4 @@ def vcs(self) -> VCSTypes | None: """Get VCS system used by the template, if any.""" if get_repo(self.url): return "git" + return None diff --git a/poetry.lock b/poetry.lock index 6d0faf92f..834199c94 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -822,7 +822,6 @@ optional = false python-versions = "*" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, - {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] [package.dependencies] @@ -1235,7 +1234,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1243,16 +1241,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1269,7 +1259,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1277,7 +1266,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1489,6 +1477,28 @@ files = [ {file = "types_backports-0.1.3-py2.py3-none-any.whl", hash = "sha256:dafcd61848081503e738a7768872d1dd6c018401b4d2a1cfb608ea87ec9864b9"}, ] +[[package]] +name = "types-colorama" +version = "0.4.15.20240311" +description = "Typing stubs for colorama" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-colorama-0.4.15.20240311.tar.gz", hash = "sha256:a28e7f98d17d2b14fb9565d32388e419f4108f557a7d939a66319969b2b99c7a"}, + {file = "types_colorama-0.4.15.20240311-py3-none-any.whl", hash = "sha256:6391de60ddc0db3f147e31ecb230006a6823e81e380862ffca1e4695c13a0b8e"}, +] + +[[package]] +name = "types-docutils" +version = "0.20.0.20240317" +description = "Typing stubs for docutils" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-docutils-0.20.0.20240317.tar.gz", hash = "sha256:23657aab0de58634d111914b677b1855867f16cd9a9ea110254e23b48653e1a8"}, + {file = "types_docutils-0.20.0.20240317-py3-none-any.whl", hash = "sha256:4f11b3986b74f39169313ab528ffac101c45fca9c36c4cd22dbeec6143c99b7f"}, +] + [[package]] name = "types-psutil" version = "5.9.5.20240311" @@ -1500,6 +1510,21 @@ files = [ {file = "types_psutil-5.9.5.20240311-py3-none-any.whl", hash = "sha256:890965f336122917091ae68b6bb2e63987ae143f917a229169b9dc83580529a9"}, ] +[[package]] +name = "types-pygments" +version = "2.17.0.20240310" +description = "Typing stubs for Pygments" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-Pygments-2.17.0.20240310.tar.gz", hash = "sha256:b1d97e905ce36343c7283b0319182ae6d4f967188f361f45502a18ae43e03e1f"}, + {file = "types_Pygments-2.17.0.20240310-py3-none-any.whl", hash = "sha256:b101ca9448aaff52af6966506f1fdd73b1e60a79b8a79a8bace3366cbf1f7ed9"}, +] + +[package.dependencies] +types-docutils = "*" +types-setuptools = "*" + [[package]] name = "types-pyyaml" version = "6.0.12.20240311" @@ -1511,6 +1536,17 @@ files = [ {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"}, ] +[[package]] +name = "types-setuptools" +version = "69.2.0.20240317" +description = "Typing stubs for setuptools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-setuptools-69.2.0.20240317.tar.gz", hash = "sha256:b607c4c48842ef3ee49dc0c7fe9c1bad75700b071e1018bb4d7e3ac492d47048"}, + {file = "types_setuptools-69.2.0.20240317-py3-none-any.whl", hash = "sha256:cf91ff7c87ab7bf0625c3f0d4d90427c9da68561f3b0feab77977aaf0bbf7531"}, +] + [[package]] name = "typing-extensions" version = "4.10.0" @@ -1627,4 +1663,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "9ddfce4f522dfba028be8e3eafee5d34651d1f81ba23af0b6c6d7643bf59cb5e" +content-hash = "826a9f7827db1a5da27b05d182e5d573ac6ce1e54a4284788d4a32c0b42bfb36" diff --git a/pyproject.toml b/pyproject.toml index 6cf8162a3..7c1ed9cc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,6 +59,8 @@ pytest-xdist = ">=2.5.0" types-backports = ">=0.1.3" types-pyyaml = ">=6.0.4" types-psutil = "*" +types-pygments = "^2.17.0.20240310" +types-colorama = "^0.4.15.20240311" [tool.poetry.group.docs] optional = true @@ -136,9 +138,17 @@ convention = "google" [tool.mypy] strict = true -ignore_missing_imports = true plugins = ["pydantic.mypy"] -warn_no_return = false + +[[tool.mypy.overrides]] +module = [ + "coverage.tracer", + "funcy", + "pexpect.*", + "poethepoet.app", + "plumbum.*", +] +ignore_missing_imports = true [[tool.mypy.overrides]] module = [ From e75c47b0940479d4c0cd954f6ce173e1fa2a0210 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sun, 17 Mar 2024 16:32:05 +0000 Subject: [PATCH 11/23] fixup --- copier/tools.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/copier/tools.py b/copier/tools.py index 3bed6e9c4..2fff1fa9d 100644 --- a/copier/tools.py +++ b/copier/tools.py @@ -1,4 +1,5 @@ """Some utility functions.""" + from __future__ import annotations import errno @@ -27,11 +28,11 @@ class Style: """Common color styles.""" - OK: IntSeq = [colorama.Fore.GREEN, colorama.Style.BRIGHT] - WARNING: IntSeq = [colorama.Fore.YELLOW, colorama.Style.BRIGHT] - IGNORE: IntSeq = [colorama.Fore.CYAN] - DANGER: IntSeq = [colorama.Fore.RED, colorama.Style.BRIGHT] - RESET: IntSeq = [colorama.Fore.RESET, colorama.Style.RESET_ALL] + OK = [colorama.Fore.GREEN, colorama.Style.BRIGHT] + WARNING = [colorama.Fore.YELLOW, colorama.Style.BRIGHT] + IGNORE = [colorama.Fore.CYAN] + DANGER = [colorama.Fore.RED, colorama.Style.BRIGHT] + RESET = [colorama.Fore.RESET, colorama.Style.RESET_ALL] INDENT = " " * 2 From 5da66c03801b48b32760b96eef6b89a41ec5497d Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sun, 17 Mar 2024 16:37:36 +0000 Subject: [PATCH 12/23] fixup --- copier/tools.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/copier/tools.py b/copier/tools.py index 2fff1fa9d..0340ad00c 100644 --- a/copier/tools.py +++ b/copier/tools.py @@ -14,14 +14,12 @@ from importlib.metadata import version from pathlib import Path from types import TracebackType -from typing import Any, Callable, Literal, TextIO, cast +from typing import Any, Callable, Literal, Sequence, TextIO, cast import colorama from packaging.version import Version from pydantic import StrictBool -from .types import IntSeq - colorama.just_fix_windows_console() @@ -65,14 +63,14 @@ def copier_version() -> Version: def printf( action: str, msg: Any = "", - style: IntSeq | None = None, + style: Sequence[str] | None = None, indent: int = 10, quiet: bool | StrictBool = False, file_: TextIO = sys.stdout, ) -> str | None: """Print string with common format.""" if quiet: - return None # HACK: Satisfy MyPy + return None _msg = str(msg) action = action.rjust(indent, " ") if not style: @@ -80,7 +78,7 @@ def printf( out = style + [action] + Style.RESET + [INDENT, _msg] # type: ignore[operator] print(*out, sep="", file=file_) - return None # HACK: Satisfy MyPy + return None def printf_exception( From faf4edb2749cfef3d6534d936e5662a2e4af7d8f Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sun, 17 Mar 2024 16:59:50 +0000 Subject: [PATCH 13/23] remove some 'OptStr' references --- copier/template.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/copier/template.py b/copier/template.py index dc61c917c..c800d8a86 100644 --- a/copier/template.py +++ b/copier/template.py @@ -29,7 +29,7 @@ UnsupportedVersionError, ) from .tools import copier_version, handle_remove_readonly -from .types import AnyByStrDict, Env, OptStr, StrSeq, VCSTypes +from .types import AnyByStrDict, Env, StrSeq, VCSTypes from .vcs import checkout_latest_tag, clone, get_git, get_repo # Default list of files in the template to exclude from the rendered project @@ -200,7 +200,7 @@ class Template: """ url: str - ref: OptStr = None + ref: str | None = None use_prereleases: bool = False def _cleanup(self) -> None: @@ -265,17 +265,19 @@ def answers_relpath(self) -> Path: return result @cached_property - def commit(self) -> OptStr: # type: ignore[return] + def commit(self) -> str | None: """If the template is VCS-tracked, get its commit description.""" if self.vcs == "git": with local.cwd(self.local_abspath): return get_git()("describe", "--tags", "--always").strip() + return None @cached_property - def commit_hash(self) -> OptStr: # type: ignore[return] + def commit_hash(self) -> str | None: """If the template is VCS-tracked, get its commit full hash.""" if self.vcs == "git": return get_git()("-C", self.local_abspath, "rev-parse", "HEAD").strip() + return None @cached_property def config_data(self) -> AnyByStrDict: From 8b890030d54b98548e22969e089d17af1dd079ac Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sun, 17 Mar 2024 17:09:00 +0000 Subject: [PATCH 14/23] remove more type errors --- copier/tools.py | 6 +++--- tests/conftest.py | 2 +- tests/test_dirty_local.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/copier/tools.py b/copier/tools.py index 0340ad00c..929a3c85b 100644 --- a/copier/tools.py +++ b/copier/tools.py @@ -14,7 +14,7 @@ from importlib.metadata import version from pathlib import Path from types import TracebackType -from typing import Any, Callable, Literal, Sequence, TextIO, cast +from typing import Any, Callable, Literal, TextIO, cast import colorama from packaging.version import Version @@ -63,7 +63,7 @@ def copier_version() -> Version: def printf( action: str, msg: Any = "", - style: Sequence[str] | None = None, + style: list[str] | None = None, indent: int = 10, quiet: bool | StrictBool = False, file_: TextIO = sys.stdout, @@ -76,7 +76,7 @@ def printf( if not style: return action + _msg - out = style + [action] + Style.RESET + [INDENT, _msg] # type: ignore[operator] + out = style + [action] + Style.RESET + [INDENT, _msg] print(*out, sep="", file=file_) return None diff --git a/tests/conftest.py b/tests/conftest.py index 25b41e407..bfdb982b6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ from coverage.tracer import CTracer from pexpect.popen_spawn import PopenSpawn from plumbum import local -from pytest_gitconfig import GitConfig # type: ignore[attr-defined] +from pytest_gitconfig.plugin import GitConfig from .helpers import Spawn diff --git a/tests/test_dirty_local.py b/tests/test_dirty_local.py index b9f624398..2d5957fca 100644 --- a/tests/test_dirty_local.py +++ b/tests/test_dirty_local.py @@ -4,7 +4,7 @@ import pytest from plumbum import local from plumbum.cmd import git -from pytest_gitconfig import GitConfig # type: ignore[attr-defined] +from pytest_gitconfig.plugin import GitConfig import copier from copier.errors import DirtyLocalWarning From a99887eae8254a463e3c534abce22a011df1c34f Mon Sep 17 00:00:00 2001 From: danieleades <33452915+danieleades@users.noreply.github.com> Date: Sun, 17 Mar 2024 17:13:46 +0000 Subject: [PATCH 15/23] Update pyproject.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Timothée Mazzucotelli --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7c1ed9cc8..bdbb5be44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,8 +59,8 @@ pytest-xdist = ">=2.5.0" types-backports = ">=0.1.3" types-pyyaml = ">=6.0.4" types-psutil = "*" -types-pygments = "^2.17.0.20240310" -types-colorama = "^0.4.15.20240311" +types-pygments = ">=2.17" +types-colorama = ">=0.4" [tool.poetry.group.docs] optional = true From c4085a46407eb9433c7e8b21adeebea34c81289e Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sun, 17 Mar 2024 17:15:24 +0000 Subject: [PATCH 16/23] fixup --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 834199c94..5c9fd8ff9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1663,4 +1663,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "826a9f7827db1a5da27b05d182e5d573ac6ce1e54a4284788d4a32c0b42bfb36" +content-hash = "265a53c7c9ecff7e83f12d4ebd4732a5f7f5f23a6775aa30c146a526d018a87d" From 4bb60cc6866b52d9ec0f4105c47f7946bb9c176e Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sun, 17 Mar 2024 17:22:02 +0000 Subject: [PATCH 17/23] remove unneeded type aliases --- copier/cli.py | 12 +++++++----- copier/main.py | 24 +++++++----------------- copier/template.py | 4 ++-- copier/types.py | 2 -- copier/user_data.py | 4 ++-- copier/vcs.py | 9 ++++++--- tests/helpers.py | 7 +++---- tests/test_answersfile.py | 7 +++++-- tests/test_complex_questions.py | 5 +++-- tests/test_templated_prompt.py | 4 ++-- 10 files changed, 37 insertions(+), 41 deletions(-) diff --git a/copier/cli.py b/copier/cli.py index 69961e992..122728cfa 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -48,11 +48,13 @@ ``` """ +from __future__ import annotations + import sys from os import PathLike from pathlib import Path from textwrap import dedent -from typing import Any, Callable +from typing import Any, Callable, Iterable import yaml from plumbum import cli, colors @@ -60,7 +62,7 @@ from .errors import UnsafeTemplateError, UserMessageError from .main import Worker from .tools import copier_version -from .types import AnyByStrDict, OptStr, StrSeq +from .types import AnyByStrDict def _handle_exceptions(method: Callable[[], None]) -> int: @@ -105,7 +107,7 @@ class CopierApp(cli.Application): # type: ignore[misc] class _Subcommand(cli.Application): # type: ignore[misc] """Base class for Copier subcommands.""" - def __init__(self, executable: "PathLike[str]") -> None: + def __init__(self, executable: PathLike[str]) -> None: self.data: AnyByStrDict = {} super().__init__(executable) @@ -162,7 +164,7 @@ def __init__(self, executable: "PathLike[str]") -> None: list=True, help="Make VARIABLE available as VALUE when rendering the template", ) - def data_switch(self, values: StrSeq) -> None: + def data_switch(self, values: Iterable[str]) -> None: """Update [data][] with provided values. Arguments: @@ -193,7 +195,7 @@ def data_file_switch(self, path: cli.ExistingFile) -> None: self.data.update(updates_without_cli_overrides) def _worker( - self, src_path: OptStr = None, dst_path: str = ".", **kwargs: Any + self, src_path: str | None = None, dst_path: str = ".", **kwargs: Any ) -> Worker: """Run Copier's internal API using CLI switches. diff --git a/copier/main.py b/copier/main.py index ac7c1c28a..994bcc8e1 100644 --- a/copier/main.py +++ b/copier/main.py @@ -47,15 +47,7 @@ from .subproject import Subproject from .template import Task, Template from .tools import OS, Style, normalize_git_path, printf, readlink -from .types import ( - MISSING, - AnyByStrDict, - JSONSerializable, - OptStr, - RelativePath, - StrOrPath, - StrSeq, -) +from .types import MISSING, AnyByStrDict, JSONSerializable, RelativePath, StrOrPath from .user_data import DEFAULT_DATA, AnswersMap, Question from .vcs import get_git @@ -173,11 +165,11 @@ class Worker: src_path: str | None = None dst_path: Path = Path(".") answers_file: RelativePath | None = None - vcs_ref: OptStr = None + vcs_ref: str | None = None data: AnyByStrDict = field(default_factory=dict) - exclude: StrSeq = () + exclude: Sequence[str] = () use_prereleases: bool = False - skip_if_exists: StrSeq = () + skip_if_exists: Sequence[str] = () cleanup_on_error: bool = True defaults: bool = False user_defaults: AnyByStrDict = field(default_factory=dict) @@ -197,14 +189,12 @@ def __enter__(self) -> Worker: return self @overload - def __exit__(self, type: None, value: None, traceback: None) -> None: - ... + def __exit__(self, type: None, value: None, traceback: None) -> None: ... @overload def __exit__( self, type: type[BaseException], value: BaseException, traceback: TracebackType - ) -> None: - ... + ) -> None: ... def __exit__( self, @@ -494,7 +484,7 @@ def answers_relpath(self) -> Path: return Path(template.render(**self.answers.combined)) @cached_property - def all_exclusions(self) -> StrSeq: + def all_exclusions(self) -> Sequence[str]: """Combine default, template and user-chosen exclusions.""" return self.template.exclude + tuple(self.exclude) diff --git a/copier/template.py b/copier/template.py index c800d8a86..1b88e5e1d 100644 --- a/copier/template.py +++ b/copier/template.py @@ -29,7 +29,7 @@ UnsupportedVersionError, ) from .tools import copier_version, handle_remove_readonly -from .types import AnyByStrDict, Env, StrSeq, VCSTypes +from .types import AnyByStrDict, Env, VCSTypes from .vcs import checkout_latest_tag, clone, get_git, get_repo # Default list of files in the template to exclude from the rendered project @@ -432,7 +432,7 @@ def secret_questions(self) -> set[str]: return result @cached_property - def skip_if_exists(self) -> StrSeq: + def skip_if_exists(self) -> Sequence[str]: """Get skip patterns from the template. These files will never be rewritten when rendering the template. diff --git a/copier/types.py b/copier/types.py index 9923a0bfc..fd3037a9d 100644 --- a/copier/types.py +++ b/copier/types.py @@ -27,13 +27,11 @@ # sequences IntSeq = Sequence[int] -StrSeq = Sequence[str] PathSeq = Sequence[Path] # optional types OptBool = Optional[bool] OptStrOrPath = Optional[StrOrPath] -OptStr = Optional[str] # miscellaneous T = TypeVar("T") diff --git a/copier/user_data.py b/copier/user_data.py index 4426d497c..76a93fef9 100644 --- a/copier/user_data.py +++ b/copier/user_data.py @@ -25,7 +25,7 @@ from .errors import InvalidTypeError, UserMessageError from .tools import cast_to_bool, cast_to_str, force_str_end -from .types import MISSING, AnyByStrDict, MissingType, OptStr, OptStrOrPath, StrOrPath +from .types import MISSING, AnyByStrDict, MissingType, OptStrOrPath, StrOrPath # TODO Remove these two functions as well as DEFAULT_DATA in a future release @@ -109,7 +109,7 @@ def combined(self) -> Mapping[str, Any]: ) ) - def old_commit(self) -> OptStr: + def old_commit(self) -> str | None: """Commit when the project was updated from this template the last time.""" return self.last.get("_commit") diff --git a/copier/vcs.py b/copier/vcs.py index a26552238..6acb01b5f 100644 --- a/copier/vcs.py +++ b/copier/vcs.py @@ -1,4 +1,7 @@ """Utilities related to VCS.""" + +from __future__ import annotations + import os import re import sys @@ -13,7 +16,7 @@ from plumbum.machines import LocalCommand from .errors import DirtyLocalWarning, ShallowCloneWarning -from .types import OptBool, OptStr, OptStrOrPath, StrOrPath +from .types import OptBool, OptStrOrPath, StrOrPath def get_git(context_dir: OptStrOrPath = None) -> LocalCommand: @@ -80,7 +83,7 @@ def is_git_bundle(path: Path) -> bool: return bool(get_git()["bundle", "verify", path] & TF) -def get_repo(url: str) -> OptStr: +def get_repo(url: str) -> str | None: """Transform `url` into a git-parseable origin URL. Args: @@ -146,7 +149,7 @@ def checkout_latest_tag(local_repo: StrOrPath, use_prereleases: OptBool = False) return latest_tag -def clone(url: str, ref: OptStr = None) -> str: +def clone(url: str, ref: str | None = None) -> str: """Clone repo into some temporary destination. Includes dirty changes for local templates by copying into a temp diff --git a/tests/helpers.py b/tests/helpers.py index 7fd710bcc..aa7d743f3 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -17,7 +17,7 @@ from prompt_toolkit.keys import Keys import copier -from copier.types import OptStr, StrOrPath +from copier.types import StrOrPath PROJECT_TEMPLATE = Path(__file__).parent / "demo" @@ -61,8 +61,7 @@ class Spawn(Protocol): - def __call__(self, cmd: tuple[str, ...], *, timeout: int | None) -> PopenSpawn: - ... + def __call__(self, cmd: tuple[str, ...], *, timeout: int | None) -> PopenSpawn: ... class Keyboard(str, Enum): @@ -124,7 +123,7 @@ def build_file_tree( def expect_prompt( - tui: PopenSpawn, name: str, expected_type: str, help: OptStr = None + tui: PopenSpawn, name: str, expected_type: str, help: str | None = None ) -> None: """Check that we get a prompt in the standard form""" if help: diff --git a/tests/test_answersfile.py b/tests/test_answersfile.py index 7374471e6..e09cf090a 100644 --- a/tests/test_answersfile.py +++ b/tests/test_answersfile.py @@ -1,10 +1,11 @@ +from __future__ import annotations + from pathlib import Path from textwrap import dedent import pytest import copier -from copier.types import OptStr from copier.user_data import load_answersfile_data from .helpers import BRACKET_ENVOPS_JSON, SUFFIX_TMPL, build_file_tree @@ -52,7 +53,9 @@ def template_path(tmp_path_factory: pytest.TempPathFactory) -> str: @pytest.mark.parametrize("answers_file", [None, ".changed-by-user.yaml"]) -def test_answersfile(template_path: str, tmp_path: Path, answers_file: OptStr) -> None: +def test_answersfile( + template_path: str, tmp_path: Path, answers_file: str | None +) -> None: """Test copier behaves properly when using an answersfile.""" round_file = tmp_path / "round.txt" diff --git a/tests/test_complex_questions.py b/tests/test_complex_questions.py index 829060cf9..8e61cbfa6 100644 --- a/tests/test_complex_questions.py +++ b/tests/test_complex_questions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json from collections import deque from pathlib import Path @@ -11,7 +13,6 @@ from plumbum.cmd import git from copier import run_copy, run_update -from copier.types import OptStr from .helpers import ( BRACKET_ENVOPS, @@ -122,7 +123,7 @@ def check_invalid( name: str, format: str, invalid_value: str, - help: OptStr = None, + help: str | None = None, err: str = "Invalid input", ) -> None: """Check that invalid input is reported correctly""" diff --git a/tests/test_templated_prompt.py b/tests/test_templated_prompt.py index b6fb2c3ed..84e3543c6 100644 --- a/tests/test_templated_prompt.py +++ b/tests/test_templated_prompt.py @@ -11,7 +11,7 @@ from copier import Worker from copier.errors import InvalidTypeError -from copier.types import AnyByStrDict, OptStr +from copier.types import AnyByStrDict from .helpers import ( BRACKET_ENVOPS, @@ -32,7 +32,7 @@ class Prompt: - def __init__(self, name: str, format: str, help: OptStr = None) -> None: + def __init__(self, name: str, format: str, help: str | None = None) -> None: self.name = name self.format = format self.help = help From 788017a681a0e582e9365b604de410cb53ee81d8 Mon Sep 17 00:00:00 2001 From: Daniel Eades Date: Sat, 23 Mar 2024 10:31:24 +0000 Subject: [PATCH 18/23] fixup --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bdbb5be44..c0b46100d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,10 +57,10 @@ pytest-cov = ">=3.0.0" pytest-gitconfig = ">=0.6.0" pytest-xdist = ">=2.5.0" types-backports = ">=0.1.3" -types-pyyaml = ">=6.0.4" +types-colorama = ">=0.4" types-psutil = "*" types-pygments = ">=2.17" -types-colorama = ">=0.4" +types-pyyaml = ">=6.0.4" [tool.poetry.group.docs] optional = true @@ -145,8 +145,8 @@ module = [ "coverage.tracer", "funcy", "pexpect.*", - "poethepoet.app", "plumbum.*", + "poethepoet.app", ] ignore_missing_imports = true From 7a34d3d8cfdf6e1786c0c9211c406815e3b69ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 23 Mar 2024 11:41:13 +0100 Subject: [PATCH 19/23] chore: poetry lock --no-update --- poetry.lock | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 5c9fd8ff9..61d94b398 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -822,6 +822,7 @@ optional = false python-versions = "*" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] [package.dependencies] @@ -1234,6 +1235,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1241,8 +1243,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1259,6 +1269,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1266,6 +1277,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, From 429e66cbe6272748672d50fdc668c5fe5aaa6d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 23 Mar 2024 11:59:42 +0100 Subject: [PATCH 20/23] style: revert style changes --- copier/cli.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/copier/cli.py b/copier/cli.py index 122728cfa..a247304bb 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -86,18 +86,21 @@ class CopierApp(cli.Application): # type: ignore[misc] """The Copier CLI application.""" DESCRIPTION = "Create a new project from a template." - DESCRIPTION_MORE = dedent( - """\ + DESCRIPTION_MORE = ( + dedent( + """\ Docs in https://copier.readthedocs.io/ """ - ) + ( - colors.yellow - | dedent( - """\ + ) + + ( + colors.yellow + | dedent( + """\ WARNING! Use only trusted project templates, as they might execute code with the same level of access as your user.\n """ + ) ) ) VERSION = copier_version() From 4dd6583483e78c909a8a8eccfc0f9173f0dbd14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 23 Mar 2024 12:04:31 +0100 Subject: [PATCH 21/23] chore: Revert blank lines --- copier/main.py | 1 - copier/subproject.py | 1 - copier/template.py | 1 - copier/tools.py | 1 - copier/user_data.py | 1 - copier/vcs.py | 1 - 6 files changed, 6 deletions(-) diff --git a/copier/main.py b/copier/main.py index 994bcc8e1..a1c2976aa 100644 --- a/copier/main.py +++ b/copier/main.py @@ -1,5 +1,4 @@ """Main functions and classes, used to generate or update projects.""" - from __future__ import annotations import os diff --git a/copier/subproject.py b/copier/subproject.py index b037baa97..81d3e968d 100644 --- a/copier/subproject.py +++ b/copier/subproject.py @@ -2,7 +2,6 @@ A *subproject* is a project that gets rendered and/or updated with Copier. """ - from __future__ import annotations from dataclasses import field diff --git a/copier/template.py b/copier/template.py index 1b88e5e1d..b4b808f18 100644 --- a/copier/template.py +++ b/copier/template.py @@ -1,5 +1,4 @@ """Tools related to template management.""" - from __future__ import annotations import re diff --git a/copier/tools.py b/copier/tools.py index 929a3c85b..23077a83a 100644 --- a/copier/tools.py +++ b/copier/tools.py @@ -1,5 +1,4 @@ """Some utility functions.""" - from __future__ import annotations import errno diff --git a/copier/user_data.py b/copier/user_data.py index 76a93fef9..8d5c1cca9 100644 --- a/copier/user_data.py +++ b/copier/user_data.py @@ -1,5 +1,4 @@ """Functions used to load user data.""" - from __future__ import annotations import json diff --git a/copier/vcs.py b/copier/vcs.py index 6acb01b5f..092259f27 100644 --- a/copier/vcs.py +++ b/copier/vcs.py @@ -1,5 +1,4 @@ """Utilities related to VCS.""" - from __future__ import annotations import os From eaf7c3e85e437fc21b20e7c081a76246b0c807dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 23 Mar 2024 12:16:05 +0100 Subject: [PATCH 22/23] chore: Try fixing Plumbum evaluation of annotations --- copier/cli.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/copier/cli.py b/copier/cli.py index a247304bb..3b86e44f6 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -48,13 +48,11 @@ ``` """ -from __future__ import annotations - import sys from os import PathLike from pathlib import Path from textwrap import dedent -from typing import Any, Callable, Iterable +from typing import Any, Callable, Iterable, Optional import yaml from plumbum import cli, colors @@ -198,7 +196,7 @@ def data_file_switch(self, path: cli.ExistingFile) -> None: self.data.update(updates_without_cli_overrides) def _worker( - self, src_path: str | None = None, dst_path: str = ".", **kwargs: Any + self, src_path: Optional[str] = None, dst_path: str = ".", **kwargs: Any # noqa: FA100 ) -> Worker: """Run Copier's internal API using CLI switches. From 6dd91f184684932d9e6e3746151e27e99436dbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 23 Mar 2024 12:23:36 +0100 Subject: [PATCH 23/23] chore: Type fix again --- copier/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copier/cli.py b/copier/cli.py index 3b86e44f6..1e52030bc 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -108,7 +108,7 @@ class CopierApp(cli.Application): # type: ignore[misc] class _Subcommand(cli.Application): # type: ignore[misc] """Base class for Copier subcommands.""" - def __init__(self, executable: PathLike[str]) -> None: + def __init__(self, executable: "PathLike[str]") -> None: self.data: AnyByStrDict = {} super().__init__(executable)