Robot Framework
escaping.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 import re
17 
18 from .robottypes import is_string
19 
20 
21 
24 _CONTROL_WORDS = frozenset(('ELSE', 'ELSE IF', 'AND', 'WITH NAME', 'AS'))
25 
28 _SEQUENCES_TO_BE_ESCAPED = ('\\', '${', '@{', '%{', '&{', '*{', '=')
29 
30 
31 def escape(item):
32  if not is_string(item):
33  return item
34  if item in _CONTROL_WORDS:
35  return '\\' + item
36  for seq in _SEQUENCES_TO_BE_ESCAPED:
37  if seq in item:
38  item = item.replace(seq, '\\' + seq)
39  return item
40 
41 
42 def glob_escape(item):
43  # Python 3.4+ has `glob.escape()` but it has special handling for drives
44  # that we don't want.
45  for char in '[*?':
46  if char in item:
47  item = item.replace(char, '[%s]' % char)
48  return item
49 
50 
51 class Unescaper:
52 
55  _escape_sequences = re.compile(r'''
56  (\\+) # escapes
57  (n|r|t # n, r, or t
58  |x[0-9a-fA-F]{2} # x+HH
59  |u[0-9a-fA-F]{4} # u+HHHH
60  |U[0-9a-fA-F]{8} # U+HHHHHHHH
61  )? # optionally
62  ''', re.VERBOSE)
63 
64  def __init__(self):
65  self._escape_handlers_escape_handlers = {
66  '': lambda value: value,
67  'n': lambda value: '\n',
68  'r': lambda value: '\r',
69  't': lambda value: '\t',
70  'x': self._hex_to_unichr_hex_to_unichr,
71  'u': self._hex_to_unichr_hex_to_unichr,
72  'U': self._hex_to_unichr_hex_to_unichr
73  }
74 
75  def _hex_to_unichr(self, value):
76  ordinal = int(value, 16)
77  # No Unicode code points above 0x10FFFF
78  if ordinal > 0x10FFFF:
79  return 'U' + value
80  # `chr` only supports ordinals up to 0xFFFF on narrow Python builds.
81  # This may not be relevant anymore.
82  if ordinal > 0xFFFF:
83  return eval(r"'\U%08x'" % ordinal)
84  return chr(ordinal)
85 
86  def unescape(self, item):
87  if not (is_string(item) and '\\' in item):
88  return item
89  return self._escape_sequences_escape_sequences.sub(self._handle_escapes_handle_escapes, item)
90 
91  def _handle_escapes(self, match):
92  escapes, text = match.groups()
93  half, is_escaped = divmod(len(escapes), 2)
94  escapes = escapes[:half]
95  text = text or ''
96  if is_escaped:
97  marker, value = text[:1], text[1:]
98  text = self._escape_handlers_escape_handlers[marker](value)
99  return escapes + text
100 
101 
102 unescape = Unescaper().unescape
103 
104 
105 def split_from_equals(string):
106  from robot.variables import VariableIterator
107  if not is_string(string) or '=' not in string:
108  return string, None
109  variables = VariableIterator(string, ignore_errors=True)
110  if not variables and '\\' not in string:
111  return tuple(string.split('=', 1))
112  try:
113  index = _find_split_index(string, variables)
114  except ValueError:
115  return string, None
116  return string[:index], string[index+1:]
117 
118 
119 def _find_split_index(string, variables):
120  relative_index = 0
121  for before, match, string in variables:
122  try:
123  return _find_split_index_from_part(before) + relative_index
124  except ValueError:
125  relative_index += len(before) + len(match)
126  return _find_split_index_from_part(string) + relative_index
127 
128 
130  index = 0
131  while '=' in string[index:]:
132  index += string[index:].index('=')
133  if _not_escaping(string[:index]):
134  return index
135  index += 1
136  raise ValueError
137 
138 
139 def _not_escaping(name):
140  backslashes = len(name) - len(name.rstrip('\\'))
141  return backslashes % 2 == 0
def _hex_to_unichr(self, value)
Definition: escaping.py:75
def _handle_escapes(self, match)
Definition: escaping.py:91
def unescape(self, item)
Definition: escaping.py:86
def escape(item)
Definition: escaping.py:31
def split_from_equals(string)
Definition: escaping.py:105
def glob_escape(item)
Definition: escaping.py:42
def _find_split_index_from_part(string)
Definition: escaping.py:129
def _find_split_index(string, variables)
Definition: escaping.py:119
def _not_escaping(name)
Definition: escaping.py:139