src/eric7/Utilities/__init__.py

branch
eric7-maintenance
changeset 10460
3b34efa2857c
parent 10349
df7edc29cbfb
parent 10439
21c28b0f9e41
child 10534
783d835d7fe4
equal deleted inserted replaced
10366:411df92e881f 10460:3b34efa2857c
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 2
3 # Copyright (c) 2003 - 2023 Detlev Offenbach <detlev@die-offenbachs.de> 3 # Copyright (c) 2003 - 2024 Detlev Offenbach <detlev@die-offenbachs.de>
4 # 4 #
5 5
6 """ 6 """
7 Package implementing various functions/classes needed everywhere within eric. 7 Package implementing various functions/classes needed everywhere within eric.
8 """ 8 """
44 ): 44 ):
45 """ 45 """
46 Module function to raise a SyntaxError for a SyntaxWarning. 46 Module function to raise a SyntaxError for a SyntaxWarning.
47 47
48 @param message warning object 48 @param message warning object
49 @type Class
49 @param category type object of the warning 50 @param category type object of the warning
50 @param filename name of the file causing the warning (string) 51 @type SyntaxWarning
51 @param lineno line number causing the warning (integer) 52 @param filename name of the file causing the warning
53 @type str
54 @param lineno line number causing the warning
55 @type int
52 @param file file to write the warning message to (ignored) 56 @param file file to write the warning message to (ignored)
57 @type file
53 @param line line causing the warning (ignored) 58 @param line line causing the warning (ignored)
54 @raise err exception of type SyntaxError 59 @type int
60 @exception err exception of type SyntaxError
55 """ 61 """
56 if category is SyntaxWarning: 62 if category is SyntaxWarning:
57 err = SyntaxError(str(message)) 63 err = SyntaxError(str(message))
58 err.filename = filename 64 err.filename = filename
59 err.lineno = lineno 65 err.lineno = lineno
170 176
171 def __init__(self, coding): 177 def __init__(self, coding):
172 """ 178 """
173 Constructor 179 Constructor
174 180
175 @param coding coding to include in the message (string) 181 @param coding coding to include in the message
182 @type str
176 """ 183 """
177 self.errorMessage = QCoreApplication.translate( 184 self.errorMessage = QCoreApplication.translate(
178 "CodingError", "The coding '{0}' is wrong for the given text." 185 "CodingError", "The coding '{0}' is wrong for the given text."
179 ).format(coding) 186 ).format(coding)
180 187
181 def __repr__(self): 188 def __repr__(self):
182 """ 189 """
183 Special method returning a representation of the exception. 190 Special method returning a representation of the exception.
184 191
185 @return string representing the error message 192 @return string representing the error message
193 @rtype str
186 """ 194 """
187 return str(self.errorMessage) 195 return str(self.errorMessage)
188 196
189 def __str__(self): 197 def __str__(self):
190 """ 198 """
191 Special method returning a string representation of the exception. 199 Special method returning a string representation of the exception.
192 200
193 @return string representing the error message 201 @return string representing the error message
202 @rtype str
194 """ 203 """
195 return str(self.errorMessage) 204 return str(self.errorMessage)
196 205
197 206
198 def get_codingBytes(text): 207 def get_codingBytes(text):
199 """ 208 """
200 Function to get the coding of a bytes text. 209 Function to get the coding of a bytes text.
201 210
202 @param text bytes text to inspect (bytes) 211 @param text bytes text to inspect
212 @type bytes
203 @return coding string 213 @return coding string
214 @rtype str
204 """ 215 """
205 lines = text.splitlines() 216 lines = text.splitlines()
206 for coding in codingBytes_regexps: 217 for coding in codingBytes_regexps:
207 coding_re = coding[1] 218 coding_re = coding[1]
208 head = lines[: coding[0]] 219 head = lines[: coding[0]]
215 226
216 def get_coding(text): 227 def get_coding(text):
217 """ 228 """
218 Function to get the coding of a text. 229 Function to get the coding of a text.
219 230
220 @param text text to inspect (string) 231 @param text text to inspect
232 @type str
221 @return coding string 233 @return coding string
234 @rtype str
222 """ 235 """
223 lines = text.splitlines() 236 lines = text.splitlines()
224 for coding in coding_regexps: 237 for coding in coding_regexps:
225 coding_re = coding[1] 238 coding_re = coding[1]
226 head = lines[: coding[0]] 239 head = lines[: coding[0]]
233 246
234 def readEncodedFile(filename): 247 def readEncodedFile(filename):
235 """ 248 """
236 Function to read a file and decode its contents into proper text. 249 Function to read a file and decode its contents into proper text.
237 250
238 @param filename name of the file to read (string) 251 @param filename name of the file to read
239 @return tuple of decoded text and encoding (string, string) 252 @type str
253 @return tuple of decoded text and encoding
254 @rtype tuple of (str, str)
240 """ 255 """
241 with open(filename, "rb") as f: 256 with open(filename, "rb") as f:
242 text = f.read() 257 text = f.read()
243 return decode(text) 258 return decode(text)
244 259
246 def readEncodedFileWithHash(filename): 261 def readEncodedFileWithHash(filename):
247 """ 262 """
248 Function to read a file, calculate a hash value and decode its contents 263 Function to read a file, calculate a hash value and decode its contents
249 into proper text. 264 into proper text.
250 265
251 @param filename name of the file to read (string) 266 @param filename name of the file to read
252 @return tuple of decoded text, encoding and hash value (string, string, 267 @type str
253 string) 268 @return tuple of decoded text, encoding and hash value
269 @rtype tuple of (str, str, str)
254 """ 270 """
255 with open(filename, "rb") as f: 271 with open(filename, "rb") as f:
256 text = f.read() 272 text = f.read()
257 hashStr = str( 273 hashStr = str(
258 QCryptographicHash.hash( 274 QCryptographicHash.hash(
265 281
266 def decode(text): 282 def decode(text):
267 """ 283 """
268 Function to decode some byte text into a string. 284 Function to decode some byte text into a string.
269 285
270 @param text byte text to decode (bytes) 286 @param text byte text to decode
271 @return tuple of decoded text and encoding (string, string) 287 @type bytes
288 @return tuple of decoded text and encoding
289 @rtype tuple of (str, str)
272 """ 290 """
273 with contextlib.suppress(UnicodeError, LookupError): 291 with contextlib.suppress(UnicodeError, LookupError):
274 if text.startswith(BOM_UTF8): 292 if text.startswith(BOM_UTF8):
275 # UTF-8 with BOM 293 # UTF-8 with BOM
276 return str(text[len(BOM_UTF8) :], "utf-8"), "utf-8-bom" 294 return str(text[len(BOM_UTF8) :], "utf-8"), "utf-8-bom"
322 340
323 def readEncodedFileWithEncoding(filename, encoding): 341 def readEncodedFileWithEncoding(filename, encoding):
324 """ 342 """
325 Function to read a file and decode its contents into proper text. 343 Function to read a file and decode its contents into proper text.
326 344
327 @param filename name of the file to read (string) 345 @param filename name of the file to read
328 @param encoding encoding to be used to read the file (string) 346 @type str
329 @return tuple of decoded text and encoding (string, string) 347 @param encoding encoding to be used to read the file
348 @type str
349 @return tuple of decoded text and encoding
350 @rtype tuple of (str, str)
330 """ 351 """
331 with open(filename, "rb") as f: 352 with open(filename, "rb") as f:
332 text = f.read() 353 text = f.read()
333 if encoding: 354 if encoding:
334 with contextlib.suppress(UnicodeError, LookupError): 355 with contextlib.suppress(UnicodeError, LookupError):
436 457
437 def decodeString(text): 458 def decodeString(text):
438 """ 459 """
439 Function to decode a string containing Unicode encoded characters. 460 Function to decode a string containing Unicode encoded characters.
440 461
441 @param text text containing encoded chars (string) 462 @param text text containing encoded chars
442 @return decoded text (string) 463 @type str
464 @return decoded text
465 @rtype str
443 """ 466 """
444 buf = b"" 467 buf = b""
445 index = 0 468 index = 0
446 while index < len(text): 469 while index < len(text):
447 if text[index] == "\\": 470 if text[index] == "\\":
457 480
458 def decodeBytes(buffer): 481 def decodeBytes(buffer):
459 """ 482 """
460 Function to decode some byte text into a string. 483 Function to decode some byte text into a string.
461 484
462 @param buffer byte buffer to decode (bytes) 485 @param buffer byte buffer to decode
463 @return decoded text (string) 486 @type bytes
487 @return decoded text
488 @rtype str
464 """ 489 """
465 # try UTF with BOM 490 # try UTF with BOM
466 with contextlib.suppress(UnicodeError, LookupError): 491 with contextlib.suppress(UnicodeError, LookupError):
467 if buffer.startswith(BOM_UTF8): 492 if buffer.startswith(BOM_UTF8):
468 # UTF-8 with BOM 493 # UTF-8 with BOM
494 519
495 def readStringFromStream(stream): 520 def readStringFromStream(stream):
496 """ 521 """
497 Module function to read a string from the given stream. 522 Module function to read a string from the given stream.
498 523
499 @param stream data stream opened for reading (QDataStream) 524 @param stream data stream opened for reading
500 @return string read from the stream (string) 525 @type QDataStream
526 @return string read from the stream
527 @rtype str
501 """ 528 """
502 data = stream.readString() 529 data = stream.readString()
503 if data is None: 530 if data is None:
504 data = b"" 531 data = b""
505 return data.decode("utf-8") 532 return data.decode("utf-8")
507 534
508 def normalizeCode(codestring): 535 def normalizeCode(codestring):
509 """ 536 """
510 Function to normalize the given code. 537 Function to normalize the given code.
511 538
512 @param codestring code to be normalized (string) 539 @param codestring code to be normalized
513 @return normalized code (string) 540 @type str
541 @return normalized code
542 @rtype str
514 """ 543 """
515 codestring = codestring.replace("\r\n", "\n").replace("\r", "\n") 544 codestring = codestring.replace("\r\n", "\n").replace("\r", "\n")
516 545
517 if codestring and codestring[-1] != "\n": 546 if codestring and codestring[-1] != "\n":
518 codestring += "\n" 547 codestring += "\n"
534 def escape_entities(m, escmap=_escape_map): 563 def escape_entities(m, escmap=_escape_map):
535 """ 564 """
536 Function to encode html entities. 565 Function to encode html entities.
537 566
538 @param m the match object 567 @param m the match object
568 @type re.Match
539 @param escmap the map of entities to encode 569 @param escmap the map of entities to encode
540 @return the converted text (string) 570 @type dict
571 @return the converted text
572 @rtype str
541 """ 573 """
542 char = m.group() 574 char = m.group()
543 text = escmap.get(char) 575 text = escmap.get(char)
544 if text is None: 576 if text is None:
545 text = "&#{0:d};".format(ord(char)) 577 text = "&#{0:d};".format(ord(char))
548 580
549 def html_encode(text, pattern=_escape): 581 def html_encode(text, pattern=_escape):
550 """ 582 """
551 Function to correctly encode a text for html. 583 Function to correctly encode a text for html.
552 584
553 @param text text to be encoded (string) 585 @param text text to be encoded
554 @param pattern search pattern for text to be encoded (string) 586 @type str
555 @return the encoded text (string) 587 @param pattern search pattern for text to be encoded
588 @type str
589 @return the encoded text
590 @rtype str
556 """ 591 """
557 if not text: 592 if not text:
558 return "" 593 return ""
559 text = pattern.sub(escape_entities, text) 594 text = pattern.sub(escape_entities, text)
560 return text 595 return text
566 def escape_uentities(m): 601 def escape_uentities(m):
567 """ 602 """
568 Function to encode html entities. 603 Function to encode html entities.
569 604
570 @param m the match object 605 @param m the match object
571 @return the converted text (string) 606 @type re.Match
607 @return the converted text
608 @rtype str
572 """ 609 """
573 char = m.group() 610 char = m.group()
574 text = "&#{0:d};".format(ord(char)) 611 text = "&#{0:d};".format(ord(char))
575 return text 612 return text
576 613
577 614
578 def html_uencode(text, pattern=_uescape): 615 def html_uencode(text, pattern=_uescape):
579 """ 616 """
580 Function to correctly encode a unicode text for html. 617 Function to correctly encode a unicode text for html.
581 618
582 @param text text to be encoded (string) 619 @param text text to be encoded
583 @param pattern search pattern for text to be encoded (string) 620 @type str
584 @return the encoded text (string) 621 @param pattern search pattern for text to be encoded
622 @type str
623 @return the encoded text
624 @rtype str
585 """ 625 """
586 if not text: 626 if not text:
587 return "" 627 return ""
588 text = pattern.sub(escape_uentities, text) 628 text = pattern.sub(escape_uentities, text)
589 return text 629 return text
595 def unescape_uentities(m): 635 def unescape_uentities(m):
596 """ 636 """
597 Function to decode html entities. 637 Function to decode html entities.
598 638
599 @param m the match object 639 @param m the match object
600 @return the converted text (string) 640 @type re.Match
641 @return the converted text
642 @rtype str
601 """ 643 """
602 char = m.group() 644 char = m.group()
603 ordinal = int(char[2:-1]) 645 ordinal = int(char[2:-1])
604 return chr(ordinal) 646 return chr(ordinal)
605 647
606 648
607 def html_udecode(text, pattern=_uunescape): 649 def html_udecode(text, pattern=_uunescape):
608 """ 650 """
609 Function to correctly decode a html text to a unicode text. 651 Function to correctly decode a html text to a unicode text.
610 652
611 @param text text to be decoded (string) 653 @param text text to be decoded
612 @param pattern search pattern for text to be decoded (string) 654 @type str
613 @return the decoded text (string) 655 @param pattern search pattern for text to be decoded
656 @type str
657 @return the decoded text
658 @rtype str
614 """ 659 """
615 if not text: 660 if not text:
616 return "" 661 return ""
617 text = pattern.sub(unescape_uentities, text) 662 text = pattern.sub(unescape_uentities, text)
618 return text 663 return text
620 665
621 def convertLineEnds(text, eol): 666 def convertLineEnds(text, eol):
622 """ 667 """
623 Function to convert the end of line characters. 668 Function to convert the end of line characters.
624 669
625 @param text text to be converted (string) 670 @param text text to be converted
626 @param eol new eol setting (string) 671 @type str
627 @return text with converted eols (string) 672 @param eol new eol setting
673 @type str
674 @return text with converted eols
675 @rtype str
628 """ 676 """
629 if eol == "\r\n": 677 if eol == "\r\n":
630 regexp = re.compile(r"""(\r(?!\n)|(?<!\r)\n)""") 678 regexp = re.compile(r"""(\r(?!\n)|(?<!\r)\n)""")
631 return regexp.sub("\r\n", text) 679 return regexp.sub("\r\n", text)
632 elif eol == "\n": 680 elif eol == "\n":
641 689
642 def linesep(): 690 def linesep():
643 """ 691 """
644 Function to return the line separator used by the editor. 692 Function to return the line separator used by the editor.
645 693
646 @return line separator used by the editor (string) 694 @return line separator used by the editor
695 @rtype str
647 """ 696 """
648 eolMode = Preferences.getEditor("EOLMode") 697 eolMode = Preferences.getEditor("EOLMode")
649 if eolMode == QsciScintilla.EolMode.EolUnix: 698 if eolMode == QsciScintilla.EolMode.EolUnix:
650 return "\n" 699 return "\n"
651 elif eolMode == QsciScintilla.EolMode.EolMac: 700 elif eolMode == QsciScintilla.EolMode.EolMac:
664 is assumed to be a string. If a key does not contain a '=' 713 is assumed to be a string. If a key does not contain a '='
665 character, it is assumed to be a boolean flag. Flags are expected 714 character, it is assumed to be a boolean flag. Flags are expected
666 at the very end of a file. The search is ended, if a line without 715 at the very end of a file. The search is ended, if a line without
667 the 'eflag:' marker is found. 716 the 'eflag:' marker is found.
668 717
669 @param text text to be scanned (string) 718 @param text text to be scanned
719 @type str
670 @return dictionary of string, boolean, complex, float and int 720 @return dictionary of string, boolean, complex, float and int
721 @rtype dict
671 """ 722 """
672 flags = {} 723 flags = {}
673 lines = text.rstrip().splitlines() if isinstance(text, str) else text 724 lines = text.rstrip().splitlines() if isinstance(text, str) else text
674 for line in reversed(lines): 725 for line in reversed(lines):
675 try: 726 try:
711 762
712 def extractFlagsFromFile(filename): 763 def extractFlagsFromFile(filename):
713 """ 764 """
714 Function to extract eric specific flags out of the given file. 765 Function to extract eric specific flags out of the given file.
715 766
716 @param filename name of the file to be scanned (string) 767 @param filename name of the file to be scanned
768 @type str
717 @return dictionary of string, boolean, complex, float and int 769 @return dictionary of string, boolean, complex, float and int
770 @rtype dict
718 """ 771 """
719 try: 772 try:
720 source, encoding = readEncodedFile(filename) 773 source, encoding = readEncodedFile(filename)
721 except (OSError, UnicodeError): 774 except (OSError, UnicodeError):
722 return {} 775 return {}
727 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False): 780 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False):
728 """ 781 """
729 Function to extract flags starting and ending with '__' from a line 782 Function to extract flags starting and ending with '__' from a line
730 comment. 783 comment.
731 784
732 @param line line to extract flags from (string) 785 @param line line to extract flags from
733 @param startComment string identifying the start of the comment (string) 786 @type str
734 @param endComment string identifying the end of a comment (string) 787 @param startComment string identifying the start of the comment
735 @param flagsLine flag indicating to check for a flags only line (bool) 788 @type str
736 @return list containing the extracted flags (list of strings) 789 @param endComment string identifying the end of a comment
790 @type str
791 @param flagsLine flag indicating to check for a flags only line
792 @type bool
793 @return list containing the extracted flags
794 @rtype list of str
737 """ 795 """
738 flags = [] 796 flags = []
739 797
740 if not flagsLine or (flagsLine and line.strip().startswith(startComment)): 798 if not flagsLine or (flagsLine and line.strip().startswith(startComment)):
741 pos = line.rfind(startComment) 799 pos = line.rfind(startComment)
985 1043
986 def getPercentReplacementHelp(): 1044 def getPercentReplacementHelp():
987 """ 1045 """
988 Function to get the help text for the supported %-codes. 1046 Function to get the help text for the supported %-codes.
989 1047
990 @returns help text (string) 1048 @return help text
1049 @rtype str
991 """ 1050 """
992 return QCoreApplication.translate( 1051 return QCoreApplication.translate(
993 "Utilities", 1052 "Utilities",
994 """<p>You may use %-codes as placeholders in the string.""" 1053 """<p>You may use %-codes as placeholders in the string."""
995 """ Supported codes are:""" 1054 """ Supported codes are:"""
1025 match = rx.search(txt) 1084 match = rx.search(txt)
1026 if match is None: 1085 if match is None:
1027 return -1 1086 return -1
1028 else: 1087 else:
1029 return match.start() 1088 return match.start()
1089
1090
1091 def unslash(txt):
1092 """
1093 Function to convert a string containing escape codes to an escaped string.
1094
1095 @param txt string to be converted
1096 @type str
1097 @return converted string containing escape codes
1098 @rtype str
1099 """
1100 s = []
1101 index = 0
1102 while index < len(txt):
1103 c = txt[index]
1104 if c == "\\" and index + 1 < len(txt):
1105 index += 1
1106 c = txt[index]
1107 if c == "a":
1108 o = "\a"
1109 elif c == "b":
1110 o = "\b"
1111 elif c == "f":
1112 o = "\f"
1113 elif c == "n":
1114 o = "\n"
1115 elif c == "r":
1116 o = "\r"
1117 elif c == "t":
1118 o = "\t"
1119 elif c == "v":
1120 o = "\v"
1121 elif c in "01234567":
1122 # octal
1123 oc = c
1124 if index + 1 < len(txt) and txt[index + 1] in "01234567":
1125 index += 1
1126 oc += txt[index]
1127 if index + 1 < len(txt) and txt[index + 1] in "01234567":
1128 index += 1
1129 oc += txt[index]
1130 o = chr(int(oc, base=8))
1131 elif c.lower() == "x":
1132 val = 0
1133 if index + 1 < len(txt) and txt[index + 1] in "0123456789abcdefABCDEF":
1134 index += 1
1135 hx = txt[index]
1136 if (
1137 index + 1 < len(txt)
1138 and txt[index + 1] in "0123456789abcdefABCDEF"
1139 ):
1140 index += 1
1141 hx += txt[index]
1142 val = int(hx, base=16)
1143 o = chr(val)
1144 else:
1145 o = c
1146 else:
1147 o = c
1148
1149 s.append(o)
1150 index += 1
1151
1152 return "".join(s)
1153
1154
1155 _slashmap = {i: hex(i).replace("0x", "\\x") for i in range(7)}
1156 _slashmap.update(
1157 {
1158 7: "\\a",
1159 8: "\\b",
1160 9: "\\t",
1161 10: "\\n",
1162 11: "\\v",
1163 12: "\\f",
1164 13: "\\r",
1165 }
1166 )
1167 _slashmap.update({i: hex(i).replace("0x", "\\x") for i in range(14, 32)})
1168 _slashmap.update({i: hex(i).replace("0x", "\\x") for i in range(127, 160)})
1169
1170
1171 def slash(txt):
1172 """
1173 Function to convert an escaped string to a string containing escape codes.
1174
1175 Note: This is the reverse of 'unslash()'.
1176
1177 @param txt string to be converted
1178 @type str
1179 @return converted string containing escaped escape codes
1180 @rtype str
1181 """
1182 return txt.translate(_slashmap)
1030 1183
1031 1184
1032 ############################################################################### 1185 ###############################################################################
1033 ## Other utility functions below 1186 ## Other utility functions below
1034 ############################################################################### 1187 ###############################################################################
1116 versions = {} 1269 versions = {}
1117 for pinfo in pm.getPluginInfos(): 1270 for pinfo in pm.getPluginInfos():
1118 versions[pinfo["module_name"]] = pinfo["version"] 1271 versions[pinfo["module_name"]] = pinfo["version"]
1119 1272
1120 info.append("Plugins Version Numbers:") 1273 info.append("Plugins Version Numbers:")
1121 for pluginModuleName in sorted(versions.keys()): 1274 for pluginModuleName in sorted(versions):
1122 info.append( 1275 info.append(
1123 " {0} {1}".format(pluginModuleName, versions[pluginModuleName]) 1276 " {0} {1}".format(pluginModuleName, versions[pluginModuleName])
1124 ) 1277 )
1125 1278
1126 return linesep.join(info) 1279 return linesep.join(info)

eric ide

mercurial