--- a/src/eric7/RemoteServerInterface/EricServerFileSystemInterface.py Fri Feb 02 14:55:14 2024 +0100 +++ b/src/eric7/RemoteServerInterface/EricServerFileSystemInterface.py Mon Feb 05 11:15:47 2024 +0100 @@ -7,6 +7,7 @@ Module implementing the file system interface to the eric-ide server. """ +import base64 import contextlib from PyQt6.QtCore import QEventLoop, QObject @@ -14,6 +15,7 @@ from eric7.RemoteServer.EricRequestCategory import EricRequestCategory +# TODO: sanitize all file name with FileSystemUtilities.plainFileName() class EricServerFileSystemInterface(QObject): """ Class implementing the file system interface to the eric-ide server. @@ -153,6 +155,142 @@ loop.exec() return listedDirectory, separator, listing + def stat(self, filename, stNames): + """ + Public method to get the status of a file. + + @param filename name of the file + @type str + @param stNames list of 'stat_result' members to retrieve + @type list of str + @return dictionary containing the requested status data + @rtype dict + @exception OSError raised in case the server reported an issue + """ + loop = QEventLoop() + ok = False + error = "" + stResult = {} + + 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 ok, error, stResult + + if reply == "Stat": + ok = params["ok"] + if ok: + stResult = params["result"] + else: + error = params["error"] + loop.quit() + + self.__serverInterface.sendJson( + category=EricRequestCategory.FileSystem, + request="Stat", + params={"filename": filename, "st_names": stNames}, + callback=callback, + ) + + loop.exec() + if not ok: + raise OSError(error) + + return stResult + + def exists(self, name): + """ + Public method the existence of a file or directory. + + @param name name of the file or directory + @type str + @return flag indicating the file existence + @rtype bool + """ + loop = QEventLoop() + nameExists = False + + 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 nameExists + + if reply == "Exists": + nameExists = params["exists"] + loop.quit() + + self.__serverInterface.sendJson( + category=EricRequestCategory.FileSystem, + request="Exists", + params={"name": name}, + callback=callback, + ) + + loop.exec() + return nameExists + + def access(self, name, modes): + """ + Public method to test the given access rights to a file or directory. + + The modes to check for are 'read', 'write' or 'execute' or any combination. + + @param name name of the file or directory + @type str + @param modes list of modes to check for + @type str or list of str + @return flag indicating the user has the asked for permissions + @rtype bool + """ + if not modes: + raise ValueError( + "At least one of 'read', 'write' or 'execute' must be specified." + ) + + if isinstance(modes, str): + # convert to a list with one element + modes = [modes] + + loop = QEventLoop() + accessOK = False + + 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 accessOK + + if reply == "Access": + accessOK = params["ok"] + loop.quit() + + self.__serverInterface.sendJson( + category=EricRequestCategory.FileSystem, + request="Access", + params={"name": name, "modes":modes}, + callback=callback, + ) + + loop.exec() + return accessOK + def mkdir(self, directory): """ Public method to create a new directory on the eric-ide server. @@ -319,27 +457,103 @@ ## Methods for reading and writing files ####################################################################### - def readFile(self, filename): + def readFile(self, filename, create=False): """ Public method to read a file from the eric-ide server. @param filename name of the file to read @type str - @return tuple containing an OK flag, the read data and an error string in - case of an issue - @rtype tuple of (bool, str, str) + @param create flag indicating to create an empty file, if it does not exist + (defaults to False) + @type bool (optional) + @return bytes data read from the eric-ide server + @rtype bytes + @exception OSError raised in case the server reported an issue """ - # TODO: 'readFile()' not implemented yet + loop = QEventLoop() + ok = False + error = "" + bText = b"" + + 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 ok, error, bText - def writeFile(self, filename, data): + if reply == "ReadFile": + ok = params["ok"] + if ok: + bText = base64.b85decode( + bytes(params["filedata"], encoding="ascii") + ) + else: + error = params["error"] + loop.quit() + + self.__serverInterface.sendJson( + category=EricRequestCategory.FileSystem, + request="ReadFile", + params={"filename": filename, "create": create}, + callback=callback, + ) + + loop.exec() + if not ok: + raise OSError(error) + + return bText + + def writeFile(self, filename, data, withBackup=False): """ Public method to write the data to a file on the eric-ide server. @param filename name of the file to write @type str @param data data to be written - @type str - @return tuple containing an OK flag and an error string in case of an issue - @rtype tuple of (bool, str) + @type bytes + @param withBackup flag indicating to create a backup file first + (defaults to False) + @type bool (optional) + @exception OSError raised in case the server reported an issue """ - # TODO: 'writeFile()' not implemented yet + loop = QEventLoop() + ok = False + error = "" + + 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 ok, error + + if reply == "WriteFile": + ok = params["ok"] + with contextlib.suppress(KeyError): + error = params["error"] + loop.quit() + + self.__serverInterface.sendJson( + category=EricRequestCategory.FileSystem, + request="WriteFile", + params={ + "filename": filename, + "filedata": str(base64.b85encode(data), encoding="ascii"), + "with_backup": withBackup, + }, + callback=callback, + ) + + loop.exec() + if not ok: + raise OSError(error)