8000 Parsing: Smallish cleanup to ast · pythonthings/robotframework@a28b0b5 · GitHub
[go: up one dir, main page]

Skip to content

Commit a28b0b5

Browse files
committed
Parsing: Smallish cleanup to ast
Fixes unit tests expecting model to have tuples, not lists.
1 parent d25a49b commit a28b0b5

File tree

4 files changed

+73
-68
lines changed

4 files changed

+73
-68
lines changed

src/robot/parsing/nodes.py

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,35 @@
1919
from robot.utils import normalize_whitespace
2020

2121

22-
class Node(AST):
23-
_fields = ()
22+
def join_doc_or_meta(lines):
23+
def lines_with_newlines():
24+
last_index = len(lines) - 1
25+
for index, line in enumerate(lines):
26+
yield line
27+
if index < last_index:
28+
match = re.search(r'(\\+)n?$', line)
29+
escaped_or_has_newline = match and len(match.group(1)) % 2 == 1
30+
if not escaped_or_has_newline:
31+
yield '\n'
32+
return ''.join(lines_with_newlines())
2433

25-
def _add_joiners(self, values):
26-
for index, item in enumerate(values):
27-
yield item
28-
if index < len(values) - 1:
29-
yield self._joiner_based_on_eol_escapes(item)
3034

31-
def _joiner_based_on_eol_escapes(self, item):
32-
eol_escapes = re.compile(r'(\\+)n?$')
33-
match = eol_escapes.search(item)
34-
if match and len(match.group(1)) % 2 == 1:
35-
return ''
36-
return '\n'
35+
class Node(AST):
36+
_fields = ()
3737

3838

39-
class Value(Node):
40-
_fields = ('value',)
39+
class MultiValue(Node):
40+
_fields = ('values',)
4141

42-
def __init__(self, value):
43-
self.value = value
42+
def __init__(self, values):
43+
self.values = tuple(values)
4444

4545

4646
class SingleValue(Node):
4747
_fields = ('value',)
4848

49-
def __init__(self, value):
50-
self.value = value[0] if value and value[0].upper() != 'NONE' else None
49+
def __init__(self, values):
50+
self.value = values[0] if values and values[0].upper() != 'NONE' else None
5151

5252

5353
class DataFile(Node):
@@ -76,7 +76,8 @@ class TestCaseSection(Node):
7676

7777
def __init__(self, tests, header):
7878
self.tests = tests
79-
self.header = header[0].strip('*').strip()
79+
section_name = normalize_whitespace(header[0]).strip('* ')
80+
self.tasks = section_name.upper() in ('TASKS', 'TASK')
8081

8182

8283
class KeywordSection(Node):
@@ -90,18 +91,23 @@ class Variable(Node):
9091
_fields = ('name', 'value')
9192

9293
def __init__(self, name, value):
94+
# TODO: Should this be done already by the parser?
95+
# Applies also to 'WITH NAME', 'NONE' and 'TASK(S)' handling
96+
# as well as joining doc/meta lines and tuple() conversion.
97+
if name.endswith('='):
98+
name = name[:-1].rstrip()
9399
self.name = name
94-
self.value = value
100+
self.values = value
95101

96102

97103
class KeywordCall(Node):
98104
# TODO: consider `keyword` -> `name`, as in Fixture
99105
_fields = ('assign', 'keyword', 'args')
100106

101107
def __init__(self, assign, keyword, args=None):
102-
self.assign = assign or ()
108+
self.assign = tuple(assign or ())
103109
self.keyword = keyword
104-
self.args = args or ()
110+
self.args = tuple(args or ())
105111

106112

107113
class ForLoop(Node):
@@ -142,7 +148,7 @@ class ImportSetting(Node):
142148

143149
def __init__(self, name, args):
144150
self.name = name
145-
self.args = args
151+
self.args = tuple(args)
146152

147153

148154
class LibrarySetting(ImportSetting):
@@ -158,32 +164,35 @@ def _split_alias(self, args):
158164
return args, None
159165

160166

161-
class ResourceSetting(ImportSetting): pass
162-
class VariablesSetting(ImportSetting): pass
167+
class ResourceSetting(ImportSetting):
168+
pass
169+
170+
171+
class VariablesSetting(ImportSetting):
172+
pass
163173

164174

165175
class MetadataSetting(Node):
166176
_fields = ('name', 'value')
167177

168-
def __init__(self, name, value):
178+
def __init__(self, name, values):
169179
self.name = name
170-
self.value = ''.join(self._add_joiners(value))
180+
self.value = join_doc_or_meta(values)
171181

172182

173-
class DocumentationSetting(Value):
183+
class DocumentationSetting(SingleValue):
174184

175-
def __init__(self, value):
176-
doc = ''.join(self._add_joiners(value))
177-
Value.__init__(self, doc)
185+
def __init__(self, values):
186+
SingleValue.__init__(self, [join_doc_or_meta(values)])
178187

179188

180189
class Fixture(Node):
181190
_fields = ('name', 'args')
182191

183-
def __init__(self, value):
184-
if value and value[0].upper() != 'NONE':
185-
self.name = value[0]
186-
self.args = tuple(value[1:])
192+
def __init__(self, values):
193+
if values and values[0].upper() != 'NONE':
194+
self.name = values[0]
195+
self.args = tuple(values[1:])
187196
else:
188197
self.name = None
189198
self.args = ()
@@ -203,8 +212,8 @@ class TestTimeoutSetting(SingleValue): pass
203212
class TimeoutSetting(SingleValue): pass
204213

205214

206-
class ForceTagsSetting(Value): pass
207-
class DefaultTagsSetting(Value): pass
208-
class TagsSetting(Value): pass
209-
class ArgumentsSetting(Value): pass
210-
class ReturnSetting(Value): pass
215+
class ForceTagsSetting(MultiValue): pass
216+
class DefaultTagsSetting(MultiValue): pass
217+
class TagsSetting(MultiValue): pass
218+
class ArgumentsSetting(MultiValue): pass
219+
class ReturnSetting(MultiValue): pass

src/robot/running/builder/builders.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ def build_suite(source, datapath=None, parent_defaults=None):
141141
defaults = TestDefaults(parent_defaults)
142142
if datapath:
143143
ast = get_test_case_file_ast(datapath)
144-
#print(ast.dump(ast))
145144
SettingsBuilder(suite, defaults).visit(ast)
146145
SuiteBuilder(suite, defaults).visit(ast)
147146
if not suite.tests:
@@ -153,10 +152,9 @@ def build_suite(source, datapath=None, parent_defaults=None):
153152
def _get_rpa_mode(data):
154153
if not data:
155154
return None
156-
modes = [s.header.lower() in ('task', 'tasks')
157-
for s in data.sections if isinstance(s, TestCaseSection)]
158-
if all(modes) or not any(modes):
159-
return modes[0] if modes else None
155+
tasks = [s.tasks for s in data.sections if isinstance(s, TestCaseSection)]
156+
if all(tasks) or not any(tasks):
157+
return tasks[0] if tasks else None
160158
raise DataError('One file cannot have both tests and tasks.')
161159

162160

src/robot/running/builder/testsettings.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def __init__(self, parent_defaults):
2727

2828
# TODO change to property
2929
def get_force_tags(self):
30-
force_tags = self.force_tags or []
31-
return force_tags + ((self.parent_defaults and self.parent_defaults.get_force_tags()) or [])
30+
force_tags = self.force_tags or ()
31+
return force_tags + ((self.parent_defaults and self.parent_defaults.get_force_tags()) or ())
3232

3333
def get_setup(self):
3434
return self.setup or (self.parent_defaults and self.parent_defaults.get_setup())
@@ -95,7 +95,7 @@ def tags(self):
9595
if self._tags is not None:
9696
tags = self._tags
9797
else:
98-
tags = self.defaults.default_tags or []
98+
tags = self.defaults.default_tags or ()
9999
return tags + self.defaults.get_force_tags()
100100

101101
@tags.setter

src/robot/running/builder/transformers.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,25 @@ def visit_TestTimeoutSetting(self, node):
5252

5353
def visit_DefaultTagsSetting(self, node):
5454
# TODO: should node be always given to test_defaults?
55-
self.test_defaults.default_tags = node.value
55+
self.test_defaults.default_tags = node.values
5656

5757
def visit_ForceTagsSetting(self, node):
58-
self.test_defaults.force_tags = node.value
58+
self.test_defaults.force_tags = node.values
5959

6060
def visit_TestTemplateSetting(self, node):
6161
self.test_defaults.test_template = node
6262

6363
def visit_ResourceSetting(self, node):
6464
self.suite.resource.imports.create(type='Resource', name=node.name,
65-
args=tuple(node.args))
65+
args=node.args)
6666

6767
def visit_LibrarySetting(self, node):
6868
self.suite.resource.imports.create(type='Library', name=node.name,
6969
args=node.args, alias=node.alias)
7070

7171
def visit_VariablesSetting(self, node):
7272
self.suite.resource.imports.create(type='Variables', name=node.name,
73-
args=tuple(node.args))
73+
args=node.args)
7474

7575
def visit_VariableSection(self, node):
7676
pass
@@ -83,6 +83,7 @@ def visit_KeywordSection(self, node):
8383

8484

8585
class SuiteBuilder(ast.NodeVisitor):
86+
8687
def __init__(self, suite, test_defaults):
8788
self.suite = suite
8889
self.test_defaults = test_defaults
@@ -94,37 +95,32 @@ def visit_Keyword(self, node):
9495
KeywordBuilder(self.suite.resource).visit(node)
9596

9697
def visit_Variable(self, node):
97-
name = node.name
98-
if node.name.endswith('='):
99-
name = name[:-1].rstrip()
100-
self.suite.resource.variables.create(name=name, value=node.value)
98+
self.suite.resource.variables.create(name=node.name, value=node.values)
10199

102100

103101
class ResourceBuilder(ast.NodeVisitor):
102+
104103
def __init__(self, resource):
105104
self.resource = resource
106105

107106
def visit_ResourceSetting(self, node):
108107
self.resource.imports.create(type='Resource', name=node.name,
109-
args=tuple(node.args))
108+
args=node.args)
110109

111110
def visit_LibrarySetting(self, node):
112111
self.resource.imports.create(type='Library', name=node.name,
113-
args=node.args, alias=node.alias)
112+
args=node.args, alias=node.alias)
114113

115114

116115
def visit_VariablesSetting(self, node):
117116
self.resource.imports.create(type='Variables', name=node.name,
118-
args=tuple(node.args))
117+
args=node.args)
119118

120119
def visit_Keyword(self, node):
121120
KeywordBuilder(self.resource).visit(node)
122121

123122
def visit_Variable(self, node):
124-
name = node.name
125-
if node.name.endswith('='):
126-
name = name[:-1].rstrip()
127-
self.resource.variables.create(name=name, value=node.value)
123+
self.resource.variables.create(name=node.name, value=node.values)
128124

129125
def visit_DocumentationSetting(self, node):
130126
self.resource.doc = node.value
@@ -196,7 +192,7 @@ def visit_TimeoutSetting(self, node):
196192
self.settings.timeout = node
197193

198194
def visit_TagsSetting(self, node):
199-
self.settings.tags = node.value
195+
self.settings.tags = node.values
200196

201197
def visit_TemplateSetting(self, node):
202198
self.settings.template = node
@@ -207,6 +203,7 @@ def visit_KeywordCall(self, node):
207203

208204

209205
class KeywordBuilder(ast.NodeVisitor):
206+
210207
def __init__(self, resource):
211208
self.resource = resource
212209
self.kw = None
@@ -223,13 +220,13 @@ def visit_DocumentationSetting(self, node):
223220
self.kw.doc = node.value
224221

225222
def visit_ArgumentsSetting(self, node):
226-
self.kw.args = node.value
223+
self.kw.args = node.values
227224

228225
def visit_TagsSetting(self, node):
229-
self.kw.tags = node.value
226+
self.kw.tags = node.values
230227

231228
def visit_ReturnSetting(self, node):
232-
self.kw.return_ = node.value
229+
self.kw.return_ = node.values
233230

234231
def visit_TimeoutSetting(self, node):
235232
self.kw.timeout = node.value
@@ -248,6 +245,7 @@ def visit_ForLoop(self, node):
248245

249246

250247
class ForLoopBuilder(ast.NodeVisitor):
248+
251249
def __init__(self, for_loop):
252250
self.for_loop = for_loop
253251

0 commit comments

Comments
 (0)
0