Coverage for /home/pradyumna/Languages/python/packages/pyprojstencil/pyprojstencil/copy_tree.py: 0%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

45 statements  

1#!/usr/bin/env python3 

2# -*- coding:utf-8; mode:python; -*- 

3# 

4# Copyright 2021 Pradyumna Paranjape 

5# This file is part of pyprojstencil. 

6# 

7# pyprojstencil is free software: you can redistribute it and/or modify 

8# it under the terms of the GNU Lesser General Public License as published by 

9# the Free Software Foundation, either version 3 of the License, or 

10# (at your option) any later version. 

11# 

12# pyprojstencil is distributed in the hope that it will be useful, 

13# but WITHOUT ANY WARRANTY; without even the implied warranty of 

14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

15# GNU Lesser General Public License for more details. 

16# 

17# You should have received a copy of the GNU Lesser General Public License 

18# along with pyprojstencil. If not, see <https://www.gnu.org/licenses/>. 

19# 

20""" 

21Deep copy the templates directory with modifications 

22""" 

23 

24import os 

25import stat 

26from pathlib import Path 

27 

28from pyprojstencil.common import edit_modify 

29from pyprojstencil.configure import PyConfig 

30from pyprojstencil.errors import MaxRecursion, TemplateMissingError 

31 

32 

33def _mod_exec(exec_file: os.PathLike) -> None: 

34 """ 

35 Make file executable 

36 

37 Args: 

38 exec_file: path of file to be made executable 

39 

40 Returns: 

41 ``None`` 

42 """ 

43 st = os.stat(exec_file) 

44 os.chmod(exec_file, st.st_mode | stat.S_IEXEC) 

45 

46 

47def mod_exec(config: PyConfig) -> None: 

48 """ 

49 make files in the directory "code-aid" executable 

50 """ 

51 for exe_path in ("code-aid/init_venv.sh", "code-aid/install.sh", 

52 "code-aid/coverage.sh", "code-aid/pypi.sh", 

53 'code-aid/build_sphinx.sh', 

54 config.project.name + "/__main__.py"): 

55 _mod_exec(config.project / exe_path) 

56 

57 

58def tree_copy(config: PyConfig, 

59 source: os.PathLike, 

60 destin: os.PathLike, 

61 skip_deep: bool = False, 

62 stack: int = 0) -> bool: 

63 """ 

64 Recursively copy file tree 

65 - Skips project-specific directories, if found: 

66 

67 - .git 

68 - .venv 

69 

70 - Nodes handling: 

71 

72 - create directories 

73 - interpret links and link them 

74 - :meth:`edit_modify` and write files 

75 

76 Args: 

77 config: pyprojstencil configuration 

78 source: Path to root of source-tree 

79 destin: Path to root of destination-tree (created if not found) 

80 

81 Raises: 

82 TemplateMissingError: source could not be located 

83 MaxRecursion: Tree structure is too deep 

84 OSError: Permission, FileExists, etc 

85 

86 Returns: 

87 ``False`` if some nodes were skipped, ``True`` ordinarily 

88 """ 

89 if stack > 128: # too deep 

90 if skip_deep: 

91 return False 

92 raise MaxRecursion(source) 

93 

94 ret_code = True 

95 dest_root = Path(destin) 

96 dest_root.mkdir(parents=True, exist_ok=True) 

97 src_root = Path(source) 

98 if not src_root.is_dir(): 

99 # Source directory does not exist 

100 raise TemplateMissingError(src_root) 

101 

102 # Add missed files 

103 if stack == 0: 

104 (dest_root / "LICENSE").write_text( 

105 edit_modify(config.license.read_text(), config)) 

106 for missing_dirs in ("docs/_build", "docs/_static", "docs/_templates", 

107 "docs/docs"): 

108 (dest_root / missing_dirs).mkdir(parents=True, exist_ok=True) 

109 (dest_root / "docs/docs/coverage.svg").symlink_to("../coverage.svg") 

110 

111 for node in src_root.glob("*"): 

112 if node.is_file(): 

113 # file 

114 if node.name[-4:] == ".pyc": 

115 continue 

116 (dest_root / node.name.replace("dot.", ".")).write_text( 

117 edit_modify(node.read_text(), config)) 

118 elif node.is_symlink(): 

119 # relative link 

120 # NEXT: in python3.9 os.readlink is replaced by Path().readlink 

121 symlink_target = Path(os.readlink(node)).relative_to(src_root) 

122 (dest_root / node.name.replace("dot.", ".")).symlink_to( 

123 (dest_root / symlink_target), 

124 target_is_directory=Path(os.readlink(node)).is_dir()) 

125 elif node.name in (".git", ".venv", "__pycache__", "licenses"): 

126 # skip 

127 continue 

128 elif node.is_dir(): 

129 # directory: recurse 

130 if node.name == "src": 

131 dirname = dest_root / config.project.name 

132 else: 

133 dirname = dest_root / node.name.replace("dot.", ".") 

134 ret_code &= tree_copy(config, 

135 node, 

136 dirname, 

137 skip_deep=skip_deep, 

138 stack=stack + 1) 

139 return ret_code