|
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 Dict, List, Optional, Tuple |
| 8 | +from typing import Callable, Dict, List, Tuple |
9 | 9 |
|
10 | 10 | import tomli |
11 | 11 |
|
|
23 | 23 | # Files not to change where IGNORE_FILES[x] is a list of tuples where substitutions |
24 | 24 | # will be ignored in that file in any substring between the two strings. |
25 | 25 | # An empty list will ignore the whole file. |
26 | | -IGNORE_FILES: Dict[str, List[Optional[Tuple[str, str]]]] = { |
| 26 | +IGNORE_FILES: Dict[str, List[Tuple[str, str]]] = { |
27 | 27 | "update-tools.rst": [], |
28 | 28 | "test_boilerplate_removed.py": [], |
29 | 29 | "pin-requirements.rst": [], |
@@ -60,6 +60,48 @@ def __truediv__(self, other) -> Path: |
60 | 60 | return Path(self.name) / other |
61 | 61 |
|
62 | 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 | + |
63 | 105 | def merge_skeleton( |
64 | 106 | path: Path, |
65 | 107 | org: str, |
@@ -119,29 +161,14 @@ def replace_in_file(file_path: Path, text_from: str, text_to: str): |
119 | 161 | and IGNORE_FILES[child.name] |
120 | 162 | ): |
121 | 163 | original_text = child.read_text() |
122 | | - replaced_text = replace_text(original_text) |
123 | | - for sub_strings in IGNORE_FILES[child.name]: |
124 | | - assert isinstance(sub_strings, tuple) |
125 | | - pre_sub_string, post_sub_string = sub_strings |
126 | | - regex = rf"(?s){pre_sub_string}(.*?){post_sub_string}" |
127 | | - # finditer so we can allow for different contents between ignore |
128 | | - # substrings, e.g: `a foo b` and then later in the file `a bar b` |
129 | | - original_substrings = list(re.finditer(regex, original_text)) |
130 | | - replaced_substrings = list(re.finditer(regex, replaced_text)) |
131 | | - assert len(original_substrings) == len(replaced_substrings), ( |
132 | | - "different number of ignored substrings between " |
133 | | - f"{pre_sub_string} and {post_sub_string} found in " |
134 | | - f"{child.name}, did you include replaced " |
135 | | - "text inside the substrings?" |
| 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 |
136 | 170 | ) |
137 | | - for original_substring, replaced_substring in zip( |
138 | | - original_substrings, |
139 | | - replaced_substrings, |
140 | | - ): |
141 | | - replaced_text = replaced_text.replace( |
142 | | - replaced_substring.group(0), original_substring.group(0) |
143 | | - ) |
144 | | - child.write_text(replaced_text) |
| 171 | + ) |
145 | 172 |
|
146 | 173 | # Change instructions in the docs to reflect which pip skeleton is in use |
147 | 174 | replace_in_file( |
|
0 commit comments