| 1 | n/a | """ |
|---|
| 2 | n/a | General functions for HTML manipulation. |
|---|
| 3 | n/a | """ |
|---|
| 4 | n/a | |
|---|
| 5 | n/a | import re as _re |
|---|
| 6 | n/a | from html.entities import html5 as _html5 |
|---|
| 7 | n/a | |
|---|
| 8 | n/a | |
|---|
| 9 | n/a | __all__ = ['escape', 'unescape'] |
|---|
| 10 | n/a | |
|---|
| 11 | n/a | |
|---|
| 12 | n/a | def escape(s, quote=True): |
|---|
| 13 | n/a | """ |
|---|
| 14 | n/a | Replace special characters "&", "<" and ">" to HTML-safe sequences. |
|---|
| 15 | n/a | If the optional flag quote is true (the default), the quotation mark |
|---|
| 16 | n/a | characters, both double quote (") and single quote (') characters are also |
|---|
| 17 | n/a | translated. |
|---|
| 18 | n/a | """ |
|---|
| 19 | n/a | s = s.replace("&", "&") # Must be done first! |
|---|
| 20 | n/a | s = s.replace("<", "<") |
|---|
| 21 | n/a | s = s.replace(">", ">") |
|---|
| 22 | n/a | if quote: |
|---|
| 23 | n/a | s = s.replace('"', """) |
|---|
| 24 | n/a | s = s.replace('\'', "'") |
|---|
| 25 | n/a | return s |
|---|
| 26 | n/a | |
|---|
| 27 | n/a | |
|---|
| 28 | n/a | # see http://www.w3.org/TR/html5/syntax.html#tokenizing-character-references |
|---|
| 29 | n/a | |
|---|
| 30 | n/a | _invalid_charrefs = { |
|---|
| 31 | n/a | 0x00: '\ufffd', # REPLACEMENT CHARACTER |
|---|
| 32 | n/a | 0x0d: '\r', # CARRIAGE RETURN |
|---|
| 33 | n/a | 0x80: '\u20ac', # EURO SIGN |
|---|
| 34 | n/a | 0x81: '\x81', # <control> |
|---|
| 35 | n/a | 0x82: '\u201a', # SINGLE LOW-9 QUOTATION MARK |
|---|
| 36 | n/a | 0x83: '\u0192', # LATIN SMALL LETTER F WITH HOOK |
|---|
| 37 | n/a | 0x84: '\u201e', # DOUBLE LOW-9 QUOTATION MARK |
|---|
| 38 | n/a | 0x85: '\u2026', # HORIZONTAL ELLIPSIS |
|---|
| 39 | n/a | 0x86: '\u2020', # DAGGER |
|---|
| 40 | n/a | 0x87: '\u2021', # DOUBLE DAGGER |
|---|
| 41 | n/a | 0x88: '\u02c6', # MODIFIER LETTER CIRCUMFLEX ACCENT |
|---|
| 42 | n/a | 0x89: '\u2030', # PER MILLE SIGN |
|---|
| 43 | n/a | 0x8a: '\u0160', # LATIN CAPITAL LETTER S WITH CARON |
|---|
| 44 | n/a | 0x8b: '\u2039', # SINGLE LEFT-POINTING ANGLE QUOTATION MARK |
|---|
| 45 | n/a | 0x8c: '\u0152', # LATIN CAPITAL LIGATURE OE |
|---|
| 46 | n/a | 0x8d: '\x8d', # <control> |
|---|
| 47 | n/a | 0x8e: '\u017d', # LATIN CAPITAL LETTER Z WITH CARON |
|---|
| 48 | n/a | 0x8f: '\x8f', # <control> |
|---|
| 49 | n/a | 0x90: '\x90', # <control> |
|---|
| 50 | n/a | 0x91: '\u2018', # LEFT SINGLE QUOTATION MARK |
|---|
| 51 | n/a | 0x92: '\u2019', # RIGHT SINGLE QUOTATION MARK |
|---|
| 52 | n/a | 0x93: '\u201c', # LEFT DOUBLE QUOTATION MARK |
|---|
| 53 | n/a | 0x94: '\u201d', # RIGHT DOUBLE QUOTATION MARK |
|---|
| 54 | n/a | 0x95: '\u2022', # BULLET |
|---|
| 55 | n/a | 0x96: '\u2013', # EN DASH |
|---|
| 56 | n/a | 0x97: '\u2014', # EM DASH |
|---|
| 57 | n/a | 0x98: '\u02dc', # SMALL TILDE |
|---|
| 58 | n/a | 0x99: '\u2122', # TRADE MARK SIGN |
|---|
| 59 | n/a | 0x9a: '\u0161', # LATIN SMALL LETTER S WITH CARON |
|---|
| 60 | n/a | 0x9b: '\u203a', # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK |
|---|
| 61 | n/a | 0x9c: '\u0153', # LATIN SMALL LIGATURE OE |
|---|
| 62 | n/a | 0x9d: '\x9d', # <control> |
|---|
| 63 | n/a | 0x9e: '\u017e', # LATIN SMALL LETTER Z WITH CARON |
|---|
| 64 | n/a | 0x9f: '\u0178', # LATIN CAPITAL LETTER Y WITH DIAERESIS |
|---|
| 65 | n/a | } |
|---|
| 66 | n/a | |
|---|
| 67 | n/a | _invalid_codepoints = { |
|---|
| 68 | n/a | # 0x0001 to 0x0008 |
|---|
| 69 | n/a | 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, |
|---|
| 70 | n/a | # 0x000E to 0x001F |
|---|
| 71 | n/a | 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, |
|---|
| 72 | n/a | 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
|---|
| 73 | n/a | # 0x007F to 0x009F |
|---|
| 74 | n/a | 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, |
|---|
| 75 | n/a | 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, |
|---|
| 76 | n/a | 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, |
|---|
| 77 | n/a | # 0xFDD0 to 0xFDEF |
|---|
| 78 | n/a | 0xfdd0, 0xfdd1, 0xfdd2, 0xfdd3, 0xfdd4, 0xfdd5, 0xfdd6, 0xfdd7, 0xfdd8, |
|---|
| 79 | n/a | 0xfdd9, 0xfdda, 0xfddb, 0xfddc, 0xfddd, 0xfdde, 0xfddf, 0xfde0, 0xfde1, |
|---|
| 80 | n/a | 0xfde2, 0xfde3, 0xfde4, 0xfde5, 0xfde6, 0xfde7, 0xfde8, 0xfde9, 0xfdea, |
|---|
| 81 | n/a | 0xfdeb, 0xfdec, 0xfded, 0xfdee, 0xfdef, |
|---|
| 82 | n/a | # others |
|---|
| 83 | n/a | 0xb, 0xfffe, 0xffff, 0x1fffe, 0x1ffff, 0x2fffe, 0x2ffff, 0x3fffe, 0x3ffff, |
|---|
| 84 | n/a | 0x4fffe, 0x4ffff, 0x5fffe, 0x5ffff, 0x6fffe, 0x6ffff, 0x7fffe, 0x7ffff, |
|---|
| 85 | n/a | 0x8fffe, 0x8ffff, 0x9fffe, 0x9ffff, 0xafffe, 0xaffff, 0xbfffe, 0xbffff, |
|---|
| 86 | n/a | 0xcfffe, 0xcffff, 0xdfffe, 0xdffff, 0xefffe, 0xeffff, 0xffffe, 0xfffff, |
|---|
| 87 | n/a | 0x10fffe, 0x10ffff |
|---|
| 88 | n/a | } |
|---|
| 89 | n/a | |
|---|
| 90 | n/a | |
|---|
| 91 | n/a | def _replace_charref(s): |
|---|
| 92 | n/a | s = s.group(1) |
|---|
| 93 | n/a | if s[0] == '#': |
|---|
| 94 | n/a | # numeric charref |
|---|
| 95 | n/a | if s[1] in 'xX': |
|---|
| 96 | n/a | num = int(s[2:].rstrip(';'), 16) |
|---|
| 97 | n/a | else: |
|---|
| 98 | n/a | num = int(s[1:].rstrip(';')) |
|---|
| 99 | n/a | if num in _invalid_charrefs: |
|---|
| 100 | n/a | return _invalid_charrefs[num] |
|---|
| 101 | n/a | if 0xD800 <= num <= 0xDFFF or num > 0x10FFFF: |
|---|
| 102 | n/a | return '\uFFFD' |
|---|
| 103 | n/a | if num in _invalid_codepoints: |
|---|
| 104 | n/a | return '' |
|---|
| 105 | n/a | return chr(num) |
|---|
| 106 | n/a | else: |
|---|
| 107 | n/a | # named charref |
|---|
| 108 | n/a | if s in _html5: |
|---|
| 109 | n/a | return _html5[s] |
|---|
| 110 | n/a | # find the longest matching name (as defined by the standard) |
|---|
| 111 | n/a | for x in range(len(s)-1, 1, -1): |
|---|
| 112 | n/a | if s[:x] in _html5: |
|---|
| 113 | n/a | return _html5[s[:x]] + s[x:] |
|---|
| 114 | n/a | else: |
|---|
| 115 | n/a | return '&' + s |
|---|
| 116 | n/a | |
|---|
| 117 | n/a | |
|---|
| 118 | n/a | _charref = _re.compile(r'&(#[0-9]+;?' |
|---|
| 119 | n/a | r'|#[xX][0-9a-fA-F]+;?' |
|---|
| 120 | n/a | r'|[^\t\n\f <&#;]{1,32};?)') |
|---|
| 121 | n/a | |
|---|
| 122 | n/a | def unescape(s): |
|---|
| 123 | n/a | """ |
|---|
| 124 | n/a | Convert all named and numeric character references (e.g. >, >, |
|---|
| 125 | n/a | &x3e;) in the string s to the corresponding unicode characters. |
|---|
| 126 | n/a | This function uses the rules defined by the HTML 5 standard |
|---|
| 127 | n/a | for both valid and invalid character references, and the list of |
|---|
| 128 | n/a | HTML 5 named character references defined in html.entities.html5. |
|---|
| 129 | n/a | """ |
|---|
| 130 | n/a | if '&' not in s: |
|---|
| 131 | n/a | return s |
|---|
| 132 | n/a | return _charref.sub(_replace_charref, s) |
|---|