18 from eric7 import Utilities |
18 from eric7 import Utilities |
19 from eric7.RemoteServer.EricRequestCategory import EricRequestCategory |
19 from eric7.RemoteServer.EricRequestCategory import EricRequestCategory |
20 from eric7.SystemUtilities import FileSystemUtilities |
20 from eric7.SystemUtilities import FileSystemUtilities |
21 |
21 |
22 |
22 |
|
23 class EricServerNotConnectedError(OSError): |
|
24 """ |
|
25 Class defining a special OSError indicating a missing server connection. |
|
26 """ |
|
27 |
|
28 def __init__(self): |
|
29 """ |
|
30 Constructor |
|
31 """ |
|
32 super().__init("Not connected to an 'eric-ide' server.") |
|
33 |
|
34 |
23 class EricServerFileSystemInterface(QObject): |
35 class EricServerFileSystemInterface(QObject): |
24 """ |
36 """ |
25 Class implementing the file system interface to the eric-ide server. |
37 Class implementing the file system interface to the eric-ide server. |
26 """ |
38 """ |
27 |
39 |
28 _MagicCheck = re.compile("([*?[])") |
40 _MagicCheck = re.compile("([*?[])") |
29 |
41 |
|
42 NotConnectedMessage = "Not connected to an 'eric-ide' server." |
|
43 |
30 def __init__(self, serverInterface): |
44 def __init__(self, serverInterface): |
31 """ |
45 """ |
32 Constructor |
46 Constructor |
33 |
47 |
34 @param serverInterface reference to the eric-ide server interface |
48 @param serverInterface reference to the eric-ide server interface |
185 |
199 |
186 loop.exec() |
200 loop.exec() |
187 return ok, error |
201 return ok, error |
188 |
202 |
189 else: |
203 else: |
190 return False, "Not connected to an 'eric-ide' server." |
204 return False, EricServerFileSystemInterface.NotConnectedMessage |
191 |
205 |
192 def listdir(self, directory=""): |
206 def listdir(self, directory=""): |
193 """ |
207 """ |
194 Public method to get a directory listing. |
208 Public method to get a directory listing. |
195 |
209 |
196 @param directory directory to be listed. An empty directory means to list |
210 @param directory directory to be listed. An empty directory means to list |
197 the eric-ide server current directory. (defaults to "") |
211 the eric-ide server current directory. (defaults to "") |
198 @type str (optional) |
212 @type str (optional) |
199 @return tuple containing the listed directory, the path separartor and the |
213 @return tuple containing the listed directory, the path separator and the |
200 directory listing. Each directory listing entry contains a dictionary |
214 directory listing. Each directory listing entry contains a dictionary |
201 with the relevant data. |
215 with the relevant data. |
202 @rtype tuple of (str, str, dict) |
216 @rtype tuple of (str, str, dict) |
203 @exception OSError raised in case the server reported an issue |
217 @exception OSError raised in case the server reported an issue |
204 """ |
218 """ |
585 |
599 |
586 loop.exec() |
600 loop.exec() |
587 return ok, error |
601 return ok, error |
588 |
602 |
589 else: |
603 else: |
590 return False, "Not connected to an 'eric-ide' server." |
604 return False, EricServerFileSystemInterface.NotConnectedMessage |
591 |
605 |
592 def makedirs(self, directory, exist_ok=False): |
606 def makedirs(self, directory, exist_ok=False): |
593 """ |
607 """ |
594 Public method to create a new directory on the eric-ide serverincluding all |
608 Public method to create a new directory on the eric-ide serverincluding all |
595 intermediate-level directories. |
609 intermediate-level directories. |
833 |
847 |
834 @return path separator |
848 @return path separator |
835 @rtype str |
849 @rtype str |
836 """ |
850 """ |
837 return self.__serverPathSep |
851 return self.__serverPathSep |
|
852 |
|
853 def isabs(self, p): |
|
854 """ |
|
855 Public method to chack a path for being an absolute path. |
|
856 |
|
857 @param p path to be checked |
|
858 @type str |
|
859 @return flag indicating an absolute path |
|
860 @rtype bool |
|
861 """ |
|
862 if self.__serverInterface.isServerConnected(): |
|
863 if self.__serverPathSep == "\\": |
|
864 s = FileSystemUtilities.plainFileName(p)[:3].replace("/", "\\") |
|
865 return s.startswith("\\)") or s.startswith(":\\", 1) |
|
866 else: |
|
867 return FileSystemUtilities.plainFileName(p).startswith("/") |
|
868 else: |
|
869 return os.path.isabs(p) |
|
870 |
|
871 def abspath(self, p): |
|
872 """ |
|
873 Public method to convert the given path to an absolute path. |
|
874 |
|
875 @param p path to be converted |
|
876 @type str |
|
877 @return absolute path |
|
878 @rtype str |
|
879 """ |
|
880 if self.__serverInterface.isServerConnected(): |
|
881 p = FileSystemUtilities.plainFileName(p) |
|
882 if not self.isabs(p): |
|
883 p = self.join(self.getcwd(), p) |
|
884 return FileSystemUtilities.remoteFileName(p) |
|
885 else: |
|
886 return os.path.abspath(p) |
838 |
887 |
839 def join(self, a, *p): |
888 def join(self, a, *p): |
840 """ |
889 """ |
841 Public method to join two or more path name components using the path separator |
890 Public method to join two or more path name components using the path separator |
842 of the server side. |
891 of the server side. |
988 @param create flag indicating to create an empty file, if it does not exist |
1037 @param create flag indicating to create an empty file, if it does not exist |
989 (defaults to False) |
1038 (defaults to False) |
990 @type bool (optional) |
1039 @type bool (optional) |
991 @return bytes data read from the eric-ide server |
1040 @return bytes data read from the eric-ide server |
992 @rtype bytes |
1041 @rtype bytes |
|
1042 @exception EricServerNotConnectedError raised to indicate a missing server |
|
1043 connection |
993 @exception OSError raised in case the server reported an issue |
1044 @exception OSError raised in case the server reported an issue |
994 """ |
1045 """ |
995 loop = QEventLoop() |
1046 loop = QEventLoop() |
996 ok = False |
1047 ok = False |
997 error = "" |
1048 error = "" |
1017 else: |
1068 else: |
1018 error = params["error"] |
1069 error = params["error"] |
1019 loop.quit() |
1070 loop.quit() |
1020 |
1071 |
1021 if not self.__serverInterface.isServerConnected(): |
1072 if not self.__serverInterface.isServerConnected(): |
1022 raise OSError("Not connected to an 'eric-ide' server.") |
1073 raise EricServerNotConnectedError() |
1023 |
1074 |
1024 else: |
1075 else: |
1025 self.__serverInterface.sendJson( |
1076 self.__serverInterface.sendJson( |
1026 category=EricRequestCategory.FileSystem, |
1077 category=EricRequestCategory.FileSystem, |
1027 request="ReadFile", |
1078 request="ReadFile", |
1071 with contextlib.suppress(KeyError): |
1124 with contextlib.suppress(KeyError): |
1072 error = params["error"] |
1125 error = params["error"] |
1073 loop.quit() |
1126 loop.quit() |
1074 |
1127 |
1075 if not self.__serverInterface.isServerConnected(): |
1128 if not self.__serverInterface.isServerConnected(): |
1076 raise OSError("Not connected to an 'eric-ide' server.") |
1129 raise EricServerNotConnectedError |
1077 |
1130 |
1078 else: |
1131 else: |
1079 self.__serverInterface.sendJson( |
1132 self.__serverInterface.sendJson( |
1080 category=EricRequestCategory.FileSystem, |
1133 category=EricRequestCategory.FileSystem, |
1081 request="WriteFile", |
1134 request="WriteFile", |
1154 ####################################################################### |
1207 ####################################################################### |
1155 ## Methods implementing some 'shutil' like functionality. |
1208 ## Methods implementing some 'shutil' like functionality. |
1156 ####################################################################### |
1209 ####################################################################### |
1157 |
1210 |
1158 def shutilCopy(self, srcName, dstName): |
1211 def shutilCopy(self, srcName, dstName): |
|
1212 """ |
|
1213 Public method to copy a source file to a given destination file or directory. |
|
1214 |
|
1215 @param srcName name of the source file |
|
1216 @type str |
|
1217 @param dstName name of the destination file or directory |
|
1218 @type str |
|
1219 @return name of the destination file |
|
1220 @rtype str |
|
1221 @exception EricServerNotConnectedError raised to indicate a missing server |
|
1222 connection |
|
1223 @exception OSError raised to indicate an issue |
|
1224 """ |
1159 loop = QEventLoop() |
1225 loop = QEventLoop() |
1160 ok = False |
1226 ok = False |
1161 error = "" |
1227 error = "" |
1162 dst = "" |
1228 dst = "" |
1163 |
1229 |
1179 else: |
1245 else: |
1180 error = params["error"] |
1246 error = params["error"] |
1181 loop.quit() |
1247 loop.quit() |
1182 |
1248 |
1183 if not self.__serverInterface.isServerConnected(): |
1249 if not self.__serverInterface.isServerConnected(): |
1184 raise OSError("Not connected to an 'eric-ide' server.") |
1250 raise EricServerNotConnectedError |
1185 |
1251 |
1186 else: |
1252 else: |
1187 self.__serverInterface.sendJson( |
1253 self.__serverInterface.sendJson( |
1188 category=EricRequestCategory.FileSystem, |
1254 category=EricRequestCategory.FileSystem, |
1189 request="ShutilCopy", |
1255 request="ShutilCopy", |
1197 loop.exec() |
1263 loop.exec() |
1198 if not ok: |
1264 if not ok: |
1199 raise OSError(error) |
1265 raise OSError(error) |
1200 |
1266 |
1201 return dst |
1267 return dst |
|
1268 |
|
1269 def shutilRmtree(self, pathname, ignore_errors=False): |
|
1270 """ |
|
1271 Public method to delete an entire directory tree. |
|
1272 |
|
1273 @param pathname name of the directory to be deleted |
|
1274 @type str |
|
1275 @param ignore_errors flag indicating to ignore error resulting from failed |
|
1276 removals (defaults to False) |
|
1277 @type bool (optional) |
|
1278 @exception EricServerNotConnectedError raised to indicate a missing server |
|
1279 connection |
|
1280 @exception OSError raised to indicate an issue |
|
1281 """ |
|
1282 loop = QEventLoop() |
|
1283 ok = False |
|
1284 error = "" |
|
1285 |
|
1286 def callback(reply, params): |
|
1287 """ |
|
1288 Function to handle the server reply |
|
1289 |
|
1290 @param reply name of the server reply |
|
1291 @type str |
|
1292 @param params dictionary containing the reply data |
|
1293 @type dict |
|
1294 """ |
|
1295 nonlocal ok, error |
|
1296 |
|
1297 if reply == "ShutilRmtree": |
|
1298 ok = params["ok"] |
|
1299 if not ok: |
|
1300 error = params["error"] |
|
1301 loop.quit() |
|
1302 |
|
1303 if not self.__serverInterface.isServerConnected(): |
|
1304 raise EricServerNotConnectedError |
|
1305 |
|
1306 else: |
|
1307 self.__serverInterface.sendJson( |
|
1308 category=EricRequestCategory.FileSystem, |
|
1309 request="ShutilRmtree", |
|
1310 params={ |
|
1311 "name": FileSystemUtilities.plainFileName(pathname), |
|
1312 "ignore_errors": ignore_errors, |
|
1313 }, |
|
1314 callback=callback, |
|
1315 ) |
|
1316 |
|
1317 loop.exec() |
|
1318 if not ok: |
|
1319 raise OSError(error) |
|
1320 |
|
1321 ####################################################################### |
|
1322 ## Utility methods. |
|
1323 ####################################################################### |
|
1324 |
|
1325 def compactPath(self, longPath, width, measure=len): |
|
1326 """ |
|
1327 Public method to return a compacted path fitting inside the given width. |
|
1328 |
|
1329 @param longPath path to be compacted |
|
1330 @type str |
|
1331 @param width width for the compacted path |
|
1332 @type int |
|
1333 @param measure reference to a function used to measure the length of the |
|
1334 string (defaults to len) |
|
1335 @type function (optional) |
|
1336 @return compacted path |
|
1337 @rtype str |
|
1338 """ |
|
1339 if measure(longPath) <= width: |
|
1340 return longPath |
|
1341 |
|
1342 ellipsis = "..." |
|
1343 |
|
1344 head, tail = self.split(longPath) |
|
1345 mid = len(head) // 2 |
|
1346 head1 = head[:mid] |
|
1347 head2 = head[mid:] |
|
1348 while head1: |
|
1349 # head1 is same size as head2 or one shorter |
|
1350 cpath = self.join(f"{head1}{ellipsis}{head2}", tail) |
|
1351 if measure(cpath) <= width: |
|
1352 return cpath |
|
1353 head1 = head1[:-1] |
|
1354 head2 = head2[1:] |
|
1355 cpath = self.join(ellipsis, tail) |
|
1356 if measure(cpath) <= width: |
|
1357 return cpath |
|
1358 remoteMarker = FileSystemUtilities.remoteFileName("") |
|
1359 if width <= len(remoteMarker): |
|
1360 return f"{remoteMarker}{ellipsis}{tail}" |
|
1361 while tail: |
|
1362 cpath = f"{remoteMarker}{ellipsis}{tail}" |
|
1363 if measure(cpath) <= width: |
|
1364 return cpath |
|
1365 tail = tail[1:] |
|
1366 return "" |