Skip to content

Commit

Permalink
Fix utils.copy_tree() to create dst if it doesn't exist (#1231)
Browse files Browse the repository at this point in the history
## Proposed Changes

Kapitan's `utils.copy_tree()` calls `shutil.copytree()` which will
create the destination directory if it doesn't exist yet. However,
currently, `utils.copy_tree()` doesn't support this case and will raise
a `SafeCopyError` when the destination directory doesn't exist.

This PR adjusts the implementation to only raise a `SafeCopyError` when
the destination exists and isn't a directory and will let
`shutil.copytree()` create the destination if the destination doesn't
exist at all.

## Docs and Tests

* [x] Tests added
* [x] Updated documentation
  • Loading branch information
simu authored Sep 4, 2024
1 parent 1be142a commit 401afb3
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 34 deletions.
7 changes: 5 additions & 2 deletions kapitan/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,14 +631,17 @@ def safe_copy_tree(src, dst):
def copy_tree(src: str, dst: str) -> list:
"""Recursively copy a given directory from `src` to `dst`.
If `dst` or a parent of `dst` doesn't exist, the missing directories are created.
Returns a list of the copied files.
"""
if not os.path.isdir(src):
raise SafeCopyError(f"Cannot copy tree {src}: not a directory")

if not os.path.isdir(dst):
raise SafeCopyError(f"Cannot copy tree {dst}: not a directory")
if os.path.exists(dst) and not os.path.isdir(dst):
raise SafeCopyError(f"Cannot copy tree to {dst}: destination exists but not a directory")

# this will generate an empty set if `dst` doesn't exist
before = set(glob.iglob(f"{dst}/*", recursive=True))
shutil.copytree(src, dst, dirs_exist_ok=True)
after = set(glob.iglob(f"{dst}/*", recursive=True))
Expand Down
64 changes: 32 additions & 32 deletions poetry.lock

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

16 changes: 16 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,21 @@ def test_validate_copy_dir(self):
with self.assertRaises(SafeCopyError):
copy_tree("non_existent_dir", self.temp_dir)

dst = os.path.join(self.temp_dir, "test")
with open(dst, "w", encoding="utf-8") as f:
f.write("Hello\n")
with self.assertRaises(SafeCopyError):
copy_tree(TEST_KUBERNETES_PATH, dst)

def test_copy_dir_missing_dst(self):
dst = os.path.join(self.temp_dir, "subdir")
original = set(glob.iglob(f"{TEST_KUBERNETES_PATH}/*", recursive=True))
copied = copy_tree(TEST_KUBERNETES_PATH, dst)
self.assertEqual(len(copied), len(original))

original_hash = directory_hash(TEST_KUBERNETES_PATH)
copied_hash = directory_hash(dst)
self.assertEqual(copied_hash, original_hash)

def tearDown(self):
shutil.rmtree(self.temp_dir)

0 comments on commit 401afb3

Please sign in to comment.