diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -2330,6 +2330,9 @@ class memfilectx(committablefilectx): if copied: self._copied = (copied, nullid) + def cmp(self, fctx): + return self.data() != fctx.data() + def data(self): return self._data diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py --- a/mercurial/pure/osutil.py +++ b/mercurial/pure/osutil.py @@ -267,7 +267,8 @@ else: return self._file.__setattr__(name, value) def __enter__(self): - return self._file.__enter__() + self._file.__enter__() + return self def __exit__(self, exc_type, exc_value, exc_tb): return self._file.__exit__(exc_type, exc_value, exc_tb) diff --git a/mercurial/vfs.py b/mercurial/vfs.py --- a/mercurial/vfs.py +++ b/mercurial/vfs.py @@ -525,7 +525,8 @@ class closewrapbase(object): return delattr(self._origfh, attr) def __enter__(self): - return self._origfh.__enter__() + self._origfh.__enter__() + return self def __exit__(self, exc_type, exc_value, exc_tb): raise NotImplementedError('attempted instantiating ' + str(type(self))) diff --git a/mercurial/windows.py b/mercurial/windows.py --- a/mercurial/windows.py +++ b/mercurial/windows.py @@ -70,7 +70,8 @@ class mixedfilemodewrapper(object): object.__setattr__(self, r'_lastop', 0) def __enter__(self): - return self._fp.__enter__() + self._fp.__enter__() + return self def __exit__(self, exc_type, exc_val, exc_tb): self._fp.__exit__(exc_type, exc_val, exc_tb) @@ -132,7 +133,10 @@ class fdproxy(object): self._fp = fp def __enter__(self): - return self._fp.__enter__() + self._fp.__enter__() + # Return this wrapper for the context manager so that the name is + # still available. + return self def __exit__(self, exc_type, exc_value, traceback): self._fp.__exit__(exc_type, exc_value, traceback) diff --git a/mercurial/worker.py b/mercurial/worker.py --- a/mercurial/worker.py +++ b/mercurial/worker.py @@ -213,11 +213,7 @@ def _posixworker(ui, func, staticargs, a waitforworkers() signal.signal(signal.SIGCHLD, oldchldhandler) selector.close() - status = problem[0] - if status: - if status < 0: - os.kill(os.getpid(), -status) - sys.exit(status) + return problem[0] try: openpipes = len(pipes) while openpipes > 0: @@ -236,7 +232,11 @@ def _posixworker(ui, func, staticargs, a killworkers() cleanup() raise - cleanup() + status = cleanup() + if status: + if status < 0: + os.kill(os.getpid(), -status) + sys.exit(status) def _posixexitstatus(code): '''convert a posix exit status into the same form returned by diff --git a/tests/run-tests.py b/tests/run-tests.py --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -587,6 +587,17 @@ def rename(src, dst): shutil.copy(src, dst) os.remove(src) +def makecleanable(path): + """Try to fix directory permission recursively so that the entire tree + can be deleted""" + for dirpath, dirnames, _filenames in os.walk(path, topdown=True): + for d in dirnames: + p = os.path.join(dirpath, d) + try: + os.chmod(p, os.stat(p).st_mode & 0o777 | 0o700) # chmod u+rwx + except OSError: + pass + _unified_diff = difflib.unified_diff if PYTHON3: import functools @@ -953,7 +964,13 @@ class Test(unittest.TestCase): (self._testtmp.decode('utf-8'), self._threadtmp.decode('utf-8'))) else: - shutil.rmtree(self._testtmp, True) + try: + shutil.rmtree(self._testtmp) + except OSError: + # unreadable directory may be left in $TESTTMP; fix permission + # and try again + makecleanable(self._testtmp) + shutil.rmtree(self._testtmp, True) shutil.rmtree(self._threadtmp, True) if self._usechg: