Fri, 16 Feb 2024 11:45:08 +0100
Adapted the Code Metrics dialog to support remote files and directories.
--- a/src/eric7/DataViews/CodeMetrics.py Fri Feb 16 09:32:27 2024 +0100 +++ b/src/eric7/DataViews/CodeMetrics.py Fri Feb 16 11:45:08 2024 +0100 @@ -24,6 +24,8 @@ from dataclasses import dataclass from eric7 import Utilities +from eric7.EricWidgets.EricApplication import ericApp +from eric7.SystemUtilities import FileSystemUtilities KEYWORD = token.NT_OFFSET + 1 COMMENT = tokenize.COMMENT @@ -226,7 +228,16 @@ @rtype SourceStat """ try: - text = Utilities.readEncodedFile(filename)[0] + if FileSystemUtilities.isRemoteFileName(filename): + remotefsInterface = ( + ericApp().getObject("EricServer").getServiceInterface("FileSystem") + ) + bText = remotefsInterface.readFile( + FileSystemUtilities.plainFileName(filename) + ) + text = Utilities.decode(bText)[0] + else: + text = Utilities.readEncodedFile(filename)[0] except (OSError, UnicodeError): return SourceStat()
--- a/src/eric7/DataViews/CodeMetricsDialog.py Fri Feb 16 09:32:27 2024 +0100 +++ b/src/eric7/DataViews/CodeMetricsDialog.py Fri Feb 16 11:45:08 2024 +0100 @@ -56,6 +56,10 @@ self.cancelled = False + self.__remotefsInterface = ( + ericApp().getObject("EricServer").getServiceInterface("FileSystem") + ) + self.__menu = QMenu(self) self.__menu.addAction(self.tr("Collapse All"), self.__resultCollapse) self.__menu.addAction(self.tr("Expand All"), self.__resultExpand) @@ -153,15 +157,26 @@ loc = QLocale() if isinstance(fn, list): files = fn - elif os.path.isdir(fn): + elif FileSystemUtilities.isRemoteFileName( + fn + ) and self.__remotefsInterface.isdir(fn): + files = [ + FileSystemUtilities.remoteFileName(f) + for f in self.__remotefsInterface.direntries(fn, True, "*.py", False) + ] + elif FileSystemUtilities.isPlainFileName(fn) and os.path.isdir(fn): files = FileSystemUtilities.direntries(fn, True, "*.py", False) else: files = [fn] files.sort() # check for missing files for f in files[:]: - if not os.path.exists(f): - files.remove(f) + if FileSystemUtilities.isRemoteFileName(f): + if not self.__remotefsInterface.exists(f): + files.remove(f) + else: + if not os.path.exists(f): + files.remove(f) self.checkProgress.setMaximum(len(files)) QApplication.processEvents()
--- a/src/eric7/QScintilla/Editor.py Fri Feb 16 09:32:27 2024 +0100 +++ b/src/eric7/QScintilla/Editor.py Fri Feb 16 11:45:08 2024 +0100 @@ -265,9 +265,9 @@ self.project = ericApp().getObject("Project") self.setFileName(fn) - self.__remotefsInterface = ericApp().getObject( - "EricServer" - ).getServiceInterface("FileSystem") + self.__remotefsInterface = ( + ericApp().getObject("EricServer").getServiceInterface("FileSystem") + ) # clear some variables self.lastHighlight = None # remember the last highlighted line @@ -3730,8 +3730,7 @@ path = "" else: path = ( - Preferences.getMultiProject("Workspace") - or OSUtilities.getHomeDir() + Preferences.getMultiProject("Workspace") or OSUtilities.getHomeDir() ) if self.fileName: @@ -3775,12 +3774,9 @@ if ex: fpath = fpath.with_suffix(ex) if ( - ( - FileSystemUtilities.isRemoteFileName(str(fpath)) - and self.__remotefsInterface.exists(str(fpath)) - ) - or (FileSystemUtilities.isPlainFileName(str(fpath)) and fpath.exists()) - ): + FileSystemUtilities.isRemoteFileName(str(fpath)) + and self.__remotefsInterface.exists(str(fpath)) + ) or (FileSystemUtilities.isPlainFileName(str(fpath)) and fpath.exists()): res = EricMessageBox.yesNo( self, title, @@ -6515,11 +6511,6 @@ ) self.coverageHideAnnotationMenuAct.setEnabled(len(self.notcoveredMarkers) > 0) - # disable actions not supporting eric-ide server - self.codeMetricsAct.setEnabled( - False if fn is None else FileSystemUtilities.isPlainFileName(fn) - ) - # TODO: disable action in Radon plugin for server files self.showMenu.emit("Show", self.menuShow, self) @@ -7056,9 +7047,9 @@ if fn: if FileSystemUtilities.isRemoteFileName(fn): - coverageInterface = ericApp().getObject( - "EricServer" - ).getServiceInterface("Coverage") + coverageInterface = ( + ericApp().getObject("EricServer").getServiceInterface("Coverage") + ) ok, error = coverageInterface.loadCoverageData(fn) if not ok and not silent: EricMessageBox.critical( @@ -8317,9 +8308,7 @@ signal if there was an attribute change. @type bool """ - if self.fileName == "" or FileSystemUtilities.isDeviceFileName( - self.fileName - ): + if self.fileName == "" or FileSystemUtilities.isDeviceFileName(self.fileName): return readOnly = self.checkReadOnly() @@ -8348,7 +8337,8 @@ or ( FileSystemUtilities.isRemoteFileName(self.fileName) and not self.__remotefsInterface.access( - FileSystemUtilities.plainFileName(self.fileName), "write") + FileSystemUtilities.plainFileName(self.fileName), "write" + ) ) or self.isReadOnly() ) @@ -8438,9 +8428,9 @@ ): plainFilename = FileSystemUtilities.plainFileName(filename) if self.__remotefsInterface.exists(plainFilename): - mtime = self.__remotefsInterface.stat( - plainFilename, ["st_mtime"] - )["st_mtime"] + mtime = self.__remotefsInterface.stat(plainFilename, ["st_mtime"])[ + "st_mtime" + ] return mtime != self.lastModified elif (
--- a/src/eric7/RemoteServer/EricServerFileSystemRequestHandler.py Fri Feb 16 09:32:27 2024 +0100 +++ b/src/eric7/RemoteServer/EricServerFileSystemRequestHandler.py Fri Feb 16 11:45:08 2024 +0100 @@ -13,6 +13,8 @@ import stat import time +from eric7.SystemUtilities import FileSystemUtilities + from .EricRequestCategory import EricRequestCategory @@ -41,9 +43,9 @@ "Stat": self.__stat, "Exists": self.__exists, "Access": self.__access, - "ReadFile": self.__readFile, "WriteFile": self.__writeFile, + "DirEntries": self.__dirEntries, } def handleRequest(self, request, params, reqestUuid): @@ -303,7 +305,7 @@ data = f.read() return { "ok": True, - "filedata": str(base64.b85encode(data), encoding="ascii") + "filedata": str(base64.b85encode(data), encoding="ascii"), } except OSError as err: return { @@ -351,3 +353,25 @@ "ok": False, "error": str(err), } + + def __dirEntries(self, params): + """ + Private method to get a list of all files and directories of a given directory. + + @param params dictionary containing the request data + @type dict + @return dictionary containing the reply data + @rtype dict + """ + directory = params["directory"] + result = FileSystemUtilities.direntries( + directory, + filesonly=params["files_only"], + pattern=params["pattern"], + followsymlinks=params["follow_symlinks"], + ignore=params["ignore"], + ) + return { + "ok": True, + "result": result, + }
--- a/src/eric7/RemoteServerInterface/EricServerFileSystemInterface.py Fri Feb 16 09:32:27 2024 +0100 +++ b/src/eric7/RemoteServerInterface/EricServerFileSystemInterface.py Fri Feb 16 11:45:08 2024 +0100 @@ -9,6 +9,7 @@ import base64 import contextlib +import stat from PyQt6.QtCore import QEventLoop, QObject @@ -166,6 +167,72 @@ return listedDirectory, separator, listing + def direntries( + self, directory, filesonly=False, pattern=None, followsymlinks=True, ignore=None + ): + """ + Public method to get a list of all files and directories of a given directory. + + @param directory root of the tree to check + @type str + @param filesonly flag indicating that only files are wanted (defaults to False) + @type bool (optional) + @param pattern a filename pattern or list of filename patterns to check + against (defaults to None) + @type str or list of str (optional) + @param followsymlinks flag indicating whether symbolic links should be + followed (defaults to True) + @type bool (optional) + @param ignore list of entries to be ignored (defaults to None) + @type list of str (optional) + @return list of all files and directories in the tree rooted at path. + The names are expanded to start with the given directory name. + @rtype list of str + @exception OSError raised in case the server reported an issue + """ + loop = QEventLoop() + ok = False + error = "" + result = [] + + def callback(reply, params): + """ + Function to handle the server reply + + @param reply name of the server reply + @type str + @param params dictionary containing the reply data + @type dict + """ + nonlocal result, ok, error + + if reply == "DirEntries": + ok = params["ok"] + if ok: + result = params["result"] + else: + error = params["error"] + loop.quit() + + self.__serverInterface.sendJson( + category=EricRequestCategory.FileSystem, + request="DirEntries", + params={ + "directory": FileSystemUtilities.plainFileName(directory), + "files_only": filesonly, + "pattern": [] if pattern is None else pattern, + "follow_symlinks": followsymlinks, + "ignore": [] if ignore is None else ignore, + }, + callback=callback, + ) + + loop.exec() + if not ok: + raise OSError(error) + + return result + def stat(self, filename, stNames): """ Public method to get the status of a file. @@ -218,6 +285,30 @@ return stResult + def isdir(self, name): + """ + Public method to check, if the given name is a directory. + + @param name name to be checked + @type str + @return flag indicating a directory + @rtype bool + """ + result = self.stat(name, ["st_mode"]) + return stat.S_ISDIR(result["st_mode"]) + + def isfile(self, name): + """ + Public method to check, if the given name is a regular file. + + @param name name to be checked + @type str + @return flag indicating a regular file + @rtype bool + """ + result = self.stat(name, ["st_mode"]) + return stat.S_ISREG(result["st_mode"]) + def exists(self, name): """ Public method the existence of a file or directory.