src/eric7/Utilities/__init__.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9214
bd28e56047d7
child 9229
326810448a9c
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
16 import glob 16 import glob
17 import os 17 import os
18 import pathlib 18 import pathlib
19 import re 19 import re
20 import shlex 20 import shlex
21 import subprocess # secok 21 import subprocess # secok
22 import sys 22 import sys
23 23
24 24
25 def __showwarning(message, category, filename, lineno, file=None, line=""): 25 def __showwarning(message, category, filename, lineno, file=None, line=""):
26 """ 26 """
27 Module function to raise a SyntaxError for a SyntaxWarning. 27 Module function to raise a SyntaxError for a SyntaxWarning.
28 28
29 @param message warning object 29 @param message warning object
30 @param category type object of the warning 30 @param category type object of the warning
31 @param filename name of the file causing the warning (string) 31 @param filename name of the file causing the warning (string)
32 @param lineno line number causing the warning (integer) 32 @param lineno line number causing the warning (integer)
33 @param file file to write the warning message to (ignored) 33 @param file file to write the warning message to (ignored)
37 if category is SyntaxWarning: 37 if category is SyntaxWarning:
38 err = SyntaxError(str(message)) 38 err = SyntaxError(str(message))
39 err.filename = filename 39 err.filename = filename
40 err.lineno = lineno 40 err.lineno = lineno
41 raise err 41 raise err
42 42
43
43 import warnings 44 import warnings
45
44 warnings.showwarning = __showwarning 46 warnings.showwarning = __showwarning
45 47
46 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32 48 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32
47 49
48 from PyQt6.QtCore import ( 50 from PyQt6.QtCore import (
49 qVersion, PYQT_VERSION_STR, QDir, QProcess, QByteArray, QCoreApplication, 51 qVersion,
50 QCryptographicHash 52 PYQT_VERSION_STR,
53 QDir,
54 QProcess,
55 QByteArray,
56 QCoreApplication,
57 QCryptographicHash,
51 ) 58 )
52 from PyQt6.Qsci import QSCINTILLA_VERSION_STR, QsciScintilla 59 from PyQt6.Qsci import QSCINTILLA_VERSION_STR, QsciScintilla
53 60
54 # import these methods into the Utilities namespace 61 # import these methods into the Utilities namespace
55 from Globals import ( # __IGNORE_WARNING__ 62 from Globals import ( # __IGNORE_WARNING__
56 isWindowsPlatform, isLinuxPlatform, isMacPlatform, desktopName, 63 isWindowsPlatform,
57 sessionType, getConfigDir, setConfigDir, getPythonLibraryDirectory, 64 isLinuxPlatform,
58 getPyQt6ModulesDirectory, getQtBinariesPath, getPyQtToolsPath, 65 isMacPlatform,
59 qVersionTuple, getPythonExecutable 66 desktopName,
67 sessionType,
68 getConfigDir,
69 setConfigDir,
70 getPythonLibraryDirectory,
71 getPyQt6ModulesDirectory,
72 getQtBinariesPath,
73 getPyQtToolsPath,
74 qVersionTuple,
75 getPythonExecutable,
60 ) 76 )
61 77
62 from EricWidgets.EricApplication import ericApp 78 from EricWidgets.EricApplication import ericApp
63 79
64 from UI.Info import Program, Version 80 from UI.Info import Program, Version
65 81
66 import Preferences 82 import Preferences
67 from Plugins.CheckerPlugins.SyntaxChecker.SyntaxCheck import ( 83 from Plugins.CheckerPlugins.SyntaxChecker.SyntaxCheck import (
68 # __IGNORE_WARNING__ 84 # __IGNORE_WARNING__
69 normalizeCode) 85 normalizeCode,
86 )
70 87
71 from eric7config import getConfig 88 from eric7config import getConfig
72 89
73 configDir = None 90 configDir = None
74 91
75 codingBytes_regexps = [ 92 codingBytes_regexps = [
76 (5, re.compile(br'''coding[:=]\s*([-\w_.]+)''')), 93 (5, re.compile(rb"""coding[:=]\s*([-\w_.]+)""")),
77 (1, re.compile(br'''<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>''')), 94 (1, re.compile(rb"""<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>""")),
78 ] 95 ]
79 coding_regexps = [ 96 coding_regexps = [
80 (5, re.compile(r'''coding[:=]\s*([-\w_.]+)''')), 97 (5, re.compile(r"""coding[:=]\s*([-\w_.]+)""")),
81 (1, re.compile(r'''<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>''')), 98 (1, re.compile(r"""<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>""")),
82 ] 99 ]
83 100
84 supportedCodecs = [ 101 supportedCodecs = [
85 'utf-8', 102 "utf-8",
86 103 "iso-8859-1",
87 'iso-8859-1', 'iso-8859-2', 'iso-8859-3', 104 "iso-8859-2",
88 'iso-8859-4', 'iso-8859-5', 'iso-8859-6', 'iso-8859-7', 105 "iso-8859-3",
89 'iso-8859-8', 'iso-8859-9', 'iso-8859-10', 'iso-8859-11', 106 "iso-8859-4",
90 'iso-8859-13', 'iso-8859-14', 'iso-8859-15', 'iso-8859-16', 107 "iso-8859-5",
91 'latin-1', 108 "iso-8859-6",
92 109 "iso-8859-7",
93 'koi8-r', 'koi8-t', 'koi8-u', 110 "iso-8859-8",
94 111 "iso-8859-9",
95 'utf-7', 112 "iso-8859-10",
96 'utf-16', 'utf-16-be', 'utf-16-le', 113 "iso-8859-11",
97 'utf-32', 'utf-32-be', 'utf-32-le', 114 "iso-8859-13",
98 115 "iso-8859-14",
99 'cp037', 'cp273', 'cp424', 'cp437', 'cp500', 'cp720', 116 "iso-8859-15",
100 'cp737', 'cp775', 'cp850', 'cp852', 'cp855', 'cp856', 117 "iso-8859-16",
101 'cp857', 'cp858', 'cp860', 'cp861', 'cp862', 'cp863', 118 "latin-1",
102 'cp864', 'cp865', 'cp866', 'cp869', 'cp874', 'cp875', 119 "koi8-r",
103 'cp932', 'cp949', 'cp950', 'cp1006', 'cp1026', 'cp1125', 120 "koi8-t",
104 'cp1140', 121 "koi8-u",
105 122 "utf-7",
106 'windows-1250', 'windows-1251', 'windows-1252', 'windows-1253', 123 "utf-16",
107 'windows-1254', 'windows-1255', 'windows-1256', 'windows-1257', 124 "utf-16-be",
108 'windows-1258', 125 "utf-16-le",
109 126 "utf-32",
110 'gb2312', 'hz', 'gb18030', 'gbk', 127 "utf-32-be",
111 128 "utf-32-le",
112 'iso-2022-jp', 'iso-2022-jp-1', 'iso-2022-jp-2', 'iso-2022-jp-2004', 129 "cp037",
113 'iso-2022-jp-3', 'iso-2022-jp-ext', 'iso-2022-kr', 130 "cp273",
114 131 "cp424",
115 'mac-cyrillic', 'mac-greek', 'mac-iceland', 'mac-latin2', 132 "cp437",
116 'mac-roman', 'mac-turkish', 133 "cp500",
117 134 "cp720",
118 'ascii', 135 "cp737",
119 'big5-tw', 'big5-hkscs', 136 "cp775",
137 "cp850",
138 "cp852",
139 "cp855",
140 "cp856",
141 "cp857",
142 "cp858",
143 "cp860",
144 "cp861",
145 "cp862",
146 "cp863",
147 "cp864",
148 "cp865",
149 "cp866",
150 "cp869",
151 "cp874",
152 "cp875",
153 "cp932",
154 "cp949",
155 "cp950",
156 "cp1006",
157 "cp1026",
158 "cp1125",
159 "cp1140",
160 "windows-1250",
161 "windows-1251",
162 "windows-1252",
163 "windows-1253",
164 "windows-1254",
165 "windows-1255",
166 "windows-1256",
167 "windows-1257",
168 "windows-1258",
169 "gb2312",
170 "hz",
171 "gb18030",
172 "gbk",
173 "iso-2022-jp",
174 "iso-2022-jp-1",
175 "iso-2022-jp-2",
176 "iso-2022-jp-2004",
177 "iso-2022-jp-3",
178 "iso-2022-jp-ext",
179 "iso-2022-kr",
180 "mac-cyrillic",
181 "mac-greek",
182 "mac-iceland",
183 "mac-latin2",
184 "mac-roman",
185 "mac-turkish",
186 "ascii",
187 "big5-tw",
188 "big5-hkscs",
120 ] 189 ]
121 190
122 191
123 class CodingError(Exception): 192 class CodingError(Exception):
124 """ 193 """
125 Class implementing an exception, which is raised, if a given coding is 194 Class implementing an exception, which is raised, if a given coding is
126 incorrect. 195 incorrect.
127 """ 196 """
197
128 def __init__(self, coding): 198 def __init__(self, coding):
129 """ 199 """
130 Constructor 200 Constructor
131 201
132 @param coding coding to include in the message (string) 202 @param coding coding to include in the message (string)
133 """ 203 """
134 self.errorMessage = QCoreApplication.translate( 204 self.errorMessage = QCoreApplication.translate(
135 "CodingError", 205 "CodingError", "The coding '{0}' is wrong for the given text."
136 "The coding '{0}' is wrong for the given text.").format(coding) 206 ).format(coding)
137 207
138 def __repr__(self): 208 def __repr__(self):
139 """ 209 """
140 Special method returning a representation of the exception. 210 Special method returning a representation of the exception.
141 211
142 @return string representing the error message 212 @return string representing the error message
143 """ 213 """
144 return str(self.errorMessage) 214 return str(self.errorMessage)
145 215
146 def __str__(self): 216 def __str__(self):
147 """ 217 """
148 Special method returning a string representation of the exception. 218 Special method returning a string representation of the exception.
149 219
150 @return string representing the error message 220 @return string representing the error message
151 """ 221 """
152 return str(self.errorMessage) 222 return str(self.errorMessage)
153 223
154 224
155 def get_codingBytes(text): 225 def get_codingBytes(text):
156 """ 226 """
157 Function to get the coding of a bytes text. 227 Function to get the coding of a bytes text.
158 228
159 @param text bytes text to inspect (bytes) 229 @param text bytes text to inspect (bytes)
160 @return coding string 230 @return coding string
161 """ 231 """
162 lines = text.splitlines() 232 lines = text.splitlines()
163 for coding in codingBytes_regexps: 233 for coding in codingBytes_regexps:
164 coding_re = coding[1] 234 coding_re = coding[1]
165 head = lines[:coding[0]] 235 head = lines[: coding[0]]
166 for line in head: 236 for line in head:
167 m = coding_re.search(line) 237 m = coding_re.search(line)
168 if m: 238 if m:
169 return str(m.group(1), "ascii").lower() 239 return str(m.group(1), "ascii").lower()
170 return None 240 return None
171 241
172 242
173 def get_coding(text): 243 def get_coding(text):
174 """ 244 """
175 Function to get the coding of a text. 245 Function to get the coding of a text.
176 246
177 @param text text to inspect (string) 247 @param text text to inspect (string)
178 @return coding string 248 @return coding string
179 """ 249 """
180 lines = text.splitlines() 250 lines = text.splitlines()
181 for coding in coding_regexps: 251 for coding in coding_regexps:
182 coding_re = coding[1] 252 coding_re = coding[1]
183 head = lines[:coding[0]] 253 head = lines[: coding[0]]
184 for line in head: 254 for line in head:
185 m = coding_re.search(line) 255 m = coding_re.search(line)
186 if m: 256 if m:
187 return m.group(1).lower() 257 return m.group(1).lower()
188 return None 258 return None
189 259
190 260
191 def readEncodedFile(filename): 261 def readEncodedFile(filename):
192 """ 262 """
193 Function to read a file and decode its contents into proper text. 263 Function to read a file and decode its contents into proper text.
194 264
195 @param filename name of the file to read (string) 265 @param filename name of the file to read (string)
196 @return tuple of decoded text and encoding (string, string) 266 @return tuple of decoded text and encoding (string, string)
197 """ 267 """
198 with open(filename, "rb") as f: 268 with open(filename, "rb") as f:
199 text = f.read() 269 text = f.read()
202 272
203 def readEncodedFileWithHash(filename): 273 def readEncodedFileWithHash(filename):
204 """ 274 """
205 Function to read a file, calculate a hash value and decode its contents 275 Function to read a file, calculate a hash value and decode its contents
206 into proper text. 276 into proper text.
207 277
208 @param filename name of the file to read (string) 278 @param filename name of the file to read (string)
209 @return tuple of decoded text, encoding and hash value (string, string, 279 @return tuple of decoded text, encoding and hash value (string, string,
210 string) 280 string)
211 """ 281 """
212 with open(filename, "rb") as f: 282 with open(filename, "rb") as f:
213 text = f.read() 283 text = f.read()
214 hashStr = str(QCryptographicHash.hash( 284 hashStr = str(
215 QByteArray(text), QCryptographicHash.Algorithm.Md5).toHex(), 285 QCryptographicHash.hash(
216 encoding="ASCII") 286 QByteArray(text), QCryptographicHash.Algorithm.Md5
217 return decode(text) + (hashStr, ) 287 ).toHex(),
288 encoding="ASCII",
289 )
290 return decode(text) + (hashStr,)
218 291
219 292
220 def decode(text): 293 def decode(text):
221 """ 294 """
222 Function to decode some byte text into a string. 295 Function to decode some byte text into a string.
223 296
224 @param text byte text to decode (bytes) 297 @param text byte text to decode (bytes)
225 @return tuple of decoded text and encoding (string, string) 298 @return tuple of decoded text and encoding (string, string)
226 """ 299 """
227 with contextlib.suppress(UnicodeError, LookupError): 300 with contextlib.suppress(UnicodeError, LookupError):
228 if text.startswith(BOM_UTF8): 301 if text.startswith(BOM_UTF8):
229 # UTF-8 with BOM 302 # UTF-8 with BOM
230 return str(text[len(BOM_UTF8):], 'utf-8'), 'utf-8-bom' 303 return str(text[len(BOM_UTF8) :], "utf-8"), "utf-8-bom"
231 elif text.startswith(BOM_UTF16): 304 elif text.startswith(BOM_UTF16):
232 # UTF-16 with BOM 305 # UTF-16 with BOM
233 return str(text[len(BOM_UTF16):], 'utf-16'), 'utf-16' 306 return str(text[len(BOM_UTF16) :], "utf-16"), "utf-16"
234 elif text.startswith(BOM_UTF32): 307 elif text.startswith(BOM_UTF32):
235 # UTF-32 with BOM 308 # UTF-32 with BOM
236 return str(text[len(BOM_UTF32):], 'utf-32'), 'utf-32' 309 return str(text[len(BOM_UTF32) :], "utf-32"), "utf-32"
237 coding = get_codingBytes(text) 310 coding = get_codingBytes(text)
238 if coding: 311 if coding:
239 return str(text, coding), coding 312 return str(text, coding), coding
240 313
241 # Assume UTF-8 314 # Assume UTF-8
242 with contextlib.suppress(UnicodeError, LookupError): 315 with contextlib.suppress(UnicodeError, LookupError):
243 return str(text, 'utf-8'), 'utf-8-guessed' 316 return str(text, "utf-8"), "utf-8-guessed"
244 317
245 guess = None 318 guess = None
246 if Preferences.getEditor("AdvancedEncodingDetection"): 319 if Preferences.getEditor("AdvancedEncodingDetection"):
247 # Try the universal character encoding detector 320 # Try the universal character encoding detector
248 try: 321 try:
249 import chardet 322 import chardet
323
250 guess = chardet.detect(text) 324 guess = chardet.detect(text)
251 if ( 325 if guess and guess["confidence"] > 0.95 and guess["encoding"] is not None:
252 guess and 326 codec = guess["encoding"].lower()
253 guess['confidence'] > 0.95 and 327 return str(text, codec), "{0}-guessed".format(codec)
254 guess['encoding'] is not None
255 ):
256 codec = guess['encoding'].lower()
257 return str(text, codec), '{0}-guessed'.format(codec)
258 except (UnicodeError, LookupError): 328 except (UnicodeError, LookupError):
259 pass 329 pass
260 except ImportError: 330 except ImportError:
261 pass 331 pass
262 332
263 # Try default encoding 333 # Try default encoding
264 with contextlib.suppress(UnicodeError, LookupError): 334 with contextlib.suppress(UnicodeError, LookupError):
265 codec = Preferences.getEditor("DefaultEncoding") 335 codec = Preferences.getEditor("DefaultEncoding")
266 return str(text, codec), '{0}-default'.format(codec) 336 return str(text, codec), "{0}-default".format(codec)
267 337
268 if ( 338 if (
269 Preferences.getEditor("AdvancedEncodingDetection") and 339 Preferences.getEditor("AdvancedEncodingDetection")
270 guess and 340 and guess
271 guess['encoding'] is not None 341 and guess["encoding"] is not None
272 ): 342 ):
273 # Use the guessed one even if confidence level is low 343 # Use the guessed one even if confidence level is low
274 with contextlib.suppress(UnicodeError, LookupError): 344 with contextlib.suppress(UnicodeError, LookupError):
275 codec = guess['encoding'].lower() 345 codec = guess["encoding"].lower()
276 return str(text, codec), '{0}-guessed'.format(codec) 346 return str(text, codec), "{0}-guessed".format(codec)
277 347
278 # Assume UTF-8 loosing information 348 # Assume UTF-8 loosing information
279 return str(text, "utf-8", "ignore"), 'utf-8-ignore' 349 return str(text, "utf-8", "ignore"), "utf-8-ignore"
280 350
281 351
282 def readEncodedFileWithEncoding(filename, encoding): 352 def readEncodedFileWithEncoding(filename, encoding):
283 """ 353 """
284 Function to read a file and decode its contents into proper text. 354 Function to read a file and decode its contents into proper text.
285 355
286 @param filename name of the file to read (string) 356 @param filename name of the file to read (string)
287 @param encoding encoding to be used to read the file (string) 357 @param encoding encoding to be used to read the file (string)
288 @return tuple of decoded text and encoding (string, string) 358 @return tuple of decoded text and encoding (string, string)
289 """ 359 """
290 with open(filename, "rb") as f: 360 with open(filename, "rb") as f:
291 text = f.read() 361 text = f.read()
292 if encoding: 362 if encoding:
293 with contextlib.suppress(UnicodeError, LookupError): 363 with contextlib.suppress(UnicodeError, LookupError):
294 return str(text, encoding), '{0}-selected'.format(encoding) 364 return str(text, encoding), "{0}-selected".format(encoding)
295 365
296 # Try default encoding 366 # Try default encoding
297 with contextlib.suppress(UnicodeError, LookupError): 367 with contextlib.suppress(UnicodeError, LookupError):
298 codec = Preferences.getEditor("DefaultEncoding") 368 codec = Preferences.getEditor("DefaultEncoding")
299 return str(text, codec), '{0}-default'.format(codec) 369 return str(text, codec), "{0}-default".format(codec)
300 370
301 # Assume UTF-8 loosing information 371 # Assume UTF-8 loosing information
302 return str(text, "utf-8", "ignore"), 'utf-8-ignore' 372 return str(text, "utf-8", "ignore"), "utf-8-ignore"
303 else: 373 else:
304 return decode(text) 374 return decode(text)
305 375
306 376
307 def writeEncodedFile(filename, text, origEncoding, forcedEncoding=""): 377 def writeEncodedFile(filename, text, origEncoding, forcedEncoding=""):
308 """ 378 """
309 Function to write a file with properly encoded text. 379 Function to write a file with properly encoded text.
310 380
311 @param filename name of the file to read 381 @param filename name of the file to read
312 @type str 382 @type str
313 @param text text to be written 383 @param text text to be written
314 @type str 384 @type str
315 @param origEncoding type of the original encoding 385 @param origEncoding type of the original encoding
319 @type str 389 @type str
320 @return encoding used for writing the file 390 @return encoding used for writing the file
321 @rtype str 391 @rtype str
322 """ 392 """
323 etext, encoding = encode(text, origEncoding, forcedEncoding=forcedEncoding) 393 etext, encoding = encode(text, origEncoding, forcedEncoding=forcedEncoding)
324 394
325 with open(filename, "wb") as f: 395 with open(filename, "wb") as f:
326 f.write(etext) 396 f.write(etext)
327 397
328 return encoding 398 return encoding
329 399
330 400
331 def encode(text, origEncoding, forcedEncoding=""): 401 def encode(text, origEncoding, forcedEncoding=""):
332 """ 402 """
333 Function to encode text into a byte text. 403 Function to encode text into a byte text.
334 404
335 @param text text to be encoded 405 @param text text to be encoded
336 @type str 406 @type str
337 @param origEncoding type of the original encoding 407 @param origEncoding type of the original encoding
338 @type str 408 @type str
339 @param forcedEncoding encoding to be used for writing, if no coding line 409 @param forcedEncoding encoding to be used for writing, if no coding line
342 @return tuple of encoded text and encoding used 412 @return tuple of encoded text and encoding used
343 @rtype tuple of (bytes, str) 413 @rtype tuple of (bytes, str)
344 @exception CodingError raised to indicate an invalid encoding 414 @exception CodingError raised to indicate an invalid encoding
345 """ 415 """
346 encoding = None 416 encoding = None
347 if origEncoding == 'utf-8-bom': 417 if origEncoding == "utf-8-bom":
348 etext, encoding = BOM_UTF8 + text.encode("utf-8"), 'utf-8-bom' 418 etext, encoding = BOM_UTF8 + text.encode("utf-8"), "utf-8-bom"
349 else: 419 else:
350 # Try declared coding spec 420 # Try declared coding spec
351 coding = get_coding(text) 421 coding = get_coding(text)
352 if coding: 422 if coding:
353 try: 423 try:
356 # Error: Declared encoding is incorrect 426 # Error: Declared encoding is incorrect
357 raise CodingError(coding) 427 raise CodingError(coding)
358 else: 428 else:
359 if forcedEncoding: 429 if forcedEncoding:
360 with contextlib.suppress(UnicodeError, LookupError): 430 with contextlib.suppress(UnicodeError, LookupError):
361 etext, encoding = ( 431 etext, encoding = (text.encode(forcedEncoding), forcedEncoding)
362 text.encode(forcedEncoding), forcedEncoding)
363 # if forced encoding is incorrect, ignore it 432 # if forced encoding is incorrect, ignore it
364 433
365 if encoding is None: 434 if encoding is None:
366 # Try the original encoding 435 # Try the original encoding
367 if origEncoding and origEncoding.endswith( 436 if origEncoding and origEncoding.endswith(
368 ('-selected', '-default', '-guessed', '-ignore')): 437 ("-selected", "-default", "-guessed", "-ignore")
438 ):
369 coding = ( 439 coding = (
370 origEncoding 440 origEncoding.replace("-selected", "")
371 .replace("-selected", "")
372 .replace("-default", "") 441 .replace("-default", "")
373 .replace("-guessed", "") 442 .replace("-guessed", "")
374 .replace("-ignore", "") 443 .replace("-ignore", "")
375 ) 444 )
376 with contextlib.suppress(UnicodeError, LookupError): 445 with contextlib.suppress(UnicodeError, LookupError):
377 etext, encoding = text.encode(coding), coding 446 etext, encoding = text.encode(coding), coding
378 447
379 if encoding is None: 448 if encoding is None:
380 # Try configured default 449 # Try configured default
381 with contextlib.suppress(UnicodeError, LookupError): 450 with contextlib.suppress(UnicodeError, LookupError):
382 codec = Preferences.getEditor("DefaultEncoding") 451 codec = Preferences.getEditor("DefaultEncoding")
383 etext, encoding = text.encode(codec), codec 452 etext, encoding = text.encode(codec), codec
384 453
385 if encoding is None: 454 if encoding is None:
386 # Try saving as ASCII 455 # Try saving as ASCII
387 with contextlib.suppress(UnicodeError): 456 with contextlib.suppress(UnicodeError):
388 etext, encoding = text.encode('ascii'), 'ascii' 457 etext, encoding = text.encode("ascii"), "ascii"
389 458
390 if encoding is None: 459 if encoding is None:
391 # Save as UTF-8 without BOM 460 # Save as UTF-8 without BOM
392 etext, encoding = text.encode('utf-8'), 'utf-8' 461 etext, encoding = text.encode("utf-8"), "utf-8"
393 462
394 return etext, encoding 463 return etext, encoding
395 464
396 465
397 def decodeString(text): 466 def decodeString(text):
398 """ 467 """
399 Function to decode a string containing Unicode encoded characters. 468 Function to decode a string containing Unicode encoded characters.
400 469
401 @param text text containing encoded chars (string) 470 @param text text containing encoded chars (string)
402 @return decoded text (string) 471 @return decoded text (string)
403 """ 472 """
404 buf = b"" 473 buf = b""
405 index = 0 474 index = 0
406 while index < len(text): 475 while index < len(text):
407 if text[index] == "\\": 476 if text[index] == "\\":
408 qb = QByteArray.fromHex(text[index:index + 4].encode()) 477 qb = QByteArray.fromHex(text[index : index + 4].encode())
409 buf += bytes(qb) 478 buf += bytes(qb)
410 index += 4 479 index += 4
411 else: 480 else:
412 buf += codecs.encode(text[index], "utf-8") 481 buf += codecs.encode(text[index], "utf-8")
413 index += 1 482 index += 1
414 buf = buf.replace(b"\x00", b"") 483 buf = buf.replace(b"\x00", b"")
415 return decodeBytes(buf) 484 return decodeBytes(buf)
416 485
417 486
418 def decodeBytes(buffer): 487 def decodeBytes(buffer):
419 """ 488 """
420 Function to decode some byte text into a string. 489 Function to decode some byte text into a string.
421 490
422 @param buffer byte buffer to decode (bytes) 491 @param buffer byte buffer to decode (bytes)
423 @return decoded text (string) 492 @return decoded text (string)
424 """ 493 """
425 # try UTF with BOM 494 # try UTF with BOM
426 with contextlib.suppress(UnicodeError, LookupError): 495 with contextlib.suppress(UnicodeError, LookupError):
427 if buffer.startswith(BOM_UTF8): 496 if buffer.startswith(BOM_UTF8):
428 # UTF-8 with BOM 497 # UTF-8 with BOM
429 return str(buffer[len(BOM_UTF8):], encoding='utf-8') 498 return str(buffer[len(BOM_UTF8) :], encoding="utf-8")
430 elif buffer.startswith(BOM_UTF16): 499 elif buffer.startswith(BOM_UTF16):
431 # UTF-16 with BOM 500 # UTF-16 with BOM
432 return str(buffer[len(BOM_UTF16):], encoding='utf-16') 501 return str(buffer[len(BOM_UTF16) :], encoding="utf-16")
433 elif buffer.startswith(BOM_UTF32): 502 elif buffer.startswith(BOM_UTF32):
434 # UTF-32 with BOM 503 # UTF-32 with BOM
435 return str(buffer[len(BOM_UTF32):], encoding='utf-32') 504 return str(buffer[len(BOM_UTF32) :], encoding="utf-32")
436 505
437 # try UTF-8 506 # try UTF-8
438 with contextlib.suppress(UnicodeError): 507 with contextlib.suppress(UnicodeError):
439 return str(buffer, encoding="utf-8") 508 return str(buffer, encoding="utf-8")
440 509
441 # try codec detection 510 # try codec detection
442 try: 511 try:
443 import chardet 512 import chardet
513
444 guess = chardet.detect(buffer) 514 guess = chardet.detect(buffer)
445 if guess and guess['encoding'] is not None: 515 if guess and guess["encoding"] is not None:
446 codec = guess['encoding'].lower() 516 codec = guess["encoding"].lower()
447 return str(buffer, encoding=codec) 517 return str(buffer, encoding=codec)
448 except (UnicodeError, LookupError): 518 except (UnicodeError, LookupError):
449 pass 519 pass
450 except ImportError: 520 except ImportError:
451 pass 521 pass
452 522
453 return str(buffer, encoding="utf-8", errors="ignore") 523 return str(buffer, encoding="utf-8", errors="ignore")
454 524
455 525
456 def readStringFromStream(stream): 526 def readStringFromStream(stream):
457 """ 527 """
458 Module function to read a string from the given stream. 528 Module function to read a string from the given stream.
459 529
460 @param stream data stream opened for reading (QDataStream) 530 @param stream data stream opened for reading (QDataStream)
461 @return string read from the stream (string) 531 @return string read from the stream (string)
462 """ 532 """
463 data = stream.readString() 533 data = stream.readString()
464 if data is None: 534 if data is None:
465 data = b"" 535 data = b""
466 return data.decode('utf-8') 536 return data.decode("utf-8")
467 537
468 538
469 _escape = re.compile("[&<>\"'\u0080-\uffff]") 539 _escape = re.compile("[&<>\"'\u0080-\uffff]")
470 540
471 _escape_map = { 541 _escape_map = {
478 548
479 549
480 def escape_entities(m, escmap=_escape_map): 550 def escape_entities(m, escmap=_escape_map):
481 """ 551 """
482 Function to encode html entities. 552 Function to encode html entities.
483 553
484 @param m the match object 554 @param m the match object
485 @param escmap the map of entities to encode 555 @param escmap the map of entities to encode
486 @return the converted text (string) 556 @return the converted text (string)
487 """ 557 """
488 char = m.group() 558 char = m.group()
489 text = escmap.get(char) 559 text = escmap.get(char)
490 if text is None: 560 if text is None:
491 text = "&#{0:d};".format(ord(char)) 561 text = "&#{0:d};".format(ord(char))
492 return text 562 return text
493 563
494 564
495 def html_encode(text, pattern=_escape): 565 def html_encode(text, pattern=_escape):
496 """ 566 """
497 Function to correctly encode a text for html. 567 Function to correctly encode a text for html.
498 568
499 @param text text to be encoded (string) 569 @param text text to be encoded (string)
500 @param pattern search pattern for text to be encoded (string) 570 @param pattern search pattern for text to be encoded (string)
501 @return the encoded text (string) 571 @return the encoded text (string)
502 """ 572 """
503 if not text: 573 if not text:
504 return "" 574 return ""
505 text = pattern.sub(escape_entities, text) 575 text = pattern.sub(escape_entities, text)
506 return text 576 return text
507 577
508 _uescape = re.compile('[\u0080-\uffff]') 578
579 _uescape = re.compile("[\u0080-\uffff]")
509 580
510 581
511 def escape_uentities(m): 582 def escape_uentities(m):
512 """ 583 """
513 Function to encode html entities. 584 Function to encode html entities.
514 585
515 @param m the match object 586 @param m the match object
516 @return the converted text (string) 587 @return the converted text (string)
517 """ 588 """
518 char = m.group() 589 char = m.group()
519 text = "&#{0:d};".format(ord(char)) 590 text = "&#{0:d};".format(ord(char))
520 return text 591 return text
521 592
522 593
523 def html_uencode(text, pattern=_uescape): 594 def html_uencode(text, pattern=_uescape):
524 """ 595 """
525 Function to correctly encode a unicode text for html. 596 Function to correctly encode a unicode text for html.
526 597
527 @param text text to be encoded (string) 598 @param text text to be encoded (string)
528 @param pattern search pattern for text to be encoded (string) 599 @param pattern search pattern for text to be encoded (string)
529 @return the encoded text (string) 600 @return the encoded text (string)
530 """ 601 """
531 if not text: 602 if not text:
532 return "" 603 return ""
533 text = pattern.sub(escape_uentities, text) 604 text = pattern.sub(escape_uentities, text)
534 return text 605 return text
535 606
536 _uunescape = re.compile(r'&#\d+;') 607
608 _uunescape = re.compile(r"&#\d+;")
537 609
538 610
539 def unescape_uentities(m): 611 def unescape_uentities(m):
540 """ 612 """
541 Function to decode html entities. 613 Function to decode html entities.
542 614
543 @param m the match object 615 @param m the match object
544 @return the converted text (string) 616 @return the converted text (string)
545 """ 617 """
546 char = m.group() 618 char = m.group()
547 ordinal = int(char[2:-1]) 619 ordinal = int(char[2:-1])
549 621
550 622
551 def html_udecode(text, pattern=_uunescape): 623 def html_udecode(text, pattern=_uunescape):
552 """ 624 """
553 Function to correctly decode a html text to a unicode text. 625 Function to correctly decode a html text to a unicode text.
554 626
555 @param text text to be decoded (string) 627 @param text text to be decoded (string)
556 @param pattern search pattern for text to be decoded (string) 628 @param pattern search pattern for text to be decoded (string)
557 @return the decoded text (string) 629 @return the decoded text (string)
558 """ 630 """
559 if not text: 631 if not text:
563 635
564 636
565 def convertLineEnds(text, eol): 637 def convertLineEnds(text, eol):
566 """ 638 """
567 Function to convert the end of line characters. 639 Function to convert the end of line characters.
568 640
569 @param text text to be converted (string) 641 @param text text to be converted (string)
570 @param eol new eol setting (string) 642 @param eol new eol setting (string)
571 @return text with converted eols (string) 643 @return text with converted eols (string)
572 """ 644 """
573 if eol == '\r\n': 645 if eol == "\r\n":
574 regexp = re.compile(r"""(\r(?!\n)|(?<!\r)\n)""") 646 regexp = re.compile(r"""(\r(?!\n)|(?<!\r)\n)""")
575 return regexp.sub(lambda m, eol='\r\n': eol, text) 647 return regexp.sub(lambda m, eol="\r\n": eol, text)
576 elif eol == '\n': 648 elif eol == "\n":
577 regexp = re.compile(r"""(\r\n|\r)""") 649 regexp = re.compile(r"""(\r\n|\r)""")
578 return regexp.sub(lambda m, eol='\n': eol, text) 650 return regexp.sub(lambda m, eol="\n": eol, text)
579 elif eol == '\r': 651 elif eol == "\r":
580 regexp = re.compile(r"""(\r\n|\n)""") 652 regexp = re.compile(r"""(\r\n|\n)""")
581 return regexp.sub(lambda m, eol='\r': eol, text) 653 return regexp.sub(lambda m, eol="\r": eol, text)
582 else: 654 else:
583 return text 655 return text
584 656
585 657
586 def linesep(): 658 def linesep():
587 """ 659 """
588 Function to return the line separator used by the editor. 660 Function to return the line separator used by the editor.
589 661
590 @return line separator used by the editor (string) 662 @return line separator used by the editor (string)
591 """ 663 """
592 eolMode = Preferences.getEditor("EOLMode") 664 eolMode = Preferences.getEditor("EOLMode")
593 if eolMode == QsciScintilla.EolMode.EolUnix: 665 if eolMode == QsciScintilla.EolMode.EolUnix:
594 return "\n" 666 return "\n"
599 671
600 672
601 def extractFlags(text): 673 def extractFlags(text):
602 """ 674 """
603 Function to extract eric specific flags out of the given text. 675 Function to extract eric specific flags out of the given text.
604 676
605 Flags are contained in comments and are introduced by 'eflag:'. 677 Flags are contained in comments and are introduced by 'eflag:'.
606 The rest of the line is interpreted as 'key = value'. value is 678 The rest of the line is interpreted as 'key = value'. value is
607 analyzed for being an integer or float value. If that fails, it 679 analyzed for being an integer or float value. If that fails, it
608 is assumed to be a string. If a key does not contain a '=' 680 is assumed to be a string. If a key does not contain a '='
609 character, it is assumed to be a boolean flag. Flags are expected 681 character, it is assumed to be a boolean flag. Flags are expected
610 at the very end of a file. The search is ended, if a line without 682 at the very end of a file. The search is ended, if a line without
611 the 'eflag:' marker is found. 683 the 'eflag:' marker is found.
612 684
613 @param text text to be scanned (string) 685 @param text text to be scanned (string)
614 @return dictionary of string, boolean, complex, float and int 686 @return dictionary of string, boolean, complex, float and int
615 """ 687 """
616 flags = {} 688 flags = {}
617 lines = text.rstrip().splitlines() if isinstance(text, str) else text 689 lines = text.rstrip().splitlines() if isinstance(text, str) else text
619 try: 691 try:
620 index = line.index("eflag:") 692 index = line.index("eflag:")
621 except ValueError: 693 except ValueError:
622 # no flag found, don't look any further 694 # no flag found, don't look any further
623 break 695 break
624 696
625 flag = line[index + 6:].strip() 697 flag = line[index + 6 :].strip()
626 if "=" in flag: 698 if "=" in flag:
627 key, value = flag.split("=", 1) 699 key, value = flag.split("=", 1)
628 key = key.strip() 700 key = key.strip()
629 value = value.strip() 701 value = value.strip()
630 702
631 if value.lower() in ["true", "false", "yes", "no", "ok"]: 703 if value.lower() in ["true", "false", "yes", "no", "ok"]:
632 # it is a flag 704 # it is a flag
633 flags[key] = value.lower() in ["true", "yes", "ok"] 705 flags[key] = value.lower() in ["true", "yes", "ok"]
634 continue 706 continue
635 707
636 try: 708 try:
637 # interpret as int first 709 # interpret as int first
638 value = int(value) 710 value = int(value)
639 except ValueError: 711 except ValueError:
640 with contextlib.suppress(ValueError): 712 with contextlib.suppress(ValueError):
641 # interpret as float next 713 # interpret as float next
642 value = float(value) 714 value = float(value)
643 715
644 flags[key] = value 716 flags[key] = value
645 else: 717 else:
646 # treat it as a boolean 718 # treat it as a boolean
647 if flag[0] == "-": 719 if flag[0] == "-":
648 # false flags start with '-' 720 # false flags start with '-'
649 flags[flag[1:]] = False 721 flags[flag[1:]] = False
650 else: 722 else:
651 flags[flag] = True 723 flags[flag] = True
652 724
653 return flags 725 return flags
654 726
655 727
656 def extractFlagsFromFile(filename): 728 def extractFlagsFromFile(filename):
657 """ 729 """
658 Function to extract eric specific flags out of the given file. 730 Function to extract eric specific flags out of the given file.
659 731
660 @param filename name of the file to be scanned (string) 732 @param filename name of the file to be scanned (string)
661 @return dictionary of string, boolean, complex, float and int 733 @return dictionary of string, boolean, complex, float and int
662 """ 734 """
663 try: 735 try:
664 source, encoding = readEncodedFile(filename) 736 source, encoding = readEncodedFile(filename)
665 except (UnicodeError, OSError): 737 except (UnicodeError, OSError):
666 return {} 738 return {}
667 739
668 return extractFlags(source) 740 return extractFlags(source)
669 741
670 742
671 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False): 743 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False):
672 """ 744 """
673 Function to extract flags starting and ending with '__' from a line 745 Function to extract flags starting and ending with '__' from a line
674 comment. 746 comment.
675 747
676 @param line line to extract flags from (string) 748 @param line line to extract flags from (string)
677 @param startComment string identifying the start of the comment (string) 749 @param startComment string identifying the start of the comment (string)
678 @param endComment string identifying the end of a comment (string) 750 @param endComment string identifying the end of a comment (string)
679 @param flagsLine flag indicating to check for a flags only line (bool) 751 @param flagsLine flag indicating to check for a flags only line (bool)
680 @return list containing the extracted flags (list of strings) 752 @return list containing the extracted flags (list of strings)
681 """ 753 """
682 flags = [] 754 flags = []
683 755
684 if not flagsLine or ( 756 if not flagsLine or (flagsLine and line.strip().startswith(startComment)):
685 flagsLine and line.strip().startswith(startComment)):
686 pos = line.rfind(startComment) 757 pos = line.rfind(startComment)
687 if pos >= 0: 758 if pos >= 0:
688 comment = line[pos + len(startComment):].strip() 759 comment = line[pos + len(startComment) :].strip()
689 if endComment: 760 if endComment:
690 endPos = line.rfind(endComment) 761 endPos = line.rfind(endComment)
691 if endPos >= 0: 762 if endPos >= 0:
692 comment = comment[:endPos] 763 comment = comment[:endPos]
693 flags = [f.strip() for f in comment.split() 764 flags = [
694 if (f.startswith("__") and f.endswith("__"))] 765 f.strip()
766 for f in comment.split()
767 if (f.startswith("__") and f.endswith("__"))
768 ]
695 return flags 769 return flags
696 770
697 771
698 def filterAnsiSequences(txt): 772 def filterAnsiSequences(txt):
699 """ 773 """
700 Function to filter out ANSI escape sequences (color only). 774 Function to filter out ANSI escape sequences (color only).
701 775
702 @param txt text to be filtered 776 @param txt text to be filtered
703 @type str 777 @type str
704 @return text without ANSI escape sequences 778 @return text without ANSI escape sequences
705 @rtype str 779 @rtype str
706 """ 780 """
707 ntxt = txt[:] 781 ntxt = txt[:]
708 while True: 782 while True:
709 start = ntxt.find("\33[") # find escape character 783 start = ntxt.find("\33[") # find escape character
710 if start == -1: 784 if start == -1:
711 break 785 break
712 end = ntxt.find("m", start) 786 end = ntxt.find("m", start)
713 if end == -1: 787 if end == -1:
714 break 788 break
715 ntxt = ntxt[:start] + ntxt[end + 1:] 789 ntxt = ntxt[:start] + ntxt[end + 1 :]
716 790
717 return ntxt 791 return ntxt
718 792
719 793
720 def toNativeSeparators(path): 794 def toNativeSeparators(path):
721 """ 795 """
722 Function returning a path, that is using native separator characters. 796 Function returning a path, that is using native separator characters.
723 797
724 @param path path to be converted 798 @param path path to be converted
725 @type str 799 @type str
726 @return path with converted separator characters 800 @return path with converted separator characters
727 @rtype str 801 @rtype str
728 """ 802 """
730 804
731 805
732 def fromNativeSeparators(path): 806 def fromNativeSeparators(path):
733 """ 807 """
734 Function returning a path, that is using "/" separator characters. 808 Function returning a path, that is using "/" separator characters.
735 809
736 @param path path to be converted 810 @param path path to be converted
737 @type str 811 @type str
738 @return path with converted separator characters 812 @return path with converted separator characters
739 @rtype str 813 @rtype str
740 """ 814 """
743 817
744 def normcasepath(path): 818 def normcasepath(path):
745 """ 819 """
746 Function returning a path, that is normalized with respect to its case 820 Function returning a path, that is normalized with respect to its case
747 and references. 821 and references.
748 822
749 @param path file path (string) 823 @param path file path (string)
750 @return case normalized path (string) 824 @return case normalized path (string)
751 """ 825 """
752 return os.path.normcase(os.path.normpath(path)) 826 return os.path.normcase(os.path.normpath(path))
753 827
754 828
755 def normcaseabspath(path): 829 def normcaseabspath(path):
756 """ 830 """
757 Function returning an absolute path, that is normalized with respect to 831 Function returning an absolute path, that is normalized with respect to
758 its case and references. 832 its case and references.
759 833
760 @param path file path (string) 834 @param path file path (string)
761 @return absolute, normalized path (string) 835 @return absolute, normalized path (string)
762 """ 836 """
763 return os.path.normcase(os.path.abspath(path)) 837 return os.path.normcase(os.path.abspath(path))
764 838
765 839
766 def normjoinpath(a, *p): 840 def normjoinpath(a, *p):
767 """ 841 """
768 Function returning a normalized path of the joined parts passed into it. 842 Function returning a normalized path of the joined parts passed into it.
769 843
770 @param a first path to be joined (string) 844 @param a first path to be joined (string)
771 @param p variable number of path parts to be joined (string) 845 @param p variable number of path parts to be joined (string)
772 @return normalized path (string) 846 @return normalized path (string)
773 """ 847 """
774 return os.path.normpath(os.path.join(a, *p)) 848 return os.path.normpath(os.path.join(a, *p))
776 850
777 def normabsjoinpath(a, *p): 851 def normabsjoinpath(a, *p):
778 """ 852 """
779 Function returning a normalized, absolute path of the joined parts passed 853 Function returning a normalized, absolute path of the joined parts passed
780 into it. 854 into it.
781 855
782 @param a first path to be joined (string) 856 @param a first path to be joined (string)
783 @param p variable number of path parts to be joind (string) 857 @param p variable number of path parts to be joind (string)
784 @return absolute, normalized path (string) 858 @return absolute, normalized path (string)
785 """ 859 """
786 return os.path.abspath(os.path.join(a, *p)) 860 return os.path.abspath(os.path.join(a, *p))
787 861
788 862
789 def isinpath(file): 863 def isinpath(file):
790 """ 864 """
791 Function to check for an executable file. 865 Function to check for an executable file.
792 866
793 @param file filename of the executable to check (string) 867 @param file filename of the executable to check (string)
794 @return flag to indicate, if the executable file is accessible 868 @return flag to indicate, if the executable file is accessible
795 via the searchpath defined by the PATH environment variable. 869 via the searchpath defined by the PATH environment variable.
796 """ 870 """
797 if os.path.isabs(file): 871 if os.path.isabs(file):
798 return os.access(file, os.X_OK) 872 return os.access(file, os.X_OK)
799 873
800 if os.path.exists(os.path.join(os.curdir, file)): 874 if os.path.exists(os.path.join(os.curdir, file)):
801 return os.access(os.path.join(os.curdir, file), os.X_OK) 875 return os.access(os.path.join(os.curdir, file), os.X_OK)
802 876
803 path = getEnvironmentEntry('PATH') 877 path = getEnvironmentEntry("PATH")
804 878
805 # environment variable not defined 879 # environment variable not defined
806 if path is None: 880 if path is None:
807 return False 881 return False
808 882
809 dirs = path.split(os.pathsep) 883 dirs = path.split(os.pathsep)
810 return any(os.access(os.path.join(directory, file), os.X_OK) 884 return any(os.access(os.path.join(directory, file), os.X_OK) for directory in dirs)
811 for directory in dirs)
812 885
813 886
814 def startswithPath(path, start): 887 def startswithPath(path, start):
815 """ 888 """
816 Function to check, if a path starts with a given start path. 889 Function to check, if a path starts with a given start path.
817 890
818 @param path path to be checked 891 @param path path to be checked
819 @type str 892 @type str
820 @param start start path 893 @param start start path
821 @type str 894 @type str
822 @return flag indicating that the path starts with the given start 895 @return flag indicating that the path starts with the given start
823 path 896 path
824 @rtype bool 897 @rtype bool
825 """ 898 """
826 return ( 899 return bool(start) and (
827 bool(start) and 900 path == start or normcasepath(path).startswith(normcasepath(start + "/"))
828 (
829 path == start or
830 normcasepath(path).startswith(normcasepath(start + "/"))
831 )
832 ) 901 )
833 902
834 903
835 def relativeUniversalPath(path, start): 904 def relativeUniversalPath(path, start):
836 """ 905 """
837 Function to convert a file path to a path relative to a start path 906 Function to convert a file path to a path relative to a start path
838 with universal separators. 907 with universal separators.
839 908
840 @param path file or directory name to convert (string) 909 @param path file or directory name to convert (string)
841 @param start start path (string) 910 @param start start path (string)
842 @return relative path or unchanged path, if path does not start with 911 @return relative path or unchanged path, if path does not start with
843 the start path with universal separators (string) 912 the start path with universal separators (string)
844 """ 913 """
847 916
848 def absolutePath(path, start): 917 def absolutePath(path, start):
849 """ 918 """
850 Public method to convert a path relative to a start path to an 919 Public method to convert a path relative to a start path to an
851 absolute path. 920 absolute path.
852 921
853 @param path file or directory name to convert (string) 922 @param path file or directory name to convert (string)
854 @param start start path (string) 923 @param start start path (string)
855 @return absolute path (string) 924 @return absolute path (string)
856 """ 925 """
857 if not os.path.isabs(path): 926 if not os.path.isabs(path):
861 930
862 def absoluteUniversalPath(path, start): 931 def absoluteUniversalPath(path, start):
863 """ 932 """
864 Public method to convert a path relative to a start path with 933 Public method to convert a path relative to a start path with
865 universal separators to an absolute path. 934 universal separators to an absolute path.
866 935
867 @param path file or directory name to convert (string) 936 @param path file or directory name to convert (string)
868 @param start start path (string) 937 @param start start path (string)
869 @return absolute path with native separators (string) 938 @return absolute path with native separators (string)
870 """ 939 """
871 if not os.path.isabs(path): 940 if not os.path.isabs(path):
874 943
875 944
876 def getExecutablePath(file): 945 def getExecutablePath(file):
877 """ 946 """
878 Function to build the full path of an executable file from the environment. 947 Function to build the full path of an executable file from the environment.
879 948
880 @param file filename of the executable to check (string) 949 @param file filename of the executable to check (string)
881 @return full executable name, if the executable file is accessible 950 @return full executable name, if the executable file is accessible
882 via the searchpath defined by the PATH environment variable, or an 951 via the searchpath defined by the PATH environment variable, or an
883 empty string otherwise. 952 empty string otherwise.
884 """ 953 """
885 if os.path.isabs(file): 954 if os.path.isabs(file):
886 if os.access(file, os.X_OK): 955 if os.access(file, os.X_OK):
887 return file 956 return file
888 else: 957 else:
889 return "" 958 return ""
890 959
891 cur_path = os.path.join(os.curdir, file) 960 cur_path = os.path.join(os.curdir, file)
892 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK): 961 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK):
893 return cur_path 962 return cur_path
894 963
895 path = os.getenv('PATH') 964 path = os.getenv("PATH")
896 965
897 # environment variable not defined 966 # environment variable not defined
898 if path is None: 967 if path is None:
899 return "" 968 return ""
900 969
901 dirs = path.split(os.pathsep) 970 dirs = path.split(os.pathsep)
902 for directory in dirs: 971 for directory in dirs:
903 exe = os.path.join(directory, file) 972 exe = os.path.join(directory, file)
904 if os.access(exe, os.X_OK): 973 if os.access(exe, os.X_OK):
905 return exe 974 return exe
906 975
907 return "" 976 return ""
908 977
909 978
910 def getExecutablePaths(file): 979 def getExecutablePaths(file):
911 """ 980 """
912 Function to build all full path of an executable file from the environment. 981 Function to build all full path of an executable file from the environment.
913 982
914 @param file filename of the executable (string) 983 @param file filename of the executable (string)
915 @return list of full executable names (list of strings), if the executable 984 @return list of full executable names (list of strings), if the executable
916 file is accessible via the searchpath defined by the PATH environment 985 file is accessible via the searchpath defined by the PATH environment
917 variable, or an empty list otherwise. 986 variable, or an empty list otherwise.
918 """ 987 """
919 paths = [] 988 paths = []
920 989
921 if os.path.isabs(file): 990 if os.path.isabs(file):
922 if os.access(file, os.X_OK): 991 if os.access(file, os.X_OK):
923 return [file] 992 return [file]
924 else: 993 else:
925 return [] 994 return []
926 995
927 cur_path = os.path.join(os.curdir, file) 996 cur_path = os.path.join(os.curdir, file)
928 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK): 997 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK):
929 paths.append(cur_path) 998 paths.append(cur_path)
930 999
931 path = os.getenv('PATH') 1000 path = os.getenv("PATH")
932 1001
933 # environment variable not defined 1002 # environment variable not defined
934 if path is not None: 1003 if path is not None:
935 dirs = path.split(os.pathsep) 1004 dirs = path.split(os.pathsep)
936 for directory in dirs: 1005 for directory in dirs:
937 exe = os.path.join(directory, file) 1006 exe = os.path.join(directory, file)
938 if os.access(exe, os.X_OK) and exe not in paths: 1007 if os.access(exe, os.X_OK) and exe not in paths:
939 paths.append(exe) 1008 paths.append(exe)
940 1009
941 return paths 1010 return paths
942 1011
943 1012
944 def getWindowsExecutablePath(file): 1013 def getWindowsExecutablePath(file):
945 """ 1014 """
946 Function to build the full path of an executable file from the environment 1015 Function to build the full path of an executable file from the environment
947 on Windows platforms. 1016 on Windows platforms.
948 1017
949 First an executable with the extension .exe is searched for, thereafter 1018 First an executable with the extension .exe is searched for, thereafter
950 such with the extensions .cmd or .bat and finally the given file name as 1019 such with the extensions .cmd or .bat and finally the given file name as
951 is. The first match is returned. 1020 is. The first match is returned.
952 1021
953 @param file filename of the executable to check (string) 1022 @param file filename of the executable to check (string)
954 @return full executable name, if the executable file is accessible 1023 @return full executable name, if the executable file is accessible
955 via the searchpath defined by the PATH environment variable, or an 1024 via the searchpath defined by the PATH environment variable, or an
956 empty string otherwise. 1025 empty string otherwise.
957 """ 1026 """
958 if os.path.isabs(file): 1027 if os.path.isabs(file):
959 if os.access(file, os.X_OK): 1028 if os.access(file, os.X_OK):
960 return file 1029 return file
961 else: 1030 else:
962 return "" 1031 return ""
963 1032
964 filenames = [file + ".exe", file + ".cmd", file + ".bat", file] 1033 filenames = [file + ".exe", file + ".cmd", file + ".bat", file]
965 1034
966 for filename in filenames: 1035 for filename in filenames:
967 cur_path = os.path.join(os.curdir, filename) 1036 cur_path = os.path.join(os.curdir, filename)
968 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK): 1037 if os.path.exists(cur_path) and os.access(cur_path, os.X_OK):
969 return os.path.abspath(cur_path) 1038 return os.path.abspath(cur_path)
970 1039
971 path = os.getenv('PATH') 1040 path = os.getenv("PATH")
972 1041
973 # environment variable not defined 1042 # environment variable not defined
974 if path is None: 1043 if path is None:
975 return "" 1044 return ""
976 1045
977 dirs = path.split(os.pathsep) 1046 dirs = path.split(os.pathsep)
978 for directory in dirs: 1047 for directory in dirs:
979 for filename in filenames: 1048 for filename in filenames:
980 exe = os.path.join(directory, filename) 1049 exe = os.path.join(directory, filename)
981 if os.access(exe, os.X_OK): 1050 if os.access(exe, os.X_OK):
982 return exe 1051 return exe
983 1052
984 return "" 1053 return ""
985 1054
986 1055
987 def isExecutable(exe): 1056 def isExecutable(exe):
988 """ 1057 """
989 Function to check, if a file is executable. 1058 Function to check, if a file is executable.
990 1059
991 @param exe filename of the executable to check (string) 1060 @param exe filename of the executable to check (string)
992 @return flag indicating executable status (boolean) 1061 @return flag indicating executable status (boolean)
993 """ 1062 """
994 return os.access(exe, os.X_OK) 1063 return os.access(exe, os.X_OK)
995 1064
996 1065
997 def isDrive(path): 1066 def isDrive(path):
998 """ 1067 """
999 Function to check, if a path is a Windows drive. 1068 Function to check, if a path is a Windows drive.
1000 1069
1001 @param path path name to be checked 1070 @param path path name to be checked
1002 @type str 1071 @type str
1003 @return flag indicating a Windows drive 1072 @return flag indicating a Windows drive
1004 @rtype bool 1073 @rtype bool
1005 """ 1074 """
1006 isDrive = False 1075 isDrive = False
1007 drive, directory = os.path.splitdrive(path) 1076 drive, directory = os.path.splitdrive(path)
1008 if ( 1077 if (
1009 drive and 1078 drive
1010 len(drive) == 2 and 1079 and len(drive) == 2
1011 drive.endswith(":") and 1080 and drive.endswith(":")
1012 directory in ["", "\\", "/"] 1081 and directory in ["", "\\", "/"]
1013 ): 1082 ):
1014 isDrive = True 1083 isDrive = True
1015 1084
1016 return isDrive 1085 return isDrive
1017 1086
1018 1087
1019 def samepath(f1, f2): 1088 def samepath(f1, f2):
1020 """ 1089 """
1021 Function to compare two paths. 1090 Function to compare two paths.
1022 1091
1023 @param f1 first path for the compare (string) 1092 @param f1 first path for the compare (string)
1024 @param f2 second path for the compare (string) 1093 @param f2 second path for the compare (string)
1025 @return flag indicating whether the two paths represent the 1094 @return flag indicating whether the two paths represent the
1026 same path on disk. 1095 same path on disk.
1027 """ 1096 """
1028 if f1 is None or f2 is None: 1097 if f1 is None or f2 is None:
1029 return False 1098 return False
1030 1099
1031 if ( 1100 if normcaseabspath(os.path.realpath(f1)) == normcaseabspath(os.path.realpath(f2)):
1032 normcaseabspath(os.path.realpath(f1)) ==
1033 normcaseabspath(os.path.realpath(f2))
1034 ):
1035 return True 1101 return True
1036 1102
1037 return False 1103 return False
1038 1104
1039 1105
1040 def samefilepath(f1, f2): 1106 def samefilepath(f1, f2):
1041 """ 1107 """
1042 Function to compare two paths. Strips the filename. 1108 Function to compare two paths. Strips the filename.
1043 1109
1044 @param f1 first filepath for the compare (string) 1110 @param f1 first filepath for the compare (string)
1045 @param f2 second filepath for the compare (string) 1111 @param f2 second filepath for the compare (string)
1046 @return flag indicating whether the two paths represent the 1112 @return flag indicating whether the two paths represent the
1047 same path on disk. 1113 same path on disk.
1048 """ 1114 """
1049 if f1 is None or f2 is None: 1115 if f1 is None or f2 is None:
1050 return False 1116 return False
1051 1117
1052 if (normcaseabspath(os.path.dirname(os.path.realpath(f1))) == 1118 if normcaseabspath(os.path.dirname(os.path.realpath(f1))) == normcaseabspath(
1053 normcaseabspath(os.path.dirname(os.path.realpath(f2)))): 1119 os.path.dirname(os.path.realpath(f2))
1120 ):
1054 return True 1121 return True
1055 1122
1056 return False 1123 return False
1124
1057 1125
1058 try: 1126 try:
1059 EXTSEP = os.extsep 1127 EXTSEP = os.extsep
1060 except AttributeError: 1128 except AttributeError:
1061 EXTSEP = "." 1129 EXTSEP = "."
1062 1130
1063 1131
1064 def splitPath(name): 1132 def splitPath(name):
1065 """ 1133 """
1066 Function to split a pathname into a directory part and a file part. 1134 Function to split a pathname into a directory part and a file part.
1067 1135
1068 @param name path name (string) 1136 @param name path name (string)
1069 @return a tuple of 2 strings (dirname, filename). 1137 @return a tuple of 2 strings (dirname, filename).
1070 """ 1138 """
1071 if os.path.isdir(name): 1139 if os.path.isdir(name):
1072 dn = os.path.abspath(name) 1140 dn = os.path.abspath(name)
1077 1145
1078 1146
1079 def joinext(prefix, ext): 1147 def joinext(prefix, ext):
1080 """ 1148 """
1081 Function to join a file extension to a path. 1149 Function to join a file extension to a path.
1082 1150
1083 The leading "." of ext is replaced by a platform specific extension 1151 The leading "." of ext is replaced by a platform specific extension
1084 separator if necessary. 1152 separator if necessary.
1085 1153
1086 @param prefix the basepart of the filename (string) 1154 @param prefix the basepart of the filename (string)
1087 @param ext the extension part (string) 1155 @param ext the extension part (string)
1088 @return the complete filename (string) 1156 @return the complete filename (string)
1089 """ 1157 """
1090 if ext[0] != ".": 1158 if ext[0] != ".":
1094 1162
1095 1163
1096 def compactPath(path, width, measure=len): 1164 def compactPath(path, width, measure=len):
1097 """ 1165 """
1098 Function to return a compacted path fitting inside the given width. 1166 Function to return a compacted path fitting inside the given width.
1099 1167
1100 @param path path to be compacted (string) 1168 @param path path to be compacted (string)
1101 @param width width for the compacted path (integer) 1169 @param width width for the compacted path (integer)
1102 @param measure reference to a function used to measure the length of the 1170 @param measure reference to a function used to measure the length of the
1103 string 1171 string
1104 @return compacted path (string) 1172 @return compacted path (string)
1105 """ 1173 """
1106 if measure(path) <= width: 1174 if measure(path) <= width:
1107 return path 1175 return path
1108 1176
1109 ellipsis = '...' 1177 ellipsis = "..."
1110 1178
1111 head, tail = os.path.split(path) 1179 head, tail = os.path.split(path)
1112 mid = len(head) // 2 1180 mid = len(head) // 2
1113 head1 = head[:mid] 1181 head1 = head[:mid]
1114 head2 = head[mid:] 1182 head2 = head[mid:]
1115 while head1: 1183 while head1:
1126 path = "{0}{1}".format(ellipsis, tail) 1194 path = "{0}{1}".format(ellipsis, tail)
1127 if measure(path) <= width: 1195 if measure(path) <= width:
1128 return path 1196 return path
1129 tail = tail[1:] 1197 tail = tail[1:]
1130 return "" 1198 return ""
1131 1199
1132 1200
1133 def direntries(path, filesonly=False, pattern=None, followsymlinks=True, 1201 def direntries(
1134 checkStop=None): 1202 path, filesonly=False, pattern=None, followsymlinks=True, checkStop=None
1203 ):
1135 """ 1204 """
1136 Function returning a list of all files and directories. 1205 Function returning a list of all files and directories.
1137 1206
1138 @param path root of the tree to check 1207 @param path root of the tree to check
1139 @type str 1208 @type str
1140 @param filesonly flag indicating that only files are wanted 1209 @param filesonly flag indicating that only files are wanted
1141 @type bool 1210 @type bool
1142 @param pattern a filename pattern or list of filename patterns to check 1211 @param pattern a filename pattern or list of filename patterns to check
1156 try: 1225 try:
1157 entries = os.listdir(path) 1226 entries = os.listdir(path)
1158 for entry in entries: 1227 for entry in entries:
1159 if checkStop and checkStop(): 1228 if checkStop and checkStop():
1160 break 1229 break
1161 1230
1162 if entry in ['.svn', 1231 if entry in [
1163 '.hg', 1232 ".svn",
1164 '.git', 1233 ".hg",
1165 '.ropeproject', 1234 ".git",
1166 '.eric7project', 1235 ".ropeproject",
1167 '.jedi']: 1236 ".eric7project",
1237 ".jedi",
1238 ]:
1168 continue 1239 continue
1169 1240
1170 fentry = os.path.join(path, entry) 1241 fentry = os.path.join(path, entry)
1171 if ( 1242 if (
1172 pattern and 1243 pattern
1173 not os.path.isdir(fentry) and 1244 and not os.path.isdir(fentry)
1174 not any(fnmatch.fnmatch(entry, p) for p in patterns) 1245 and not any(fnmatch.fnmatch(entry, p) for p in patterns)
1175 ): 1246 ):
1176 # entry doesn't fit the given pattern 1247 # entry doesn't fit the given pattern
1177 continue 1248 continue
1178 1249
1179 if os.path.isdir(fentry): 1250 if os.path.isdir(fentry):
1180 if os.path.islink(fentry) and not followsymlinks: 1251 if os.path.islink(fentry) and not followsymlinks:
1181 continue 1252 continue
1182 files += direntries( 1253 files += direntries(
1183 fentry, filesonly, pattern, followsymlinks, checkStop) 1254 fentry, filesonly, pattern, followsymlinks, checkStop
1255 )
1184 else: 1256 else:
1185 files.append(fentry) 1257 files.append(fentry)
1186 except OSError: 1258 except OSError:
1187 pass 1259 pass
1188 except UnicodeDecodeError: 1260 except UnicodeDecodeError:
1191 1263
1192 1264
1193 def getDirs(path, excludeDirs): 1265 def getDirs(path, excludeDirs):
1194 """ 1266 """
1195 Function returning a list of all directories below path. 1267 Function returning a list of all directories below path.
1196 1268
1197 @param path root of the tree to check 1269 @param path root of the tree to check
1198 @param excludeDirs basename of directories to ignore 1270 @param excludeDirs basename of directories to ignore
1199 @return list of all directories found 1271 @return list of all directories found
1200 """ 1272 """
1201 try: 1273 try:
1203 except OSError: 1275 except OSError:
1204 return [] 1276 return []
1205 1277
1206 dirs = [] 1278 dirs = []
1207 for name in names: 1279 for name in names:
1208 if ( 1280 if os.path.isdir(os.path.join(path, name)) and not os.path.islink(
1209 os.path.isdir(os.path.join(path, name)) and 1281 os.path.join(path, name)
1210 not os.path.islink(os.path.join(path, name))
1211 ): 1282 ):
1212 exclude = 0 1283 exclude = 0
1213 for e in excludeDirs: 1284 for e in excludeDirs:
1214 if name.split(os.sep, 1)[0] == e: 1285 if name.split(os.sep, 1)[0] == e:
1215 exclude = 1 1286 exclude = 1
1225 1296
1226 1297
1227 def findVolume(volumeName, findAll=False): 1298 def findVolume(volumeName, findAll=False):
1228 """ 1299 """
1229 Function to find the directory belonging to a given volume name. 1300 Function to find the directory belonging to a given volume name.
1230 1301
1231 @param volumeName name of the volume to search for 1302 @param volumeName name of the volume to search for
1232 @type str 1303 @type str
1233 @param findAll flag indicating to get the directories for all volumes 1304 @param findAll flag indicating to get the directories for all volumes
1234 starting with the given name (defaults to False) 1305 starting with the given name (defaults to False)
1235 @type bool (optional) 1306 @type bool (optional)
1237 name 1308 name
1238 @rtype str or list of str 1309 @rtype str or list of str
1239 """ 1310 """
1240 volumeDirectories = [] 1311 volumeDirectories = []
1241 volumeDirectory = None 1312 volumeDirectory = None
1242 1313
1243 if isWindowsPlatform(): 1314 if isWindowsPlatform():
1244 # we are on a Windows platform 1315 # we are on a Windows platform
1245 def getVolumeName(diskName): 1316 def getVolumeName(diskName):
1246 """ 1317 """
1247 Local function to determine the volume of a disk or device. 1318 Local function to determine the volume of a disk or device.
1248 1319
1249 Each disk or external device connected to windows has an 1320 Each disk or external device connected to windows has an
1250 attribute called "volume name". This function returns the 1321 attribute called "volume name". This function returns the
1251 volume name for the given disk/device. 1322 volume name for the given disk/device.
1252 1323
1253 Code from http://stackoverflow.com/a/12056414 1324 Code from http://stackoverflow.com/a/12056414
1254 """ 1325 """
1255 volumeNameBuffer = ctypes.create_unicode_buffer(1024) 1326 volumeNameBuffer = ctypes.create_unicode_buffer(1024)
1256 ctypes.windll.kernel32.GetVolumeInformationW( 1327 ctypes.windll.kernel32.GetVolumeInformationW(
1257 ctypes.c_wchar_p(diskName), volumeNameBuffer, 1328 ctypes.c_wchar_p(diskName),
1258 ctypes.sizeof(volumeNameBuffer), None, None, None, None, 0) 1329 volumeNameBuffer,
1330 ctypes.sizeof(volumeNameBuffer),
1331 None,
1332 None,
1333 None,
1334 None,
1335 0,
1336 )
1259 return volumeNameBuffer.value 1337 return volumeNameBuffer.value
1260 1338
1261 # 1339 #
1262 # In certain circumstances, volumes are allocated to USB 1340 # In certain circumstances, volumes are allocated to USB
1263 # storage devices which cause a Windows popup to raise if their 1341 # storage devices which cause a Windows popup to raise if their
1264 # volume contains no media. Wrapping the check in SetErrorMode 1342 # volume contains no media. Wrapping the check in SetErrorMode
1265 # with SEM_FAILCRITICALERRORS (1) prevents this popup. 1343 # with SEM_FAILCRITICALERRORS (1) prevents this popup.
1280 ctypes.windll.kernel32.SetErrorMode(oldMode) 1358 ctypes.windll.kernel32.SetErrorMode(oldMode)
1281 else: 1359 else:
1282 # we are on a Linux or macOS platform 1360 # we are on a Linux or macOS platform
1283 for mountCommand in ["mount", "/sbin/mount", "/usr/sbin/mount"]: 1361 for mountCommand in ["mount", "/sbin/mount", "/usr/sbin/mount"]:
1284 with contextlib.suppress(FileNotFoundError): 1362 with contextlib.suppress(FileNotFoundError):
1285 mountOutput = subprocess.run( # secok 1363 mountOutput = subprocess.run( # secok
1286 mountCommand, check=True, capture_output=True, text=True 1364 mountCommand, check=True, capture_output=True, text=True
1287 ).stdout.splitlines() 1365 ).stdout.splitlines()
1288 mountedVolumes = [ 1366 mountedVolumes = [
1289 x.split(" type")[0].split(maxsplit=2)[2] 1367 x.split(" type")[0].split(maxsplit=2)[2] for x in mountOutput
1290 for x in mountOutput
1291 ] 1368 ]
1292 if findAll: 1369 if findAll:
1293 for volume in mountedVolumes: 1370 for volume in mountedVolumes:
1294 if volumeName in volume: 1371 if volumeName in volume:
1295 volumeDirectories.append(volume) 1372 volumeDirectories.append(volume)
1300 if volume.endswith(volumeName): 1377 if volume.endswith(volumeName):
1301 volumeDirectory = volume 1378 volumeDirectory = volume
1302 break 1379 break
1303 if volumeDirectory: 1380 if volumeDirectory:
1304 break 1381 break
1305 1382
1306 if findAll: 1383 if findAll:
1307 return volumeDirectories 1384 return volumeDirectories
1308 else: 1385 else:
1309 return volumeDirectory 1386 return volumeDirectory
1310 1387
1311 1388
1312 def getTestFileNames(fn): 1389 def getTestFileNames(fn):
1313 """ 1390 """
1314 Function to build the potential file names of a test file. 1391 Function to build the potential file names of a test file.
1315 1392
1316 The file names for the test file is built by prepending the string 1393 The file names for the test file is built by prepending the string
1317 "test" and "test_" to the file name passed into this function and 1394 "test" and "test_" to the file name passed into this function and
1318 by appending the string "_test". 1395 by appending the string "_test".
1319 1396
1320 @param fn file name basis to be used for the test file names 1397 @param fn file name basis to be used for the test file names
1321 @type str 1398 @type str
1322 @return file names of the corresponding test file 1399 @return file names of the corresponding test file
1323 @rtype list of str 1400 @rtype list of str
1324 """ 1401 """
1325 dn, fn = os.path.split(fn) 1402 dn, fn = os.path.split(fn)
1326 fn, ext = os.path.splitext(fn) 1403 fn, ext = os.path.splitext(fn)
1327 prefixes = ["test", "test_"] 1404 prefixes = ["test", "test_"]
1328 postfixes = ["_test"] 1405 postfixes = ["_test"]
1329 return [ 1406 return [
1330 os.path.join(dn, "{0}{1}{2}".format(prefix, fn, ext)) 1407 os.path.join(dn, "{0}{1}{2}".format(prefix, fn, ext)) for prefix in prefixes
1331 for prefix in prefixes
1332 ] + [ 1408 ] + [
1333 os.path.join(dn, "{0}{1}{2}".format(fn, postfix, ext)) 1409 os.path.join(dn, "{0}{1}{2}".format(fn, postfix, ext)) for postfix in postfixes
1334 for postfix in postfixes
1335 ] 1410 ]
1336 1411
1337 1412
1338 def getCoverageFileNames(fn): 1413 def getCoverageFileNames(fn):
1339 """ 1414 """
1340 Function to build a list of coverage data file names. 1415 Function to build a list of coverage data file names.
1341 1416
1342 @param fn file name basis to be used for the coverage data file 1417 @param fn file name basis to be used for the coverage data file
1343 @type str 1418 @type str
1344 @return list of existing coverage data files 1419 @return list of existing coverage data files
1345 @rtype list of str 1420 @rtype list of str
1346 """ 1421 """
1353 1428
1354 1429
1355 def getCoverageFileName(fn, mustExist=True): 1430 def getCoverageFileName(fn, mustExist=True):
1356 """ 1431 """
1357 Function to build a file name for a coverage data file. 1432 Function to build a file name for a coverage data file.
1358 1433
1359 @param fn file name basis to be used for the coverage data file name 1434 @param fn file name basis to be used for the coverage data file name
1360 @type str 1435 @type str
1361 @param mustExist flag indicating to check that the file exists (defaults 1436 @param mustExist flag indicating to check that the file exists (defaults
1362 to True) 1437 to True)
1363 @type bool (optional) 1438 @type bool (optional)
1376 1451
1377 1452
1378 def getProfileFileNames(fn): 1453 def getProfileFileNames(fn):
1379 """ 1454 """
1380 Function to build a list of profile data file names. 1455 Function to build a list of profile data file names.
1381 1456
1382 @param fn file name basis to be used for the profile data file 1457 @param fn file name basis to be used for the profile data file
1383 @type str 1458 @type str
1384 @return list of existing profile data files 1459 @return list of existing profile data files
1385 @rtype list of str 1460 @rtype list of str
1386 """ 1461 """
1393 1468
1394 1469
1395 def getProfileFileName(fn, mustExist=True): 1470 def getProfileFileName(fn, mustExist=True):
1396 """ 1471 """
1397 Function to build a file name for a profile data file. 1472 Function to build a file name for a profile data file.
1398 1473
1399 @param fn file name basis to be used for the profile data file name 1474 @param fn file name basis to be used for the profile data file name
1400 @type str 1475 @type str
1401 @param mustExist flag indicating to check that the file exists (defaults 1476 @param mustExist flag indicating to check that the file exists (defaults
1402 to True) 1477 to True)
1403 @type bool (optional) 1478 @type bool (optional)
1416 1491
1417 1492
1418 def parseOptionString(s): 1493 def parseOptionString(s):
1419 """ 1494 """
1420 Function used to convert an option string into a list of options. 1495 Function used to convert an option string into a list of options.
1421 1496
1422 @param s option string 1497 @param s option string
1423 @type str 1498 @type str
1424 @return list of options 1499 @return list of options
1425 @rtype list of str 1500 @rtype list of str
1426 """ 1501 """
1429 1504
1430 1505
1431 def _percentReplacementFunc(matchobj): 1506 def _percentReplacementFunc(matchobj):
1432 """ 1507 """
1433 Protected function called for replacing % codes. 1508 Protected function called for replacing % codes.
1434 1509
1435 @param matchobj match object for the code 1510 @param matchobj match object for the code
1436 @type re.Match 1511 @type re.Match
1437 @return replacement string 1512 @return replacement string
1438 @rtype str 1513 @rtype str
1439 """ 1514 """
1440 return getPercentReplacement(matchobj.group(0)) 1515 return getPercentReplacement(matchobj.group(0))
1441 1516
1442 1517
1443 def getPercentReplacement(code): 1518 def getPercentReplacement(code):
1444 """ 1519 """
1445 Function to get the replacement for code. 1520 Function to get the replacement for code.
1446 1521
1447 @param code code indicator 1522 @param code code indicator
1448 @type str 1523 @type str
1449 @return replacement string 1524 @return replacement string
1450 @rtype str 1525 @rtype str
1451 """ 1526 """
1515 # the percent sign 1590 # the percent sign
1516 return "%" 1591 return "%"
1517 else: 1592 else:
1518 # unknown code, just return it 1593 # unknown code, just return it
1519 return code 1594 return code
1520 1595
1521 1596
1522 def getPercentReplacementHelp(): 1597 def getPercentReplacementHelp():
1523 """ 1598 """
1524 Function to get the help text for the supported %-codes. 1599 Function to get the help text for the supported %-codes.
1525 1600
1526 @returns help text (string) 1601 @returns help text (string)
1527 """ 1602 """
1528 return QCoreApplication.translate( 1603 return QCoreApplication.translate(
1529 "Utilities", 1604 "Utilities",
1530 """<p>You may use %-codes as placeholders in the string.""" 1605 """<p>You may use %-codes as placeholders in the string."""
1540 """<tr><td>%P</td><td>path of the current project</td></tr>""" 1615 """<tr><td>%P</td><td>path of the current project</td></tr>"""
1541 """<tr><td>%S</td><td>selected text of the current editor</td></tr>""" 1616 """<tr><td>%S</td><td>selected text of the current editor</td></tr>"""
1542 """<tr><td>%U</td><td>username of the current user</td></tr>""" 1617 """<tr><td>%U</td><td>username of the current user</td></tr>"""
1543 """<tr><td>%%</td><td>the percent sign</td></tr>""" 1618 """<tr><td>%%</td><td>the percent sign</td></tr>"""
1544 """</table>""" 1619 """</table>"""
1545 """</p>""") 1620 """</p>""",
1621 )
1546 1622
1547 1623
1548 def getUserName(): 1624 def getUserName():
1549 """ 1625 """
1550 Function to get the user name. 1626 Function to get the user name.
1551 1627
1552 @return user name (string) 1628 @return user name (string)
1553 """ 1629 """
1554 user = getpass.getuser() 1630 user = getpass.getuser()
1555 1631
1556 if isWindowsPlatform() and not user: 1632 if isWindowsPlatform() and not user:
1557 return win32_GetUserName() 1633 return win32_GetUserName()
1558 1634
1559 return user 1635 return user
1560 1636
1561 1637
1562 def getRealName(): 1638 def getRealName():
1563 """ 1639 """
1564 Function to get the real name of the user. 1640 Function to get the real name of the user.
1565 1641
1566 @return real name of the user (string) 1642 @return real name of the user (string)
1567 """ 1643 """
1568 if isWindowsPlatform(): 1644 if isWindowsPlatform():
1569 return win32_getRealName() 1645 return win32_getRealName()
1570 else: 1646 else:
1571 import pwd 1647 import pwd
1648
1572 user = getpass.getuser() 1649 user = getpass.getuser()
1573 return pwd.getpwnam(user).pw_gecos 1650 return pwd.getpwnam(user).pw_gecos
1574 1651
1575 1652
1576 def getHomeDir(): 1653 def getHomeDir():
1577 """ 1654 """
1578 Function to get a users home directory. 1655 Function to get a users home directory.
1579 1656
1580 @return home directory (string) 1657 @return home directory (string)
1581 """ 1658 """
1582 return QDir.homePath() 1659 return QDir.homePath()
1583 1660
1584 1661
1585 def getPythonLibPath(): 1662 def getPythonLibPath():
1586 """ 1663 """
1587 Function to determine the path to Python's library. 1664 Function to determine the path to Python's library.
1588 1665
1589 @return path to the Python library (string) 1666 @return path to the Python library (string)
1590 """ 1667 """
1591 pyFullVers = sys.version.split()[0] 1668 pyFullVers = sys.version.split()[0]
1592 1669
1593 vl = re.findall("[0-9.]*", pyFullVers)[0].split(".") 1670 vl = re.findall("[0-9.]*", pyFullVers)[0].split(".")
1602 try: 1679 try:
1603 syslib = sys.lib 1680 syslib = sys.lib
1604 except AttributeError: 1681 except AttributeError:
1605 syslib = "lib" 1682 syslib = "lib"
1606 libDir = sys.prefix + "/" + syslib + "/python" + pyVers 1683 libDir = sys.prefix + "/" + syslib + "/python" + pyVers
1607 1684
1608 return libDir 1685 return libDir
1609 1686
1610 1687
1611 def getPythonVersion(): 1688 def getPythonVersion():
1612 """ 1689 """
1613 Function to get the Python version (major, minor) as an integer value. 1690 Function to get the Python version (major, minor) as an integer value.
1614 1691
1615 @return An integer representing major and minor version number (integer) 1692 @return An integer representing major and minor version number (integer)
1616 """ 1693 """
1617 return sys.hexversion >> 16 1694 return sys.hexversion >> 16
1618 1695
1619 1696
1620 def determinePythonVersion(filename, source, editor=None): 1697 def determinePythonVersion(filename, source, editor=None):
1621 """ 1698 """
1622 Function to determine the python version of a given file. 1699 Function to determine the python version of a given file.
1623 1700
1624 @param filename name of the file with extension (str) 1701 @param filename name of the file with extension (str)
1625 @param source of the file (str) 1702 @param source of the file (str)
1626 @param editor reference to the editor, if the file is opened 1703 @param editor reference to the editor, if the file is opened
1627 already (Editor object) 1704 already (Editor object)
1628 @return Python version if file is Python3 (int) 1705 @return Python version if file is Python3 (int)
1630 pyAssignment = { 1707 pyAssignment = {
1631 "Python3": 3, 1708 "Python3": 3,
1632 "MicroPython": 3, 1709 "MicroPython": 3,
1633 "Cython": 3, 1710 "Cython": 3,
1634 } 1711 }
1635 1712
1636 if not editor: 1713 if not editor:
1637 viewManager = ericApp().getObject('ViewManager') 1714 viewManager = ericApp().getObject("ViewManager")
1638 editor = viewManager.getOpenEditor(filename) 1715 editor = viewManager.getOpenEditor(filename)
1639 1716
1640 # Maybe the user has changed the language 1717 # Maybe the user has changed the language
1641 if editor and editor.getFileType() in pyAssignment: 1718 if editor and editor.getFileType() in pyAssignment:
1642 return pyAssignment[editor.getFileType()] 1719 return pyAssignment[editor.getFileType()]
1643 1720
1644 pyVer = 0 1721 pyVer = 0
1646 if not source: 1723 if not source:
1647 source = readEncodedFile(filename)[0] 1724 source = readEncodedFile(filename)[0]
1648 flags = extractFlags(source) 1725 flags = extractFlags(source)
1649 ext = os.path.splitext(filename)[1] 1726 ext = os.path.splitext(filename)[1]
1650 py3Ext = Preferences.getPython("Python3Extensions") 1727 py3Ext = Preferences.getPython("Python3Extensions")
1651 project = ericApp().getObject('Project') 1728 project = ericApp().getObject("Project")
1652 basename = os.path.basename(filename) 1729 basename = os.path.basename(filename)
1653 1730
1654 if "FileType" in flags: 1731 if "FileType" in flags:
1655 pyVer = pyAssignment.get(flags["FileType"], 0) 1732 pyVer = pyAssignment.get(flags["FileType"], 0)
1656 elif project.isOpen() and project.isProjectFile(filename): 1733 elif project.isOpen() and project.isProjectFile(filename):
1657 language = project.getEditorLexerAssoc(basename) 1734 language = project.getEditorLexerAssoc(basename)
1658 if not language: 1735 if not language:
1659 language = Preferences.getEditorLexerAssoc(basename) 1736 language = Preferences.getEditorLexerAssoc(basename)
1660 if language == 'Python3': 1737 if language == "Python3":
1661 pyVer = pyAssignment[language] 1738 pyVer = pyAssignment[language]
1662 1739
1663 if pyVer: 1740 if pyVer:
1664 # Skip the next tests 1741 # Skip the next tests
1665 pass 1742 pass
1666 elif (Preferences.getProject("DeterminePyFromProject") and 1743 elif (
1667 project.isOpen() and 1744 Preferences.getProject("DeterminePyFromProject")
1668 project.isProjectFile(filename) and 1745 and project.isOpen()
1669 ext in py3Ext): 1746 and project.isProjectFile(filename)
1747 and ext in py3Ext
1748 ):
1670 pyVer = pyAssignment.get(project.getProjectLanguage(), 0) 1749 pyVer = pyAssignment.get(project.getProjectLanguage(), 0)
1671 elif ext in py3Ext: 1750 elif ext in py3Ext:
1672 pyVer = 3 1751 pyVer = 3
1673 elif source: 1752 elif source:
1674 if isinstance(source, str): 1753 if isinstance(source, str):
1675 line0 = source.splitlines()[0] 1754 line0 = source.splitlines()[0]
1676 else: 1755 else:
1677 line0 = source[0] 1756 line0 = source[0]
1678 if ( 1757 if line0.startswith("#!") and (("python3" in line0) or ("python" in line0)):
1679 line0.startswith("#!") and
1680 (("python3" in line0) or ("python" in line0))
1681 ):
1682 pyVer = 3 1758 pyVer = 3
1683 1759
1684 if pyVer == 0 and ext in py3Ext: 1760 if pyVer == 0 and ext in py3Ext:
1685 pyVer = 3 1761 pyVer = 3
1686 1762
1687 return pyVer 1763 return pyVer
1688 1764
1689 1765
1690 def rxIndex(rx, txt): 1766 def rxIndex(rx, txt):
1691 """ 1767 """
1692 Function to get the index (start position) of a regular expression match 1768 Function to get the index (start position) of a regular expression match
1693 within some text. 1769 within some text.
1694 1770
1695 @param rx regular expression object as created by re.compile() 1771 @param rx regular expression object as created by re.compile()
1696 @type re.Pattern 1772 @type re.Pattern
1697 @param txt text to be scanned 1773 @param txt text to be scanned
1698 @type str 1774 @type str
1699 @return start position of the match or -1 indicating no match was found 1775 @return start position of the match or -1 indicating no match was found
1712 1788
1713 1789
1714 def getEnvironmentEntry(key, default=None): 1790 def getEnvironmentEntry(key, default=None):
1715 """ 1791 """
1716 Module function to get an environment entry. 1792 Module function to get an environment entry.
1717 1793
1718 @param key key of the requested environment entry (string) 1794 @param key key of the requested environment entry (string)
1719 @param default value to be returned, if the environment doesn't contain 1795 @param default value to be returned, if the environment doesn't contain
1720 the requested entry (string) 1796 the requested entry (string)
1721 @return the requested entry or the default value, if the entry wasn't 1797 @return the requested entry or the default value, if the entry wasn't
1722 found (string or None) 1798 found (string or None)
1723 """ 1799 """
1724 pattern = "^{0}[ \t]*=".format(key) 1800 pattern = "^{0}[ \t]*=".format(key)
1725 filterRe = ( 1801 filterRe = (
1726 re.compile(pattern, re.IGNORECASE) 1802 re.compile(pattern, re.IGNORECASE)
1727 if isWindowsPlatform() else 1803 if isWindowsPlatform()
1728 re.compile(pattern) 1804 else re.compile(pattern)
1729 ) 1805 )
1730 1806
1731 entries = [e for e in QProcess.systemEnvironment() 1807 entries = [
1732 if filterRe.search(e) is not None] 1808 e for e in QProcess.systemEnvironment() if filterRe.search(e) is not None
1809 ]
1733 if not entries: 1810 if not entries:
1734 return default 1811 return default
1735 1812
1736 # if there are multiple entries, just consider the first one 1813 # if there are multiple entries, just consider the first one
1737 ename, value = entries[0].split("=", 1) 1814 ename, value = entries[0].split("=", 1)
1738 return value.strip() 1815 return value.strip()
1739 1816
1740 1817
1741 def hasEnvironmentEntry(key): 1818 def hasEnvironmentEntry(key):
1742 """ 1819 """
1743 Module function to check, if the environment contains an entry. 1820 Module function to check, if the environment contains an entry.
1744 1821
1745 @param key key of the requested environment entry 1822 @param key key of the requested environment entry
1746 @type str 1823 @type str
1747 @return flag indicating the presence of the requested entry 1824 @return flag indicating the presence of the requested entry
1748 @rtype bool 1825 @rtype bool
1749 """ 1826 """
1750 pattern = "^{0}[ \t]*=".format(key) 1827 pattern = "^{0}[ \t]*=".format(key)
1751 filterRe = ( 1828 filterRe = (
1752 re.compile(pattern, re.IGNORECASE) 1829 re.compile(pattern, re.IGNORECASE)
1753 if isWindowsPlatform() else 1830 if isWindowsPlatform()
1754 re.compile(pattern) 1831 else re.compile(pattern)
1755 ) 1832 )
1756 1833
1757 entries = [e for e in QProcess.systemEnvironment() 1834 entries = [
1758 if filterRe.search(e) is not None] 1835 e for e in QProcess.systemEnvironment() if filterRe.search(e) is not None
1836 ]
1759 return len(entries) > 0 1837 return len(entries) > 0
1838
1760 1839
1761 ############################################################################### 1840 ###############################################################################
1762 ## Qt utility functions below 1841 ## Qt utility functions below
1763 ############################################################################### 1842 ###############################################################################
1764 1843
1765 1844
1766 def generateQtToolName(toolname): 1845 def generateQtToolName(toolname):
1767 """ 1846 """
1768 Module function to generate the executable name for a Qt tool like 1847 Module function to generate the executable name for a Qt tool like
1769 designer. 1848 designer.
1770 1849
1771 @param toolname base name of the tool (string) 1850 @param toolname base name of the tool (string)
1772 @return the Qt tool name without extension (string) 1851 @return the Qt tool name without extension (string)
1773 """ 1852 """
1774 return "{0}{1}{2}".format(Preferences.getQt("QtToolsPrefix"), 1853 return "{0}{1}{2}".format(
1775 toolname, 1854 Preferences.getQt("QtToolsPrefix"),
1776 Preferences.getQt("QtToolsPostfix") 1855 toolname,
1777 ) 1856 Preferences.getQt("QtToolsPostfix"),
1857 )
1778 1858
1779 1859
1780 def getQtMacBundle(toolname): 1860 def getQtMacBundle(toolname):
1781 """ 1861 """
1782 Module function to determine the correct Mac OS X bundle name for Qt tools. 1862 Module function to determine the correct Mac OS X bundle name for Qt tools.
1783 1863
1784 @param toolname plain name of the tool (e.g. "designer") (string) 1864 @param toolname plain name of the tool (e.g. "designer") (string)
1785 @return bundle name of the Qt tool (string) 1865 @return bundle name of the Qt tool (string)
1786 """ 1866 """
1787 qtDir = getQtBinariesPath() 1867 qtDir = getQtBinariesPath()
1788 bundles = [ 1868 bundles = [
1789 os.path.join( 1869 os.path.join(qtDir, "bin", generateQtToolName(toolname.capitalize())) + ".app",
1790 qtDir, 'bin', generateQtToolName(toolname.capitalize())) + ".app", 1870 os.path.join(qtDir, "bin", generateQtToolName(toolname)) + ".app",
1791 os.path.join(qtDir, 'bin', generateQtToolName(toolname)) + ".app", 1871 os.path.join(qtDir, generateQtToolName(toolname.capitalize())) + ".app",
1792 os.path.join(
1793 qtDir, generateQtToolName(toolname.capitalize())) + ".app",
1794 os.path.join(qtDir, generateQtToolName(toolname)) + ".app", 1872 os.path.join(qtDir, generateQtToolName(toolname)) + ".app",
1795 ] 1873 ]
1796 if toolname == "designer": 1874 if toolname == "designer":
1797 # support the standalone Qt Designer installer from 1875 # support the standalone Qt Designer installer from
1798 # https://build-system.fman.io/qt-designer-download 1876 # https://build-system.fman.io/qt-designer-download
1799 designer = "Qt Designer.app" 1877 designer = "Qt Designer.app"
1800 bundles.extend([ 1878 bundles.extend(
1801 os.path.join(qtDir, 'bin', designer), 1879 [
1802 os.path.join(qtDir, designer), 1880 os.path.join(qtDir, "bin", designer),
1803 ]) 1881 os.path.join(qtDir, designer),
1882 ]
1883 )
1804 for bundle in bundles: 1884 for bundle in bundles:
1805 if os.path.exists(bundle): 1885 if os.path.exists(bundle):
1806 return bundle 1886 return bundle
1807 return "" 1887 return ""
1808 1888
1829 newArgs.append("--args") 1909 newArgs.append("--args")
1830 newArgs += args 1910 newArgs += args
1831 1911
1832 return ("open", newArgs) 1912 return ("open", newArgs)
1833 1913
1914
1834 ############################################################################### 1915 ###############################################################################
1835 ## PyQt utility functions below 1916 ## PyQt utility functions below
1836 ############################################################################### 1917 ###############################################################################
1837 1918
1838 1919
1839 def generatePyQtToolPath(toolname, alternatives=None): 1920 def generatePyQtToolPath(toolname, alternatives=None):
1840 """ 1921 """
1841 Module function to generate the executable path for a PyQt tool. 1922 Module function to generate the executable path for a PyQt tool.
1842 1923
1843 @param toolname base name of the tool 1924 @param toolname base name of the tool
1844 @type str 1925 @type str
1845 @param alternatives list of alternative tool names to try 1926 @param alternatives list of alternative tool names to try
1846 @type list of str 1927 @type list of str
1847 @return executable path name of the tool 1928 @return executable path name of the tool
1856 else: 1937 else:
1857 if isWindowsPlatform(): 1938 if isWindowsPlatform():
1858 exe = getWindowsExecutablePath(toolname) 1939 exe = getWindowsExecutablePath(toolname)
1859 else: 1940 else:
1860 exe = toolname 1941 exe = toolname
1861 1942
1862 if not isinpath(exe) and alternatives: 1943 if not isinpath(exe) and alternatives:
1863 ex_ = generatePyQtToolPath(alternatives[0], alternatives[1:]) 1944 ex_ = generatePyQtToolPath(alternatives[0], alternatives[1:])
1864 if isinpath(ex_): 1945 if isinpath(ex_):
1865 exe = ex_ 1946 exe = ex_
1866 1947
1867 return exe 1948 return exe
1949
1868 1950
1869 ############################################################################### 1951 ###############################################################################
1870 ## PySide2/PySide6 utility functions below 1952 ## PySide2/PySide6 utility functions below
1871 ############################################################################### 1953 ###############################################################################
1872 1954
1873 1955
1874 def generatePySideToolPath(toolname, variant=2): 1956 def generatePySideToolPath(toolname, variant=2):
1875 """ 1957 """
1876 Module function to generate the executable path for a PySide2/PySide6 tool. 1958 Module function to generate the executable path for a PySide2/PySide6 tool.
1877 1959
1878 @param toolname base name of the tool 1960 @param toolname base name of the tool
1879 @type str 1961 @type str
1880 @param variant indicator for the PySide variant 1962 @param variant indicator for the PySide variant
1881 @type int or str 1963 @type int or str
1882 @return the PySide2/PySide6 tool path with extension 1964 @return the PySide2/PySide6 tool path with extension
1884 """ 1966 """
1885 if isWindowsPlatform(): 1967 if isWindowsPlatform():
1886 hasPyside = checkPyside(variant) 1968 hasPyside = checkPyside(variant)
1887 if not hasPyside: 1969 if not hasPyside:
1888 return "" 1970 return ""
1889 1971
1890 venvName = Preferences.getQt("PySide{0}VenvName".format(variant)) 1972 venvName = Preferences.getQt("PySide{0}VenvName".format(variant))
1891 if not venvName: 1973 if not venvName:
1892 venvName = Preferences.getDebugger("Python3VirtualEnv") 1974 venvName = Preferences.getDebugger("Python3VirtualEnv")
1893 interpreter = ericApp().getObject( 1975 interpreter = (
1894 "VirtualEnvManager").getVirtualenvInterpreter(venvName) 1976 ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName)
1977 )
1895 if interpreter == "" or not isinpath(interpreter): 1978 if interpreter == "" or not isinpath(interpreter):
1896 interpreter = getPythonExecutable() 1979 interpreter = getPythonExecutable()
1897 prefix = os.path.dirname(interpreter) 1980 prefix = os.path.dirname(interpreter)
1898 if not prefix.endswith("Scripts"): 1981 if not prefix.endswith("Scripts"):
1899 prefix = os.path.join(prefix, "Scripts") 1982 prefix = os.path.join(prefix, "Scripts")
1900 return os.path.join(prefix, toolname + '.exe') 1983 return os.path.join(prefix, toolname + ".exe")
1901 else: 1984 else:
1902 # step 1: check, if the user has configured a tools path 1985 # step 1: check, if the user has configured a tools path
1903 path = Preferences.getQt("PySide{0}ToolsDir".format(variant)) 1986 path = Preferences.getQt("PySide{0}ToolsDir".format(variant))
1904 if path: 1987 if path:
1905 return os.path.join(path, toolname) 1988 return os.path.join(path, toolname)
1906 1989
1907 # step 2: determine from used Python interpreter 1990 # step 2: determine from used Python interpreter
1908 dirName = os.path.dirname(sys.executable) 1991 dirName = os.path.dirname(sys.executable)
1909 if os.path.exists(os.path.join(dirName, toolname)): 1992 if os.path.exists(os.path.join(dirName, toolname)):
1910 return os.path.join(dirName, toolname) 1993 return os.path.join(dirName, toolname)
1911 1994
1912 return toolname 1995 return toolname
1913 1996
1914 1997
1915 @functools.lru_cache() 1998 @functools.lru_cache()
1916 def checkPyside(variant=2): 1999 def checkPyside(variant=2):
1917 """ 2000 """
1918 Module function to check the presence of PySide2/PySide6. 2001 Module function to check the presence of PySide2/PySide6.
1919 2002
1920 @param variant indicator for the PySide variant 2003 @param variant indicator for the PySide variant
1921 @type int or str 2004 @type int or str
1922 @return flags indicating the presence of PySide2/PySide6 2005 @return flags indicating the presence of PySide2/PySide6
1923 @rtype bool 2006 @rtype bool
1924 """ 2007 """
1925 venvName = Preferences.getQt("PySide{0}VenvName".format(variant)) 2008 venvName = Preferences.getQt("PySide{0}VenvName".format(variant))
1926 if not venvName: 2009 if not venvName:
1927 venvName = Preferences.getDebugger("Python3VirtualEnv") 2010 venvName = Preferences.getDebugger("Python3VirtualEnv")
1928 interpreter = ericApp().getObject( 2011 interpreter = (
1929 "VirtualEnvManager").getVirtualenvInterpreter(venvName) 2012 ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName)
2013 )
1930 if interpreter == "" or not isinpath(interpreter): 2014 if interpreter == "" or not isinpath(interpreter):
1931 interpreter = getPythonExecutable() 2015 interpreter = getPythonExecutable()
1932 2016
1933 checker = os.path.join( 2017 checker = os.path.join(getConfig("ericDir"), "Utilities", "PySideImporter.py")
1934 getConfig('ericDir'), "Utilities", "PySideImporter.py")
1935 args = [checker, "--variant={0}".format(variant)] 2018 args = [checker, "--variant={0}".format(variant)]
1936 proc = QProcess() 2019 proc = QProcess()
1937 proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels) 2020 proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
1938 proc.start(interpreter, args) 2021 proc.start(interpreter, args)
1939 finished = proc.waitForFinished(30000) 2022 finished = proc.waitForFinished(30000)
1940 return finished and proc.exitCode() == 0 2023 return finished and proc.exitCode() == 0
1941 2024
2025
1942 ############################################################################### 2026 ###############################################################################
1943 ## Other utility functions below 2027 ## Other utility functions below
1944 ############################################################################### 2028 ###############################################################################
1945 2029
1946 2030
1947 def generateVersionInfo(linesep='\n'): 2031 def generateVersionInfo(linesep="\n"):
1948 """ 2032 """
1949 Module function to generate a string with various version infos. 2033 Module function to generate a string with various version infos.
1950 2034
1951 @param linesep string to be used to separate lines 2035 @param linesep string to be used to separate lines
1952 @type str 2036 @type str
1953 @return string with version infos 2037 @return string with version infos
1954 @rtype str 2038 @rtype str
1955 """ 2039 """
1959 except ImportError: 2043 except ImportError:
1960 import sip 2044 import sip
1961 sip_version_str = sip.SIP_VERSION_STR 2045 sip_version_str = sip.SIP_VERSION_STR
1962 except (ImportError, AttributeError): 2046 except (ImportError, AttributeError):
1963 sip_version_str = "sip version not available" 2047 sip_version_str = "sip version not available"
1964 2048
1965 sizeStr = "64-Bit" if sys.maxsize > 2**32 else "32-Bit" 2049 sizeStr = "64-Bit" if sys.maxsize > 2**32 else "32-Bit"
1966 2050
1967 info = ["Version Numbers:"] 2051 info = ["Version Numbers:"]
1968 2052
1969 info.append(" Python {0}, {1}".format(sys.version.split()[0], sizeStr)) 2053 info.append(" Python {0}, {1}".format(sys.version.split()[0], sizeStr))
1970 info.append(" Qt {0}".format(qVersion())) 2054 info.append(" Qt {0}".format(qVersion()))
1971 info.append(" PyQt6 {0}".format(PYQT_VERSION_STR)) 2055 info.append(" PyQt6 {0}".format(PYQT_VERSION_STR))
1972 try: 2056 try:
1973 from PyQt6 import QtCharts 2057 from PyQt6 import QtCharts
1974 info.append(" PyQt6-Charts {0}".format( 2058
1975 QtCharts.PYQT_CHART_VERSION_STR)) 2059 info.append(" PyQt6-Charts {0}".format(QtCharts.PYQT_CHART_VERSION_STR))
1976 except (ImportError, AttributeError): 2060 except (ImportError, AttributeError):
1977 info.append(" PyQt6-Charts not installed") 2061 info.append(" PyQt6-Charts not installed")
1978 try: 2062 try:
1979 from PyQt6 import QtWebEngineCore 2063 from PyQt6 import QtWebEngineCore
1980 info.append(" PyQt6-WebEngine {0}".format( 2064
1981 QtWebEngineCore.PYQT_WEBENGINE_VERSION_STR)) 2065 info.append(
2066 " PyQt6-WebEngine {0}".format(QtWebEngineCore.PYQT_WEBENGINE_VERSION_STR)
2067 )
1982 except (ImportError, AttributeError): 2068 except (ImportError, AttributeError):
1983 info.append(" PyQt6-WebEngine not installed") 2069 info.append(" PyQt6-WebEngine not installed")
1984 info.append(" PyQt6-QScintilla {0}".format(QSCINTILLA_VERSION_STR)) 2070 info.append(" PyQt6-QScintilla {0}".format(QSCINTILLA_VERSION_STR))
1985 info.append(" sip {0}".format(sip_version_str)) 2071 info.append(" sip {0}".format(sip_version_str))
1986 with contextlib.suppress(ImportError): 2072 with contextlib.suppress(ImportError):
1987 from PyQt6 import QtWebEngineWidgets # __IGNORE_WARNING__ 2073 from PyQt6 import QtWebEngineWidgets # __IGNORE_WARNING__
1988 from WebBrowser.Tools import WebBrowserTools 2074 from WebBrowser.Tools import WebBrowserTools
1989 chromiumVersion, chromiumSecurityVersion = ( 2075
1990 WebBrowserTools.getWebEngineVersions()[0:2] 2076 (
1991 ) 2077 chromiumVersion,
2078 chromiumSecurityVersion,
2079 ) = WebBrowserTools.getWebEngineVersions()[0:2]
1992 info.append(" WebEngine {0}".format(chromiumVersion)) 2080 info.append(" WebEngine {0}".format(chromiumVersion))
1993 if chromiumSecurityVersion: 2081 if chromiumSecurityVersion:
1994 info.append(" (Security) {0}".format(chromiumSecurityVersion)) 2082 info.append(" (Security) {0}".format(chromiumSecurityVersion))
1995 info.append(" {0} {1}".format(Program, Version)) 2083 info.append(" {0} {1}".format(Program, Version))
1996 info.append("") 2084 info.append("")
2002 info.append("Desktop: {0}".format(desktop)) 2090 info.append("Desktop: {0}".format(desktop))
2003 session = sessionType() 2091 session = sessionType()
2004 if session: 2092 if session:
2005 info.append("") 2093 info.append("")
2006 info.append("Session Type: {0}".format(session)) 2094 info.append("Session Type: {0}".format(session))
2007 2095
2008 return linesep.join(info) 2096 return linesep.join(info)
2009 2097
2010 2098
2011 def generatePluginsVersionInfo(linesep='\n'): 2099 def generatePluginsVersionInfo(linesep="\n"):
2012 """ 2100 """
2013 Module function to generate a string with plugins version infos. 2101 Module function to generate a string with plugins version infos.
2014 2102
2015 @param linesep string to be used to separate lines 2103 @param linesep string to be used to separate lines
2016 @type str 2104 @type str
2017 @return string with plugins version infos 2105 @return string with plugins version infos
2018 @rtype str 2106 @rtype str
2019 """ 2107 """
2023 with contextlib.suppress(KeyError): 2111 with contextlib.suppress(KeyError):
2024 pm = app.getObject("PluginManager") 2112 pm = app.getObject("PluginManager")
2025 versions = {} 2113 versions = {}
2026 for pinfo in pm.getPluginInfos(): 2114 for pinfo in pm.getPluginInfos():
2027 versions[pinfo["module_name"]] = pinfo["version"] 2115 versions[pinfo["module_name"]] = pinfo["version"]
2028 2116
2029 info.append("Plugins Version Numbers:") 2117 info.append("Plugins Version Numbers:")
2030 for pluginModuleName in sorted(versions.keys()): 2118 for pluginModuleName in sorted(versions.keys()):
2031 info.append(" {0} {1}".format( 2119 info.append(
2032 pluginModuleName, versions[pluginModuleName])) 2120 " {0} {1}".format(pluginModuleName, versions[pluginModuleName])
2033 2121 )
2122
2034 return linesep.join(info) 2123 return linesep.join(info)
2035 2124
2036 2125
2037 def generateDistroInfo(linesep='\n'): 2126 def generateDistroInfo(linesep="\n"):
2038 """ 2127 """
2039 Module function to generate a string with distribution infos. 2128 Module function to generate a string with distribution infos.
2040 2129
2041 @param linesep string to be used to separate lines 2130 @param linesep string to be used to separate lines
2042 @type str 2131 @type str
2043 @return string with distribution infos 2132 @return string with distribution infos
2044 @rtype str 2133 @rtype str
2045 """ 2134 """
2052 try: 2141 try:
2053 with open(rfile, "r") as f: 2142 with open(rfile, "r") as f:
2054 lines = f.read().splitlines() 2143 lines = f.read().splitlines()
2055 except OSError: 2144 except OSError:
2056 continue 2145 continue
2057 2146
2058 info.append(' {0}'.format(rfile)) 2147 info.append(" {0}".format(rfile))
2059 info.extend([' {0}'.format(line) for line in lines]) 2148 info.extend([" {0}".format(line) for line in lines])
2060 info.append("") 2149 info.append("")
2061 2150
2062 return linesep.join(info) 2151 return linesep.join(info)
2063 2152
2064 2153
2065 def toBool(dataStr): 2154 def toBool(dataStr):
2066 """ 2155 """
2067 Module function to convert a string to a boolean value. 2156 Module function to convert a string to a boolean value.
2068 2157
2069 @param dataStr string to be converted (string) 2158 @param dataStr string to be converted (string)
2070 @return converted boolean value (boolean) 2159 @return converted boolean value (boolean)
2071 """ 2160 """
2072 if dataStr in ["True", "true", "1", "Yes", "yes"]: 2161 if dataStr in ["True", "true", "1", "Yes", "yes"]:
2073 return True 2162 return True
2079 2168
2080 def getSysPath(interpreter): 2169 def getSysPath(interpreter):
2081 """ 2170 """
2082 Module function to get the Python path (sys.path) of a specific 2171 Module function to get the Python path (sys.path) of a specific
2083 interpreter. 2172 interpreter.
2084 2173
2085 @param interpreter Python interpreter executable to get sys.path for 2174 @param interpreter Python interpreter executable to get sys.path for
2086 @type str 2175 @type str
2087 @return list containing sys.path of the interpreter; an empty list 2176 @return list containing sys.path of the interpreter; an empty list
2088 is returned, if the interpreter is the one used to run eric itself 2177 is returned, if the interpreter is the one used to run eric itself
2089 @rtype list of str 2178 @rtype list of str
2090 """ 2179 """
2091 import json 2180 import json
2092 2181
2093 sysPath = [] 2182 sysPath = []
2094 2183
2095 getSysPathSkript = os.path.join( 2184 getSysPathSkript = os.path.join(os.path.dirname(__file__), "GetSysPath.py")
2096 os.path.dirname(__file__), "GetSysPath.py")
2097 args = [getSysPathSkript] 2185 args = [getSysPathSkript]
2098 proc = QProcess() 2186 proc = QProcess()
2099 proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels) 2187 proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
2100 proc.start(interpreter, args) 2188 proc.start(interpreter, args)
2101 finished = proc.waitForFinished(30000) 2189 finished = proc.waitForFinished(30000)
2104 sysPathResult = str(text, "utf-8", "replace").strip() 2192 sysPathResult = str(text, "utf-8", "replace").strip()
2105 with contextlib.suppress(TypeError, ValueError): 2193 with contextlib.suppress(TypeError, ValueError):
2106 sysPath = json.loads(sysPathResult) 2194 sysPath = json.loads(sysPathResult)
2107 if "" in sysPath: 2195 if "" in sysPath:
2108 sysPath.remove("") 2196 sysPath.remove("")
2109 2197
2110 return sysPath 2198 return sysPath
2199
2111 2200
2112 ############################################################################### 2201 ###############################################################################
2113 ## posix compatibility functions below 2202 ## posix compatibility functions below
2114 ############################################################################### 2203 ###############################################################################
2115 2204
2121 2210
2122 2211
2123 def win32_Kill(pid): 2212 def win32_Kill(pid):
2124 """ 2213 """
2125 Function to provide an os.kill equivalent for Win32. 2214 Function to provide an os.kill equivalent for Win32.
2126 2215
2127 @param pid process id (integer) 2216 @param pid process id (integer)
2128 @return result of the kill (boolean) 2217 @return result of the kill (boolean)
2129 """ 2218 """
2130 import win32api 2219 import win32api
2220
2131 handle = win32api.OpenProcess(1, 0, pid) 2221 handle = win32api.OpenProcess(1, 0, pid)
2132 return (0 != win32api.TerminateProcess(handle, 0)) 2222 return 0 != win32api.TerminateProcess(handle, 0)
2133 2223
2134 2224
2135 def win32_GetUserName(): 2225 def win32_GetUserName():
2136 """ 2226 """
2137 Function to get the user name under Win32. 2227 Function to get the user name under Win32.
2138 2228
2139 @return user name (string) 2229 @return user name (string)
2140 """ 2230 """
2141 try: 2231 try:
2142 import win32api 2232 import win32api
2233
2143 return win32api.GetUserName() 2234 return win32api.GetUserName()
2144 except ImportError: 2235 except ImportError:
2145 try: 2236 try:
2146 u = getEnvironmentEntry('USERNAME') 2237 u = getEnvironmentEntry("USERNAME")
2147 except KeyError: 2238 except KeyError:
2148 u = getEnvironmentEntry('username', None) 2239 u = getEnvironmentEntry("username", None)
2149 return u 2240 return u
2150 2241
2151 2242
2152 def win32_getRealName(): 2243 def win32_getRealName():
2153 """ 2244 """
2154 Function to get the user's real name (aka. display name) under Win32. 2245 Function to get the user's real name (aka. display name) under Win32.
2155 2246
2156 @return real name of the current user (string) 2247 @return real name of the current user (string)
2157 """ 2248 """
2158 import ctypes 2249 import ctypes
2159 2250
2160 GetUserNameEx = ctypes.windll.secur32.GetUserNameExW 2251 GetUserNameEx = ctypes.windll.secur32.GetUserNameExW
2161 NameDisplay = 3 2252 NameDisplay = 3
2162 2253
2163 size = ctypes.pointer(ctypes.c_ulong(0)) 2254 size = ctypes.pointer(ctypes.c_ulong(0))
2164 GetUserNameEx(NameDisplay, None, size) 2255 GetUserNameEx(NameDisplay, None, size)

eric ide

mercurial