|
5 | 5 | from pathlib import Path
|
6 | 6 | from subprocess import STDOUT, CalledProcessError, call, check_output
|
7 | 7 | from tempfile import TemporaryDirectory
|
8 |
| -from typing import List |
| 8 | +from typing import Callable, Dict, List, Tuple |
9 | 9 |
|
10 | 10 | import tomli
|
11 | 11 |
|
|
20 | 20 | MERGE_BRANCH = "skeleton-merge-branch"
|
21 | 21 | # Extensions to change
|
22 | 22 | CHANGE_SUFFIXES = [".py", ".rst", ".cfg", "", ".toml"]
|
23 |
| -# Files not to change |
24 |
| -IGNORE_FILES = [ |
25 |
| - "test_boilerplate_removed.py", |
26 |
| - "pin-requirements.rst", |
27 |
| - "update-tools.rst", |
28 |
| -] |
| 23 | +# Files not to change where IGNORE_FILES[x] is a list of tuples where substitutions |
| 24 | +# will be ignored in that file in any substring between the two strings. |
| 25 | +# An empty list will ignore the whole file. |
| 26 | +IGNORE_FILES: Dict[str, List[Tuple[str, str]]] = { |
| 27 | + "update-tools.rst": [], |
| 28 | + "test_boilerplate_removed.py": [], |
| 29 | + "pin-requirements.rst": [], |
| 30 | + "0002-switched-to-pip-skeleton.rst:": [], |
| 31 | + "README.rst": [ |
| 32 | + ("adopt this skeleton project see", "that describes what your module does") |
| 33 | + ], |
| 34 | +} |
29 | 35 |
|
30 | 36 | SKELETON_ROOT_COMMIT = "ededf00035e6ccfac78946213009c1ecd7c110a9"
|
31 | 37 |
|
@@ -54,6 +60,48 @@ def __truediv__(self, other) -> Path:
|
54 | 60 | return Path(self.name) / other
|
55 | 61 |
|
56 | 62 |
|
| 63 | +def find_ignore_sections( |
| 64 | + file_name: str, file_text: str, ignore_sections: List[Tuple[str, str]] |
| 65 | +) -> List[re.Match]: |
| 66 | + ignore_section_matches = [] |
| 67 | + for sub_strings in ignore_sections: |
| 68 | + pre_sub_string, post_sub_string = sub_strings |
| 69 | + regex = rf"(?s){pre_sub_string}(.*?){post_sub_string}" |
| 70 | + # finditer so we can throw an error if the used ignore strings ignore |
| 71 | + # more than once in the file |
| 72 | + original_substrings = list(re.finditer(regex, file_text)) |
| 73 | + assert original_substrings, ( |
| 74 | + f"could not find substrings {pre_sub_string} or " |
| 75 | + f"{post_sub_string} in {file_name}." |
| 76 | + ) |
| 77 | + assert len(original_substrings) == 1, ( |
| 78 | + f"multiple substrings found between {pre_sub_string} and " |
| 79 | + f"{post_sub_string} in {file_name}." |
| 80 | + ) |
| 81 | + ignore_section_matches.append(original_substrings[0]) |
| 82 | + |
| 83 | + ignore_section_matches.sort(key=lambda x: x.start()) |
| 84 | + return ignore_section_matches |
| 85 | + |
| 86 | + |
| 87 | +def replace_text_ignoring_sections( |
| 88 | + text: str, |
| 89 | + ignore_section_matches: List[re.Match], |
| 90 | + text_replacement_method: Callable, |
| 91 | +) -> str: |
| 92 | + replacement_text = "" |
| 93 | + next_start = 0 |
| 94 | + for ignore_section in ignore_section_matches: |
| 95 | + replacement_text += text_replacement_method( |
| 96 | + text[next_start : ignore_section.start()] |
| 97 | + ) |
| 98 | + replacement_text += text[ignore_section.start() : ignore_section.end()] |
| 99 | + next_start = ignore_section.end() |
| 100 | + |
| 101 | + replacement_text += text_replacement_method(text[next_start : len(text)]) |
| 102 | + return replacement_text |
| 103 | + |
| 104 | + |
57 | 105 | def merge_skeleton(
|
58 | 106 | path: Path,
|
59 | 107 | org: str,
|
@@ -106,6 +154,21 @@ def replace_in_file(file_path: Path, text_from: str, text_to: str):
|
106 | 154 | if child.suffix in CHANGE_SUFFIXES and child.name not in IGNORE_FILES:
|
107 | 155 | text = replace_text(child.read_text())
|
108 | 156 | child.write_text(text)
|
| 157 | + # Replace the file, ignoring text between specified substrings |
| 158 | + elif ( |
| 159 | + child.suffix in CHANGE_SUFFIXES |
| 160 | + and child.name in IGNORE_FILES |
| 161 | + and IGNORE_FILES[child.name] |
| 162 | + ): |
| 163 | + original_text = child.read_text() |
| 164 | + ignore_sections = find_ignore_sections( |
| 165 | + child.name, original_text, IGNORE_FILES[child.name] |
| 166 | + ) |
| 167 | + child.write_text( |
| 168 | + replace_text_ignoring_sections( |
| 169 | + original_text, ignore_sections, replace_text |
| 170 | + ) |
| 171 | + ) |
109 | 172 |
|
110 | 173 | # Change instructions in the docs to reflect which pip skeleton is in use
|
111 | 174 | replace_in_file(
|
|
0 commit comments