forked from robotframework/robotframework
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsplitter.py
More file actions
188 lines (162 loc) · 6.34 KB
/
Copy pathsplitter.py
File metadata and controls
188 lines (162 loc) · 6.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# 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.
from robot.utils import is_string, py2to3
class VariableSplitter(object):
def __init__(self, string, identifiers='$@%&*'):
self.identifier = None
self.base = None
self.items = []
self.start = -1
self.end = -1
self._identifiers = identifiers
self._may_have_internal_variables = False
if not is_string(string):
self._max_end = -1
return
self._max_end = len(string)
if self._split(string):
self._finalize()
def get_replaced_variable(self, replacer):
if self._may_have_internal_variables:
base = replacer.replace_string(self.base)
else:
base = self.base
# This omits possible variable items.
return '%s{%s}' % (self.identifier, base)
def is_variable(self):
return bool(self.identifier and self.base and
self.start == 0 and self.end == self._max_end)
def is_list_variable(self):
return bool(self.identifier == '@' and self.base and
self.start == 0 and self.end == self._max_end and
not self.items)
def is_dict_variable(self):
return bool(self.identifier == '&' and self.base and
self.start == 0 and self.end == self._max_end and
not self.items)
def _finalize(self):
self.identifier = self._variable_chars[0]
self.base = ''.join(self._variable_chars[2:-1])
self.end = self.start + len(self._variable_chars)
if self.items:
self.end += len(''.join(self.items)) + 2 * len(self.items)
def _split(self, string):
start_index, max_index = self._find_variable(string)
if start_index == -1:
return False
self.start = start_index
self._open_curly = 1
self._state = self._variable_state
self._variable_chars = [string[start_index], '{']
self._item_chars = []
self._string = string
start_index += 2
for index, char in enumerate(string[start_index:], start=start_index):
try:
self._state(char, index)
except StopIteration:
break
if index == max_index and not self._scanning_item():
break
return True
def _scanning_item(self):
return self._state in (self._waiting_item_state, self._item_state)
def _find_variable(self, string):
max_end_index = string.rfind('}')
if max_end_index == -1:
return -1, -1
if self._is_escaped(string, max_end_index):
return self._find_variable(string[:max_end_index])
start_index = self._find_start_index(string, 1, max_end_index)
if start_index == -1:
return -1, -1
return start_index, max_end_index
def _find_start_index(self, string, start, end):
while True:
index = string.find('{', start, end) - 1
if index < 0:
return -1
if self._start_index_is_ok(string, index):
return index
start = index + 2
def _start_index_is_ok(self, string, index):
return (string[index] in self._identifiers
and not self._is_escaped(string, index))
def _is_escaped(self, string, index):
escaped = False
while index > 0 and string[index-1] == '\\':
index -= 1
escaped = not escaped
return escaped
def _variable_state(self, char, index):
self._variable_chars.append(char)
if char == '}' and not self._is_escaped(self._string, index):
self._open_curly -= 1
if self._open_curly == 0:
if not self._can_have_item():
raise StopIteration
self._state = self._waiting_item_state
elif char in self._identifiers:
self._state = self._internal_variable_start_state
def _can_have_item(self):
return self._variable_chars[0] in '$@&'
def _internal_variable_start_state(self, char, index):
self._state = self._variable_state
if char == '{':
self._variable_chars.append(char)
self._open_curly += 1
self._may_have_internal_variables = True
else:
self._variable_state(char, index)
def _waiting_item_state(self, char, index):
if char != '[':
raise StopIteration
self._state = self._item_state
def _item_state(self, char, index):
if char != ']':
self._item_chars.append(char)
return
self.items.append(''.join(self._item_chars))
self._item_chars = []
# Don't support nested item access with olf @ and & syntax.
# In RF 3.2 old syntax is to be deprecated and in RF 3.3 it
# will be reassigned to mean using variable in list/dict context.
if self._variable_chars[0] in '@&':
raise StopIteration
self._state = self._waiting_item_state
@py2to3
class VariableIterator(object):
def __init__(self, string, identifiers='$@%&*'):
self._string = string
self._identifiers = identifiers
def __iter__(self):
string = self._string
while True:
var = VariableSplitter(string, self._identifiers)
if var.identifier is None:
break
before = string[:var.start]
variable = '%s{%s}' % (var.identifier, var.base)
string = string[var.end:]
yield before, variable, string
def __len__(self):
return sum(1 for _ in self)
def __nonzero__(self):
try:
next(iter(self))
except StopIteration:
return False
else:
return True