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
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
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"""
24import os
25import stat
26from pathlib import Path
28from pyprojstencil.common import edit_modify
29from pyprojstencil.configure import PyConfig
30from pyprojstencil.errors import MaxRecursion, TemplateMissingError
33def _mod_exec(exec_file: os.PathLike) -> None:
34 """
35 Make file executable
37 Args:
38 exec_file: path of file to be made executable
40 Returns:
41 ``None``
42 """
43 st = os.stat(exec_file)
44 os.chmod(exec_file, st.st_mode | stat.S_IEXEC)
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)
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:
67 - .git
68 - .venv
70 - Nodes handling:
72 - create directories
73 - interpret links and link them
74 - :meth:`edit_modify` and write files
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)
81 Raises:
82 TemplateMissingError: source could not be located
83 MaxRecursion: Tree structure is too deep
84 OSError: Permission, FileExists, etc
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)
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)
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")
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