Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

add 'flake-future-annotations' lint group #1506

Merged
merged 4 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
25 changes: 11 additions & 14 deletions copier/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Main functions and classes, used to generate or update projects."""
from __future__ import annotations

import os
import platform
Expand All @@ -15,13 +16,9 @@
from typing import (
Callable,
Iterable,
List,
Literal,
Mapping,
Optional,
Sequence,
Set,
Union,
get_args,
)
from unicodedata import normalize
Expand Down Expand Up @@ -169,9 +166,9 @@ class Worker:
See [unsafe][]
"""

src_path: Optional[str] = None
src_path: str | None = None
dst_path: Path = Path(".")
answers_file: Optional[RelativePath] = None
answers_file: RelativePath | None = None
vcs_ref: OptStr = None
data: AnyByStrDict = field(default_factory=dict)
exclude: StrSeq = ()
Expand All @@ -189,7 +186,7 @@ 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] = field(default_factory=list, init=False)

def __enter__(self):
"""Allow using worker as a context manager."""
Expand All @@ -215,7 +212,7 @@ def _check_unsafe(self, mode: Literal["copy", "update"]) -> None:
"""Check whether a template uses unsafe features."""
if self.unsafe:
return
features: Set[str] = set()
features: set[str] = set()
if self.template.jinja_extensions:
features.add("jinja_extensions")
if self.template.tasks:
Expand Down Expand Up @@ -353,7 +350,7 @@ def _render_allowed(
dst_relpath: Path,
is_dir: bool = False,
is_symlink: bool = False,
expected_contents: Union[bytes, Path] = b"",
expected_contents: bytes | Path = b"",
) -> bool:
"""Determine if a file or directory can be rendered.

Expand All @@ -373,7 +370,7 @@ def _render_allowed(
dst_abspath = Path(self.subproject.local_abspath, dst_relpath)
previous_is_symlink = dst_abspath.is_symlink()
try:
previous_content: Union[bytes, Path]
previous_content: bytes | Path
if previous_is_symlink:
previous_content = readlink(dst_abspath)
else:
Expand Down Expand Up @@ -646,7 +643,7 @@ def _render_folder(self, src_abspath: Path) -> None:
else:
self._render_file(file)

def _render_path(self, relpath: Path) -> Optional[Path]:
def _render_path(self, relpath: Path) -> Path | None:
"""Render one relative path.

Args:
Expand Down Expand Up @@ -1010,7 +1007,7 @@ def _git_initialize_repo(self):
def run_copy(
src_path: str,
dst_path: StrOrPath = ".",
data: Optional[AnyByStrDict] = None,
data: AnyByStrDict | None = None,
**kwargs,
) -> Worker:
"""Copy a template to a destination, from zero.
Expand All @@ -1027,7 +1024,7 @@ def run_copy(


def run_recopy(
dst_path: StrOrPath = ".", data: Optional[AnyByStrDict] = None, **kwargs
dst_path: StrOrPath = ".", data: AnyByStrDict | None = None, **kwargs
) -> Worker:
"""Update a subproject from its template, discarding subproject evolution.

Expand All @@ -1044,7 +1041,7 @@ def run_recopy(

def run_update(
dst_path: StrOrPath = ".",
data: Optional[AnyByStrDict] = None,
data: AnyByStrDict | None = None,
**kwargs,
) -> Worker:
"""Update a subproject, from its template.
Expand Down
9 changes: 5 additions & 4 deletions copier/subproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

A *subproject* is a project that gets rendered and/or updated with Copier.
"""
from __future__ import annotations

from dataclasses import field
from functools import cached_property
from pathlib import Path
from typing import Callable, List, Optional
from typing import Callable

import yaml
from plumbum.machines import local
Expand All @@ -32,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] = field(default_factory=list, init=False)

def is_dirty(self) -> bool:
"""Indicate if the local template root is dirty.
Expand Down Expand Up @@ -69,7 +70,7 @@ def last_answers(self) -> AnyByStrDict:
}

@cached_property
def template(self) -> Optional[Template]:
def template(self) -> Template | None:
"""Template, as it was used the last time."""
last_url = self.last_answers.get("_src_path")
last_ref = self.last_answers.get("_commit")
Expand All @@ -79,7 +80,7 @@ def template(self) -> Optional[Template]:
return result

@cached_property
def vcs(self) -> Optional[VCSTypes]:
def vcs(self) -> VCSTypes | None:
"""VCS type of the subproject."""
if is_in_git_repo(self.local_abspath):
return "git"
26 changes: 14 additions & 12 deletions copier/template.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Tools related to template management."""
from __future__ import annotations

import re
import sys
from collections import ChainMap, defaultdict
Expand All @@ -7,7 +9,7 @@
from functools import cached_property
from pathlib import Path
from shutil import rmtree
from typing import List, Literal, Mapping, Optional, Sequence, Set, Tuple
from typing import Literal, Mapping, Sequence
from warnings import warn

import dunamai
Expand All @@ -31,7 +33,7 @@
from .vcs import checkout_latest_tag, clone, get_git, get_repo

# Default list of files in the template to exclude from the rendered project
DEFAULT_EXCLUDE: Tuple[str, ...] = (
DEFAULT_EXCLUDE: tuple[str, ...] = (
"copier.yaml",
"copier.yml",
"~*",
Expand All @@ -45,7 +47,7 @@
DEFAULT_TEMPLATES_SUFFIX = ".jinja"


def filter_config(data: AnyByStrDict) -> Tuple[AnyByStrDict, AnyByStrDict]:
def filter_config(data: AnyByStrDict) -> tuple[AnyByStrDict, AnyByStrDict]:
"""Separates config and questions data."""
config_data: AnyByStrDict = {}
questions_data = {}
Expand Down Expand Up @@ -212,7 +214,7 @@ def _cleanup(self) -> None:
onerror=handle_remove_readonly,
)

def _temp_clone(self) -> Optional[Path]:
def _temp_clone(self) -> Path | None:
"""Get the path to the temporary clone of the template.

If the template hasn't yet been cloned, or if it was a local template,
Expand Down Expand Up @@ -297,7 +299,7 @@ def envops(self) -> Mapping:
return result

@cached_property
def exclude(self) -> Tuple[str, ...]:
def exclude(self) -> tuple[str, ...]:
"""Get exclusions specified in the template, or default ones.

See [exclude][].
Expand All @@ -310,7 +312,7 @@ def exclude(self) -> Tuple[str, ...]:
)

@cached_property
def jinja_extensions(self) -> Tuple[str, ...]:
def jinja_extensions(self) -> tuple[str, ...]:
"""Get Jinja2 extensions specified in the template, or `()`.

See [jinja_extensions][].
Expand Down Expand Up @@ -350,7 +352,7 @@ def metadata(self) -> AnyByStrDict:
return result

def migration_tasks(
self, stage: Literal["before", "after"], from_template: "Template"
self, stage: Literal["before", "after"], from_template: Template
) -> Sequence[Task]:
"""Get migration objects that match current version spec.

Expand All @@ -362,7 +364,7 @@ def migration_tasks(
stage: A valid stage name to find tasks for.
from_template: Original template, from which we are migrating.
"""
result: List[Task] = []
result: list[Task] = []
if not (self.version and from_template.version):
return result
extra_env: Env = {
Expand All @@ -386,7 +388,7 @@ def migration_tasks(
return result

@cached_property
def min_copier_version(self) -> Optional[Version]:
def min_copier_version(self) -> Version | None:
"""Get minimal copier version for the template and validates it.

See [min_copier_version][].
Expand All @@ -409,7 +411,7 @@ def questions_data(self) -> AnyByStrDict:
return result

@cached_property
def secret_questions(self) -> Set[str]:
def secret_questions(self) -> set[str]:
"""Get names of secret questions from the template.

These questions shouldn't be saved into the answers file.
Expand Down Expand Up @@ -503,7 +505,7 @@ def url_expanded(self) -> str:
return get_repo(self.url) or self.url

@cached_property
def version(self) -> Optional[Version]:
def version(self) -> Version | None:
"""PEP440-compliant version object."""
if self.vcs != "git" or not self.commit:
return None
Expand Down Expand Up @@ -531,7 +533,7 @@ def version(self) -> Optional[Version]:
return None

@cached_property
def vcs(self) -> Optional[VCSTypes]:
def vcs(self) -> VCSTypes | None:
"""Get VCS system used by the template, if any."""
if get_repo(self.url):
return "git"
13 changes: 7 additions & 6 deletions copier/tools.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Some utility functions."""
from __future__ import annotations

import errno
import os
Expand All @@ -12,7 +13,7 @@
from importlib.metadata import version
from pathlib import Path
from types import TracebackType
from typing import Any, Callable, Literal, Optional, TextIO, Tuple, Type, Union, cast
from typing import Any, Callable, Literal, TextIO, cast

import colorama
from packaging.version import Version
Expand All @@ -36,7 +37,7 @@ class Style:
INDENT = " " * 2
HLINE = "-" * 42

OS: Optional[Literal["linux", "macos", "windows"]] = cast(
OS: Literal["linux", "macos", "windows"] | None = cast(
Any,
{
"Linux": "linux",
Expand All @@ -63,11 +64,11 @@ def copier_version() -> Version:
def printf(
action: str,
msg: Any = "",
style: Optional[IntSeq] = None,
style: IntSeq | None = None,
indent: int = 10,
quiet: Union[bool, StrictBool] = False,
quiet: bool | StrictBool = False,
file_: TextIO = sys.stdout,
) -> Optional[str]:
) -> str | None:
"""Print string with common format."""
if quiet:
return None # HACK: Satisfy MyPy
Expand Down Expand Up @@ -152,7 +153,7 @@ def handle_remove_readonly(
func: Callable,
path: str,
# TODO: Change this union to simply `BaseException` when Python 3.11 support is dropped
exc: Union[BaseException, Tuple[Type[BaseException], BaseException, TracebackType]],
exc: BaseException | tuple[type[BaseException], BaseException, TracebackType],
) -> None:
"""Handle errors when trying to remove read-only files through `shutil.rmtree`.

Expand Down
16 changes: 9 additions & 7 deletions copier/user_data.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Functions used to load user data."""
from __future__ import annotations

import json
import warnings
from collections import ChainMap
Expand All @@ -8,7 +10,7 @@
from hashlib import sha512
from os import urandom
from pathlib import Path
from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Set, Union
from typing import Any, Callable, Mapping, Sequence

import yaml
from jinja2 import UndefinedError
Expand Down Expand Up @@ -84,7 +86,7 @@ class AnswersMap:
"""

# Private
hidden: Set[str] = field(default_factory=set, init=False)
hidden: set[str] = field(default_factory=set, init=False)

# Public
user: AnyByStrDict = field(default_factory=dict)
Expand Down Expand Up @@ -176,16 +178,16 @@ class Question:
var_name: str
answers: AnswersMap
jinja_env: SandboxedEnvironment
choices: Union[Sequence[Any], Dict[Any, Any]] = field(default_factory=list)
choices: Sequence[Any] | dict[Any, Any] = field(default_factory=list)
multiselect: bool = False
default: Any = MISSING
help: str = ""
multiline: Union[str, bool] = False
multiline: str | bool = False
placeholder: str = ""
secret: bool = False
type: str = Field(default="", validate_default=True)
validator: str = ""
when: Union[str, bool] = True
when: str | bool = True

@field_validator("var_name")
@classmethod
Expand Down Expand Up @@ -246,7 +248,7 @@ def get_default(self) -> Any:
result = self.cast_answer(result)
return result

def get_default_rendered(self) -> Union[bool, str, Choice, None, MissingType]:
def get_default_rendered(self) -> bool | str | Choice | None | MissingType:
"""Get default answer rendered for the questionary lib.

The questionary lib expects some specific data types, and returns
Expand Down Expand Up @@ -416,7 +418,7 @@ def get_when(self) -> bool:
return cast_to_bool(self.render_value(self.when))

def render_value(
self, value: Any, extra_answers: Optional[AnyByStrDict] = None
self, value: Any, extra_answers: AnyByStrDict | None = None
) -> str:
"""Render a single templated value using Jinja.

Expand Down
16 changes: 15 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading