| 137 | |
| 138 | @contextmanager |
| 139 | def rewrite( |
| 140 | path: StrPath, |
| 141 | encoding: Optional[str], |
| 142 | follow_symlinks: bool = False, |
| 143 | ) -> Iterator[Tuple[IO[str], IO[str]]]: |
| 144 | if follow_symlinks: |
| 145 | path = os.path.realpath(path) |
| 146 | |
| 147 | try: |
| 148 | source: IO[str] = open(path, encoding=encoding) |
| 149 | try: |
| 150 | path_stat = os.lstat(path) |
| 151 | original_mode: Optional[int] = ( |
| 152 | stat.S_IMODE(path_stat.st_mode) |
| 153 | if stat.S_ISREG(path_stat.st_mode) |
| 154 | else None |
| 155 | ) |
| 156 | except BaseException: |
| 157 | source.close() |
| 158 | raise |
| 159 | except FileNotFoundError: |
| 160 | source = io.StringIO("") |
| 161 | original_mode = None |
| 162 | |
| 163 | with tempfile.NamedTemporaryFile( |
| 164 | mode="w", |
| 165 | encoding=encoding, |
| 166 | delete=False, |
| 167 | prefix=".tmp_", |
| 168 | dir=os.path.dirname(os.path.abspath(path)), |
| 169 | ) as dest: |
| 170 | dest_path = pathlib.Path(dest.name) |
| 171 | error = None |
| 172 | |
| 173 | try: |
| 174 | with source: |
| 175 | yield (source, dest) |
| 176 | except BaseException as err: |
| 177 | error = err |
| 178 | |
| 179 | if error is None: |
| 180 | try: |
| 181 | if original_mode is not None: |
| 182 | os.chmod(dest_path, original_mode) |
| 183 | |
| 184 | os.replace(dest_path, path) |
| 185 | except BaseException: |
| 186 | dest_path.unlink(missing_ok=True) |
| 187 | raise |
| 188 | else: |
| 189 | dest_path.unlink(missing_ok=True) |
| 190 | raise error from None |
| 191 | |
| 192 | |
| 193 | def set_key( |