Coverage for src/robotide/validators/__init__.py: 72%

178 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-06 10:40 +0100

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 

16import builtins 1ab

17import os 1ab

18import wx 1ab

19 

20from .. import robotapi, utils 1ab

21from ..widgets import RIDEDialog 1ab

22 

23_ = wx.GetTranslation # To keep linter/code analyser happy 1ab

24builtins.__dict__['_'] = wx.GetTranslation 1ab

25 

26 

27class _AbstractValidator(wx.Validator): 1ab

28 """Implements methods to keep wxPython happy and some helper methods.""" 

29 

30 def Clone(self): 1ab

31 return self.__class__() 1qprstuvwxy

32 

33 def TransferFromWindow(self): 1ab

34 return True 

35 

36 def TransferToWindow(self): 1ab

37 return True 1zp

38 

39 def Validate(self, win): 1ab

40 value = self.Window.Value 

41 error = self._validate(value) 

42 if error: 

43 self._show_error(error) 

44 return False 

45 return True 

46 

47 def _validate(self, value): 1ab

48 return NotImplemented 

49 

50 def _show_error(self, message, title="Validation Error"): 1ab

51 message_box = RIDEDialog(title=title, message=message, style=wx.ICON_ERROR) 

52 ret = message_box.execute() 

53 self._set_focus_to_text_control(self.Window) 

54 return ret 

55 

56 @staticmethod 1ab

57 def _set_focus_to_text_control(ctrl): 1ab

58 ctrl.SetFocus() 

59 ctrl.SelectAll() 

60 

61 

62class TimeoutValidator(_AbstractValidator): 1ab

63 

64 def _validate(self, value): 1ab

65 time_tokens = utils.split_value(value) 1ohi

66 if not time_tokens: 66 ↛ 67line 66 didn't jump to line 67 because the condition on line 66 was never true1ohi

67 return None 

68 timestr = time_tokens[0] 1ohi

69 try: 1ohi

70 secs = utils.timestr_to_secs(timestr) 1ohi

71 if secs <= 0: 1hi

72 raise ValueError("Timestring must be over zero") 1h

73 time_tokens[0] = utils.secs_to_timestr(secs) 1i

74 except ValueError as err: 1oh

75 if '${' not in timestr: 75 ↛ 77line 75 didn't jump to line 77 because the condition on line 75 was always true1oh

76 return str(err) 1oh

77 self._set_window_value(utils.join_value(time_tokens)) 1i

78 return None 1i

79 

80 def _set_window_value(self, value): 1ab

81 self.Window.SetValue(value) 

82 

83 

84class ArgumentTypes(object): 1ab

85 NAMED = 1 1ab

86 LIST = 2 1ab

87 DICT = 3 1ab

88 SCALAR = 4 1ab

89 DEFAULT = 5 1ab

90 

91 

92class ArgumentsValidator(_AbstractValidator): 1ab

93 

94 def _validate(self, args_str): 1ab

95 try: 1Agdefc

96 types = [self._get_type(arg) 1Agdefc

97 for arg in utils.split_value(args_str)] 

98 except ValueError as e: 1g

99 return "Invalid argument syntax '%s'" % str(e) # DEBUG was arg 1g

100 return self._validate_argument_order(types) 1Adefc

101 

102 @staticmethod 1ab

103 def _get_type(arg): 1ab

104 if '=' in arg and len(arg.split("=")) > 1: 1gdefc

105 default_arg = arg.split("=")[0] 1gfc

106 else: 

107 default_arg = False 1gdefc

108 if robotapi.is_scalar_var(default_arg): 1gdefc

109 return ArgumentTypes.DEFAULT 1fc

110 elif robotapi.is_scalar_var(arg): 1gdefc

111 return ArgumentTypes.SCALAR 1gdfc

112 elif arg == '@{}': 1gdec

113 return ArgumentTypes.NAMED 1dc

114 elif robotapi.is_list_var(arg): 1gdec

115 return ArgumentTypes.LIST 1ec

116 elif robotapi.is_dict_var(arg): 1gdec

117 return ArgumentTypes.DICT 1dec

118 else: 

119 raise ValueError(arg) 1g

120 

121 @staticmethod 1ab

122 def _validate_argument_order(types): 1ab

123 if types: 1Adefc

124 prev = types[0] 1defc

125 active_named_only = False 1defc

126 dict_in_list = False 1defc

127 for idx, t in enumerate(types): 1defc

128 if prev == ArgumentTypes.DICT: 1defc

129 dict_in_list = True 1ec

130 if t == ArgumentTypes.NAMED: 1defc

131 active_named_only = True 1dc

132 prev = ArgumentTypes.DEFAULT # Force max value 1dc

133 continue 1dc

134 if idx == len(types)-1: 1defc

135 if t in [ArgumentTypes.LIST, ArgumentTypes.DICT] and not dict_in_list: 1efc

136 return None 1c

137 elif t == ArgumentTypes.LIST and dict_in_list: 1efc

138 return "Only last argument can be kwargs (dictionary argument)." 1e

139 if t < prev: 1defc

140 if ((not active_named_only and t not in [ArgumentTypes.LIST, ArgumentTypes.DICT]) 1dfc

141 or (active_named_only and t in [ArgumentTypes.LIST, ArgumentTypes.DICT])): 

142 return ("List and scalar arguments must be before named and " 1df

143 "dictionary arguments") 

144 prev = t 1defc

145 return None 1Ac

146 

147 

148class NonEmptyValidator(_AbstractValidator): 1ab

149 

150 def __init__(self, field_name): 1ab

151 _AbstractValidator.__init__(self) 

152 self._field_name = field_name 

153 

154 def Clone(self): 1ab

155 return self.__class__(self._field_name) 

156 

157 def _validate(self, value): 1ab

158 if not value: 

159 return _("%s cannot be empty") % self._field_name 

160 return None 

161 

162 

163class SuiteFileNameValidator(NonEmptyValidator): 1ab

164 

165 def __init__(self, field_name, is_dir_type): 1ab

166 NonEmptyValidator.__init__(self, field_name) 

167 self._is_dir_type = is_dir_type 

168 

169 def Clone(self): 1ab

170 return self.__class__(self._field_name, self._is_dir_type) 

171 

172 def _validate(self, value): 1ab

173 validity = NonEmptyValidator._validate(self, value) 

174 if not self._is_dir_type() and not validity: 

175 if value.lower() == '__init__': 

176 return "Invalid suite file name \"%s\"" % value 

177 return validity 

178 

179 

180class DirectoryExistsValidator(_AbstractValidator): 1ab

181 

182 def _validate(self, value): 1ab

183 if not os.path.isdir(value): 

184 return "Chosen directory must exist" 

185 return None 

186 

187 

188class NewSuitePathValidator(_AbstractValidator): 1ab

189 

190 def _validate(self, value): 1ab

191 path = os.path.normpath(value) 

192 if os.path.exists(path): 

193 return "Target file or directory must not exist" 

194 parentdir, filename = os.path.split(path) 

195 if "__init__" in filename: 

196 parentdir = os.path.dirname(parentdir) 

197 if not os.path.exists(parentdir): 

198 try: 

199 os.makedirs(parentdir) 

200 except OSError: 

201 return f"Failed to create directory: {parentdir}" 

202 return None 

203 

204 

205class _NameValidator(_AbstractValidator): 1ab

206 

207 def __init__(self, controller, orig_name=None): 1ab

208 _AbstractValidator.__init__(self) 1CzDEFGqprstuvwxyjklmnB

209 self._controller = controller 1CzDEFGqprstuvwxyjklmnB

210 self._orig_name = orig_name 1CzDEFGqprstuvwxyjklmnB

211 

212 def Clone(self): 1ab

213 return self.__class__(self._controller, self._orig_name) 1CzDEFGqprstuvwxy

214 

215 def _validate(self, name): 1ab

216 if self._orig_name is not None and utils.eq( 1jklmnB

217 name, self._orig_name, ignore=['_']): 

218 return '' 1B

219 return self._validation_method(name).error_message 1jklmn

220 

221 @property 1ab

222 def _validation_method(self): 1ab

223 return NotImplemented 

224 

225 

226class TestCaseNameValidator(_NameValidator): 1ab

227 __test__ = False 1ab

228 

229 @property 1ab

230 def _validation_method(self): 1ab

231 return self._controller.validate_test_name 1jklmn

232 

233 

234class UserKeywordNameValidator(_NameValidator): 1ab

235 @property 1ab

236 def _validation_method(self): 1ab

237 return self._controller.validate_keyword_name 1jklmn

238 

239 

240class ScalarVariableNameValidator(_NameValidator): 1ab

241 @property 1ab

242 def _validation_method(self): 1ab

243 return self._controller.validate_scalar_variable_name 1jklmn

244 

245 

246class ListVariableNameValidator(_NameValidator): 1ab

247 @property 1ab

248 def _validation_method(self): 1ab

249 return self._controller.validate_list_variable_name 1jklmn

250 

251 

252class DictionaryVariableNameValidator(_NameValidator): 1ab

253 @property 1ab

254 def _validation_method(self): 1ab

255 return self._controller.validate_dict_variable_name