Source code for robot.utils.escaping

#  Copyright 2008-2015 Nokia Networks
#  Copyright 2016-     Robot Framework Foundation
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import re

from .platform import PY3
from .robottypes import is_string


if PY3:
    unichr = chr

_CONTROL_WORDS = frozenset(('ELSE', 'ELSE IF', 'AND', 'WITH NAME'))
_SEQUENCES_TO_BE_ESCAPED = ('\\', '${', '@{', '%{', '&{', '*{', '=')


[docs]def escape(item): if not is_string(item): return item if item in _CONTROL_WORDS: return '\\' + item for seq in _SEQUENCES_TO_BE_ESCAPED: if seq in item: item = item.replace(seq, '\\' + seq) return item
[docs]def unescape(item): if not (is_string(item) and '\\' in item): return item return Unescaper().unescape(item)
[docs]class Unescaper(object):
[docs] def unescape(self, string): return ''.join(self._yield_unescaped(string))
def _yield_unescaped(self, string): while '\\' in string: finder = EscapeFinder(string) yield finder.before + finder.backslashes if finder.escaped and finder.text: yield self._unescape(finder.text) else: yield finder.text string = finder.after yield string def _unescape(self, text): try: escape = str(text[0]) except UnicodeError: return text try: unescaper = getattr(self, '_unescaper_for_' + escape) except AttributeError: return text else: return unescaper(text[1:]) def _unescaper_for_n(self, text): # TODO: Deprecate ignoring space after newline in RF 3.2. if text.startswith(' '): text = text[1:] return '\n' + text def _unescaper_for_r(self, text): return '\r' + text def _unescaper_for_t(self, text): return '\t' + text def _unescaper_for_x(self, text): return self._unescape_character(text, 2, 'x') def _unescaper_for_u(self, text): return self._unescape_character(text, 4, 'u') def _unescaper_for_U(self, text): return self._unescape_character(text, 8, 'U') def _unescape_character(self, text, length, escape): try: char = self._get_character(text[:length], length) except ValueError: return escape + text else: return char + text[length:] def _get_character(self, text, length): if len(text) < length or not text.isalnum(): raise ValueError ordinal = int(text, 16) # No Unicode code points above 0x10FFFF if ordinal > 0x10FFFF: raise ValueError # unichr only supports ordinals up to 0xFFFF with narrow Python builds if ordinal > 0xFFFF: return eval("u'\\U%08x'" % ordinal) return unichr(ordinal)
[docs]class EscapeFinder(object): _escaped = re.compile(r'(\\+)([^\\]*)') def __init__(self, string): res = self._escaped.search(string) self.before = string[:res.start()] escape_chars = len(res.group(1)) self.backslashes = '\\' * (escape_chars // 2) self.escaped = bool(escape_chars % 2) self.text = res.group(2) self.after = string[res.end():]
[docs]def split_from_equals(string): if not is_string(string) or '=' not in string: return string, None index = _get_split_index(string) if index == -1: return string, None return string[:index], string[index+1:]
def _get_split_index(string): index = 0 while '=' in string[index:]: index += string[index:].index('=') if _not_escaping(string[:index]): return index index += 1 return -1 def _not_escaping(name): backslashes = len(name) - len(name.rstrip('\\')) return backslashes % 2 == 0