Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/wtforms/fields/core.py: 29%

179 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:32 +0000

1import inspect 

2import itertools 

3import warnings 

4 

5from markupsafe import escape 

6from markupsafe import Markup 

7 

8from wtforms import widgets 

9from wtforms.i18n import DummyTranslations 

10from wtforms.utils import unset_value 

11from wtforms.validators import StopValidation 

12from wtforms.validators import ValidationError 

13 

14 

15class Field: 

16 """ 

17 Field base class 

18 """ 

19 

20 errors = tuple() 

21 process_errors = tuple() 

22 raw_data = None 

23 validators = tuple() 

24 widget = None 

25 _formfield = True 

26 _translations = DummyTranslations() 

27 do_not_call_in_templates = True # Allow Django 1.4 traversal 

28 

29 def __new__(cls, *args, **kwargs): 

30 if "_form" in kwargs: 

31 return super().__new__(cls) 

32 else: 

33 return UnboundField(cls, *args, **kwargs) 

34 

35 def __init__( 

36 self, 

37 label=None, 

38 validators=None, 

39 filters=(), 

40 description="", 

41 id=None, 

42 default=None, 

43 widget=None, 

44 render_kw=None, 

45 name=None, 

46 _form=None, 

47 _prefix="", 

48 _translations=None, 

49 _meta=None, 

50 ): 

51 """ 

52 Construct a new field. 

53 

54 :param label: 

55 The label of the field. 

56 :param validators: 

57 A sequence of validators to call when `validate` is called. 

58 :param filters: 

59 A sequence of filters which are run on input data by `process`. 

60 :param description: 

61 A description for the field, typically used for help text. 

62 :param id: 

63 An id to use for the field. A reasonable default is set by the form, 

64 and you shouldn't need to set this manually. 

65 :param default: 

66 The default value to assign to the field, if no form or object 

67 input is provided. May be a callable. 

68 :param widget: 

69 If provided, overrides the widget used to render the field. 

70 :param dict render_kw: 

71 If provided, a dictionary which provides default keywords that 

72 will be given to the widget at render time. 

73 :param name: 

74 The HTML name of this field. The default value is the Python 

75 attribute name. 

76 :param _form: 

77 The form holding this field. It is passed by the form itself during 

78 construction. You should never pass this value yourself. 

79 :param _prefix: 

80 The prefix to prepend to the form name of this field, passed by 

81 the enclosing form during construction. 

82 :param _translations: 

83 A translations object providing message translations. Usually 

84 passed by the enclosing form during construction. See 

85 :doc:`I18n docs <i18n>` for information on message translations. 

86 :param _meta: 

87 If provided, this is the 'meta' instance from the form. You usually 

88 don't pass this yourself. 

89 

90 If `_form` isn't provided, an :class:`UnboundField` will be 

91 returned instead. Call its :func:`bind` method with a form instance and 

92 a name to construct the field. 

93 """ 

94 if _translations is not None: 

95 self._translations = _translations 

96 

97 if _meta is not None: 

98 self.meta = _meta 

99 elif _form is not None: 

100 self.meta = _form.meta 

101 else: 

102 raise TypeError("Must provide one of _form or _meta") 

103 

104 self.default = default 

105 self.description = description 

106 self.render_kw = render_kw 

107 self.filters = filters 

108 self.flags = Flags() 

109 self.name = _prefix + name 

110 self.short_name = name 

111 self.type = type(self).__name__ 

112 

113 self.check_validators(validators) 

114 self.validators = validators or self.validators 

115 

116 self.id = id or self.name 

117 self.label = Label( 

118 self.id, 

119 label 

120 if label is not None 

121 else self.gettext(name.replace("_", " ").title()), 

122 ) 

123 

124 if widget is not None: 

125 self.widget = widget 

126 

127 for v in itertools.chain(self.validators, [self.widget]): 

128 flags = getattr(v, "field_flags", {}) 

129 

130 # check for legacy format, remove eventually 

131 if isinstance(flags, tuple): # pragma: no cover 

132 warnings.warn( 

133 "Flags should be stored in dicts and not in tuples. " 

134 "The next version of WTForms will abandon support " 

135 "for flags in tuples.", 

136 DeprecationWarning, 

137 stacklevel=2, 

138 ) 

139 flags = {flag_name: True for flag_name in flags} 

140 

141 for k, v in flags.items(): 

142 setattr(self.flags, k, v) 

143 

144 def __str__(self): 

145 """ 

146 Returns a HTML representation of the field. For more powerful rendering, 

147 see the `__call__` method. 

148 """ 

149 return self() 

150 

151 def __html__(self): 

152 """ 

153 Returns a HTML representation of the field. For more powerful rendering, 

154 see the :meth:`__call__` method. 

155 """ 

156 return self() 

157 

158 def __call__(self, **kwargs): 

159 """ 

160 Render this field as HTML, using keyword args as additional attributes. 

161 

162 This delegates rendering to 

163 :meth:`meta.render_field <wtforms.meta.DefaultMeta.render_field>` 

164 whose default behavior is to call the field's widget, passing any 

165 keyword arguments from this call along to the widget. 

166 

167 In all of the WTForms HTML widgets, keyword arguments are turned to 

168 HTML attributes, though in theory a widget is free to do anything it 

169 wants with the supplied keyword arguments, and widgets don't have to 

170 even do anything related to HTML. 

171 """ 

172 return self.meta.render_field(self, kwargs) 

173 

174 @classmethod 

175 def check_validators(cls, validators): 

176 if validators is not None: 

177 for validator in validators: 

178 if not callable(validator): 

179 raise TypeError( 

180 "{} is not a valid validator because it is not " 

181 "callable".format(validator) 

182 ) 

183 

184 if inspect.isclass(validator): 

185 raise TypeError( 

186 "{} is not a valid validator because it is a class, " 

187 "it should be an instance".format(validator) 

188 ) 

189 

190 def gettext(self, string): 

191 """ 

192 Get a translation for the given message. 

193 

194 This proxies for the internal translations object. 

195 

196 :param string: A string to be translated. 

197 :return: A string which is the translated output. 

198 """ 

199 return self._translations.gettext(string) 

200 

201 def ngettext(self, singular, plural, n): 

202 """ 

203 Get a translation for a message which can be pluralized. 

204 

205 :param str singular: The singular form of the message. 

206 :param str plural: The plural form of the message. 

207 :param int n: The number of elements this message is referring to 

208 """ 

209 return self._translations.ngettext(singular, plural, n) 

210 

211 def validate(self, form, extra_validators=()): 

212 """ 

213 Validates the field and returns True or False. `self.errors` will 

214 contain any errors raised during validation. This is usually only 

215 called by `Form.validate`. 

216 

217 Subfields shouldn't override this, but rather override either 

218 `pre_validate`, `post_validate` or both, depending on needs. 

219 

220 :param form: The form the field belongs to. 

221 :param extra_validators: A sequence of extra validators to run. 

222 """ 

223 self.errors = list(self.process_errors) 

224 stop_validation = False 

225 

226 # Check the type of extra_validators 

227 self.check_validators(extra_validators) 

228 

229 # Call pre_validate 

230 try: 

231 self.pre_validate(form) 

232 except StopValidation as e: 

233 if e.args and e.args[0]: 

234 self.errors.append(e.args[0]) 

235 stop_validation = True 

236 except ValidationError as e: 

237 self.errors.append(e.args[0]) 

238 

239 # Run validators 

240 if not stop_validation: 

241 chain = itertools.chain(self.validators, extra_validators) 

242 stop_validation = self._run_validation_chain(form, chain) 

243 

244 # Call post_validate 

245 try: 

246 self.post_validate(form, stop_validation) 

247 except ValidationError as e: 

248 self.errors.append(e.args[0]) 

249 

250 return len(self.errors) == 0 

251 

252 def _run_validation_chain(self, form, validators): 

253 """ 

254 Run a validation chain, stopping if any validator raises StopValidation. 

255 

256 :param form: The Form instance this field belongs to. 

257 :param validators: a sequence or iterable of validator callables. 

258 :return: True if validation was stopped, False otherwise. 

259 """ 

260 for validator in validators: 

261 try: 

262 validator(form, self) 

263 except StopValidation as e: 

264 if e.args and e.args[0]: 

265 self.errors.append(e.args[0]) 

266 return True 

267 except ValidationError as e: 

268 self.errors.append(e.args[0]) 

269 

270 return False 

271 

272 def pre_validate(self, form): 

273 """ 

274 Override if you need field-level validation. Runs before any other 

275 validators. 

276 

277 :param form: The form the field belongs to. 

278 """ 

279 pass 

280 

281 def post_validate(self, form, validation_stopped): 

282 """ 

283 Override if you need to run any field-level validation tasks after 

284 normal validation. This shouldn't be needed in most cases. 

285 

286 :param form: The form the field belongs to. 

287 :param validation_stopped: 

288 `True` if any validator raised StopValidation. 

289 """ 

290 pass 

291 

292 def process(self, formdata, data=unset_value, extra_filters=None): 

293 """ 

294 Process incoming data, calling process_data, process_formdata as needed, 

295 and run filters. 

296 

297 If `data` is not provided, process_data will be called on the field's 

298 default. 

299 

300 Field subclasses usually won't override this, instead overriding the 

301 process_formdata and process_data methods. Only override this for 

302 special advanced processing, such as when a field encapsulates many 

303 inputs. 

304 

305 :param extra_filters: A sequence of extra filters to run. 

306 """ 

307 self.process_errors = [] 

308 if data is unset_value: 

309 try: 

310 data = self.default() 

311 except TypeError: 

312 data = self.default 

313 

314 self.object_data = data 

315 

316 try: 

317 self.process_data(data) 

318 except ValueError as e: 

319 self.process_errors.append(e.args[0]) 

320 

321 if formdata is not None: 

322 if self.name in formdata: 

323 self.raw_data = formdata.getlist(self.name) 

324 else: 

325 self.raw_data = [] 

326 

327 try: 

328 self.process_formdata(self.raw_data) 

329 except ValueError as e: 

330 self.process_errors.append(e.args[0]) 

331 

332 try: 

333 for filter in itertools.chain(self.filters, extra_filters or []): 

334 self.data = filter(self.data) 

335 except ValueError as e: 

336 self.process_errors.append(e.args[0]) 

337 

338 def process_data(self, value): 

339 """ 

340 Process the Python data applied to this field and store the result. 

341 

342 This will be called during form construction by the form's `kwargs` or 

343 `obj` argument. 

344 

345 :param value: The python object containing the value to process. 

346 """ 

347 self.data = value 

348 

349 def process_formdata(self, valuelist): 

350 """ 

351 Process data received over the wire from a form. 

352 

353 This will be called during form construction with data supplied 

354 through the `formdata` argument. 

355 

356 :param valuelist: A list of strings to process. 

357 """ 

358 if valuelist: 

359 self.data = valuelist[0] 

360 

361 def populate_obj(self, obj, name): 

362 """ 

363 Populates `obj.<name>` with the field's data. 

364 

365 :note: This is a destructive operation. If `obj.<name>` already exists, 

366 it will be overridden. Use with caution. 

367 """ 

368 setattr(obj, name, self.data) 

369 

370 

371class UnboundField: 

372 _formfield = True 

373 creation_counter = 0 

374 

375 def __init__(self, field_class, *args, name=None, **kwargs): 

376 UnboundField.creation_counter += 1 

377 self.field_class = field_class 

378 self.args = args 

379 self.name = name 

380 self.kwargs = kwargs 

381 self.creation_counter = UnboundField.creation_counter 

382 validators = kwargs.get("validators") 

383 if validators: 

384 self.field_class.check_validators(validators) 

385 

386 def bind(self, form, name, prefix="", translations=None, **kwargs): 

387 kw = dict( 

388 self.kwargs, 

389 name=name, 

390 _form=form, 

391 _prefix=prefix, 

392 _translations=translations, 

393 **kwargs, 

394 ) 

395 return self.field_class(*self.args, **kw) 

396 

397 def __repr__(self): 

398 return "<UnboundField({}, {!r}, {!r})>".format( 

399 self.field_class.__name__, self.args, self.kwargs 

400 ) 

401 

402 

403class Flags: 

404 """ 

405 Holds a set of flags as attributes. 

406 

407 Accessing a non-existing attribute returns None for its value. 

408 """ 

409 

410 def __getattr__(self, name): 

411 if name.startswith("_"): 

412 return super().__getattr__(name) 

413 return None 

414 

415 def __contains__(self, name): 

416 return getattr(self, name) 

417 

418 def __repr__(self): 

419 flags = (name for name in dir(self) if not name.startswith("_")) 

420 return "<wtforms.fields.Flags: {%s}>" % ", ".join(flags) 

421 

422 

423class Label: 

424 """ 

425 An HTML form label. 

426 """ 

427 

428 def __init__(self, field_id, text): 

429 self.field_id = field_id 

430 self.text = text 

431 

432 def __str__(self): 

433 return self() 

434 

435 def __html__(self): 

436 return self() 

437 

438 def __call__(self, text=None, **kwargs): 

439 if "for_" in kwargs: 

440 kwargs["for"] = kwargs.pop("for_") 

441 else: 

442 kwargs.setdefault("for", self.field_id) 

443 

444 attributes = widgets.html_params(**kwargs) 

445 text = escape(text or self.text) 

446 return Markup(f"<label {attributes}>{text}</label>") 

447 

448 def __repr__(self): 

449 return f"Label({self.field_id!r}, {self.text!r})"