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 |
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]) |
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 """ |
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 """ |
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) |
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. |
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 """ |
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(".") |
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 |
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("") |