Skip to content

Commit 9419f61

Browse files
committed
Enhance reporting errors if multiple keywords match.
Earlier conflicts with keywords having embedded args were handled when getting a handler from a particular file. Now all matching handlers are returned and all conflicts are handled in the same place. The main motivation for this is making it possible to select the best match if there are conflicts with embedded args as proposed in #4454. Earlier implementation allowed handling conflicts only within a single file, but now we can handle them across all files.
1 parent 28a50a1 commit 9419f61

17 files changed

+192
-186
lines changed

atest/robot/keywords/embedded_arguments.robot

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@ Non String Variable Is Accepted With Custom Regexp
100100

101101
Regexp Extensions Are Not Supported
102102
Check Test Case ${TEST NAME}
103-
Creating Keyword Failed 1 287
103+
Creating Keyword Failed 1 291
104104
... Regexp extensions like \${x:(?x)re} are not supported
105105
... Regexp extensions are not allowed in embedded arguments.
106106

107107
Invalid Custom Regexp
108108
Check Test Case ${TEST NAME}
109-
Creating Keyword Failed 2 290
109+
Creating Keyword Failed 2 294
110110
... Invalid \${x:(} Regexp
111111
... Compiling embedded arguments regexp failed: *
112112

@@ -143,7 +143,7 @@ Keyword with embedded args cannot be used as "normal" keyword
143143
Check Test Case ${TEST NAME}
144144

145145
Creating keyword with both normal and embedded arguments fails
146-
Creating Keyword Failed 0 234
146+
Creating Keyword Failed 0 238
147147
... Keyword with \${embedded} and normal args is invalid
148148
... Keyword cannot have both normal and embedded arguments.
149149
Check Test Case ${TEST NAME}

atest/testdata/keywords/duplicate_dynamic_keywords.robot

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ Using keyword defined multiple times fails
1111

1212
Keyword with embedded arguments defined multiple times fails at run-time
1313
[Documentation] FAIL
14-
... Library 'DupeDynamicKeywords' contains multiple keywords matching name 'Embedded twice':
15-
... ${INDENT}EMBEDDED \${ARG}
16-
... ${INDENT}Embedded \${twice}
14+
... Multiple keywords matching name 'Embedded twice' found:
15+
... ${INDENT}DupeDynamicKeywords.EMBEDDED \${ARG}
16+
... ${INDENT}DupeDynamicKeywords.Embedded \${twice}
1717
Embedded twice
1818

1919
Exact duplicate is accepted

atest/testdata/keywords/duplicate_hybrid_keywords.robot

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ Using keyword defined multiple times fails
1111

1212
Keyword with embedded arguments defined multiple times fails at run-time
1313
[Documentation] FAIL
14-
... Library 'DupeHybridKeywords' contains multiple keywords matching name 'Embedded twice':
15-
... ${INDENT}EMBEDDED \${ARG}
16-
... ${INDENT}Embedded \${twice}
14+
... Multiple keywords matching name 'Embedded twice' found:
15+
... ${INDENT}DupeHybridKeywords.EMBEDDED \${ARG}
16+
... ${INDENT}DupeHybridKeywords.Embedded \${twice}
1717
Embedded twice
1818

1919
Exact duplicate is accepted

atest/testdata/keywords/duplicate_static_keywords.robot

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ Using keyword defined thrice fails as well
1515

1616
Keyword with embedded arguments defined twice fails at run-time: Called with embedded args
1717
[Documentation] FAIL
18-
... Library 'DupeKeywords' contains multiple keywords matching name 'Embedded arguments twice':
19-
... ${INDENT}Embedded \${arguments match} TWICE
20-
... ${INDENT}Embedded \${arguments} twice
18+
... Multiple keywords matching name 'Embedded arguments twice' found:
19+
... ${INDENT}DupeKeywords.Embedded \${arguments match} TWICE
20+
... ${INDENT}DupeKeywords.Embedded \${arguments} twice
2121
Embedded arguments twice
2222

2323
Keyword with embedded arguments defined twice fails at run-time: Called with exact name
2424
[Documentation] FAIL
25-
... Library 'DupeKeywords' contains multiple keywords matching name 'Embedded ${arguments match} twice':
26-
... ${INDENT}Embedded \${arguments match} TWICE
27-
... ${INDENT}Embedded \${arguments} twice
25+
... Multiple keywords matching name 'Embedded \${arguments match} twice' found:
26+
... ${INDENT}DupeKeywords.Embedded \${arguments match} TWICE
27+
... ${INDENT}DupeKeywords.Embedded \${arguments} twice
2828
Embedded ${arguments match} twice

atest/testdata/keywords/duplicate_user_keywords.robot

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ Using keyword defined thrice fails as well
1515

1616
Keyword with embedded arguments defined twice fails at run-time: Called with embedded args
1717
[Documentation] FAIL
18-
... Test case file contains multiple keywords matching name 'Embedded arguments twice':
18+
... Multiple keywords matching name 'Embedded arguments twice' found:
1919
... ${INDENT}Embedded \${arguments match} TWICE
2020
... ${INDENT}Embedded \${arguments} twice
2121
Embedded arguments twice
2222

2323
Keyword with embedded arguments defined twice fails at run-time: Called with exact name
2424
[Documentation] FAIL
25-
... Test case file contains multiple keywords matching name 'Embedded ${arguments match} twice':
25+
... Multiple keywords matching name 'Embedded \${arguments match} twice' found:
2626
... ${INDENT}Embedded \${arguments match} TWICE
2727
... ${INDENT}Embedded \${arguments} twice
2828
Embedded ${arguments match} twice
@@ -33,9 +33,9 @@ Using keyword defined multiple times in resource fails
3333

3434
Keyword with embedded arguments defined multiple times in resource fails at run-time
3535
[Documentation] FAIL
36-
... Resource file 'dupe_keywords.resource' contains multiple keywords matching name 'Embedded arguments twice in resource':
37-
... ${INDENT}Embedded \${arguments match} TWICE IN RESOURCE
38-
... ${INDENT}Embedded \${arguments} twice in resource
36+
... Multiple keywords matching name 'Embedded arguments twice in resource' found:
37+
... ${INDENT}dupe_keywords.Embedded \${arguments match} TWICE IN RESOURCE
38+
... ${INDENT}dupe_keywords.Embedded \${arguments} twice in resource
3939
Embedded arguments twice in resource
4040

4141
*** Keywords ***

atest/testdata/keywords/embedded_arguments.robot

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ Creating keyword with both normal and embedded arguments fails
167167
Keyword with ${embedded} and normal args is invalid arg1 arg2
168168

169169
Keyword Matching Multiple Keywords In Test Case File
170-
[Documentation] FAIL Test case file contains multiple keywords matching name 'foo+tc+bar-tc-zap':
170+
[Documentation] FAIL
171+
... Multiple keywords matching name 'foo+tc+bar-tc-zap' found:
171172
... ${INDENT}\${a}+tc+\${b}
172173
... ${INDENT}\${a}-tc-\${b}
173174
foo+tc+bar
@@ -176,26 +177,29 @@ Keyword Matching Multiple Keywords In Test Case File
176177
foo+tc+bar-tc-zap
177178

178179
Keyword Matching Multiple Keywords In One Resource File
179-
[Documentation] FAIL Resource file 'embedded_args_in_uk_1.robot' contains multiple keywords matching name 'foo+r1+bar-r1-zap':
180-
... ${INDENT}\${a}+r1+\${b}
181-
... ${INDENT}\${a}-r1-\${b}
180+
[Documentation] FAIL
181+
... Multiple keywords matching name 'foo+r1+bar-r1-zap' found:
182+
... ${INDENT}embedded_args_in_uk_1.\${a}+r1+\${b}
183+
... ${INDENT}embedded_args_in_uk_1.\${a}-r1-\${b}
182184
foo+r1+bar
183185
foo-r1-bar
184186
foo+r1+bar-r1-zap
185187

186188
Keyword Matching Multiple Keywords In Different Resource Files
187-
[Documentation] FAIL Multiple keywords with name 'foo-r1-bar-r2-zap' found. \
188-
... Give the full name of the keyword you want to use:
189-
... ${INDENT}embedded_args_in_uk_1.foo-r1-bar-r2-zap
190-
... ${INDENT}embedded_args_in_uk_2.foo-r1-bar-r2-zap
189+
[Documentation] FAIL
190+
... Multiple keywords matching name 'foo-r1-bar-r2-zap' found:
191+
... ${INDENT}embedded_args_in_uk_1.\${a}-r1-\${b}
192+
... ${INDENT}embedded_args_in_uk_2.\${arg1}-r2-\${arg2}
191193
foo-r1-bar
192194
foo-r2-bar
193195
foo-r1-bar-r2-zap
194196

195197
Keyword Matching Multiple Keywords In One And Different Resource Files
196-
[Documentation] FAIL Resource file 'embedded_args_in_uk_1.robot' contains multiple keywords matching name '-r1-r2-+r1+':
197-
... ${INDENT}\${a}+r1+\${b}
198-
... ${INDENT}\${a}-r1-\${b}
198+
[Documentation] FAIL
199+
... Multiple keywords matching name '-r1-r2-+r1+' found:
200+
... ${INDENT}embedded_args_in_uk_1.\${a}+r1+\${b}
201+
... ${INDENT}embedded_args_in_uk_1.\${a}-r1-\${b}
202+
... ${INDENT}embedded_args_in_uk_2.\${arg1}-r2-\${arg2}
199203
-r1-r2-+r1+
200204

201205
Same name with different regexp works
@@ -205,14 +209,14 @@ Same name with different regexp works
205209

206210
Same name with different regexp matching multiple fails
207211
[Documentation] FAIL
208-
... Test case file contains multiple keywords matching name 'It is a cat':
212+
... Multiple keywords matching name 'It is a cat' found:
209213
... ${INDENT}It is \${animal:a (cat|cow)}
210214
... ${INDENT}It is \${animal:a (dog|cat)}
211215
It is a cat
212216

213217
Same name with same regexp fails
214218
[Documentation] FAIL
215-
... Test case file contains multiple keywords matching name 'It is totally same':
219+
... Multiple keywords matching name 'It is totally same' found:
216220
... ${INDENT}It is totally \${same}
217221
... ${INDENT}It is totally \${same}
218222
It is totally same

atest/testdata/keywords/embedded_arguments_library_keywords.robot

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,19 +116,20 @@ Embedded Arguments Syntax is Underscore Sensitive
116116
User Janne Selects x from_webshop
117117

118118
Keyword Matching Multiple Keywords In Library File
119-
[Documentation] FAIL Library 'embedded_args_in_lk_1' contains multiple keywords matching name 'foo+lib+bar-lib-zap':
120-
... ${INDENT}\${a}+lib+\${b}
121-
... ${INDENT}\${a}-lib-\${b}
119+
[Documentation] FAIL
120+
... Multiple keywords matching name 'foo+lib+bar-lib-zap' found:
121+
... ${INDENT}embedded_args_in_lk_1.\${a}+lib+\${b}
122+
... ${INDENT}embedded_args_in_lk_1.\${a}-lib-\${b}
122123
foo+lib+bar
123124
foo-lib-bar
124125
foo+lib+bar+lib+zap
125126
foo+lib+bar-lib-zap
126127

127128
Keyword Matching Multiple Keywords In Different Library Files
128-
[Documentation] FAIL Multiple keywords with name 'foo*lib*bar' found. \
129-
... Give the full name of the keyword you want to use:
130-
... ${INDENT}embedded_args_in_lk_1.foo*lib*bar
131-
... ${INDENT}embedded_args_in_lk_2.foo*lib*bar
129+
[Documentation] FAIL
130+
... Multiple keywords matching name 'foo*lib*bar' found:
131+
... ${INDENT}embedded_args_in_lk_1.\${a}*lib*\${b}
132+
... ${INDENT}embedded_args_in_lk_2.\${a}*lib*\${b}
132133
foo*lib*bar
133134

134135
Embedded And Positional Arguments Do Not Work Together
@@ -166,14 +167,14 @@ Same name with different regexp works
166167

167168
Same name with different regexp matching multiple fails
168169
[Documentation] FAIL
169-
... Library 'embedded_args_in_lk_1' contains multiple keywords matching name 'It is a cat':
170-
... ${INDENT}It is ${animal:a (cat|cow)}
171-
... ${INDENT}It is ${animal:a (dog|cat)}
170+
... Multiple keywords matching name 'It is a cat' found:
171+
... ${INDENT}embedded_args_in_lk_1.It is \${animal:a (cat|cow)}
172+
... ${INDENT}embedded_args_in_lk_1.It is \${animal:a (dog|cat)}
172173
It is a cat
173174

174175
Same name with same regexp fails
175176
[Documentation] FAIL
176-
... Library 'embedded_args_in_lk_1' contains multiple keywords matching name 'It is totally same':
177-
... ${INDENT}It is totally ${same}
178-
... ${INDENT}It is totally ${same}
177+
... Multiple keywords matching name 'It is totally same' found:
178+
... ${INDENT}embedded_args_in_lk_1.It is totally ${same}
179+
... ${INDENT}embedded_args_in_lk_1.It is totally ${same}
179180
It is totally same

src/robot/running/handlers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def InitHandler(library, method=None, docgetter=None):
4545

4646

4747
class _RunnableHandler:
48+
supports_embedded_args = False
4849

4950
def __init__(self, library, handler_name, handler_method, doc='', tags=None):
5051
self.library = library
@@ -282,6 +283,7 @@ def _parse_arguments(self, init_method):
282283

283284

284285
class EmbeddedArgumentsHandler:
286+
supports_embedded_args = True
285287

286288
def __init__(self, embedded, orig_handler):
287289
self.arguments = ArgumentSpec() # Show empty argument spec for Libdoc

src/robot/running/handlerstore.py

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,17 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
from operator import attrgetter
16+
from itertools import chain
1717

18-
from robot.errors import DataError, KeywordError
19-
from robot.utils import NormalizedDict
18+
from robot.errors import DataError
19+
from robot.utils import NormalizedDict, seq2str
2020

2121
from .usererrorhandler import UserErrorHandler
2222

2323

2424
class HandlerStore:
25-
LIBRARY_TYPE = 'Library'
26-
TEST_CASE_FILE_TYPE = 'Test case file'
27-
RESOURCE_FILE_TYPE = 'Resource file'
2825

29-
def __init__(self, source, source_type):
30-
self.source = source
31-
self.source_type = source_type
26+
def __init__(self):
3227
self._normal = NormalizedDict(ignore='_')
3328
self._embedded = []
3429

@@ -44,8 +39,7 @@ def add(self, handler, embedded=False):
4439
raise error
4540

4641
def __iter__(self):
47-
handlers = list(self._normal.values()) + self._embedded
48-
return iter(sorted(handlers, key=attrgetter('name')))
42+
return chain(self._normal.values(), self._embedded)
4943

5044
def __len__(self):
5145
return len(self._normal) + len(self._embedded)
@@ -55,30 +49,16 @@ def __contains__(self, name):
5549
return True
5650
return any(template.matches(name) for template in self._embedded)
5751

58-
def create_runner(self, name, languages=None):
59-
return self[name].create_runner(name, languages)
60-
6152
def __getitem__(self, name):
62-
try:
63-
return self._normal[name]
64-
except KeyError:
65-
return self._find_embedded(name)
66-
67-
def _find_embedded(self, name):
68-
embedded = [template for template in self._embedded if template.matches(name)]
69-
if len(embedded) == 1:
70-
return embedded[0]
71-
self._raise_no_single_match(name, embedded)
53+
handlers = self.get_handlers(name)
54+
if len(handlers) == 1:
55+
return handlers[0]
56+
if not handlers:
57+
raise ValueError(f"No handler with name '{name}' found.")
58+
names = seq2str([handler.name for handler in handlers])
59+
raise ValueError(f"Multiple handlers matching name '{name}' found: {names}")
7260

73-
def _raise_no_single_match(self, name, found):
74-
if self.source_type == self.TEST_CASE_FILE_TYPE:
75-
source = self.source_type
76-
else:
77-
source = "%s '%s'" % (self.source_type, self.source)
78-
if not found:
79-
raise KeywordError("%s contains no keywords matching name '%s'."
80-
% (source, name))
81-
error = ["%s contains multiple keywords matching name '%s':"
82-
% (source, name)]
83-
names = sorted(handler.name for handler in found)
84-
raise KeywordError('\n '.join(error + names))
61+
def get_handlers(self, name):
62+
if name in self._normal:
63+
return [self._normal[name]]
64+
return [template for template in self._embedded if template.matches(name)]

src/robot/running/importer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def _copy_library(self, orig, name):
9898
lib.name = name
9999
lib.scope = type(lib.scope)(lib)
100100
lib.reset_instance()
101-
lib.handlers = HandlerStore(orig.handlers.source, orig.handlers.source_type)
101+
lib.handlers = HandlerStore()
102102
for handler in orig.handlers._normal.values():
103103
handler = copy.copy(handler)
104104
handler.library = lib

0 commit comments

Comments
 (0)