1 # Copyright (C) 2013 W. Trevor King <wking@tremily.us>
3 # This file is part of pycalender.
5 # pycalender is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
10 # pycalender is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along with
15 # pycalender. If not, see <http://www.gnu.org/licenses/>.
17 """Functions for processing text
19 As defined in :RFC:`5545`, section 3.3.11 (Text).
22 import logging as _logging
26 _LOG = _logging.getLogger(__name__)
37 _UNESCAPE_REGEXP = None
40 def _backslash_escape(text):
41 r"""Escape backslashes, but nothing else
43 This is used in ``_setup_escapes`` to build regular
46 >>> _backslash_escape('\\')
48 >>> _backslash_escape('stuff')
59 global _UNESCAPE_REGEXP
61 for key,values in _ESCAPES.items():
64 raise NotImplementedError(
65 '{!r} escape value too long ({})'.format(
68 raise NotImplementedError(
69 '{!r} escape does not begin with a backslash'.format(
71 _UNESCAPES[value] = key
72 escape_regexp = '({})'.format('|'.join(
73 _backslash_escape(char) for char in _ESCAPES.keys()))
74 _LOG.debug('text-escape regexp: {!r}'.format(escape_regexp))
75 _ESCAPE_REGEXP = _re.compile(escape_regexp)
76 unescape_regexp = r'(\\({}))'.format('|'.join(
77 _backslash_escape(escape[1]) for escape in _UNESCAPES.keys()))
78 _LOG.debug('text-unescape regexp: {!r}'.format(unescape_regexp))
79 _UNESCAPE_REGEXP = _re.compile(unescape_regexp)
83 def _escape_replacer(match):
84 return _ESCAPES[match.group(1)][0]
87 def _unescape_replacer(match):
88 return _UNESCAPES[match.group(1)][0]
92 r"""Convert a Python string to :RFC:`5545`-compliant text
94 Conforming to section 3.3.11 (text)
96 >>> print(escape(text='Hello!\nLook: newlines!'))
97 Hello!\nLook: newlines!
98 >>> print(escape(text='Single backslashes \\ may be tricky\n'))
99 Single backslashes \\ may be tricky\n
101 return _ESCAPE_REGEXP.subn(
102 repl=_escape_replacer, string=text)[0]
106 r"""Convert :RFC:`5545`-compliant text to a Python string
108 Conforming to section 3.3.11 (text)
111 ... 'Hello!\nLook: newlines!',
113 ... escaped = escape(text=text)
114 ... unescaped = unescape(text=escaped)
115 ... if unescaped != text:
116 ... raise ValueError(unescaped)
118 return _UNESCAPE_REGEXP.subn(
119 repl=_unescape_replacer, string=text)[0]