Coverage for sel_tools/file_export/copy_item.py: 100%

22 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-04 21:22 +0000

1"""Copy file or folder with support for an ignore file.""" 

2 

3import shutil 

4from collections.abc import Callable 

5from pathlib import Path 

6 

7from sel_tools.file_export.config import EXPORT_IGNORE 

8 

9 

10def copy_item(source: Path, dest: Path) -> None: 

11 """Copy file or folder 'source' with support for an ignore file to dest.""" 

12 if source.is_file(): 

13 shutil.copyfile(str(source), str(dest), follow_symlinks=False) 

14 else: 

15 ignored_items = get_ignored_items_from(source) 

16 shutil.copytree( 

17 str(source), 

18 str(dest), 

19 symlinks=False, 

20 dirs_exist_ok=True, 

21 ignore=ignore_files(ignored_items), 

22 ) 

23 

24 

25def get_ignored_items_from(folder: Path) -> set[Path]: 

26 """Get list of ignored items from export ignore file with absolute paths.""" 

27 ignore_file = folder / EXPORT_IGNORE 

28 if not ignore_file.exists(): 

29 return set() 

30 

31 ignore_file_lines = ignore_file.read_text().splitlines() 

32 non_empty_lines = list(filter(None, ignore_file_lines)) 

33 non_empty_lines.append(f"**/{EXPORT_IGNORE}") 

34 ignore_list = [file for ignore_file_line in non_empty_lines for file in folder.glob(ignore_file_line)] 

35 

36 return {item.resolve() for item in ignore_list} 

37 

38 

39def ignore_files(ignore_set: set[Path]) -> Callable: 

40 """Create callable for ignoring files with shutil.copytree.""" 

41 

42 def ignore_callable(directory: str, contents: list[str]) -> list[str]: 

43 """Callable for ignoring files with shutil.copytree.""" 

44 return [item for item in contents if Path(directory).joinpath(item).resolve() in ignore_set] 

45 

46 return ignore_callable