src/eric7/Utilities/__init__.py

branch
eric7
changeset 9624
b47dfa7a137d
parent 9531
155b2646799a
child 9631
bf174ac41631
equal deleted inserted replaced
9623:9c1f429cb56b 9624:b47dfa7a137d
7 Package implementing various functions/classes needed everywhere within eric. 7 Package implementing various functions/classes needed everywhere within eric.
8 """ 8 """
9 9
10 import codecs 10 import codecs
11 import contextlib 11 import contextlib
12 import ctypes
13 import fnmatch
14 import functools
15 import getpass
16 import glob 12 import glob
17 import json 13 import json
18 import os 14 import os
19 import pathlib
20 import re 15 import re
21 import shlex 16 import shlex
22 import subprocess # secok
23 import sys 17 import sys
24
25 with contextlib.suppress(ImportError):
26 import pwd # only available on Unix systems
27
28 import warnings 18 import warnings
29 19
30 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32 20 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32
31 21
32 import chardet 22 import chardet
36 from PyQt6.QtCore import ( 26 from PyQt6.QtCore import (
37 PYQT_VERSION_STR, 27 PYQT_VERSION_STR,
38 QByteArray, 28 QByteArray,
39 QCoreApplication, 29 QCoreApplication,
40 QCryptographicHash, 30 QCryptographicHash,
41 QDir,
42 QProcess, 31 QProcess,
43 qVersion, 32 qVersion,
44 ) 33 )
45 34
46 from eric7 import Preferences 35 from eric7 import Preferences
47 from eric7.EricWidgets.EricApplication import ericApp 36 from eric7.EricWidgets.EricApplication import ericApp
48 37
49 # import these methods into the Utilities namespace 38 # imports from eric7.SystemUtilities are for backward compatibility
50 from eric7.Globals import ( # __IGNORE_WARNING__ 39 from eric7.Globals import ( # __IGNORE_FLAKES_WARNING__
51 desktopName, 40 desktopName,
52 getConfig, 41 getConfig,
53 getConfigDir, 42 getConfigDir,
54 getPyQt6ModulesDirectory,
55 getPyQtToolsPath,
56 getPythonExecutable, 43 getPythonExecutable,
57 getPythonLibraryDirectory, 44 getPythonLibraryDirectory,
58 getPythonScriptsDirectory, 45 getPythonScriptsDirectory,
59 getQtBinariesPath, 46 sessionType,
47 setConfigDir,
48 toBool,
49 )
50 from eric7.SystemUtilities import DesktopUtilities
51 from eric7.SystemUtilities.FileSystemUtilities import ( # __IGNORE_FLAKES_WARNING__
52 absolutePath,
53 absoluteUniversalPath,
54 compactPath,
55 direntries,
56 findVolume,
57 fromNativeSeparators,
58 getDirs,
59 getExecutablePath,
60 getExecutablePaths,
61 getWindowsExecutablePath,
62 isDrive,
63 isExecutable,
64 isinpath,
65 joinext,
66 normabsjoinpath,
67 normcaseabspath,
68 normcasepath,
69 normjoinpath,
70 relativeUniversalPath,
71 samefilepath,
72 samepath,
73 splitPath,
74 startswithPath,
75 toNativeSeparators,
76 )
77 from eric7.SystemUtilities.OSUtilities import ( # __IGNORE_FLAKES_WARNING__
78 getEnvironmentEntry,
79 getHomeDir,
80 getUserName,
81 hasEnvironmentEntry,
60 isLinuxPlatform, 82 isLinuxPlatform,
61 isMacPlatform, 83 isMacPlatform,
62 isWindowsPlatform, 84 isWindowsPlatform,
85 win32_getRealName,
86 win32_GetUserName,
87 win32_Kill,
88 )
89 from eric7.SystemUtilities.PythonUtilities import ( # __IGNORE_FLAKES_WARNING__
90 determinePythonVersion,
91 getPythonLibPath,
92 getPythonVersion,
93 )
94 from eric7.SystemUtilities.QtUtilities import ( # __IGNORE_FLAKES_WARNING__
95 checkPyside,
96 generatePyQtToolPath,
97 generatePySideToolPath,
98 generateQtToolName,
99 getPyQt6ModulesDirectory,
100 getPyQtToolsPath,
101 getQtBinariesPath,
102 getQtMacBundle,
103 prepareQtMacBundle,
63 qVersionTuple, 104 qVersionTuple,
64 sessionType,
65 setConfigDir,
66 ) 105 )
67 from eric7.UI.Info import Program, Version 106 from eric7.UI.Info import Program, Version
68 107
69 108
70 def __showwarning(message, category, filename, lineno, file=None, line=""): 109 def __showwarning(message, category, filename, lineno, file=None, line=""):
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 ###############################################################################
2091 info.append("") 1154 info.append("")
2092 info.append("Platform: {0}".format(sys.platform)) 1155 info.append("Platform: {0}".format(sys.platform))
2093 if os.environ.get("SOMMELIER_VERSION", ""): 1156 if os.environ.get("SOMMELIER_VERSION", ""):
2094 info[-1] += ", ChromeOS" 1157 info[-1] += ", ChromeOS"
2095 info.append(sys.version) 1158 info.append(sys.version)
2096 desktop = desktopName() 1159 desktop = DesktopUtilities.desktopName()
2097 if desktop: 1160 if desktop:
2098 info.append("") 1161 info.append("")
2099 info.append("Desktop: {0}".format(desktop)) 1162 info.append("Desktop: {0}".format(desktop))
2100 session = sessionType() 1163 session = DesktopUtilities.sessionType()
2101 if session: 1164 if session:
2102 info.append("") 1165 info.append("")
2103 info.append("Session Type: {0}".format(session)) 1166 info.append("Session Type: {0}".format(session))
2104 1167
2105 return linesep.join(info) 1168 return linesep.join(info)
2156 info.append(" {0}".format(rfile)) 1219 info.append(" {0}".format(rfile))
2157 info.extend([" {0}".format(line) for line in lines]) 1220 info.extend([" {0}".format(line) for line in lines])
2158 info.append("") 1221 info.append("")
2159 1222
2160 return linesep.join(info) 1223 return linesep.join(info)
2161
2162
2163 def toBool(dataStr):
2164 """
2165 Module function to convert a string to a boolean value.
2166
2167 @param dataStr string to be converted (string)
2168 @return converted boolean value (boolean)
2169 """
2170 if dataStr in ["True", "true", "1", "Yes", "yes"]:
2171 return True
2172 elif dataStr in ["False", "false", "0", "No", "no"]:
2173 return False
2174 else:
2175 return bool(dataStr)
2176 1224
2177 1225
2178 def getSysPath(interpreter): 1226 def getSysPath(interpreter):
2179 """ 1227 """
2180 Module function to get the Python path (sys.path) of a specific 1228 Module function to get the Python path (sys.path) of a specific
2201 sysPath = json.loads(sysPathResult) 1249 sysPath = json.loads(sysPathResult)
2202 if "" in sysPath: 1250 if "" in sysPath:
2203 sysPath.remove("") 1251 sysPath.remove("")
2204 1252
2205 return sysPath 1253 return sysPath
2206
2207
2208 ###############################################################################
2209 ## posix compatibility functions below
2210 ###############################################################################
2211
2212 # None right now
2213
2214 ###############################################################################
2215 ## win32 compatibility functions below
2216 ###############################################################################
2217
2218
2219 def win32_Kill(pid):
2220 """
2221 Function to provide an os.kill equivalent for Win32.
2222
2223 @param pid process id (integer)
2224 @return result of the kill (boolean)
2225 """
2226 import win32api # __IGNORE_WARNING_I102__
2227
2228 handle = win32api.OpenProcess(1, 0, pid)
2229 return 0 != win32api.TerminateProcess(handle, 0)
2230
2231
2232 def win32_GetUserName():
2233 """
2234 Function to get the user name under Win32.
2235
2236 @return user name (string)
2237 """
2238 try:
2239 import win32api # __IGNORE_WARNING_I10__
2240
2241 return win32api.GetUserName()
2242 except ImportError:
2243 try:
2244 u = getEnvironmentEntry("USERNAME")
2245 except KeyError:
2246 u = getEnvironmentEntry("username", None)
2247 return u
2248
2249
2250 def win32_getRealName():
2251 """
2252 Function to get the user's real name (aka. display name) under Win32.
2253
2254 @return real name of the current user (string)
2255 """
2256 GetUserNameEx = ctypes.windll.secur32.GetUserNameExW
2257 NameDisplay = 3
2258
2259 size = ctypes.pointer(ctypes.c_ulong(0))
2260 GetUserNameEx(NameDisplay, None, size)
2261
2262 nameBuffer = ctypes.create_unicode_buffer(size.contents.value)
2263 GetUserNameEx(NameDisplay, nameBuffer, size)
2264 return nameBuffer.value

eric ide

mercurial