diff --git a/copier/cli.py b/copier/cli.py index 9cb269e..47532c1 100644 --- a/copier/cli.py +++ b/copier/cli.py @@ -189,7 +189,7 @@ class _Subcommand(cli.Application): # type: ignore[misc] Arguments: path: The path to the YAML file to load. """ - with open(path) as f: + with Path(path).open() as f: file_updates: AnyByStrDict = yaml.safe_load(f) updates_without_cli_overrides = { diff --git a/copier/main.py b/copier/main.py index 898dc00..84dc32b 100644 --- a/copier/main.py +++ b/copier/main.py @@ -204,7 +204,7 @@ class Worker: """ src_path: str | None = None - dst_path: Path = Path(".") + dst_path: Path = Path() answers_file: RelativePath | None = None vcs_ref: str | None = None data: AnyByStrDict = field(default_factory=dict) @@ -1235,7 +1235,7 @@ class Worker: # The 3-way merge might have resolved conflicts automatically, # so we need to check if the file contains conflict markers # before storing the file name for marking it as unmerged after the loop. - with open(fname) as conflicts_candidate: + with Path(fname).open() as conflicts_candidate: if any( line.rstrip() in {"<<<<<<< before updating", ">>>>>>> after updating"} diff --git a/copier/settings.py b/copier/settings.py index d67813b..6a9069b 100644 --- a/copier/settings.py +++ b/copier/settings.py @@ -57,5 +57,5 @@ class Settings(BaseModel): def normalize(self, url: str) -> str: """Normalize an URL using user settings.""" if url.startswith("~"): # Only expand on str to avoid messing with URLs - url = expanduser(url) + url = expanduser(url) # noqa: PTH111 return url diff --git a/copier/template.py b/copier/template.py index e47b1a3..fca1f92 100644 --- a/copier/template.py +++ b/copier/template.py @@ -173,7 +173,7 @@ class Task: cmd: str | Sequence[str] extra_vars: dict[str, Any] = field(default_factory=dict) condition: str | bool = True - working_directory: Path = Path(".") + working_directory: Path = Path() @dataclass @@ -325,7 +325,7 @@ class Template: return tuple( self.config_data.get( "exclude", - DEFAULT_EXCLUDE if Path(self.subdirectory) == Path(".") else [], + DEFAULT_EXCLUDE if Path(self.subdirectory) == Path() else [], ) ) diff --git a/copier/tools.py b/copier/tools.py index ea3d0e1..28250d3 100644 --- a/copier/tools.py +++ b/copier/tools.py @@ -169,7 +169,7 @@ def handle_remove_readonly( excvalue = cast(OSError, exc if isinstance(exc, BaseException) else exc[1]) if func in (os.rmdir, os.remove, os.unlink) and excvalue.errno == errno.EACCES: - os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777 + Path(path).chmod(stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # 0777 func(path) else: raise @@ -242,7 +242,7 @@ def get_git_objects_dir(path: Path) -> Path: ).absolute() -def set_git_alternates(*repos: Path, path: Path = Path(".")) -> None: +def set_git_alternates(*repos: Path, path: Path = Path()) -> None: """Set Git alternates to borrow Git objects from other repositories. Alternates are paths of other repositories' object directories written to diff --git a/copier/vcs.py b/copier/vcs.py index 8fdd2e6..1917a7f 100644 --- a/copier/vcs.py +++ b/copier/vcs.py @@ -2,7 +2,6 @@ from __future__ import annotations -import os import re import sys from contextlib import suppress @@ -190,12 +189,12 @@ def clone(url: str, ref: str | None = None) -> str: _clone = _clone["--filter=blob:none"] _clone() # Include dirty changes if checking out a local HEAD - if ref in {None, "HEAD"} and os.path.exists(url) and Path(url).is_dir(): + url_abspath = Path(url).absolute() + if ref in {None, "HEAD"} and url_abspath.is_dir(): is_dirty = False with local.cwd(url): is_dirty = bool(git("status", "--porcelain").strip()) if is_dirty: - url_abspath = Path(url).absolute() with local.cwd(location): git("--git-dir=.git", f"--work-tree={url_abspath}", "add", "-A") git( diff --git a/pyproject.toml b/pyproject.toml index 9b1dcc6..110802c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,6 +118,7 @@ extend-select = [ "I", "PERF", "PGH", + "PTH", "UP", ] extend-ignore = ['B028', "B904", "D105", "D107", "E501"] diff --git a/tests/demo_legacy_migrations/migrations.py b/tests/demo_legacy_migrations/migrations.py index 9f45c99..486fd78 100755 --- a/tests/demo_legacy_migrations/migrations.py +++ b/tests/demo_legacy_migrations/migrations.py @@ -2,6 +2,7 @@ import json import os import sys +from pathlib import Path NAMES = ( "{VERSION_FROM}-{VERSION_CURRENT}-{VERSION_TO}-{STAGE}.json", @@ -9,5 +10,5 @@ NAMES = ( ) for name in NAMES: - with open(name.format(**os.environ), "w") as fd: + with Path(name.format(**os.environ)).open("w") as fd: json.dump(sys.argv, fd) diff --git a/tests/demo_legacy_migrations/tasks.py b/tests/demo_legacy_migrations/tasks.py index a046b97..34ebd8f 100755 --- a/tests/demo_legacy_migrations/tasks.py +++ b/tests/demo_legacy_migrations/tasks.py @@ -1,10 +1,10 @@ #!/usr/bin/env python import os -import os.path import sys from contextlib import suppress +from pathlib import Path -with open("created-with-tasks.txt", "a", newline="\n") as cwt: +with Path("created-with-tasks.txt").open("a", newline="\n") as cwt: cwt.write(" ".join([os.environ["STAGE"]] + sys.argv[1:]) + "\n") with suppress(FileNotFoundError): - os.unlink("delete-in-tasks.txt") + Path("delete-in-tasks.txt").unlink() diff --git a/tests/test_config.py b/tests/test_config.py index 1546cd4..53b2644 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -329,7 +329,7 @@ def test_flags_extra_fails() -> None: with pytest.raises(ValidationError): copier.Worker( # type: ignore[call-arg] src_path="..", - dst_path=Path("."), + dst_path=Path(), i_am_not_a_member="and_i_do_not_belong_here", ) diff --git a/tests/test_copy.py b/tests/test_copy.py index c98060a..8e442b8 100644 --- a/tests/test_copy.py +++ b/tests/test_copy.py @@ -1,7 +1,6 @@ from __future__ import annotations import filecmp -import os import platform import stat import sys @@ -302,10 +301,10 @@ def test_skip_if_exists(tmp_path: Path) -> None: def test_timestamp_identical(tmp_path: Path) -> None: copier.run_copy(str(Path("tests", "demo_skip_dst")), tmp_path) - modification_time_before = os.path.getmtime(tmp_path / "a.noeof.txt") + modification_time_before = (tmp_path / "a.noeof.txt").stat().st_mtime sleep(2) copier.run_copy(str(Path("tests", "demo_skip_dst")), tmp_path) - modification_time_after = os.path.getmtime(tmp_path / "a.noeof.txt") + modification_time_after = (tmp_path / "a.noeof.txt").stat().st_mtime assert modification_time_before == modification_time_after diff --git a/tests/test_dirty_local.py b/tests/test_dirty_local.py index 9b210bc..d34b087 100644 --- a/tests/test_dirty_local.py +++ b/tests/test_dirty_local.py @@ -122,7 +122,7 @@ def test_update(tmp_path_factory: pytest.TempPathFactory) -> None: Path("test_file.txt").write_text("Test content") # test updating a file - with open("aaaa.txt", "a") as f: + with Path("aaaa.txt").open("a") as f: f.write("dolor sit amet") # test updating a symlink diff --git a/tests/test_symlinks.py b/tests/test_symlinks.py index 40a942e..a652803 100644 --- a/tests/test_symlinks.py +++ b/tests/test_symlinks.py @@ -38,9 +38,9 @@ def test_copy_symlink(tmp_path_factory: pytest.TempPathFactory) -> None: vcs_ref="HEAD", ) - assert os.path.exists(dst / "target.txt") - assert os.path.exists(dst / "symlink.txt") - assert os.path.islink(dst / "symlink.txt") + assert (dst / "target.txt").exists() + assert (dst / "symlink.txt").exists() + assert (dst / "symlink.txt").is_symlink() assert (dst / "symlink.txt").readlink() == Path("target.txt") @@ -73,9 +73,9 @@ def test_copy_symlink_templated_name(tmp_path_factory: pytest.TempPathFactory) - vcs_ref="HEAD", ) - assert os.path.exists(dst / "target.txt") - assert os.path.exists(dst / "symlink.txt") - assert os.path.islink(dst / "symlink.txt") + assert (dst / "target.txt").exists() + assert (dst / "symlink.txt").exists() + assert (dst / "symlink.txt").is_symlink() assert (dst / "symlink.txt").readlink() == Path("target.txt") @@ -111,14 +111,13 @@ def test_copy_symlink_templated_target( vcs_ref="HEAD", ) - assert os.path.exists(dst / "target.txt") - - assert os.path.exists(dst / "symlink1.txt") - assert os.path.islink(dst / "symlink1.txt") + assert (dst / "target.txt").exists() + assert (dst / "symlink1.txt").exists() + assert (dst / "symlink1.txt").is_symlink() assert (dst / "symlink1.txt").readlink() == Path("target.txt") - assert not os.path.exists(dst / "symlink2.txt") - assert os.path.islink(dst / "symlink2.txt") + assert not (dst / "symlink2.txt").exists() + assert (dst / "symlink2.txt").is_symlink() assert (dst / "symlink2.txt").readlink() == Path("{{ target_name }}.txt") @@ -149,11 +148,11 @@ def test_copy_symlink_missing_target(tmp_path_factory: pytest.TempPathFactory) - vcs_ref="HEAD", ) - assert os.path.islink(dst / "symlink.txt") + assert (dst / "symlink.txt").is_symlink() assert (dst / "symlink.txt").readlink() == Path("target.txt") - assert not os.path.exists( + assert not ( dst / "symlink.txt" - ) # exists follows symlinks, It returns False as the target doesn't exist + ).exists() # exists follows symlinks, It returns False as the target doesn't exist def test_option_preserve_symlinks_false( @@ -186,9 +185,9 @@ def test_option_preserve_symlinks_false( vcs_ref="HEAD", ) - assert os.path.exists(dst / "target.txt") - assert os.path.exists(dst / "symlink.txt") - assert not os.path.islink(dst / "symlink.txt") + assert (dst / "target.txt").exists() + assert (dst / "symlink.txt").exists() + assert not (dst / "symlink.txt").is_symlink() def test_option_preserve_symlinks_default( @@ -220,9 +219,9 @@ def test_option_preserve_symlinks_default( vcs_ref="HEAD", ) - assert os.path.exists(dst / "target.txt") - assert os.path.exists(dst / "symlink.txt") - assert not os.path.islink(dst / "symlink.txt") + assert (dst / "target.txt").exists() + assert (dst / "symlink.txt").exists() + assert not (dst / "symlink.txt").is_symlink() def test_update_symlink(tmp_path_factory: pytest.TempPathFactory) -> None: @@ -256,7 +255,7 @@ def test_update_symlink(tmp_path_factory: pytest.TempPathFactory) -> None: with local.cwd(src): # test updating a symlink - os.remove("symlink.txt") + Path("symlink.txt").unlink() os.symlink("bbbb.txt", "symlink.txt") # dst must be vcs-tracked to use run_update @@ -312,10 +311,10 @@ def test_update_file_to_symlink(tmp_path_factory: pytest.TempPathFactory) -> Non with local.cwd(src): # test updating a symlink - os.remove("bbbb.txt") + Path("bbbb.txt").unlink() os.symlink("aaaa.txt", "bbbb.txt") - os.remove("cccc.txt") - with open("cccc.txt", "w+") as f: + Path("cccc.txt").unlink() + with Path("cccc.txt").open("w+") as f: f.write("Lorem ipsum") # dst must be vcs-tracked to use run_update @@ -431,9 +430,9 @@ def test_copy_symlink_none_path(tmp_path_factory: pytest.TempPathFactory) -> Non vcs_ref="HEAD", ) - assert os.path.exists(dst / "target.txt") - assert not os.path.exists(dst / "symlink.txt") - assert not os.path.islink(dst / "symlink.txt") + assert (dst / "target.txt").exists() + assert not (dst / "symlink.txt").exists() + assert not (dst / "symlink.txt").is_symlink() def test_recursive_symlink(tmp_path_factory: pytest.TempPathFactory) -> None: diff --git a/tests/test_tools.py b/tests/test_tools.py index e48c825..0bc6794 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -12,7 +12,7 @@ from .helpers import git def test_types() -> None: """Ensure source code static typing.""" - result = PoeThePoet(Path("."))(["types"]) + result = PoeThePoet(Path())(["types"]) assert result == 0