3 """Functions for processing text
5 As defined in :RFC:`5545`, section 3.3.11 (Text).
8 import logging as _logging
12 _LOG = _logging.getLogger(__name__)
23 _UNESCAPE_REGEXP = None
26 def _backslash_escape(text):
27 r"""Escape backslashes, but nothing else
29 This is used in ``_setup_escapes`` to build regular
32 >>> _backslash_escape('\\')
34 >>> _backslash_escape('stuff')
45 global _UNESCAPE_REGEXP
47 for key,values in _ESCAPES.items():
50 raise NotImplementedError(
51 '{!r} escape value too long ({})'.format(
54 raise NotImplementedError(
55 '{!r} escape does not begin with a backslash'.format(
57 _UNESCAPES[value] = key
58 escape_regexp = '({})'.format('|'.join(
59 _backslash_escape(char) for char in _ESCAPES.keys()))
60 _LOG.debug('text-escape regexp: {!r}'.format(escape_regexp))
61 _ESCAPE_REGEXP = _re.compile(escape_regexp)
62 unescape_regexp = r'(\\({}))'.format('|'.join(
63 _backslash_escape(escape[1]) for escape in _UNESCAPES.keys()))
64 _LOG.debug('text-unescape regexp: {!r}'.format(unescape_regexp))
65 _UNESCAPE_REGEXP = _re.compile(unescape_regexp)
69 def _escape_replacer(match):
70 return _ESCAPES[match.group(1)][0]
73 def _unescape_replacer(match):
74 return _UNESCAPES[match.group(1)][0]
78 r"""Convert a Python string to :RFC:`5545`-compliant text
80 Conforming to section 3.3.11 (text)
82 >>> print(escape(text='Hello!\nLook: newlines!'))
83 Hello!\nLook: newlines!
84 >>> print(escape(text='Single backslashes \\ may be tricky\n'))
85 Single backslashes \\ may be tricky\n
87 return _ESCAPE_REGEXP.subn(
88 repl=_escape_replacer, string=text)[0]
92 r"""Convert :RFC:`5545`-compliant text to a Python string
94 Conforming to section 3.3.11 (text)
97 ... 'Hello!\nLook: newlines!',
99 ... escaped = escape(text=text)
100 ... unescaped = unescape(text=escaped)
101 ... if unescaped != text:
102 ... raise ValueError(unescaped)
104 return _UNESCAPE_REGEXP.subn(
105 repl=_unescape_replacer, string=text)[0]