Skip to content

Commit d77fadd

Browse files
authored
Merge pull request #1700 from EliahKagan/untangle-skips
Fix bugs affecting exception wrapping in rmtree callback
2 parents 8107cbf + a9b05ec commit d77fadd

File tree

6 files changed

+262
-123
lines changed

6 files changed

+262
-123
lines changed

git/objects/submodule/base.py

+11-33
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,37 @@
1-
# need a dict to set bloody .name field
21
from io import BytesIO
32
import logging
43
import os
4+
import os.path as osp
55
import stat
66
import uuid
77

88
import git
99
from git.cmd import Git
10-
from git.compat import (
11-
defenc,
12-
is_win,
13-
)
14-
from git.config import SectionConstraint, GitConfigParser, cp
10+
from git.compat import defenc, is_win
11+
from git.config import GitConfigParser, SectionConstraint, cp
1512
from git.exc import (
13+
BadName,
1614
InvalidGitRepositoryError,
1715
NoSuchPathError,
1816
RepositoryDirtyError,
19-
BadName,
2017
)
2118
from git.objects.base import IndexObject, Object
2219
from git.objects.util import TraversableIterableObj
23-
2420
from git.util import (
25-
join_path_native,
26-
to_native_path_linux,
21+
IterableList,
2722
RemoteProgress,
23+
join_path_native,
2824
rmtree,
25+
to_native_path_linux,
2926
unbare_repo,
30-
IterableList,
3127
)
32-
from git.util import HIDE_WINDOWS_KNOWN_ERRORS
33-
34-
import os.path as osp
3528

3629
from .util import (
30+
SubmoduleConfigParser,
31+
find_first_remote_branch,
3732
mkhead,
3833
sm_name,
3934
sm_section,
40-
SubmoduleConfigParser,
41-
find_first_remote_branch,
4235
)
4336

4437

@@ -1060,28 +1053,13 @@ def remove(
10601053
import gc
10611054

10621055
gc.collect()
1063-
try:
1064-
rmtree(str(wtd))
1065-
except Exception as ex:
1066-
if HIDE_WINDOWS_KNOWN_ERRORS:
1067-
from unittest import SkipTest
1068-
1069-
raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex
1070-
raise
1056+
rmtree(str(wtd))
10711057
# END delete tree if possible
10721058
# END handle force
10731059

10741060
if not dry_run and osp.isdir(git_dir):
10751061
self._clear_cache()
1076-
try:
1077-
rmtree(git_dir)
1078-
except Exception as ex:
1079-
if HIDE_WINDOWS_KNOWN_ERRORS:
1080-
from unittest import SkipTest
1081-
1082-
raise SkipTest(f"FIXME: fails with: PermissionError\n {ex}") from ex
1083-
else:
1084-
raise
1062+
rmtree(git_dir)
10851063
# end handle separate bare repository
10861064
# END handle module deletion
10871065

git/util.py

+42-26
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,24 @@
55
# the BSD License: https://opensource.org/license/bsd-3-clause/
66

77
from abc import abstractmethod
8-
import os.path as osp
9-
from .compat import is_win
108
import contextlib
119
from functools import wraps
1210
import getpass
1311
import logging
1412
import os
13+
import os.path as osp
14+
import pathlib
1515
import platform
16-
import subprocess
1716
import re
1817
import shutil
1918
import stat
20-
from sys import maxsize
19+
import subprocess
20+
import sys
2121
import time
2222
from urllib.parse import urlsplit, urlunsplit
2323
import warnings
2424

25-
# from git.objects.util import Traversable
25+
from .compat import is_win
2626

2727
# typing ---------------------------------------------------------
2828

@@ -42,22 +42,17 @@
4242
Tuple,
4343
TypeVar,
4444
Union,
45-
cast,
4645
TYPE_CHECKING,
46+
cast,
4747
overload,
4848
)
4949

50-
import pathlib
51-
5250
if TYPE_CHECKING:
5351
from git.remote import Remote
5452
from git.repo.base import Repo
5553
from git.config import GitConfigParser, SectionConstraint
5654
from git import Git
5755

58-
# from git.objects.base import IndexObject
59-
60-
6156
from .types import (
6257
Literal,
6358
SupportsIndex,
@@ -75,7 +70,6 @@
7570

7671
# ---------------------------------------------------------------------
7772

78-
7973
from gitdb.util import ( # NOQA @IgnorePep8
8074
make_sha,
8175
LockedFD, # @UnusedImport
@@ -88,7 +82,6 @@
8882
hex_to_bin, # @UnusedImport
8983
)
9084

91-
9285
# NOTE: Some of the unused imports might be used/imported by others.
9386
# Handle once test-cases are back up and running.
9487
# Most of these are unused here, but are for use by git-python modules so these
@@ -116,14 +109,33 @@
116109

117110
log = logging.getLogger(__name__)
118111

119-
# types############################################################
112+
113+
def _read_env_flag(name: str, default: bool) -> bool:
114+
try:
115+
value = os.environ[name]
116+
except KeyError:
117+
return default
118+
119+
log.warning(
120+
"The %s environment variable is deprecated. Its effect has never been documented and changes without warning.",
121+
name,
122+
)
123+
124+
adjusted_value = value.strip().lower()
125+
126+
if adjusted_value in {"", "0", "false", "no"}:
127+
return False
128+
if adjusted_value in {"1", "true", "yes"}:
129+
return True
130+
log.warning("%s has unrecognized value %r, treating as %r.", name, value, default)
131+
return default
120132

121133

122134
#: We need an easy way to see if Appveyor TCs start failing,
123135
#: so the errors marked with this var are considered "acknowledged" ones, awaiting remedy,
124136
#: till then, we wish to hide them.
125-
HIDE_WINDOWS_KNOWN_ERRORS = is_win and os.environ.get("HIDE_WINDOWS_KNOWN_ERRORS", True)
126-
HIDE_WINDOWS_FREEZE_ERRORS = is_win and os.environ.get("HIDE_WINDOWS_FREEZE_ERRORS", True)
137+
HIDE_WINDOWS_KNOWN_ERRORS = is_win and _read_env_flag("HIDE_WINDOWS_KNOWN_ERRORS", True)
138+
HIDE_WINDOWS_FREEZE_ERRORS = is_win and _read_env_flag("HIDE_WINDOWS_FREEZE_ERRORS", True)
127139

128140
# { Utility Methods
129141

@@ -177,25 +189,29 @@ def patch_env(name: str, value: str) -> Generator[None, None, None]:
177189

178190

179191
def rmtree(path: PathLike) -> None:
180-
"""Remove the given recursively.
192+
"""Remove the given directory tree recursively.
181193
182-
:note: we use shutil rmtree but adjust its behaviour to see whether files that
183-
couldn't be deleted are read-only. Windows will not remove them in that case"""
194+
:note: We use :func:`shutil.rmtree` but adjust its behaviour to see whether files that
195+
couldn't be deleted are read-only. Windows will not remove them in that case."""
184196

185-
def onerror(func: Callable, path: PathLike, exc_info: str) -> None:
186-
# Is the error an access error ?
197+
def handler(function: Callable, path: PathLike, _excinfo: Any) -> None:
198+
"""Callback for :func:`shutil.rmtree`. Works either as ``onexc`` or ``onerror``."""
199+
# Is the error an access error?
187200
os.chmod(path, stat.S_IWUSR)
188201

189202
try:
190-
func(path) # Will scream if still not possible to delete.
191-
except Exception as ex:
203+
function(path)
204+
except PermissionError as ex:
192205
if HIDE_WINDOWS_KNOWN_ERRORS:
193206
from unittest import SkipTest
194207

195-
raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex
208+
raise SkipTest(f"FIXME: fails with: PermissionError\n {ex}") from ex
196209
raise
197210

198-
return shutil.rmtree(path, False, onerror)
211+
if sys.version_info >= (3, 12):
212+
shutil.rmtree(path, onexc=handler)
213+
else:
214+
shutil.rmtree(path, onerror=handler)
199215

200216

201217
def rmfile(path: PathLike) -> None:
@@ -995,7 +1011,7 @@ def __init__(
9951011
self,
9961012
file_path: PathLike,
9971013
check_interval_s: float = 0.3,
998-
max_block_time_s: int = maxsize,
1014+
max_block_time_s: int = sys.maxsize,
9991015
) -> None:
10001016
"""Configure the instance
10011017

test-requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ pre-commit
77
pytest
88
pytest-cov
99
pytest-instafail
10+
pytest-subtests
1011
pytest-sugar

test/test_docs.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ def tearDown(self):
2121

2222
gc.collect()
2323

24-
# @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, ## ACTUALLY skipped by `git.submodule.base#L869`.
24+
# ACTUALLY skipped by git.util.rmtree (in local onerror function), from the last call to it via
25+
# git.objects.submodule.base.Submodule.remove (at "handle separate bare repository"), line 1062.
26+
#
27+
# @skipIf(HIDE_WINDOWS_KNOWN_ERRORS,
2528
# "FIXME: helper.wrapper fails with: PermissionError: [WinError 5] Access is denied: "
2629
# "'C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\test_work_tree_unsupportedryfa60di\\master_repo\\.git\\objects\\pack\\pack-bc9e0787aef9f69e1591ef38ea0a6f566ec66fe3.idx") # noqa E501
2730
@with_rw_directory

test/test_submodule.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,10 @@ def _do_base_tests(self, rwrepo):
457457
True,
458458
)
459459

460-
# @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, ## ACTUALLY skipped by `git.submodule.base#L869`.
460+
# ACTUALLY skipped by git.util.rmtree (in local onerror function), called via
461+
# git.objects.submodule.base.Submodule.remove at "method(mp)", line 1011.
462+
#
463+
# @skipIf(HIDE_WINDOWS_KNOWN_ERRORS,
461464
# "FIXME: fails with: PermissionError: [WinError 32] The process cannot access the file because"
462465
# "it is being used by another process: "
463466
# "'C:\\Users\\ankostis\\AppData\\Local\\Temp\\tmp95c3z83bnon_bare_test_base_rw\\git\\ext\\gitdb\\gitdb\\ext\\smmap'") # noqa E501
@@ -819,9 +822,11 @@ def test_git_submodules_and_add_sm_with_new_commit(self, rwdir):
819822
assert commit_sm.binsha == sm_too.binsha
820823
assert sm_too.binsha != sm.binsha
821824

822-
# @skipIf(HIDE_WINDOWS_KNOWN_ERRORS, ## ACTUALLY skipped by `git.submodule.base#L869`.
823-
# "FIXME: helper.wrapper fails with: PermissionError: [WinError 5] Access is denied: "
824-
# "'C:\\Users\\appveyor\\AppData\\Local\\Temp\\1\\test_work_tree_unsupportedryfa60di\\master_repo\\.git\\objects\\pack\\pack-bc9e0787aef9f69e1591ef38ea0a6f566ec66fe3.idx") # noqa E501
825+
@pytest.mark.xfail(
826+
HIDE_WINDOWS_KNOWN_ERRORS,
827+
reason='"The process cannot access the file because it is being used by another process" on call to sm.move',
828+
raises=PermissionError,
829+
)
825830
@with_rw_directory
826831
def test_git_submodule_compatibility(self, rwdir):
827832
parent = git.Repo.init(osp.join(rwdir, "parent"))

0 commit comments

Comments
 (0)