|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | 5 | import asyncio |
| 6 | +import errno |
6 | 7 | import shutil |
| 8 | +import stat |
7 | 9 | import sys |
8 | 10 | import warnings |
9 | 11 | from contextlib import asynccontextmanager |
10 | 12 | from pathlib import Path |
11 | | -from typing import TYPE_CHECKING, AsyncGenerator |
| 13 | +from typing import TYPE_CHECKING, AsyncGenerator, Callable |
12 | 14 | from urllib.parse import urlparse |
13 | 15 |
|
14 | 16 | from gitingest.clone import clone_repo |
|
22 | 24 | from gitingest.utils.query_parser_utils import KNOWN_GIT_HOSTS |
23 | 25 |
|
24 | 26 | if TYPE_CHECKING: |
| 27 | + from types import TracebackType |
| 28 | + |
25 | 29 | from gitingest.schemas import IngestionQuery |
26 | 30 |
|
27 | 31 |
|
@@ -258,11 +262,32 @@ async def _clone_repo_if_remote(query: IngestionQuery, *, token: str | None) -> |
258 | 262 | try: |
259 | 263 | yield |
260 | 264 | finally: |
261 | | - shutil.rmtree(query.local_path.parent) |
| 265 | + shutil.rmtree(query.local_path.parent, onerror=_handle_remove_readonly) |
262 | 266 | else: |
263 | 267 | yield |
264 | 268 |
|
265 | 269 |
|
| 270 | +def _handle_remove_readonly( |
| 271 | + func: Callable, |
| 272 | + path: str, |
| 273 | + exc_info: tuple[type[BaseException], BaseException, TracebackType], |
| 274 | +) -> None: |
| 275 | + """Handle permission errors raised by ``shutil.rmtree()``. |
| 276 | +
|
| 277 | + * Makes the target writable (removes the read-only attribute). |
| 278 | + * Retries the original operation (``func``) once. |
| 279 | +
|
| 280 | + """ |
| 281 | + exc = exc_info[1] |
| 282 | + # Handle only'Permission denied' and 'Operation not permitted' |
| 283 | + if not isinstance(exc, OSError) or exc.errno not in {errno.EACCES, errno.EPERM}: |
| 284 | + raise exc |
| 285 | + |
| 286 | + # Make the target writable |
| 287 | + Path(path).chmod(stat.S_IWRITE) |
| 288 | + func(path) |
| 289 | + |
| 290 | + |
266 | 291 | async def _write_output(tree: str, content: str, target: str | None) -> None: |
267 | 292 | """Write combined output to ``target`` (``"-"`` ⇒ stdout). |
268 | 293 |
|
|
0 commit comments