diff --git a/jaraco/context/__init__.py b/jaraco/context/__init__.py index 3fb3c7a..9c64406 100644 --- a/jaraco/context/__init__.py +++ b/jaraco/context/__init__.py @@ -133,6 +133,30 @@ def composed(*args, **kwargs): """ +def remove_readonly(func, path, exc_info): + import errno + import stat + + excvalue = exc_info[1] + if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES: + # change the file to be readable,writable,executable: 0777 + os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + # retry + func(path) + else: + raise + + +def windows_safe_remover(): + import platform + + return ( + functools.partial(shutil.rmtree, onerror=remove_readonly) + if platform.system() == 'Windows' + else shutil.rmtree + ) + + @contextlib.contextmanager def temp_dir(remover: Callable[[str], None] = shutil.rmtree) -> Iterator[str]: """ @@ -142,7 +166,6 @@ def temp_dir(remover: Callable[[str], None] = shutil.rmtree) -> Iterator[str]: >>> import pathlib >>> with temp_dir() as the_dir: ... assert os.path.isdir(the_dir) - ... _ = pathlib.Path(the_dir).joinpath('somefile').write_text('contents', encoding='utf-8') >>> assert not os.path.exists(the_dir) """ temp_dir = tempfile.mkdtemp() @@ -152,12 +175,17 @@ def temp_dir(remover: Callable[[str], None] = shutil.rmtree) -> Iterator[str]: remover(temp_dir) +windows_safe_temp_dir = functools.partial(temp_dir, remover=windows_safe_remover) + + @contextlib.contextmanager def repo_context( url, branch: str | None = None, quiet: bool = True, - dest_ctx: Callable[[], contextlib.AbstractContextManager[str]] = temp_dir, + dest_ctx: Callable[ + [], contextlib.AbstractContextManager[str] + ] = windows_safe_temp_dir, ): """ Check out the repo indicated by url. @@ -168,7 +196,7 @@ def repo_context( >>> repo = repo_context('https://github.com/jaraco/jaraco.context') >>> with repo as dest: ... listing = os.listdir(dest) - ... __import__('time').sleep(__import__('platform').system() == 'Windows') + ... __import__('time').sleep(__import__('platform').system() == 'Windows') # #12 >>> 'README.rst' in listing True """