Changed code to our own context manager type mutex locker class.

Sat, 10 Oct 2020 16:03:53 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 10 Oct 2020 16:03:53 +0200
changeset 7774
9eed155411f0
parent 7773
fe42bd17d4fe
child 7775
4a1db75550bd

Changed code to our own context manager type mutex locker class.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/E5Gui/E5OverrideCursor.py file | annotate | diff | comparison | revisions
eric6/E5Utilities/E5MutexLocker.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnBlameDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnChangeListsDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnDiffDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnInfoDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnLogBrowserDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnPropListDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnRepoBrowserDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnStatusDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/SvnTagBranchListDialog.py file | annotate | diff | comparison | revisions
eric6/Plugins/VcsPlugins/vcsPySvn/subversion.py file | annotate | diff | comparison | revisions
eric6/WebBrowser/AdBlock/AdBlockManager.py file | annotate | diff | comparison | revisions
eric6/WebBrowser/Network/EricSchemeHandler.py file | annotate | diff | comparison | revisions
eric6/WebBrowser/Network/NetworkUrlInterceptor.py file | annotate | diff | comparison | revisions
eric6/WebBrowser/Network/QtHelpSchemeHandler.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6.e4p	Sat Oct 10 16:03:53 2020 +0200
@@ -195,6 +195,7 @@
     <Source>eric6/E5Network/__init__.py</Source>
     <Source>eric6/E5Network/data/__init__.py</Source>
     <Source>eric6/E5Utilities/E5Cache.py</Source>
+    <Source>eric6/E5Utilities/E5MutexLocker.py</Source>
     <Source>eric6/E5Utilities/__init__.py</Source>
     <Source>eric6/E5XML/Config.py</Source>
     <Source>eric6/E5XML/DebuggerPropertiesReader.py</Source>
@@ -2096,6 +2097,9 @@
     <Other>eric6/APIs/MicroPython/circuitpython.api</Other>
     <Other>eric6/APIs/MicroPython/microbit.api</Other>
     <Other>eric6/APIs/MicroPython/micropython.api</Other>
+    <Other>eric6/APIs/Python/zope-2.10.7.api</Other>
+    <Other>eric6/APIs/Python/zope-2.11.2.api</Other>
+    <Other>eric6/APIs/Python/zope-3.3.1.api</Other>
     <Other>eric6/APIs/Python3/PyQt4.bas</Other>
     <Other>eric6/APIs/Python3/PyQt5.bas</Other>
     <Other>eric6/APIs/Python3/PyQtChart.bas</Other>
@@ -2103,9 +2107,6 @@
     <Other>eric6/APIs/Python3/QScintilla2.bas</Other>
     <Other>eric6/APIs/Python3/eric6.api</Other>
     <Other>eric6/APIs/Python3/eric6.bas</Other>
-    <Other>eric6/APIs/Python/zope-2.10.7.api</Other>
-    <Other>eric6/APIs/Python/zope-2.11.2.api</Other>
-    <Other>eric6/APIs/Python/zope-3.3.1.api</Other>
     <Other>eric6/APIs/QSS/qss.api</Other>
     <Other>eric6/APIs/Ruby/Ruby-1.8.7.api</Other>
     <Other>eric6/APIs/Ruby/Ruby-1.8.7.bas</Other>
--- a/eric6/E5Gui/E5OverrideCursor.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/E5Gui/E5OverrideCursor.py	Sat Oct 10 16:03:53 2020 +0200
@@ -14,7 +14,6 @@
 from PyQt5.QtGui import QCursor, QGuiApplication
 
 
-# TODO: add similar class for QMutexLocker
 class E5OverrideCursor(contextlib.AbstractContextManager):
     """
     Class implementing a context manager class for an override cursor.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/E5Utilities/E5MutexLocker.py	Sat Oct 10 16:03:53 2020 +0200
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a context manager locking and unlocking a mutex.
+"""
+
+import contextlib
+
+
+class E5MutexLocker(contextlib.AbstractContextManager):
+    """
+    Class implementing a context manager locking and unlocking a mutex.
+    """
+    def __init__(self, mutex):
+        """
+        Constructor
+        
+        @param mutex reference to the mutex to be locked
+        @type QMutex or QRecursiveMutex
+        """
+        self.__mutex = mutex
+    
+    def __enter__(self):
+        """
+        Special method called when entering the runtime ccontext.
+        
+        @return reference to the context manager object
+        @rtype E5OverrideCursor
+        """
+        self.__mutex.lock()
+        
+        return self
+    
+    def __exit__(self, exc_type, exc_value, traceback):
+        """
+        Special method called when exiting the runtime ccontext.
+        
+        @param exc_type type of an exception raised in the runtime context
+        @param exc_value value of an exception raised in the runtime context
+        @param traceback traceback of an exception raised in the runtime
+            context
+        @return always returns None to not suppress any exception
+        @rtype None
+        """
+        self.__mutex.unlock()
+        
+        return None
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnBlameDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnBlameDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -12,11 +12,13 @@
 
 import pysvn
 
-from PyQt5.QtCore import QMutexLocker, Qt
+from PyQt5.QtCore import Qt
 from PyQt5.QtWidgets import (
     QHeaderView, QDialog, QDialogButtonBox, QTreeWidgetItem
 )
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnDialogMixin import SvnDialogMixin
 from .Ui_SvnBlameDialog import Ui_SvnBlameDialog
 
@@ -67,12 +69,11 @@
         
         dname, fname = self.vcs.splitPath(fn)
         
-        locker = QMutexLocker(self.vcs.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         try:
-            annotations = self.client.annotate(fname)
-            locker.unlock()
+            with E5MutexLocker(self.vcs.vcsExecutionMutex):
+                annotations = self.client.annotate(fname)
             for annotation in annotations:
                 author = annotation["author"]
                 line = annotation["line"]
@@ -80,7 +81,6 @@
                     annotation["revision"].number, author,
                     annotation["number"] + 1, line)
         except pysvn.ClientError as e:
-            locker.unlock()
             self.__showError(e.args[0] + '\n')
         self.__finish()
         os.chdir(cwd)
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnChangeListsDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnChangeListsDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -11,11 +11,13 @@
 
 import pysvn
 
-from PyQt5.QtCore import pyqtSlot, Qt, QMutexLocker
+from PyQt5.QtCore import pyqtSlot, Qt
 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem
 
 from E5Gui.E5OverrideCursor import E5OverrideCursor
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnDialogMixin import SvnDialogMixin
 
 from .Ui_SvnChangeListsDialog import Ui_SvnChangeListsDialog
@@ -77,10 +79,10 @@
             self.tr("Files (relative to {0}):").format(path))
         
         with E5OverrideCursor():
-            locker = QMutexLocker(self.vcs.vcsExecutionMutex)
             try:
-                entries = self.client.get_changelist(
-                    path, depth=pysvn.depth.infinity)
+                with E5MutexLocker(self.vcs.vcsExecutionMutex):
+                    entries = self.client.get_changelist(
+                        path, depth=pysvn.depth.infinity)
                 for entry in entries:
                     file = entry[0]
                     changelist = entry[1]
@@ -90,7 +92,6 @@
                     if filename not in self.changeListsDict[changelist]:
                         self.changeListsDict[changelist].append(filename)
             except pysvn.ClientError as e:
-                locker.unlock()
                 self.__showError(e.args[0])
         self.__finish()
     
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnDiffDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnDiffDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -13,7 +13,7 @@
 
 import pysvn
 
-from PyQt5.QtCore import QMutexLocker, QFileInfo, QDateTime, Qt, pyqtSlot
+from PyQt5.QtCore import QFileInfo, QDateTime, Qt, pyqtSlot
 from PyQt5.QtGui import QTextCursor
 from PyQt5.QtWidgets import QWidget, QDialogButtonBox
 
@@ -21,6 +21,8 @@
 from E5Gui import E5MessageBox, E5FileDialog
 from E5Gui.E5OverrideCursor import E5OverrideCursor
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnDialogMixin import SvnDialogMixin
 from .Ui_SvnDiffDialog import Ui_SvnDiffDialog
 from .SvnDiffHighlighter import SvnDiffHighlighter
@@ -200,66 +202,70 @@
             fnames = [fname]
         
         with E5OverrideCursor():
-            locker = QMutexLocker(self.vcs.vcsExecutionMutex)
             cwd = os.getcwd()
             os.chdir(dname)
             try:
                 dname = e5App().getObject('Project').getRelativePath(dname)
                 if dname:
                     dname += "/"
-                for name in fnames:
-                    self.__showError(
-                        self.tr("Processing file '{0}'...\n").format(name))
-                    if urls is not None:
-                        url1 = "{0}/{1}{2}".format(urls[0], dname, name)
-                        url2 = "{0}/{1}{2}".format(urls[1], dname, name)
-                        if summary:
-                            diff_summary = self.client.diff_summarize(
-                                url1, revision1=rev1,
-                                url_or_path2=url2, revision2=rev2,
-                                recurse=recurse)
-                            diff_list = []
-                            for diff_sum in diff_summary:
-                                path = diff_sum['path']
-                                diff_list.append("{0} {1}".format(
-                                    self.__getDiffSummaryKind(
-                                        diff_sum['summarize_kind']),
-                                    path))
-                            diffText = os.linesep.join(diff_list)
+                with E5MutexLocker(self.vcs.vcsExecutionMutex):
+                    for name in fnames:
+                        self.__showError(
+                            self.tr("Processing file '{0}'...\n").format(name))
+                        if urls is not None:
+                            url1 = "{0}/{1}{2}".format(urls[0], dname, name)
+                            url2 = "{0}/{1}{2}".format(urls[1], dname, name)
+                            if summary:
+                                diff_summary = self.client.diff_summarize(
+                                    url1, revision1=rev1,
+                                    url_or_path2=url2, revision2=rev2,
+                                    recurse=recurse)
+                                diff_list = []
+                                for diff_sum in diff_summary:
+                                    path = diff_sum['path']
+                                    diff_list.append("{0} {1}".format(
+                                        self.__getDiffSummaryKind(
+                                            diff_sum['summarize_kind']),
+                                        path))
+                                diffText = os.linesep.join(diff_list)
+                            else:
+                                diffText = self.client.diff(
+                                    tmpdir,
+                                    url1, revision1=rev1,
+                                    url_or_path2=url2, revision2=rev2,
+                                    recurse=recurse)
                         else:
-                            diffText = self.client.diff(
-                                tmpdir,
-                                url1, revision1=rev1,
-                                url_or_path2=url2, revision2=rev2,
-                                recurse=recurse)
-                    else:
-                        if pegRev is not None:
-                            diffText = self.client.diff_peg(
-                                tmpdir, name,
-                                peg_revision=self.__getVersionArg(pegRev),
-                                revision_start=rev1, revision_end=rev2,
-                                recurse=recurse)
-                        else:
-                            diffText = self.client.diff(
-                                tmpdir, name,
-                                revision1=rev1, revision2=rev2, recurse=recurse)
-                    counter = 0
-                    for line in diffText.splitlines():
-                        if line.startswith("--- ") or line.startswith("+++ "):
-                            self.__processFileLine(line)
-                        
-                        self.__appendText("{0}{1}".format(line, os.linesep))
-                        counter += 1
-                        if counter == 30:
-                            # check for cancel every 30 lines
-                            counter = 0
-                            if self._clientCancelCallback():
-                                break
-                    if self._clientCancelCallback():
-                        break
+                            if pegRev is not None:
+                                diffText = self.client.diff_peg(
+                                    tmpdir, name,
+                                    peg_revision=self.__getVersionArg(pegRev),
+                                    revision_start=rev1, revision_end=rev2,
+                                    recurse=recurse)
+                            else:
+                                diffText = self.client.diff(
+                                    tmpdir, name,
+                                    revision1=rev1, revision2=rev2,
+                                    recurse=recurse)
+                        counter = 0
+                        for line in diffText.splitlines():
+                            if (
+                                line.startswith("--- ") or
+                                line.startswith("+++ ")
+                            ):
+                                self.__processFileLine(line)
+                            
+                            self.__appendText(
+                                "{0}{1}".format(line, os.linesep))
+                            counter += 1
+                            if counter == 30:
+                                # check for cancel every 30 lines
+                                counter = 0
+                                if self._clientCancelCallback():
+                                    break
+                        if self._clientCancelCallback():
+                            break
             except pysvn.ClientError as e:
                 self.__showError(e.args[0])
-            locker.unlock()
             os.chdir(cwd)
         self.__finish()
         
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnInfoDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnInfoDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -13,9 +13,11 @@
 
 import pysvn
 
-from PyQt5.QtCore import Qt, QMutexLocker
+from PyQt5.QtCore import Qt
 from PyQt5.QtWidgets import QDialog, QApplication
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnUtilities import formatTime
 from .SvnDialogMixin import SvnDialogMixin
 from VCS.Ui_RepositoryInfoDialog import Ui_VcsRepositoryInfoDialog
@@ -57,11 +59,11 @@
         @param projectPath path name of the project (string)
         @param fn file or directory name relative to the project (string)
         """
-        locker = QMutexLocker(self.vcs.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(projectPath)
         try:
-            entries = self.client.info2(fn, recurse=False)
+            with E5MutexLocker(self.vcs.vcsExecutionMutex):
+                entries = self.client.info2(fn, recurse=False)
             infoStr = "<table>"
             for path, info in entries:
                 infoStr += self.tr(
@@ -177,7 +179,6 @@
             self.infoBrowser.setHtml(infoStr)
         except pysvn.ClientError as e:
             self.__showError(e.args[0])
-        locker.unlock()
         os.chdir(cwd)
         
     def __showError(self, msg):
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnLogBrowserDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnLogBrowserDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -12,7 +12,7 @@
 
 import pysvn
 
-from PyQt5.QtCore import QMutexLocker, QDate, QRegExp, Qt, pyqtSlot, QPoint
+from PyQt5.QtCore import Qt, QDate, QRegExp, pyqtSlot, QPoint
 from PyQt5.QtWidgets import (
     QHeaderView, QWidget, QApplication, QDialogButtonBox, QTreeWidgetItem
 )
@@ -20,6 +20,8 @@
 from E5Gui import E5MessageBox
 from E5Gui.E5OverrideCursor import E5OverrideCursor
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnUtilities import formatTime, dateFromTime_t
 from .SvnDialogMixin import SvnDialogMixin
 
@@ -275,32 +277,34 @@
                 start = pysvn.Revision(pysvn.opt_revision_kind.head)
         
         with E5OverrideCursor():
-            locker = QMutexLocker(self.vcs.vcsExecutionMutex)
             cwd = os.getcwd()
             os.chdir(self.dname)
             try:
                 nextRev = 0
                 fetched = 0
                 logs = []
-                while fetched < limit:
-                    flimit = min(fetchLimit, limit - fetched)
-                    if fetched == 0:
-                        revstart = start
-                    else:
-                        revstart = pysvn.Revision(
-                            pysvn.opt_revision_kind.number, nextRev)
-                    allLogs = self.client.log(
-                        self.fname, revision_start=revstart,
-                        discover_changed_paths=True, limit=flimit + 1,
-                        strict_node_history=self.stopCheckBox.isChecked())
-                    if len(allLogs) <= flimit or self._clientCancelCallback():
-                        logs.extend(allLogs)
-                        break
-                    else:
-                        logs.extend(allLogs[:-1])
-                        nextRev = allLogs[-1]["revision"].number
-                        fetched += fetchLimit
-                locker.unlock()
+                with E5MutexLocker(self.vcs.vcsExecutionMutex):
+                    while fetched < limit:
+                        flimit = min(fetchLimit, limit - fetched)
+                        if fetched == 0:
+                            revstart = start
+                        else:
+                            revstart = pysvn.Revision(
+                                pysvn.opt_revision_kind.number, nextRev)
+                        allLogs = self.client.log(
+                            self.fname, revision_start=revstart,
+                            discover_changed_paths=True, limit=flimit + 1,
+                            strict_node_history=self.stopCheckBox.isChecked())
+                        if (
+                            len(allLogs) <= flimit or
+                            self._clientCancelCallback()
+                        ):
+                            logs.extend(allLogs)
+                            break
+                        else:
+                            logs.extend(allLogs[:-1])
+                            nextRev = allLogs[-1]["revision"].number
+                            fetched += fetchLimit
                 
                 for log in logs:
                     author = log["author"]
@@ -336,7 +340,6 @@
                 self.__resortLog()
                 self.__filterLogs()
             except pysvn.ClientError as e:
-                locker.unlock()
                 self.__showError(e.args[0])
             os.chdir(cwd)
         self.__finish()
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnPropListDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnPropListDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -13,11 +13,13 @@
 
 import pysvn
 
-from PyQt5.QtCore import pyqtSlot, QMutexLocker, Qt
+from PyQt5.QtCore import pyqtSlot, Qt
 from PyQt5.QtWidgets import (
     QWidget, QHeaderView, QApplication, QDialogButtonBox, QTreeWidgetItem
 )
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnDialogMixin import SvnDialogMixin
 from .Ui_SvnPropListDialog import Ui_SvnPropListDialog
 
@@ -110,28 +112,28 @@
             dname, fname = self.vcs.splitPath(fn)
             fnames = [fname]
         
-        locker = QMutexLocker(self.vcs.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
-        try:
-            for name in fnames:
-                proplist = self.client.proplist(name, recurse=recursive)
-                counter = 0
-                for path, prop in proplist:
-                    for propName, propVal in list(prop.items()):
-                        self.__generateItem(path, propName, propVal)
-                        self.propsFound = True
-                    counter += 1
-                    if counter == 30:
-                        # check for cancel every 30 items
-                        counter = 0
-                        if self._clientCancelCallback():
-                            break
-                if self._clientCancelCallback():
-                    break
-        except pysvn.ClientError as e:
-            self.__showError(e.args[0])
-        locker.unlock()
+        with E5MutexLocker(self.vcs.vcsExecutionMutex):
+            try:
+                for name in fnames:
+                    proplist = self.client.proplist(name, recurse=recursive)
+                    counter = 0
+                    for path, prop in proplist:
+                        for propName, propVal in list(prop.items()):
+                            self.__generateItem(path, propName, propVal)
+                            self.propsFound = True
+                        counter += 1
+                        if counter == 30:
+                            # check for cancel every 30 items
+                            counter = 0
+                            if self._clientCancelCallback():
+                                break
+                    if self._clientCancelCallback():
+                        break
+            except pysvn.ClientError as e:
+                self.__showError(e.args[0])
+        
         self.__finish()
         os.chdir(cwd)
         
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnRepoBrowserDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnRepoBrowserDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -10,7 +10,7 @@
 
 import pysvn
 
-from PyQt5.QtCore import QMutexLocker, Qt, pyqtSlot
+from PyQt5.QtCore import Qt, pyqtSlot
 from PyQt5.QtWidgets import (
     QHeaderView, QDialog, QApplication, QDialogButtonBox, QTreeWidgetItem
 )
@@ -18,6 +18,8 @@
 from E5Gui import E5MessageBox
 from E5Gui.E5OverrideCursor import E5OverrideCursor
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnUtilities import formatTime
 from .SvnDialogMixin import SvnDialogMixin
 
@@ -154,58 +156,55 @@
             parent = self.repoTree
         
         with E5OverrideCursor():
-            locker = QMutexLocker(self.vcs.vcsExecutionMutex)
             try:
-                try:
+                with E5MutexLocker(self.vcs.vcsExecutionMutex):
                     entries = self.client.list(url, recurse=False)
-                    firstTime = parent == self.repoTree
-                    for dirent, _lock in entries:
-                        if (
-                            (firstTime and dirent["path"] != url) or
-                            (parent != self.repoTree and dirent["path"] == url)
-                        ):
-                            continue
-                        if firstTime:
-                            if dirent["repos_path"] != "/":
-                                repoUrl = dirent["path"].replace(
-                                    dirent["repos_path"], "")
-                            else:
-                                repoUrl = dirent["path"]
-                            if repoUrl != url:
-                                self.__ignoreExpand = True
-                                itm = self.__generateItem(
-                                    parent, "/", "", "", 0, "",
-                                    pysvn.node_kind.dir, repoUrl)
-                                itm.setExpanded(True)
-                                parent = itm
-                                urlPart = repoUrl
-                                for element in (
-                                    dirent["repos_path"].split("/")[:-1]
-                                ):
-                                    if element:
-                                        urlPart = "{0}/{1}".format(urlPart,
-                                                                   element)
-                                        itm = self.__generateItem(
-                                            parent, element, "", "", 0, "",
-                                            pysvn.node_kind.dir, urlPart)
-                                        itm.setExpanded(True)
-                                        parent = itm
-                                self.__ignoreExpand = False
-                        itm = self.__generateItem(
-                            parent, dirent["repos_path"],
-                            dirent["created_rev"], dirent["last_author"],
-                            dirent["size"], dirent["time"], dirent["kind"],
-                            dirent["path"])
-                    self.__resort()
-                    self.__resizeColumns()
-                except pysvn.ClientError as e:
-                    self.__showError(e.args[0])
-                except AttributeError:
-                    self.__showError(
-                        self.tr("The installed version of PySvn should be "
-                                "1.4.0 or better."))
-            finally:
-                locker.unlock()
+                firstTime = parent == self.repoTree
+                for dirent, _lock in entries:
+                    if (
+                        (firstTime and dirent["path"] != url) or
+                        (parent != self.repoTree and dirent["path"] == url)
+                    ):
+                        continue
+                    if firstTime:
+                        if dirent["repos_path"] != "/":
+                            repoUrl = dirent["path"].replace(
+                                dirent["repos_path"], "")
+                        else:
+                            repoUrl = dirent["path"]
+                        if repoUrl != url:
+                            self.__ignoreExpand = True
+                            itm = self.__generateItem(
+                                parent, "/", "", "", 0, "",
+                                pysvn.node_kind.dir, repoUrl)
+                            itm.setExpanded(True)
+                            parent = itm
+                            urlPart = repoUrl
+                            for element in (
+                                dirent["repos_path"].split("/")[:-1]
+                            ):
+                                if element:
+                                    urlPart = "{0}/{1}".format(urlPart,
+                                                               element)
+                                    itm = self.__generateItem(
+                                        parent, element, "", "", 0, "",
+                                        pysvn.node_kind.dir, urlPart)
+                                    itm.setExpanded(True)
+                                    parent = itm
+                            self.__ignoreExpand = False
+                    itm = self.__generateItem(
+                        parent, dirent["repos_path"],
+                        dirent["created_rev"], dirent["last_author"],
+                        dirent["size"], dirent["time"], dirent["kind"],
+                        dirent["path"])
+                self.__resort()
+                self.__resizeColumns()
+            except pysvn.ClientError as e:
+                self.__showError(e.args[0])
+            except AttributeError:
+                self.__showError(
+                    self.tr("The installed version of PySvn should be "
+                            "1.4.0 or better."))
     
     def __normalizeUrl(self, url):
         """
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnStatusDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnStatusDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -13,7 +13,7 @@
 
 import pysvn
 
-from PyQt5.QtCore import QMutexLocker, Qt, pyqtSlot
+from PyQt5.QtCore import Qt, pyqtSlot
 from PyQt5.QtWidgets import (
     QWidget, QHeaderView, QApplication, QMenu, QDialogButtonBox,
     QTreeWidgetItem
@@ -23,6 +23,8 @@
 from E5Gui import E5MessageBox
 from E5Gui.E5OverrideCursor import E5OverrideCursor
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnConst import svnStatusMap
 from .SvnDialogMixin import SvnDialogMixin
 
@@ -310,122 +312,134 @@
         hideSwitchedColumn = True
         
         with E5OverrideCursor():
-            locker = QMutexLocker(self.vcs.vcsExecutionMutex)
             cwd = os.getcwd()
             os.chdir(self.dname)
             try:
-                for name in fnames:
-                    # step 1: determine changelists and their files
-                    changelistsDict = {}
-                    if hasattr(self.client, 'get_changelist'):
-                        if recurse:
-                            depth = pysvn.depth.infinity
-                        else:
-                            depth = pysvn.depth.immediate
-                        changelists = self.client.get_changelist(
-                            name, depth=depth)
-                        for fpath, changelist in changelists:
-                            fpath = Utilities.normcasepath(fpath)
-                            changelistsDict[fpath] = changelist
-                    hideChangelistColumn = (
-                        hideChangelistColumn and len(changelistsDict) == 0
-                    )
-                    
-                    # step 2: determine status of files
-                    allFiles = self.client.status(name, recurse=recurse,
-                                                  get_all=verbose, ignore=True,
-                                                  update=update)
-                    counter = 0
-                    for file in allFiles:
-                        uptodate = True
-                        if file.repos_text_status != pysvn.wc_status_kind.none:
-                            uptodate = (
-                                uptodate and
-                                file.repos_text_status !=
-                                pysvn.wc_status_kind.modified
-                            )
-                        if file.repos_prop_status != pysvn.wc_status_kind.none:
-                            uptodate = (
-                                uptodate and
-                                file.repos_prop_status !=
-                                pysvn.wc_status_kind.modified
-                            )
-                        
-                        lockState = " "
-                        if (
-                            file.entry is not None and
-                            hasattr(file.entry, 'lock_token') and
-                            file.entry.lock_token is not None
-                        ):
-                            lockState = "L"
-                        if hasattr(file, 'repos_lock') and update:
-                            if lockState == "L" and file.repos_lock is None:
-                                lockState = "B"
-                            elif (
-                                lockState == " " and
-                                file.repos_lock is not None
-                            ):
-                                lockState = "O"
-                            elif (
-                                lockState == "L" and
-                                file.repos_lock is not None and
-                                file.entry.lock_token !=
-                                    file.repos_lock["token"]
-                            ):
-                                lockState = "S"
-                        
-                        fpath = Utilities.normcasepath(
-                            os.path.join(self.dname, file.path))
-                        if fpath in changelistsDict:
-                            changelist = changelistsDict[fpath]
-                        else:
-                            changelist = ""
-                        
-                        hidePropertyStatusColumn = (
-                            hidePropertyStatusColumn and
-                            file.prop_status in [
-                                pysvn.wc_status_kind.none,
-                                pysvn.wc_status_kind.normal
-                            ]
-                        )
-                        hideLockColumns = (
-                            hideLockColumns and
-                            not file.is_locked and
-                            lockState == " "
-                        )
-                        hideUpToDateColumn = hideUpToDateColumn and uptodate
-                        hideHistoryColumn = (
-                            hideHistoryColumn and
-                            not file.is_copied
-                        )
-                        hideSwitchedColumn = (
-                            hideSwitchedColumn and
-                            not file.is_switched
+                with E5MutexLocker(self.vcs.vcsExecutionMutex):
+                    for name in fnames:
+                        # step 1: determine changelists and their files
+                        changelistsDict = {}
+                        if hasattr(self.client, 'get_changelist'):
+                            if recurse:
+                                depth = pysvn.depth.infinity
+                            else:
+                                depth = pysvn.depth.immediate
+                            changelists = self.client.get_changelist(
+                                name, depth=depth)
+                            for fpath, changelist in changelists:
+                                fpath = Utilities.normcasepath(fpath)
+                                changelistsDict[fpath] = changelist
+                        hideChangelistColumn = (
+                            hideChangelistColumn and len(changelistsDict) == 0
                         )
                         
-                        self.__generateItem(
-                            changelist,
-                            file.text_status,
-                            file.prop_status,
-                            file.is_locked,
-                            file.is_copied,
-                            file.is_switched,
-                            lockState,
-                            uptodate,
-                            file.entry.revision.number if file.entry else "",
-                            file.entry.commit_revision.number
-                            if file.entry else "",
-                            file.entry.commit_author if file.entry else "",
-                            file.path
-                        )
-                        counter += 1
-                        if counter == 30:
-                            # check for cancel every 30 items
-                            counter = 0
-                            if self._clientCancelCallback():
-                                break
-                    if self._clientCancelCallback():
-                        break
+                        # step 2: determine status of files
+                        allFiles = self.client.status(
+                            name, recurse=recurse, get_all=verbose,
+                            ignore=True, update=update)
+                        counter = 0
+                        for file in allFiles:
+                            uptodate = True
+                            if (
+                                file.repos_text_status !=
+                                pysvn.wc_status_kind.none
+                            ):
+                                uptodate = (
+                                    uptodate and
+                                    file.repos_text_status !=
+                                    pysvn.wc_status_kind.modified
+                                )
+                            if (
+                                file.repos_prop_status !=
+                                pysvn.wc_status_kind.none
+                            ):
+                                uptodate = (
+                                    uptodate and
+                                    file.repos_prop_status !=
+                                    pysvn.wc_status_kind.modified
+                                )
+                            
+                            lockState = " "
+                            if (
+                                file.entry is not None and
+                                hasattr(file.entry, 'lock_token') and
+                                file.entry.lock_token is not None
+                            ):
+                                lockState = "L"
+                            if hasattr(file, 'repos_lock') and update:
+                                if (
+                                    lockState == "L" and
+                                    file.repos_lock is None
+                                ):
+                                    lockState = "B"
+                                elif (
+                                    lockState == " " and
+                                    file.repos_lock is not None
+                                ):
+                                    lockState = "O"
+                                elif (
+                                    lockState == "L" and
+                                    file.repos_lock is not None and
+                                    file.entry.lock_token !=
+                                        file.repos_lock["token"]
+                                ):
+                                    lockState = "S"
+                            
+                            fpath = Utilities.normcasepath(
+                                os.path.join(self.dname, file.path))
+                            if fpath in changelistsDict:
+                                changelist = changelistsDict[fpath]
+                            else:
+                                changelist = ""
+                            
+                            hidePropertyStatusColumn = (
+                                hidePropertyStatusColumn and
+                                file.prop_status in [
+                                    pysvn.wc_status_kind.none,
+                                    pysvn.wc_status_kind.normal
+                                ]
+                            )
+                            hideLockColumns = (
+                                hideLockColumns and
+                                not file.is_locked and
+                                lockState == " "
+                            )
+                            hideUpToDateColumn = (
+                                hideUpToDateColumn and uptodate
+                            )
+                            hideHistoryColumn = (
+                                hideHistoryColumn and
+                                not file.is_copied
+                            )
+                            hideSwitchedColumn = (
+                                hideSwitchedColumn and
+                                not file.is_switched
+                            )
+                            
+                            self.__generateItem(
+                                changelist,
+                                file.text_status,
+                                file.prop_status,
+                                file.is_locked,
+                                file.is_copied,
+                                file.is_switched,
+                                lockState,
+                                uptodate,
+                                file.entry.revision.number if file.entry
+                                else "",
+                                file.entry.commit_revision.number
+                                if file.entry else "",
+                                file.entry.commit_author if file.entry else "",
+                                file.path
+                            )
+                            counter += 1
+                            if counter == 30:
+                                # check for cancel every 30 items
+                                counter = 0
+                                if self._clientCancelCallback():
+                                    break
+                        if self._clientCancelCallback():
+                            break
             except pysvn.ClientError as e:
                 self.__showError(e.args[0] + '\n')
             
@@ -443,8 +457,6 @@
                                             hideSwitchedColumn)
             self.statusList.setColumnHidden(self.__changelistColumn,
                                             hideChangelistColumn)
-            
-            locker.unlock()
         self.__finish()
         os.chdir(cwd)
         
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/SvnTagBranchListDialog.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/SvnTagBranchListDialog.py	Sat Oct 10 16:03:53 2020 +0200
@@ -12,7 +12,7 @@
 
 import pysvn
 
-from PyQt5.QtCore import QMutexLocker, QRegExp, Qt
+from PyQt5.QtCore import QRegExp, Qt
 from PyQt5.QtWidgets import (
     QHeaderView, QLineEdit, QDialog, QInputDialog, QApplication,
     QDialogButtonBox, QTreeWidgetItem
@@ -20,6 +20,8 @@
 
 from E5Gui import E5MessageBox
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .SvnUtilities import formatTime
 
 from .SvnDialogMixin import SvnDialogMixin
@@ -130,12 +132,12 @@
                 return False
             path = reposPath
         
-        locker = QMutexLocker(self.vcs.vcsExecutionMutex)
         self.tagsList = []
         cwd = os.getcwd()
         os.chdir(dname)
         try:
-            entries = self.client.list(path, recurse=False)
+            with E5MutexLocker(self.vcs.vcsExecutionMutex):
+                entries = self.client.list(path, recurse=False)
             for dirent, _lock in entries:
                 if dirent["path"] != path:
                     name = dirent["path"].replace(path + '/', "")
@@ -158,7 +160,6 @@
                 self.tr("The installed version of PySvn should be"
                         " 1.4.0 or better."))
             res = False
-        locker.unlock()
         self.__finish()
         os.chdir(cwd)
         return res
--- a/eric6/Plugins/VcsPlugins/vcsPySvn/subversion.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/Plugins/VcsPlugins/vcsPySvn/subversion.py	Sat Oct 10 16:03:53 2020 +0200
@@ -14,13 +14,15 @@
 from urllib.parse import quote
 
 from PyQt5.QtCore import (
-    Qt, QMutexLocker, pyqtSignal, QRegExp, QDateTime, QCoreApplication
+    pyqtSignal, Qt, QRegExp, QDateTime, QCoreApplication
 )
 from PyQt5.QtWidgets import QLineEdit, QDialog, QInputDialog, QApplication
 
 from E5Gui.E5Application import e5App
 from E5Gui import E5MessageBox
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from VCS.VersionControl import VersionControl
 
 import pysvn
@@ -301,7 +303,6 @@
                 shutil.rmtree(tmpDir, True)
             return False, False
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(os.path.join(tmpDir, project))
         opts = self.options['global']
@@ -316,14 +317,14 @@
                 client)
             QApplication.processEvents()
         try:
-            rev = client.import_(".", url, msg, recurse, ignore=True)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                rev = client.import_(".", url, msg, recurse, ignore=True)
             status = True
         except pysvn.ClientError as e:
             status = False
             rev = None
             if not noDialog:
                 dlg.showError(e.args[0])
-        locker.unlock()
         if not noDialog:
             rev and dlg.showMessage(self.tr("Imported revision {0}.\n")
                                     .format(rev.number))
@@ -391,15 +392,14 @@
                     url, projectDir),
                 client)
             QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            client.checkout(url, projectDir, recurse)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.checkout(url, projectDir, recurse)
             status = True
         except pysvn.ClientError as e:
             status = False
             if not noDialog:
                 dlg.showError(e.args[0])
-        locker.unlock()
         if not noDialog:
             dlg.finish()
             dlg.exec()
@@ -457,14 +457,13 @@
                 url, projectDir),
             client)
         QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            client.export(url, projectDir, force=True, recurse=recurse)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.export(url, projectDir, force=True, recurse=recurse)
             status = True
         except pysvn.ClientError as e:
             status = False
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         return status
@@ -567,7 +566,6 @@
         ):
             noDialog = False
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         opts = self.options['global'] + self.options['commit']
@@ -587,19 +585,19 @@
                 client)
             QApplication.processEvents()
         try:
-            if changelists:
-                rev = client.checkin(fnames, msg,
-                                     recurse=recurse, keep_locks=keeplocks,
-                                     keep_changelist=keepChangelists,
-                                     changelists=changelists)
-            else:
-                rev = client.checkin(fnames, msg,
-                                     recurse=recurse, keep_locks=keeplocks)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                if changelists:
+                    rev = client.checkin(fnames, msg,
+                                         recurse=recurse, keep_locks=keeplocks,
+                                         keep_changelist=keepChangelists,
+                                         changelists=changelists)
+                else:
+                    rev = client.checkin(fnames, msg,
+                                         recurse=recurse, keep_locks=keeplocks)
         except pysvn.ClientError as e:
             rev = None
             if not noDialog:
                 dlg.showError(e.args[0])
-        locker.unlock()
         if not noDialog:
             rev and dlg.showMessage(self.tr("Committed revision {0}.")
                                     .format(rev.number))
@@ -626,7 +624,6 @@
             dname, fname = self.splitPath(name)
             fnames = [fname]
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         opts = self.options['global'] + self.options['update']
@@ -641,10 +638,10 @@
                 client)
         QApplication.processEvents()
         try:
-            client.update(fnames, recurse)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.update(fnames, recurse)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         if not noDialog:
             dlg.finish()
             dlg.exec()
@@ -735,7 +732,6 @@
         else:
             names.append(name)
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(wdir)
         opts = self.options['global'] + self.options['add']
@@ -754,12 +750,12 @@
                 client)
             QApplication.processEvents()
         try:
-            client.add(names, recurse=recurse, force=force,
-                       ignore=not noignore)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.add(names, recurse=recurse, force=force,
+                           ignore=not noignore)
         except pysvn.ClientError as e:
             if not noDialog:
                 dlg.showError(e.args[0])
-        locker.unlock()
         if not noDialog:
             dlg.finish()
             dlg.exec()
@@ -846,7 +842,6 @@
         else:
             names.append(path)
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         opts = self.options['global'] + self.options['add']
@@ -863,10 +858,10 @@
             client)
         QApplication.processEvents()
         try:
-            client.add(names, recurse=recurse, force=force, ignore=ignore)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.add(names, recurse=recurse, force=force, ignore=ignore)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         os.chdir(cwd)
@@ -899,15 +894,14 @@
                     " ".join(name)),
                 client)
             QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            client.remove(name, force=force)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.remove(name, force=force)
             res = True
         except pysvn.ClientError as e:
             res = False
             if not noDialog:
                 dlg.showError(e.args[0])
-        locker.unlock()
         if not noDialog:
             dlg.finish()
             dlg.exec()
@@ -964,15 +958,14 @@
                         name, target),
                     client, log=log)
                 QApplication.processEvents()
-            locker = QMutexLocker(self.vcsExecutionMutex)
             try:
-                client.move(name, target, force=force)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    client.move(name, target, force=force)
                 res = True
             except pysvn.ClientError as e:
                 res = False
                 if not noDialog:
                     dlg.showError(e.args[0])
-            locker.unlock()
             if not noDialog:
                 dlg.finish()
                 dlg.exec()
@@ -1104,12 +1097,11 @@
                 "copy --message {0} {1} {2}".format(log, reposURL, url),
                 client, log=log)
             QApplication.processEvents()
-            locker = QMutexLocker(self.vcsExecutionMutex)
             try:
-                rev = client.copy(reposURL, url)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    rev = client.copy(reposURL, url)
             except pysvn.ClientError as e:
                 dlg.showError(e.args[0])
-            locker.unlock()
         else:
             log = 'Deleted tag <{0}>'.format(self.tagName)
             dlg = SvnDialog(
@@ -1118,12 +1110,11 @@
                 "remove --message {0} {1}".format(log, url),
                 client, log=log)
             QApplication.processEvents()
-            locker = QMutexLocker(self.vcsExecutionMutex)
             try:
-                rev = client.remove(url)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    rev = client.remove(url)
             except pysvn.ClientError as e:
                 dlg.showError(e.args[0])
-            locker.unlock()
         rev and dlg.showMessage(
             self.tr("Revision {0}.\n").format(rev.number))
         dlg.finish()
@@ -1170,12 +1161,11 @@
                     " ".join(name)),
                 client)
             QApplication.processEvents()
-            locker = QMutexLocker(self.vcsExecutionMutex)
             try:
-                client.revert(name, recurse)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    client.revert(name, recurse)
             except pysvn.ClientError as e:
                 dlg.showError(e.args[0])
-            locker.unlock()
             dlg.finish()
             dlg.exec()
             self.checkVCSStatus()
@@ -1246,13 +1236,12 @@
                         "switch {0} {1}".format(url, name),
                         client)
         QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            rev = client.switch(name, url)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                rev = client.switch(name, url)
             dlg.showMessage(self.tr("Revision {0}.\n").format(rev.number))
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         res = dlg.hasAddOrDelete()
@@ -1286,7 +1275,6 @@
         
         rx_rev = QRegExp('\\d+|HEAD|head')
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         recurse = "--non-recursive" not in opts
@@ -1355,11 +1343,11 @@
             client)
         QApplication.processEvents()
         try:
-            client.merge(url1, revision1, url2, revision2, fname,
-                         recurse=recurse, force=force)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.merge(url1, revision1, url2, revision2, fname,
+                             recurse=recurse, force=force)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         os.chdir(cwd)
@@ -1496,10 +1484,9 @@
             )
             
             try:
-                locker = QMutexLocker(self.vcsExecutionMutex)
-                allFiles = client.status(dname, recurse=True, get_all=True,
-                                         ignore=True, update=False)
-                locker.unlock()
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    allFiles = client.status(dname, recurse=True, get_all=True,
+                                             ignore=True, update=False)
                 dirs = [x for x in names.keys() if os.path.isdir(x)]
                 for file in allFiles:
                     name = os.path.normcase(file.path)
@@ -1530,7 +1517,8 @@
                     else:
                         self.statusCache[name] = self.canBeAdded
             except pysvn.ClientError:
-                locker.unlock()    # ignore pysvn errors
+                # ignore pysvn errors
+                pass
         
         return names
         
@@ -1576,10 +1564,9 @@
             )
             
             try:
-                locker = QMutexLocker(self.vcsExecutionMutex)
-                allFiles = client.status(dname, recurse=True, get_all=True,
-                                         ignore=True, update=False)
-                locker.unlock()
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    allFiles = client.status(dname, recurse=True, get_all=True,
+                                             ignore=True, update=False)
                 for file in allFiles:
                     name = os.path.normcase(file.path)
                     if self.__isVersioned(file):
@@ -1589,7 +1576,8 @@
                     else:
                         self.statusCache[name] = self.canBeAdded
             except pysvn.ClientError:
-                locker.unlock()    # ignore pysvn errors
+                # ignore pysvn errors
+                pass
         
         return names
         
@@ -1652,12 +1640,11 @@
                         "cleanup {0}".format(name),
                         client)
         QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            client.cleanup(name)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.cleanup(name)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
     
@@ -1779,13 +1766,12 @@
         @return string with the repository path URL
         """
         client = pysvn.Client()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            entry = client.info(path)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                entry = client.info(path)
             url = entry.url
         except pysvn.ClientError:
             url = ""
-        locker.unlock()
         return url
 
     def svnResolve(self, name):
@@ -1800,7 +1786,6 @@
             dname, fname = self.splitPath(name)
             fnames = [fname]
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         opts = self.options['global']
@@ -1813,11 +1798,11 @@
                         client)
         QApplication.processEvents()
         try:
-            for name in fnames:
-                client.resolved(name, recurse=recurse)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                for name in fnames:
+                    client.resolved(name, recurse=recurse)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         os.chdir(cwd)
@@ -1852,14 +1837,13 @@
                     name, target),
                 client, log=log)
             QApplication.processEvents()
-            locker = QMutexLocker(self.vcsExecutionMutex)
             try:
-                client.copy(name, target)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    client.copy(name, target)
                 res = True
             except pysvn.ClientError as e:
                 res = False
                 dlg.showError(e.args[0])
-            locker.unlock()
             dlg.finish()
             dlg.exec()
             if (
@@ -1913,7 +1897,6 @@
                 dname, fname = self.splitPath(name)
                 fnames = [fname]
             
-            locker = QMutexLocker(self.vcsExecutionMutex)
             cwd = os.getcwd()
             os.chdir(dname)
             opts = self.options['global']
@@ -1929,12 +1912,12 @@
                 client)
             QApplication.processEvents()
             try:
-                for name in fnames:
-                    client.propset(propName, propValue, name,
-                                   recurse=recurse, skip_checks=skipchecks)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    for name in fnames:
+                        client.propset(propName, propValue, name,
+                                       recurse=recurse, skip_checks=skipchecks)
             except pysvn.ClientError as e:
                 dlg.showError(e.args[0])
-            locker.unlock()
             dlg.showMessage(self.tr("Property set."))
             dlg.finish()
             dlg.exec()
@@ -1966,7 +1949,6 @@
                 dname, fname = self.splitPath(name)
                 fnames = [fname]
             
-            locker = QMutexLocker(self.vcsExecutionMutex)
             cwd = os.getcwd()
             os.chdir(dname)
             opts = self.options['global']
@@ -1981,12 +1963,12 @@
                 client)
             QApplication.processEvents()
             try:
-                for name in fnames:
-                    client.propdel(propName, name,
-                                   recurse=recurse, skip_checks=skipchecks)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    for name in fnames:
+                        client.propdel(propName, name,
+                                       recurse=recurse, skip_checks=skipchecks)
             except pysvn.ClientError as e:
                 dlg.showError(e.args[0])
-            locker.unlock()
             dlg.showMessage(self.tr("Property deleted."))
             dlg.finish()
             dlg.exec()
@@ -2274,7 +2256,6 @@
             dname, fname = self.splitPath(name)
             fnames = [fname]
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         client = self.getClient()
@@ -2287,12 +2268,12 @@
             client, parent=parent)
         QApplication.processEvents()
         try:
-            client.lock(fnames, comment, force=stealIt)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.lock(fnames, comment, force=stealIt)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
         except AttributeError as e:
             dlg.showError(str(e))
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         os.chdir(cwd)
@@ -2313,7 +2294,6 @@
             dname, fname = self.splitPath(name)
             fnames = [fname]
         
-        locker = QMutexLocker(self.vcsExecutionMutex)
         cwd = os.getcwd()
         os.chdir(dname)
         client = self.getClient()
@@ -2324,12 +2304,12 @@
             client, parent=parent)
         QApplication.processEvents()
         try:
-            client.unlock(fnames, force=breakIt)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.unlock(fnames, force=breakIt)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
         except AttributeError as e:
             dlg.showError(str(e))
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         os.chdir(cwd)
@@ -2365,15 +2345,15 @@
             client = self.getClient()
             dlg = SvnDialog(self.tr('Relocating'), msg, client)
             QApplication.processEvents()
-            locker = QMutexLocker(self.vcsExecutionMutex)
             try:
-                if inside:
-                    client.switch(projectPath, newUrl)
-                else:
-                    client.relocate(currUrl, newUrl, projectPath, recurse=True)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    if inside:
+                        client.switch(projectPath, newUrl)
+                    else:
+                        client.relocate(currUrl, newUrl, projectPath,
+                                        recurse=True)
             except pysvn.ClientError as e:
                 dlg.showError(e.args[0])
-            locker.unlock()
             dlg.finish()
             dlg.exec()
         
@@ -2421,13 +2401,12 @@
             "changelist --remove {0}".format(" ".join(names)),
             client)
         QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            for name in names:
-                client.remove_from_changelists(name)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                for name in names:
+                    client.remove_from_changelists(name)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
         
@@ -2458,14 +2437,13 @@
             "changelist {0}".format(" ".join(names)),
             client)
         QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            for name in names:
-                client.add_to_changelist(name, clname,
-                                         depth=pysvn.depth.infinity)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                for name in names:
+                    client.add_to_changelist(
+                        name, clname, depth=pysvn.depth.infinity)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
     
@@ -2491,17 +2469,16 @@
         client = self.getClient()
         if hasattr(client, 'get_changelist'):
             ppath = e5App().getObject("Project").getProjectPath()
-            locker = QMutexLocker(self.vcsExecutionMutex)
             try:
-                entries = client.get_changelist(ppath,
-                                                depth=pysvn.depth.infinity)
+                with E5MutexLocker(self.vcsExecutionMutex):
+                    entries = client.get_changelist(
+                        ppath, depth=pysvn.depth.infinity)
                 for entry in entries:
                     changelist = entry[1]
                     if changelist not in changelists:
                         changelists.append(changelist)
             except pysvn.ClientError:
                 pass
-            locker.unlock()
         
         return changelists
         
@@ -2517,12 +2494,11 @@
             "upgrade {0}".format(path),
             client)
         QApplication.processEvents()
-        locker = QMutexLocker(self.vcsExecutionMutex)
         try:
-            client.upgrade(path)
+            with E5MutexLocker(self.vcsExecutionMutex):
+                client.upgrade(path)
         except pysvn.ClientError as e:
             dlg.showError(e.args[0])
-        locker.unlock()
         dlg.finish()
         dlg.exec()
 
--- a/eric6/WebBrowser/AdBlock/AdBlockManager.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/WebBrowser/AdBlock/AdBlockManager.py	Sat Oct 10 16:03:53 2020 +0200
@@ -11,13 +11,14 @@
 import os
 
 from PyQt5.QtCore import (
-    pyqtSignal, QObject, QUrl, QUrlQuery, QFile, QByteArray, QMutex,
-    QMutexLocker
+    pyqtSignal, QObject, QUrl, QUrlQuery, QFile, QByteArray, QMutex
 )
 from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInfo
 
 from E5Gui import E5MessageBox
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from .AdBlockSubscription import AdBlockSubscription
 from .AdBlockUrlInterceptor import AdBlockUrlInterceptor
 from .AdBlockMatcher import AdBlockMatcher
@@ -148,41 +149,40 @@
         @return flag indicating to block the request
         @rtype bool
         """
-        locker = QMutexLocker(self.__mutex)     # __IGNORE_WARNING__
-        
-        if not self.isEnabled():
-            return False
-        
-        urlString = bytes(info.requestUrl().toEncoded()).decode().lower()
-        urlDomain = info.requestUrl().host().lower()
-        urlScheme = info.requestUrl().scheme().lower()
-        
-        if (
-            not self.canRunOnScheme(urlScheme) or
-            not self.__canBeBlocked(info.firstPartyUrl())
-        ):
-            return False
-        
-        res = False
-        blockedRule = self.__matcher.match(info, urlDomain, urlString)
-        
-        if blockedRule:
-            res = True
+        with E5MutexLocker(self.__mutex):
+            if not self.isEnabled():
+                return False
+            
+            urlString = bytes(info.requestUrl().toEncoded()).decode().lower()
+            urlDomain = info.requestUrl().host().lower()
+            urlScheme = info.requestUrl().scheme().lower()
+            
             if (
-                info.resourceType() ==
-                    QWebEngineUrlRequestInfo.ResourceTypeMainFrame
+                not self.canRunOnScheme(urlScheme) or
+                not self.__canBeBlocked(info.firstPartyUrl())
             ):
-                url = QUrl("eric:adblock")
-                query = QUrlQuery()
-                query.addQueryItem("rule", blockedRule.filter())
-                query.addQueryItem(
-                    "subscription", blockedRule.subscription().title())
-                url.setQuery(query)
-                info.redirect(url)
-            else:
-                info.block(True)
-        
-        return res
+                return False
+            
+            res = False
+            blockedRule = self.__matcher.match(info, urlDomain, urlString)
+            
+            if blockedRule:
+                res = True
+                if (
+                    info.resourceType() ==
+                        QWebEngineUrlRequestInfo.ResourceTypeMainFrame
+                ):
+                    url = QUrl("eric:adblock")
+                    query = QUrlQuery()
+                    query.addQueryItem("rule", blockedRule.filter())
+                    query.addQueryItem(
+                        "subscription", blockedRule.subscription().title())
+                    url.setQuery(query)
+                    info.redirect(url)
+                else:
+                    info.block(True)
+            
+            return res
     
     def canRunOnScheme(self, scheme):
         """
--- a/eric6/WebBrowser/Network/EricSchemeHandler.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/WebBrowser/Network/EricSchemeHandler.py	Sat Oct 10 16:03:53 2020 +0200
@@ -8,12 +8,14 @@
 """
 
 from PyQt5.QtCore import (
-    pyqtSignal, QByteArray, QBuffer, QIODevice, QUrlQuery, QMutex, QMutexLocker
+    pyqtSignal, QByteArray, QBuffer, QIODevice, QUrlQuery, QMutex
 )
 from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler
 
 from E5Gui.E5Application import e5App
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from ..Tools.WebBrowserTools import (
     getHtmlPage, getJavascript, pixmapFileToDataUrl
 )
@@ -104,21 +106,19 @@
         if self.__loaded:
             return
         
-        lock = QMutexLocker(self.__mutex)
-        
-        if self.__pageName == "adblock":
-            contents = self.__adBlockPage()
-        elif self.__pageName in ["home", "start", "startpage"]:
-            contents = self.__startPage()
-        elif self.__pageName == "speeddial":
-            contents = self.__speedDialPage()
-        else:
-            contents = ""
-        
-        self.__buffer.setData(contents.encode("utf-8"))
-        self.__buffer.open(QIODevice.ReadOnly)
-        self.open(QIODevice.ReadOnly)
-        lock.unlock()
+        with E5MutexLocker(self.__mutex):
+            if self.__pageName == "adblock":
+                contents = self.__adBlockPage()
+            elif self.__pageName in ["home", "start", "startpage"]:
+                contents = self.__startPage()
+            elif self.__pageName == "speeddial":
+                contents = self.__speedDialPage()
+            else:
+                contents = ""
+            
+            self.__buffer.setData(contents.encode("utf-8"))
+            self.__buffer.open(QIODevice.ReadOnly)
+            self.open(QIODevice.ReadOnly)
         
         self.readyRead.emit()
         
@@ -131,8 +131,8 @@
         @return number of available bytes
         @rtype int
         """
-        lock = QMutexLocker(self.__mutex)       # __IGNORE_WARNING__
-        return self.__buffer.bytesAvailable()
+        with E5MutexLocker(self.__mutex):
+            return self.__buffer.bytesAvailable()
     
     def readData(self, maxlen):
         """
@@ -141,8 +141,8 @@
         @param maxlen maximum number of bytes to read (integer)
         @return string containing the data (bytes)
         """
-        lock = QMutexLocker(self.__mutex)       # __IGNORE_WARNING__
-        return self.__buffer.read(maxlen)
+        with E5MutexLocker(self.__mutex):
+            return self.__buffer.read(maxlen)
     
     def close(self):
         """
--- a/eric6/WebBrowser/Network/NetworkUrlInterceptor.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/WebBrowser/Network/NetworkUrlInterceptor.py	Sat Oct 10 16:03:53 2020 +0200
@@ -9,11 +9,13 @@
 """
 
 
-from PyQt5.QtCore import QMutex, QMutexLocker, QUrl
+from PyQt5.QtCore import QMutex, QUrl
 from PyQt5.QtWebEngineCore import (
     QWebEngineUrlRequestInterceptor, QWebEngineUrlRequestInfo
 )
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 from ..WebBrowserPage import WebBrowserPage
 
 import Preferences
@@ -44,24 +46,23 @@
         @param info URL request information
         @type QWebEngineUrlRequestInfo
         """
-        locker = QMutexLocker(self.__mutex)     # __IGNORE_WARNING__
-        
-        # Do Not Track feature
-        if self.__doNotTrack:
-            info.setHttpHeader(b"DNT", b"1")
-            info.setHttpHeader(b"X-Do-Not-Track", b"1")
-        
-        # Send referrer header?
-        if info.requestUrl().host() not in Preferences.getWebBrowser(
-                "SendRefererWhitelist"):
-            self.__setRefererHeader(info)
-        
-        # User Agents header
-        userAgent = WebBrowserPage.userAgentForUrl(info.requestUrl())
-        info.setHttpHeader(b"User-Agent", userAgent.encode())
-        
-        for interceptor in self.__interceptors:
-            interceptor.interceptRequest(info)
+        with E5MutexLocker(self.__mutex):
+            # Do Not Track feature
+            if self.__doNotTrack:
+                info.setHttpHeader(b"DNT", b"1")
+                info.setHttpHeader(b"X-Do-Not-Track", b"1")
+            
+            # Send referrer header?
+            if info.requestUrl().host() not in Preferences.getWebBrowser(
+                    "SendRefererWhitelist"):
+                self.__setRefererHeader(info)
+            
+            # User Agents header
+            userAgent = WebBrowserPage.userAgentForUrl(info.requestUrl())
+            info.setHttpHeader(b"User-Agent", userAgent.encode())
+            
+            for interceptor in self.__interceptors:
+                interceptor.interceptRequest(info)
     
     def installUrlInterceptor(self, interceptor):
         """
@@ -70,10 +71,9 @@
         @param interceptor URL interceptor to be installed
         @type UrlInterceptor
         """
-        locker = QMutexLocker(self.__mutex)     # __IGNORE_WARNING__
-        
-        if interceptor not in self.__interceptors:
-            self.__interceptors.append(interceptor)
+        with E5MutexLocker(self.__mutex):
+            if interceptor not in self.__interceptors:
+                self.__interceptors.append(interceptor)
     
     def removeUrlInterceptor(self, interceptor):
         """
@@ -82,23 +82,21 @@
         @param interceptor URL interceptor to be removed
         @type UrlInterceptor
         """
-        locker = QMutexLocker(self.__mutex)     # __IGNORE_WARNING__
-        
-        if interceptor in self.__interceptors:
-            self.__interceptors.remove(interceptor)
+        with E5MutexLocker(self.__mutex):
+            if interceptor in self.__interceptors:
+                self.__interceptors.remove(interceptor)
     
     def __loadSettings(self):
         """
         Private method to load the Network Manager settings.
         """
-        locker = QMutexLocker(self.__mutex)     # __IGNORE_WARNING__
-        
-        self.__doNotTrack = Preferences.getWebBrowser("DoNotTrack")
-        self.__sendReferer = Preferences.getWebBrowser("RefererSendReferer")
-        self.__refererDefaultPolicy = Preferences.getWebBrowser(
-            "RefererDefaultPolicy")
-        self.__refererTrimmingPolicy = Preferences.getWebBrowser(
-            "RefererTrimmingPolicy")
+        with E5MutexLocker(self.__mutex):
+            self.__doNotTrack = Preferences.getWebBrowser("DoNotTrack")
+            self.__sendReferer = Preferences.getWebBrowser("RefererSendReferer")
+            self.__refererDefaultPolicy = Preferences.getWebBrowser(
+                "RefererDefaultPolicy")
+            self.__refererTrimmingPolicy = Preferences.getWebBrowser(
+                "RefererTrimmingPolicy")
     
     def preferencesChanged(self):
         """
--- a/eric6/WebBrowser/Network/QtHelpSchemeHandler.py	Sat Oct 10 15:17:29 2020 +0200
+++ b/eric6/WebBrowser/Network/QtHelpSchemeHandler.py	Sat Oct 10 16:03:53 2020 +0200
@@ -12,12 +12,14 @@
 import os
 
 from PyQt5.QtCore import (
-    pyqtSignal, QByteArray, QIODevice, QBuffer, QMutex, QMutexLocker
+    pyqtSignal, QByteArray, QIODevice, QBuffer, QMutex
 )
 from PyQt5.QtWebEngineCore import (
     QWebEngineUrlSchemeHandler, QWebEngineUrlRequestJob
 )
 
+from E5Utilities.E5MutexLocker import E5MutexLocker
+
 QtDocPath = "qthelp://org.qt-project."
 
 ExtensionMap = {
@@ -169,11 +171,10 @@
                 """</html>""").format(url.toString())
                 .encode("utf-8"))
         
-        lock = QMutexLocker(self.__mutex)
-        self.__buffer.setData(data)
-        self.__buffer.open(QIODevice.ReadOnly)
-        self.open(QIODevice.ReadOnly)
-        lock.unlock()
+        with E5MutexLocker(self.__mutex):
+            self.__buffer.setData(data)
+            self.__buffer.open(QIODevice.ReadOnly)
+            self.open(QIODevice.ReadOnly)
         
         self.readyRead.emit()
     
@@ -184,8 +185,8 @@
         @return number of available bytes
         @rtype int
         """
-        lock = QMutexLocker(self.__mutex)       # __IGNORE_WARNING__
-        return self.__buffer.bytesAvailable()
+        with E5MutexLocker(self.__mutex):
+            return self.__buffer.bytesAvailable()
     
     def readData(self, maxlen):
         """
@@ -194,8 +195,8 @@
         @param maxlen maximum number of bytes to read (integer)
         @return string containing the data (bytes)
         """
-        lock = QMutexLocker(self.__mutex)       # __IGNORE_WARNING__
-        return self.__buffer.read(maxlen)
+        with E5MutexLocker(self.__mutex):
+            return self.__buffer.read(maxlen)
     
     def close(self):
         """

eric ide

mercurial