801 ntxt = ntxt[:start] + ntxt[end + 1 :] |
840 ntxt = ntxt[:start] + ntxt[end + 1 :] |
802 |
841 |
803 return ntxt |
842 return ntxt |
804 |
843 |
805 |
844 |
806 def toNativeSeparators(path): |
|
807 """ |
|
808 Function returning a path, that is using native separator characters. |
|
809 |
|
810 @param path path to be converted |
|
811 @type str |
|
812 @return path with converted separator characters |
|
813 @rtype str |
|
814 """ |
|
815 return str(pathlib.PurePath(path)) if bool(path) else "" |
|
816 |
|
817 |
|
818 def fromNativeSeparators(path): |
|
819 """ |
|
820 Function returning a path, that is using "/" separator characters. |
|
821 |
|
822 @param path path to be converted |
|
823 @type str |
|
824 @return path with converted separator characters |
|
825 @rtype str |
|
826 """ |
|
827 return pathlib.PurePath(path).as_posix() if bool(path) else "" |
|
828 |
|
829 |
|
830 def normcasepath(path): |
|
831 """ |
|
832 Function returning a path, that is normalized with respect to its case |
|
833 and references. |
|
834 |
|
835 @param path file path (string) |
|
836 @return case normalized path (string) |
|
837 """ |
|
838 return os.path.normcase(os.path.normpath(path)) |
|
839 |
|
840 |
|
841 def normcaseabspath(path): |
|
842 """ |
|
843 Function returning an absolute path, that is normalized with respect to |
|
844 its case and references. |
|
845 |
|
846 @param path file path (string) |
|
847 @return absolute, normalized path (string) |
|
848 """ |
|
849 return os.path.normcase(os.path.abspath(path)) |
|
850 |
|
851 |
|
852 def normjoinpath(a, *p): |
|
853 """ |
|
854 Function returning a normalized path of the joined parts passed into it. |
|
855 |
|
856 @param a first path to be joined (string) |
|
857 @param p variable number of path parts to be joined (string) |
|
858 @return normalized path (string) |
|
859 """ |
|
860 return os.path.normpath(os.path.join(a, *p)) |
|
861 |
|
862 |
|
863 def normabsjoinpath(a, *p): |
|
864 """ |
|
865 Function returning a normalized, absolute path of the joined parts passed |
|
866 into it. |
|
867 |
|
868 @param a first path to be joined (string) |
|
869 @param p variable number of path parts to be joind (string) |
|
870 @return absolute, normalized path (string) |
|
871 """ |
|
872 return os.path.abspath(os.path.join(a, *p)) |
|
873 |
|
874 |
|
875 def isinpath(file): |
|
876 """ |
|
877 Function to check for an executable file. |
|
878 |
|
879 @param file filename of the executable to check (string) |
|
880 @return flag to indicate, if the executable file is accessible |
|
881 via the searchpath defined by the PATH environment variable. |
|
882 """ |
|
883 if os.path.isabs(file): |
|
884 return os.access(file, os.X_OK) |
|
885 |
|
886 if os.path.exists(os.path.join(os.curdir, file)): |
|
887 return os.access(os.path.join(os.curdir, file), os.X_OK) |
|
888 |
|
889 path = getEnvironmentEntry("PATH") |
|
890 |
|
891 # environment variable not defined |
|
892 if path is None: |
|
893 return False |
|
894 |
|
895 dirs = path.split(os.pathsep) |
|
896 return any(os.access(os.path.join(directory, file), os.X_OK) for directory in dirs) |
|
897 |
|
898 |
|
899 def startswithPath(path, start): |
|
900 """ |
|
901 Function to check, if a path starts with a given start path. |
|
902 |
|
903 @param path path to be checked |
|
904 @type str |
|
905 @param start start path |
|
906 @type str |
|
907 @return flag indicating that the path starts with the given start |
|
908 path |
|
909 @rtype bool |
|
910 """ |
|
911 return bool(start) and ( |
|
912 path == start or normcasepath(path).startswith(normcasepath(start + "/")) |
|
913 ) |
|
914 |
|
915 |
|
916 def relativeUniversalPath(path, start): |
|
917 """ |
|
918 Function to convert a file path to a path relative to a start path |
|
919 with universal separators. |
|
920 |
|
921 @param path file or directory name to convert (string) |
|
922 @param start start path (string) |
|
923 @return relative path or unchanged path, if path does not start with |
|
924 the start path with universal separators (string) |
|
925 """ |
|
926 return fromNativeSeparators(os.path.relpath(path, start)) |
|
927 |
|
928 |
|
929 def absolutePath(path, start): |
|
930 """ |
|
931 Public method to convert a path relative to a start path to an |
|
932 absolute path. |
|
933 |
|
934 @param path file or directory name to convert (string) |
|
935 @param start start path (string) |
|
936 @return absolute path (string) |
|
937 """ |
|
938 if not os.path.isabs(path): |
|
939 path = os.path.normpath(os.path.join(start, path)) |
|
940 return path |
|
941 |
|
942 |
|
943 def absoluteUniversalPath(path, start): |
|
944 """ |
|
945 Public method to convert a path relative to a start path with |
|
946 universal separators to an absolute path. |
|
947 |
|
948 @param path file or directory name to convert (string) |
|
949 @param start start path (string) |
|
950 @return absolute path with native separators (string) |
|
951 """ |
|
952 if not os.path.isabs(path): |
|
953 path = toNativeSeparators(os.path.normpath(os.path.join(start, path))) |
|
954 return path |
|
955 |
|
956 |
|
957 def getExecutablePath(file): |
|
958 """ |
|
959 Function to build the full path of an executable file from the environment. |
|
960 |
|
961 @param file filename of the executable to check (string) |
|
962 @return full executable name, if the executable file is accessible |
|
963 via the searchpath defined by the PATH environment variable, or an |
|
964 empty string otherwise. |
|
965 """ |
|
966 if os.path.isabs(file): |
|
967 if os.access(file, os.X_OK): |
|
968 return file |
|
969 else: |
|
970 return "" |
|
971 |
|
972 cur_path = os.path.join(os.curdir, file) |
|
973 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK): |
|
974 return cur_path |
|
975 |
|
976 path = os.getenv("PATH") |
|
977 |
|
978 # environment variable not defined |
|
979 if path is None: |
|
980 return "" |
|
981 |
|
982 dirs = path.split(os.pathsep) |
|
983 for directory in dirs: |
|
984 exe = os.path.join(directory, file) |
|
985 if os.access(exe, os.X_OK): |
|
986 return exe |
|
987 |
|
988 return "" |
|
989 |
|
990 |
|
991 def getExecutablePaths(file): |
|
992 """ |
|
993 Function to build all full path of an executable file from the environment. |
|
994 |
|
995 @param file filename of the executable (string) |
|
996 @return list of full executable names (list of strings), if the executable |
|
997 file is accessible via the searchpath defined by the PATH environment |
|
998 variable, or an empty list otherwise. |
|
999 """ |
|
1000 paths = [] |
|
1001 |
|
1002 if os.path.isabs(file): |
|
1003 if os.access(file, os.X_OK): |
|
1004 return [file] |
|
1005 else: |
|
1006 return [] |
|
1007 |
|
1008 cur_path = os.path.join(os.curdir, file) |
|
1009 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK): |
|
1010 paths.append(cur_path) |
|
1011 |
|
1012 path = os.getenv("PATH") |
|
1013 |
|
1014 # environment variable not defined |
|
1015 if path is not None: |
|
1016 dirs = path.split(os.pathsep) |
|
1017 for directory in dirs: |
|
1018 exe = os.path.join(directory, file) |
|
1019 if os.access(exe, os.X_OK) and exe not in paths: |
|
1020 paths.append(exe) |
|
1021 |
|
1022 return paths |
|
1023 |
|
1024 |
|
1025 def getWindowsExecutablePath(file): |
|
1026 """ |
|
1027 Function to build the full path of an executable file from the environment |
|
1028 on Windows platforms. |
|
1029 |
|
1030 First an executable with the extension .exe is searched for, thereafter |
|
1031 such with the extensions .cmd or .bat and finally the given file name as |
|
1032 is. The first match is returned. |
|
1033 |
|
1034 @param file filename of the executable to check (string) |
|
1035 @return full executable name, if the executable file is accessible |
|
1036 via the searchpath defined by the PATH environment variable, or an |
|
1037 empty string otherwise. |
|
1038 """ |
|
1039 if os.path.isabs(file): |
|
1040 if os.access(file, os.X_OK): |
|
1041 return file |
|
1042 else: |
|
1043 return "" |
|
1044 |
|
1045 filenames = [file + ".exe", file + ".cmd", file + ".bat", file] |
|
1046 |
|
1047 for filename in filenames: |
|
1048 cur_path = os.path.join(os.curdir, filename) |
|
1049 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK): |
|
1050 return os.path.abspath(cur_path) |
|
1051 |
|
1052 path = os.getenv("PATH") |
|
1053 |
|
1054 # environment variable not defined |
|
1055 if path is None: |
|
1056 return "" |
|
1057 |
|
1058 dirs = path.split(os.pathsep) |
|
1059 for directory in dirs: |
|
1060 for filename in filenames: |
|
1061 exe = os.path.join(directory, filename) |
|
1062 if os.access(exe, os.X_OK): |
|
1063 return exe |
|
1064 |
|
1065 return "" |
|
1066 |
|
1067 |
|
1068 def isExecutable(exe): |
|
1069 """ |
|
1070 Function to check, if a file is executable. |
|
1071 |
|
1072 @param exe filename of the executable to check (string) |
|
1073 @return flag indicating executable status (boolean) |
|
1074 """ |
|
1075 return os.access(exe, os.X_OK) |
|
1076 |
|
1077 |
|
1078 def isDrive(path): |
|
1079 """ |
|
1080 Function to check, if a path is a Windows drive. |
|
1081 |
|
1082 @param path path name to be checked |
|
1083 @type str |
|
1084 @return flag indicating a Windows drive |
|
1085 @rtype bool |
|
1086 """ |
|
1087 isDrive = False |
|
1088 drive, directory = os.path.splitdrive(path) |
|
1089 if ( |
|
1090 drive |
|
1091 and len(drive) == 2 |
|
1092 and drive.endswith(":") |
|
1093 and directory in ["", "\\", "/"] |
|
1094 ): |
|
1095 isDrive = True |
|
1096 |
|
1097 return isDrive |
|
1098 |
|
1099 |
|
1100 def samepath(f1, f2): |
|
1101 """ |
|
1102 Function to compare two paths. |
|
1103 |
|
1104 @param f1 first path for the compare (string) |
|
1105 @param f2 second path for the compare (string) |
|
1106 @return flag indicating whether the two paths represent the |
|
1107 same path on disk. |
|
1108 """ |
|
1109 if f1 is None or f2 is None: |
|
1110 return False |
|
1111 |
|
1112 if normcaseabspath(os.path.realpath(f1)) == normcaseabspath(os.path.realpath(f2)): |
|
1113 return True |
|
1114 |
|
1115 return False |
|
1116 |
|
1117 |
|
1118 def samefilepath(f1, f2): |
|
1119 """ |
|
1120 Function to compare two paths. Strips the filename. |
|
1121 |
|
1122 @param f1 first filepath for the compare (string) |
|
1123 @param f2 second filepath for the compare (string) |
|
1124 @return flag indicating whether the two paths represent the |
|
1125 same path on disk. |
|
1126 """ |
|
1127 if f1 is None or f2 is None: |
|
1128 return False |
|
1129 |
|
1130 if normcaseabspath(os.path.dirname(os.path.realpath(f1))) == normcaseabspath( |
|
1131 os.path.dirname(os.path.realpath(f2)) |
|
1132 ): |
|
1133 return True |
|
1134 |
|
1135 return False |
|
1136 |
|
1137 |
|
1138 try: |
|
1139 EXTSEP = os.extsep |
|
1140 except AttributeError: |
|
1141 EXTSEP = "." |
|
1142 |
|
1143 |
|
1144 def splitPath(name): |
|
1145 """ |
|
1146 Function to split a pathname into a directory part and a file part. |
|
1147 |
|
1148 @param name path name (string) |
|
1149 @return a tuple of 2 strings (dirname, filename). |
|
1150 """ |
|
1151 if os.path.isdir(name): |
|
1152 dn = os.path.abspath(name) |
|
1153 fn = "." |
|
1154 else: |
|
1155 dn, fn = os.path.split(name) |
|
1156 return (dn, fn) |
|
1157 |
|
1158 |
|
1159 def joinext(prefix, ext): |
|
1160 """ |
|
1161 Function to join a file extension to a path. |
|
1162 |
|
1163 The leading "." of ext is replaced by a platform specific extension |
|
1164 separator if necessary. |
|
1165 |
|
1166 @param prefix the basepart of the filename (string) |
|
1167 @param ext the extension part (string) |
|
1168 @return the complete filename (string) |
|
1169 """ |
|
1170 if ext[0] != ".": |
|
1171 ext = ".{0}".format(ext) |
|
1172 # require leading separator to match os.path.splitext |
|
1173 return prefix + EXTSEP + ext[1:] |
|
1174 |
|
1175 |
|
1176 def compactPath(path, width, measure=len): |
|
1177 """ |
|
1178 Function to return a compacted path fitting inside the given width. |
|
1179 |
|
1180 @param path path to be compacted (string) |
|
1181 @param width width for the compacted path (integer) |
|
1182 @param measure reference to a function used to measure the length of the |
|
1183 string |
|
1184 @return compacted path (string) |
|
1185 """ |
|
1186 if measure(path) <= width: |
|
1187 return path |
|
1188 |
|
1189 ellipsis = "..." |
|
1190 |
|
1191 head, tail = os.path.split(path) |
|
1192 mid = len(head) // 2 |
|
1193 head1 = head[:mid] |
|
1194 head2 = head[mid:] |
|
1195 while head1: |
|
1196 # head1 is same size as head2 or one shorter |
|
1197 path = os.path.join("{0}{1}{2}".format(head1, ellipsis, head2), tail) |
|
1198 if measure(path) <= width: |
|
1199 return path |
|
1200 head1 = head1[:-1] |
|
1201 head2 = head2[1:] |
|
1202 path = os.path.join(ellipsis, tail) |
|
1203 if measure(path) <= width: |
|
1204 return path |
|
1205 while tail: |
|
1206 path = "{0}{1}".format(ellipsis, tail) |
|
1207 if measure(path) <= width: |
|
1208 return path |
|
1209 tail = tail[1:] |
|
1210 return "" |
|
1211 |
|
1212 |
|
1213 def direntries( |
|
1214 path, filesonly=False, pattern=None, followsymlinks=True, checkStop=None |
|
1215 ): |
|
1216 """ |
|
1217 Function returning a list of all files and directories. |
|
1218 |
|
1219 @param path root of the tree to check |
|
1220 @type str |
|
1221 @param filesonly flag indicating that only files are wanted |
|
1222 @type bool |
|
1223 @param pattern a filename pattern or list of filename patterns to check |
|
1224 against |
|
1225 @type str or list of str |
|
1226 @param followsymlinks flag indicating whether symbolic links |
|
1227 should be followed |
|
1228 @type bool |
|
1229 @param checkStop function to be called to check for a stop |
|
1230 @type function |
|
1231 @return list of all files and directories in the tree rooted |
|
1232 at path. The names are expanded to start with path. |
|
1233 @rtype list of strs |
|
1234 """ |
|
1235 patterns = pattern if isinstance(pattern, list) else [pattern] |
|
1236 files = [] if filesonly else [path] |
|
1237 try: |
|
1238 entries = os.listdir(path) |
|
1239 for entry in entries: |
|
1240 if checkStop and checkStop(): |
|
1241 break |
|
1242 |
|
1243 if entry in [ |
|
1244 ".svn", |
|
1245 ".hg", |
|
1246 ".git", |
|
1247 ".ropeproject", |
|
1248 ".eric7project", |
|
1249 ".jedi", |
|
1250 ]: |
|
1251 continue |
|
1252 |
|
1253 fentry = os.path.join(path, entry) |
|
1254 if ( |
|
1255 pattern |
|
1256 and not os.path.isdir(fentry) |
|
1257 and not any(fnmatch.fnmatch(entry, p) for p in patterns) |
|
1258 ): |
|
1259 # entry doesn't fit the given pattern |
|
1260 continue |
|
1261 |
|
1262 if os.path.isdir(fentry): |
|
1263 if os.path.islink(fentry) and not followsymlinks: |
|
1264 continue |
|
1265 files += direntries( |
|
1266 fentry, filesonly, pattern, followsymlinks, checkStop |
|
1267 ) |
|
1268 else: |
|
1269 files.append(fentry) |
|
1270 except OSError: |
|
1271 pass |
|
1272 except UnicodeDecodeError: |
|
1273 pass |
|
1274 return files |
|
1275 |
|
1276 |
|
1277 def getDirs(path, excludeDirs): |
|
1278 """ |
|
1279 Function returning a list of all directories below path. |
|
1280 |
|
1281 @param path root of the tree to check |
|
1282 @param excludeDirs basename of directories to ignore |
|
1283 @return list of all directories found |
|
1284 """ |
|
1285 try: |
|
1286 names = os.listdir(path) |
|
1287 except OSError: |
|
1288 return [] |
|
1289 |
|
1290 dirs = [] |
|
1291 for name in names: |
|
1292 if os.path.isdir(os.path.join(path, name)) and not os.path.islink( |
|
1293 os.path.join(path, name) |
|
1294 ): |
|
1295 exclude = 0 |
|
1296 for e in excludeDirs: |
|
1297 if name.split(os.sep, 1)[0] == e: |
|
1298 exclude = 1 |
|
1299 break |
|
1300 if not exclude: |
|
1301 dirs.append(os.path.join(path, name)) |
|
1302 |
|
1303 for name in dirs[:]: |
|
1304 if not os.path.islink(name): |
|
1305 dirs += getDirs(name, excludeDirs) |
|
1306 |
|
1307 return dirs |
|
1308 |
|
1309 |
|
1310 def findVolume(volumeName, findAll=False): |
|
1311 """ |
|
1312 Function to find the directory belonging to a given volume name. |
|
1313 |
|
1314 @param volumeName name of the volume to search for |
|
1315 @type str |
|
1316 @param findAll flag indicating to get the directories for all volumes |
|
1317 starting with the given name (defaults to False) |
|
1318 @type bool (optional) |
|
1319 @return directory path or list of directory paths for the given volume |
|
1320 name |
|
1321 @rtype str or list of str |
|
1322 """ |
|
1323 volumeDirectories = [] |
|
1324 volumeDirectory = None |
|
1325 |
|
1326 if isWindowsPlatform(): |
|
1327 # we are on a Windows platform |
|
1328 def getVolumeName(diskName): |
|
1329 """ |
|
1330 Local function to determine the volume of a disk or device. |
|
1331 |
|
1332 Each disk or external device connected to windows has an |
|
1333 attribute called "volume name". This function returns the |
|
1334 volume name for the given disk/device. |
|
1335 |
|
1336 Code from http://stackoverflow.com/a/12056414 |
|
1337 """ |
|
1338 volumeNameBuffer = ctypes.create_unicode_buffer(1024) |
|
1339 ctypes.windll.kernel32.GetVolumeInformationW( |
|
1340 ctypes.c_wchar_p(diskName), |
|
1341 volumeNameBuffer, |
|
1342 ctypes.sizeof(volumeNameBuffer), |
|
1343 None, |
|
1344 None, |
|
1345 None, |
|
1346 None, |
|
1347 0, |
|
1348 ) |
|
1349 return volumeNameBuffer.value |
|
1350 |
|
1351 # |
|
1352 # In certain circumstances, volumes are allocated to USB |
|
1353 # storage devices which cause a Windows popup to raise if their |
|
1354 # volume contains no media. Wrapping the check in SetErrorMode |
|
1355 # with SEM_FAILCRITICALERRORS (1) prevents this popup. |
|
1356 # |
|
1357 oldMode = ctypes.windll.kernel32.SetErrorMode(1) |
|
1358 try: |
|
1359 for disk in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": |
|
1360 dirpath = "{0}:\\".format(disk) |
|
1361 if os.path.exists(dirpath): |
|
1362 if findAll: |
|
1363 if getVolumeName(dirpath).startswith(volumeName): |
|
1364 volumeDirectories.append(dirpath) |
|
1365 else: |
|
1366 if getVolumeName(dirpath) == volumeName: |
|
1367 volumeDirectory = dirpath |
|
1368 break |
|
1369 finally: |
|
1370 ctypes.windll.kernel32.SetErrorMode(oldMode) |
|
1371 else: |
|
1372 # we are on a Linux or macOS platform |
|
1373 for mountCommand in ["mount", "/sbin/mount", "/usr/sbin/mount"]: |
|
1374 with contextlib.suppress(FileNotFoundError): |
|
1375 mountOutput = subprocess.run( # secok |
|
1376 mountCommand, check=True, capture_output=True, text=True |
|
1377 ).stdout.splitlines() |
|
1378 mountedVolumes = [ |
|
1379 x.split(" type")[0].split(maxsplit=2)[2] for x in mountOutput |
|
1380 ] |
|
1381 if findAll: |
|
1382 for volume in mountedVolumes: |
|
1383 if volumeName in volume: |
|
1384 volumeDirectories.append(volume) |
|
1385 if volumeDirectories: |
|
1386 break |
|
1387 else: |
|
1388 for volume in mountedVolumes: |
|
1389 if volume.endswith(volumeName): |
|
1390 volumeDirectory = volume |
|
1391 break |
|
1392 if volumeDirectory: |
|
1393 break |
|
1394 |
|
1395 if findAll: |
|
1396 return volumeDirectories |
|
1397 else: |
|
1398 return volumeDirectory |
|
1399 |
|
1400 |
|
1401 def getTestFileNames(fn): |
845 def getTestFileNames(fn): |
1402 """ |
846 """ |
1403 Function to build the potential file names of a test file. |
847 Function to build the potential file names of a test file. |
1404 |
848 |
1405 The file names for the test file is built by prepending the string |
849 The file names for the test file is built by prepending the string |
1631 """</table>""" |
1075 """</table>""" |
1632 """</p>""", |
1076 """</p>""", |
1633 ) |
1077 ) |
1634 |
1078 |
1635 |
1079 |
1636 def getUserName(): |
|
1637 """ |
|
1638 Function to get the user name. |
|
1639 |
|
1640 @return user name (string) |
|
1641 """ |
|
1642 user = getpass.getuser() |
|
1643 |
|
1644 if isWindowsPlatform() and not user: |
|
1645 return win32_GetUserName() |
|
1646 |
|
1647 return user |
|
1648 |
|
1649 |
|
1650 def getRealName(): |
|
1651 """ |
|
1652 Function to get the real name of the user. |
|
1653 |
|
1654 @return real name of the user (string) |
|
1655 """ |
|
1656 if isWindowsPlatform(): |
|
1657 return win32_getRealName() |
|
1658 else: |
|
1659 user = getpass.getuser() |
|
1660 return pwd.getpwnam(user).pw_gecos |
|
1661 |
|
1662 |
|
1663 def getHomeDir(): |
|
1664 """ |
|
1665 Function to get a users home directory. |
|
1666 |
|
1667 @return home directory (string) |
|
1668 """ |
|
1669 return QDir.homePath() |
|
1670 |
|
1671 |
|
1672 def getPythonLibPath(): |
|
1673 """ |
|
1674 Function to determine the path to Python's library. |
|
1675 |
|
1676 @return path to the Python library (string) |
|
1677 """ |
|
1678 pyFullVers = sys.version.split()[0] |
|
1679 |
|
1680 vl = re.findall("[0-9.]*", pyFullVers)[0].split(".") |
|
1681 major = vl[0] |
|
1682 minor = vl[1] |
|
1683 |
|
1684 pyVers = major + "." + minor |
|
1685 |
|
1686 if isWindowsPlatform(): |
|
1687 libDir = sys.prefix + "\\Lib" |
|
1688 else: |
|
1689 try: |
|
1690 syslib = sys.lib |
|
1691 except AttributeError: |
|
1692 syslib = "lib" |
|
1693 libDir = sys.prefix + "/" + syslib + "/python" + pyVers |
|
1694 |
|
1695 return libDir |
|
1696 |
|
1697 |
|
1698 def getPythonVersion(): |
|
1699 """ |
|
1700 Function to get the Python version (major, minor) as an integer value. |
|
1701 |
|
1702 @return An integer representing major and minor version number (integer) |
|
1703 """ |
|
1704 return sys.hexversion >> 16 |
|
1705 |
|
1706 |
|
1707 def determinePythonVersion(filename, source, editor=None): |
|
1708 """ |
|
1709 Function to determine the python version of a given file. |
|
1710 |
|
1711 @param filename name of the file with extension (str) |
|
1712 @param source of the file (str) |
|
1713 @param editor reference to the editor, if the file is opened |
|
1714 already (Editor object) |
|
1715 @return Python version if file is Python3 (int) |
|
1716 """ |
|
1717 pyAssignment = { |
|
1718 "Python3": 3, |
|
1719 "MicroPython": 3, |
|
1720 "Cython": 3, |
|
1721 } |
|
1722 |
|
1723 if not editor: |
|
1724 viewManager = ericApp().getObject("ViewManager") |
|
1725 editor = viewManager.getOpenEditor(filename) |
|
1726 |
|
1727 # Maybe the user has changed the language |
|
1728 if editor and editor.getFileType() in pyAssignment: |
|
1729 return pyAssignment[editor.getFileType()] |
|
1730 |
|
1731 pyVer = 0 |
|
1732 if filename: |
|
1733 if not source: |
|
1734 source = readEncodedFile(filename)[0] |
|
1735 flags = extractFlags(source) |
|
1736 ext = os.path.splitext(filename)[1] |
|
1737 py3Ext = Preferences.getPython("Python3Extensions") |
|
1738 project = ericApp().getObject("Project") |
|
1739 basename = os.path.basename(filename) |
|
1740 |
|
1741 if "FileType" in flags: |
|
1742 pyVer = pyAssignment.get(flags["FileType"], 0) |
|
1743 elif project.isOpen() and project.isProjectFile(filename): |
|
1744 language = project.getEditorLexerAssoc(basename) |
|
1745 if not language: |
|
1746 language = Preferences.getEditorLexerAssoc(basename) |
|
1747 if language == "Python3": |
|
1748 pyVer = pyAssignment[language] |
|
1749 |
|
1750 if pyVer: |
|
1751 # Skip the next tests |
|
1752 pass |
|
1753 elif ( |
|
1754 Preferences.getProject("DeterminePyFromProject") |
|
1755 and project.isOpen() |
|
1756 and project.isProjectFile(filename) |
|
1757 and ext in py3Ext |
|
1758 ): |
|
1759 pyVer = pyAssignment.get(project.getProjectLanguage(), 0) |
|
1760 elif ext in py3Ext: |
|
1761 pyVer = 3 |
|
1762 elif source: |
|
1763 if isinstance(source, str): |
|
1764 line0 = source.splitlines()[0] |
|
1765 else: |
|
1766 line0 = source[0] |
|
1767 if line0.startswith("#!") and (("python3" in line0) or ("python" in line0)): |
|
1768 pyVer = 3 |
|
1769 |
|
1770 if pyVer == 0 and ext in py3Ext: |
|
1771 pyVer = 3 |
|
1772 |
|
1773 return pyVer |
|
1774 |
|
1775 |
|
1776 def rxIndex(rx, txt): |
1080 def rxIndex(rx, txt): |
1777 """ |
1081 """ |
1778 Function to get the index (start position) of a regular expression match |
1082 Function to get the index (start position) of a regular expression match |
1779 within some text. |
1083 within some text. |
1780 |
1084 |
1788 match = rx.search(txt) |
1092 match = rx.search(txt) |
1789 if match is None: |
1093 if match is None: |
1790 return -1 |
1094 return -1 |
1791 else: |
1095 else: |
1792 return match.start() |
1096 return match.start() |
1793 |
|
1794 |
|
1795 ############################################################################### |
|
1796 ## functions for environment handling |
|
1797 ############################################################################### |
|
1798 |
|
1799 |
|
1800 def getEnvironmentEntry(key, default=None): |
|
1801 """ |
|
1802 Module function to get an environment entry. |
|
1803 |
|
1804 @param key key of the requested environment entry (string) |
|
1805 @param default value to be returned, if the environment doesn't contain |
|
1806 the requested entry (string) |
|
1807 @return the requested entry or the default value, if the entry wasn't |
|
1808 found (string or None) |
|
1809 """ |
|
1810 pattern = "^{0}[ \t]*=".format(key) |
|
1811 filterRe = ( |
|
1812 re.compile(pattern, re.IGNORECASE) |
|
1813 if isWindowsPlatform() |
|
1814 else re.compile(pattern) |
|
1815 ) |
|
1816 |
|
1817 entries = [ |
|
1818 e for e in QProcess.systemEnvironment() if filterRe.search(e) is not None |
|
1819 ] |
|
1820 if not entries: |
|
1821 return default |
|
1822 |
|
1823 # if there are multiple entries, just consider the first one |
|
1824 ename, value = entries[0].split("=", 1) |
|
1825 return value.strip() |
|
1826 |
|
1827 |
|
1828 def hasEnvironmentEntry(key): |
|
1829 """ |
|
1830 Module function to check, if the environment contains an entry. |
|
1831 |
|
1832 @param key key of the requested environment entry |
|
1833 @type str |
|
1834 @return flag indicating the presence of the requested entry |
|
1835 @rtype bool |
|
1836 """ |
|
1837 pattern = "^{0}[ \t]*=".format(key) |
|
1838 filterRe = ( |
|
1839 re.compile(pattern, re.IGNORECASE) |
|
1840 if isWindowsPlatform() |
|
1841 else re.compile(pattern) |
|
1842 ) |
|
1843 |
|
1844 entries = [ |
|
1845 e for e in QProcess.systemEnvironment() if filterRe.search(e) is not None |
|
1846 ] |
|
1847 return len(entries) > 0 |
|
1848 |
|
1849 |
|
1850 ############################################################################### |
|
1851 ## Qt utility functions below |
|
1852 ############################################################################### |
|
1853 |
|
1854 |
|
1855 def generateQtToolName(toolname): |
|
1856 """ |
|
1857 Module function to generate the executable name for a Qt tool like |
|
1858 designer. |
|
1859 |
|
1860 @param toolname base name of the tool (string) |
|
1861 @return the Qt tool name without extension (string) |
|
1862 """ |
|
1863 return "{0}{1}{2}".format( |
|
1864 Preferences.getQt("QtToolsPrefix"), |
|
1865 toolname, |
|
1866 Preferences.getQt("QtToolsPostfix"), |
|
1867 ) |
|
1868 |
|
1869 |
|
1870 def getQtMacBundle(toolname): |
|
1871 """ |
|
1872 Module function to determine the correct Mac OS X bundle name for Qt tools. |
|
1873 |
|
1874 @param toolname plain name of the tool (e.g. "designer") (string) |
|
1875 @return bundle name of the Qt tool (string) |
|
1876 """ |
|
1877 qtDir = getQtBinariesPath() |
|
1878 bundles = [ |
|
1879 os.path.join(qtDir, "bin", generateQtToolName(toolname.capitalize())) + ".app", |
|
1880 os.path.join(qtDir, "bin", generateQtToolName(toolname)) + ".app", |
|
1881 os.path.join(qtDir, generateQtToolName(toolname.capitalize())) + ".app", |
|
1882 os.path.join(qtDir, generateQtToolName(toolname)) + ".app", |
|
1883 ] |
|
1884 if toolname == "designer": |
|
1885 # support the standalone Qt Designer installer from |
|
1886 # https://build-system.fman.io/qt-designer-download |
|
1887 designer = "Qt Designer.app" |
|
1888 bundles.extend( |
|
1889 [ |
|
1890 os.path.join(qtDir, "bin", designer), |
|
1891 os.path.join(qtDir, designer), |
|
1892 ] |
|
1893 ) |
|
1894 for bundle in bundles: |
|
1895 if os.path.exists(bundle): |
|
1896 return bundle |
|
1897 return "" |
|
1898 |
|
1899 |
|
1900 def prepareQtMacBundle(toolname, args): |
|
1901 """ |
|
1902 Module function for starting Qt tools that are Mac OS X bundles. |
|
1903 |
|
1904 @param toolname plain name of the tool (e.g. "designer") |
|
1905 @type str |
|
1906 @param args name of input file for tool, if any |
|
1907 @type list of str |
|
1908 @return command-name and args for QProcess |
|
1909 @rtype tuple of (str, list of str) |
|
1910 """ |
|
1911 fullBundle = getQtMacBundle(toolname) |
|
1912 if fullBundle == "": |
|
1913 return ("", []) |
|
1914 |
|
1915 newArgs = [] |
|
1916 newArgs.append("-a") |
|
1917 newArgs.append(fullBundle) |
|
1918 if args: |
|
1919 newArgs.append("--args") |
|
1920 newArgs += args |
|
1921 |
|
1922 return ("open", newArgs) |
|
1923 |
|
1924 |
|
1925 ############################################################################### |
|
1926 ## PyQt utility functions below |
|
1927 ############################################################################### |
|
1928 |
|
1929 |
|
1930 def generatePyQtToolPath(toolname, alternatives=None): |
|
1931 """ |
|
1932 Module function to generate the executable path for a PyQt tool. |
|
1933 |
|
1934 @param toolname base name of the tool |
|
1935 @type str |
|
1936 @param alternatives list of alternative tool names to try |
|
1937 @type list of str |
|
1938 @return executable path name of the tool |
|
1939 @rtype str |
|
1940 """ |
|
1941 pyqtVariant = int(toolname[-1]) |
|
1942 pyqtToolsPath = getPyQtToolsPath(pyqtVariant) |
|
1943 if pyqtToolsPath: |
|
1944 exe = os.path.join(pyqtToolsPath, toolname) |
|
1945 if isWindowsPlatform(): |
|
1946 exe += ".exe" |
|
1947 else: |
|
1948 if isWindowsPlatform(): |
|
1949 exe = getWindowsExecutablePath(toolname) |
|
1950 else: |
|
1951 exe = toolname |
|
1952 |
|
1953 if not isinpath(exe) and alternatives: |
|
1954 ex_ = generatePyQtToolPath(alternatives[0], alternatives[1:]) |
|
1955 if isinpath(ex_): |
|
1956 exe = ex_ |
|
1957 |
|
1958 return exe |
|
1959 |
|
1960 |
|
1961 ############################################################################### |
|
1962 ## PySide2/PySide6 utility functions below |
|
1963 ############################################################################### |
|
1964 |
|
1965 |
|
1966 def generatePySideToolPath(toolname, variant=2): |
|
1967 """ |
|
1968 Module function to generate the executable path for a PySide2/PySide6 tool. |
|
1969 |
|
1970 @param toolname base name of the tool |
|
1971 @type str |
|
1972 @param variant indicator for the PySide variant |
|
1973 @type int or str |
|
1974 @return the PySide2/PySide6 tool path with extension |
|
1975 @rtype str |
|
1976 """ |
|
1977 if isWindowsPlatform(): |
|
1978 hasPyside = checkPyside(variant) |
|
1979 if not hasPyside: |
|
1980 return "" |
|
1981 |
|
1982 venvName = Preferences.getQt("PySide{0}VenvName".format(variant)) |
|
1983 if not venvName: |
|
1984 venvName = Preferences.getDebugger("Python3VirtualEnv") |
|
1985 interpreter = ( |
|
1986 ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName) |
|
1987 ) |
|
1988 if interpreter == "" or not isinpath(interpreter): |
|
1989 interpreter = getPythonExecutable() |
|
1990 prefix = os.path.dirname(interpreter) |
|
1991 if not prefix.endswith("Scripts"): |
|
1992 prefix = os.path.join(prefix, "Scripts") |
|
1993 return os.path.join(prefix, toolname + ".exe") |
|
1994 else: |
|
1995 # step 1: check, if the user has configured a tools path |
|
1996 path = Preferences.getQt("PySide{0}ToolsDir".format(variant)) |
|
1997 if path: |
|
1998 return os.path.join(path, toolname) |
|
1999 |
|
2000 # step 2: determine from used Python interpreter |
|
2001 dirName = os.path.dirname(sys.executable) |
|
2002 if os.path.exists(os.path.join(dirName, toolname)): |
|
2003 return os.path.join(dirName, toolname) |
|
2004 |
|
2005 return toolname |
|
2006 |
|
2007 |
|
2008 @functools.lru_cache() |
|
2009 def checkPyside(variant=2): |
|
2010 """ |
|
2011 Module function to check the presence of PySide2/PySide6. |
|
2012 |
|
2013 @param variant indicator for the PySide variant |
|
2014 @type int or str |
|
2015 @return flags indicating the presence of PySide2/PySide6 |
|
2016 @rtype bool |
|
2017 """ |
|
2018 venvName = Preferences.getQt("PySide{0}VenvName".format(variant)) |
|
2019 if not venvName: |
|
2020 venvName = Preferences.getDebugger("Python3VirtualEnv") |
|
2021 interpreter = ( |
|
2022 ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName) |
|
2023 ) |
|
2024 if interpreter == "" or not isinpath(interpreter): |
|
2025 interpreter = getPythonExecutable() |
|
2026 |
|
2027 checker = os.path.join(getConfig("ericDir"), "Utilities", "PySideImporter.py") |
|
2028 args = [checker, "--variant={0}".format(variant)] |
|
2029 proc = QProcess() |
|
2030 proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels) |
|
2031 proc.start(interpreter, args) |
|
2032 finished = proc.waitForFinished(30000) |
|
2033 return finished and proc.exitCode() == 0 |
|
2034 |
1097 |
2035 |
1098 |
2036 ############################################################################### |
1099 ############################################################################### |
2037 ## Other utility functions below |
1100 ## Other utility functions below |
2038 ############################################################################### |
1101 ############################################################################### |