Skip to content

Commit

Permalink
Merge pull request #33 from poponealex/30-uninstalled-git-raises-a-wi…
Browse files Browse the repository at this point in the history
…nerror-2-which-is-not-caught-by-subprocesscalledprocesserror

deal with missing git
  • Loading branch information
laowantong authored Apr 26, 2022
2 parents da83fd9 + 21c23b4 commit d57c99a
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 16 deletions.
58 changes: 42 additions & 16 deletions src/renamings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Renamer:
def __init__(self, context: Context, testing: bool = False):
self.logger = context.logger
self.print_ = context.print_
self.rename_one_file = self._rename_one_file_with_git # set the default renaming strategy
if testing:
self.logger.create_new_log_file()

Expand Down Expand Up @@ -68,26 +69,51 @@ def get_arcs_for_undoing(
def rename_and_log_all_files(self, arcs: List[Arc]):
self.arcs_to_rollback: List[Arc] = []
for (source, target) in arcs:
try:
subprocess.run(
[
"git", # git fails when launched from a non-versioned directory.
"-C", # This option runs git as if it was started...
source.parent, # ... in the source's parent directory.
"mv",
source.name,
target.name,
],
check=True,
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
source.rename(target)
git_flag = self.rename_one_file(source, target)
self.logger.info(f"{'git:' if git_flag else ''}SOURCE:{source}\tTARGET:{target}")
self.arcs_to_rollback.insert(0, Arc(target, source))
self.logger.info(f"SOURCE:{source}\tTARGET:{target}")
self.print_arcs(arcs)
self.print_.newline()

def _rename_one_file_with_git(self, source: Path, target: Path) -> bool:
"""Try to use git to operate a renaming (default strategy for `rename_one_file()`).
Args:
source (Path): the path to the file to rename
target (Path): the new path to the file
Returns:
bool: `True` if the renaming was done using git, `False` otherwise. In the latter case,
the renaming is done without git, as well as all the subsequent ones.
"""
try:
subprocess.run(
[
"git",
"-C", # Run git as if it was started...
source.parent, # ... from the source's parent directory.
"mv",
source.name,
target.name,
],
check=True,
stderr=subprocess.DEVNULL,
)
return True
except FileNotFoundError:
self.rename_one_file = self._rename_one_file
self.logger.warning(f"Git is not installed. Falling back to a non-git strategy.")
except subprocess.CalledProcessError:
pass
except Exception as e:
self.logger.warning(f"Error while git-renaming '{source}' to '{target}': {e}.")
return self._rename_one_file(source, target)

def _rename_one_file(self, source: Path, target: Path) -> bool:
"""Fallback strategy as soon as a git-renaming has found that git was not installed."""
source.rename(target)
return False

def print_arcs(self, arcs: List[Arc]):
previous_parent = Path()
for (source, target) in arcs:
Expand Down
52 changes: 52 additions & 0 deletions test/test_renamings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from pathlib import Path

import os
import pytest

__import__("sys").path[0:0] = "."
Expand Down Expand Up @@ -259,5 +260,56 @@ def test_undo_with_empty_log_file():
arcs_for_undoing = renamer.get_arcs_for_undoing(logger.get_contents()) # doesn't raise an exception
assert arcs_for_undoing == []


def test_rename_when_git_is_not_installed():
"""All renamings succeed."""
renamer = Renamer(context, testing=True)
base = Path("test") / "happy_path"
rm_tree(base)
base.mkdir()
arcs = [
Arc(base / "source_0", base / "target_0"),
Arc(base / "source_1", base / "target_1"),
Arc(base / "source_2", base / "target_2"),
]
for arc in arcs:
arc.source.touch()
path_backup = os.environ["PATH"]
os.environ["PATH"] = "" # make git unavailable
renamer.perform_renamings(arcs)
os.environ["PATH"] = path_backup
assert set(base.iterdir()) == set(arc.target for arc in arcs)
print(logger.get_contents())
assert logger.get_contents() == "\n".join([
"INFO:root:3 items to rename.",
"WARNING:root:Git is not installed. Falling back to a non-git strategy.",
"INFO:root:SOURCE:test/happy_path/source_0\tTARGET:test/happy_path/target_0",
"INFO:root:SOURCE:test/happy_path/source_1\tTARGET:test/happy_path/target_1",
"INFO:root:SOURCE:test/happy_path/source_2\tTARGET:test/happy_path/target_2",
"INFO:root:3 items renamed.",
])
rm_tree(base)


def test_rename_files_under_version_control():
"""Swap to files under version control."""
renamer = Renamer(context, testing=True)
base = Path("test")
arcs = [
Arc(base / "test_renamings_data_1.md", base / "test_renamings_data_aux.md"),
Arc(base / "test_renamings_data_2.md", base / "test_renamings_data_1.md"),
Arc(base / "test_renamings_data_aux.md", base / "test_renamings_data_2.md"),
]
renamer.perform_renamings(arcs)
print(logger.get_contents())
assert logger.get_contents() == "\n".join([
"INFO:root:3 items to rename.",
"INFO:root:git:SOURCE:test/test_renamings_data_1.md\tTARGET:test/test_renamings_data_aux.md",
"INFO:root:git:SOURCE:test/test_renamings_data_2.md\tTARGET:test/test_renamings_data_1.md",
"INFO:root:git:SOURCE:test/test_renamings_data_aux.md\tTARGET:test/test_renamings_data_2.md",
"INFO:root:3 items renamed.",
])


if __name__ == "__main__": # pragma: no cover
pytest.main(["-qq", __import__("sys").argv[0]])
3 changes: 3 additions & 0 deletions test/test_renamings_data_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# About this file

The purpose of `test_renamings_data_1.md` and `test_renamings_data_2.md` is to test the renaming of files under version control. Launching `test_renamings.py` simply swaps their names. Since they have the same content, this operation has no impact on the commit history.
3 changes: 3 additions & 0 deletions test/test_renamings_data_2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# About this file

The purpose of `test_renamings_data_1.md` and `test_renamings_data_2.md` is to test the renaming of files under version control. Launching `test_renamings.py` simply swaps their names. Since they have the same content, this operation has no impact on the commit history.

0 comments on commit d57c99a

Please sign in to comment.