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


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


[docs] def escape(item): if not isinstance(item, str): 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 glob_escape(item): # Python 3.4+ has `glob.escape()` but it has special handling for drives # that we don't want. for char in '[*?': if char in item: item = item.replace(char, '[%s]' % char) return item
[docs] class Unescaper: _escape_sequences = re.compile(r''' (\\+) # escapes (n|r|t # n, r, or t |x[0-9a-fA-F]{2} # x+HH |u[0-9a-fA-F]{4} # u+HHHH |U[0-9a-fA-F]{8} # U+HHHHHHHH )? # optionally ''', re.VERBOSE) def __init__(self): self._escape_handlers = { '': lambda value: value, 'n': lambda value: '\n', 'r': lambda value: '\r', 't': lambda value: '\t', 'x': self._hex_to_unichr, 'u': self._hex_to_unichr, 'U': self._hex_to_unichr } def _hex_to_unichr(self, value): ordinal = int(value, 16) # No Unicode code points above 0x10FFFF if ordinal > 0x10FFFF: return 'U' + value # `chr` only supports ordinals up to 0xFFFF on narrow Python builds. # This may not be relevant anymore. if ordinal > 0xFFFF: return eval(r"'\U%08x'" % ordinal) return chr(ordinal)
[docs] def unescape(self, item): if not isinstance(item, str) or '\\' not in item: return item return self._escape_sequences.sub(self._handle_escapes, item)
def _handle_escapes(self, match): escapes, text = match.groups() half, is_escaped = divmod(len(escapes), 2) escapes = escapes[:half] text = text or '' if is_escaped: marker, value = text[:1], text[1:] text = self._escape_handlers[marker](value) return escapes + text
unescape = Unescaper().unescape
[docs] def split_from_equals(value): from robot.variables import VariableMatches if not isinstance(value, str) or '=' not in value: return value, None matches = VariableMatches(value, ignore_errors=True) if not matches and '\\' not in value: return tuple(value.split('=', 1)) try: index = _find_split_index(value, matches) except ValueError: return value, None return value[:index], value[index + 1:]
def _find_split_index(string, matches): remaining = string relative_index = 0 for match in matches: try: return _find_split_index_from_part(match.before) + relative_index except ValueError: remaining = match.after relative_index += match.end return _find_split_index_from_part(remaining) + relative_index def _find_split_index_from_part(string): index = 0 while '=' in string[index:]: index += string[index:].index('=') if _not_escaping(string[:index]): return index index += 1 raise ValueError def _not_escaping(name): backslashes = len(name) - len(name.rstrip('\\')) return backslashes % 2 == 0