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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

194 statements  

1import inspect 

2import itertools 

3 

4from markupsafe import escape 

5from markupsafe import Markup 

6 

7from wtforms import widgets 

8from wtforms.i18n import DummyTranslations 

9from wtforms.utils import unset_value 

10from wtforms.validators import StopValidation 

11from wtforms.validators import ValidationError 

12 

13 

14class Field: 

15 """ 

16 Field base class 

17 """ 

18 

19 errors = tuple() 

20 process_errors = tuple() 

21 raw_data = None 

22 validators = tuple() 

23 widget = None 

24 _formfield = True 

25 _translations = DummyTranslations() 

26 do_not_call_in_templates = True # Allow Django 1.4 traversal 

27 

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

29 if "_form" in kwargs: 

30 return super().__new__(cls) 

31 else: 

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

33 

34 def __init__( 

35 self, 

36 label=None, 

37 validators=None, 

38 filters=(), 

39 description="", 

40 id=None, 

41 default=None, 

42 invalid_value_message=None, 

43 widget=None, 

44 render_kw=None, 

45 name=None, 

46 datalist=None, 

47 _form=None, 

48 _prefix="", 

49 _translations=None, 

50 _meta=None, 

51 ): 

52 """ 

53 Construct a new field. 

54 

55 :param label: 

56 The label of the field. 

57 :param validators: 

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

59 :param filters: 

60 A sequence of callable which are run by :meth:`~Field.process` 

61 to filter or transform the input data. For example 

62 ``StringForm(filters=[lambda x: x.strip() if x is not None else x])``. 

63 Note that filters are applied after processing the default and 

64 incoming data, but before validation. Filters should handle 

65 empty values such as `None`. 

66 :param description: 

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

68 :param id: 

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

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

71 :param default: 

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

73 input is provided. May be a callable. 

74 :param invalid_value_message: 

75 Optional custom message used when processing submitted or Python 

76 data fails because the value is invalid for this field. 

77 :param widget: 

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

79 :param dict render_kw: 

80 If provided, a dictionary which provides default keywords that 

81 will be given to the widget at render time. 

82 :param name: 

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

84 attribute name. 

85 :param _form: 

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

87 construction. You should never pass this value yourself. 

88 :param _prefix: 

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

90 the enclosing form during construction. 

91 :param _translations: 

92 A translations object providing message translations. Usually 

93 passed by the enclosing form during construction. See 

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

95 :param _meta: 

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

97 don't pass this yourself. 

98 

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

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

101 a name to construct the field. 

102 """ 

103 if _translations is not None: 

104 self._translations = _translations 

105 

106 if _meta is not None: 

107 self.meta = _meta 

108 elif _form is not None: 

109 self.meta = _form.meta 

110 else: 

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

112 

113 self._form = _form 

114 

115 self.default = default 

116 self.description = description 

117 self.invalid_value_message = invalid_value_message 

118 self.render_kw = render_kw 

119 self.filters = filters 

120 self.flags = Flags() 

121 self.name = _prefix + name 

122 self.short_name = name 

123 self.type = type(self).__name__ 

124 

125 self.check_validators(validators) 

126 self.validators = validators or self.validators 

127 

128 self.id = id or self.name 

129 self.label = Label( 

130 self.id, 

131 label 

132 if label is not None 

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

134 ) 

135 

136 if widget is not None: 

137 self.widget = widget 

138 

139 self._datalist = None 

140 if datalist is not None: 

141 if isinstance(datalist, str): 

142 self._datalist = datalist 

143 else: 

144 self._datalist = datalist._clone(id=f"{self.id}-datalist") 

145 

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

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

148 

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

150 setattr(self.flags, k, v) 

151 

152 def __str__(self): 

153 """ 

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

155 see the `__call__` method. 

156 """ 

157 return self() 

158 

159 def __html__(self): 

160 """ 

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

162 see the :meth:`__call__` method. 

163 """ 

164 return self() 

165 

166 def __call__(self, **kwargs): 

167 """ 

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

169 

170 This delegates rendering to 

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

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

173 keyword arguments from this call along to the widget. 

174 

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

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

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

178 even do anything related to HTML. 

179 """ 

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

181 

182 def datalist(self, **kwargs): 

183 """Render the inline ``<datalist>`` bound to this field, or 

184 empty markup when there is none.""" 

185 if self._datalist is None or isinstance(self._datalist, str): 

186 return Markup("") 

187 return self._datalist(self, **kwargs) 

188 

189 @classmethod 

190 def check_validators(cls, validators): 

191 if validators is not None: 

192 for validator in validators: 

193 if not callable(validator): 

194 raise TypeError( 

195 f"{validator} is not a valid validator because it is not " 

196 "callable" 

197 ) 

198 

199 if inspect.isclass(validator): 

200 raise TypeError( 

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

202 "it should be an instance" 

203 ) 

204 

205 def gettext(self, string): 

206 """ 

207 Get a translation for the given message. 

208 

209 This proxies for the internal translations object. 

210 

211 :param string: A string to be translated. 

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

213 """ 

214 return self._translations.gettext(string) 

215 

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

217 """ 

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

219 

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

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

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

223 """ 

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

225 

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

227 """ 

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

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

230 called by `Form.validate`. 

231 

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

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

234 

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

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

237 """ 

238 self.errors = list(self.process_errors) 

239 stop_validation = False 

240 

241 # Check the type of extra_validators 

242 self.check_validators(extra_validators) 

243 

244 # Call pre_validate 

245 try: 

246 self.pre_validate(form) 

247 except StopValidation as e: 

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

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

250 stop_validation = True 

251 except ValidationError as e: 

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

253 

254 # Run validators 

255 if not stop_validation: 

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

257 stop_validation = self._run_validation_chain(form, chain) 

258 

259 # Call post_validate 

260 try: 

261 self.post_validate(form, stop_validation) 

262 except ValidationError as e: 

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

264 

265 return len(self.errors) == 0 

266 

267 def _run_validation_chain(self, form, validators): 

268 """ 

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

270 

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

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

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

274 """ 

275 for validator in validators: 

276 try: 

277 validator(form, self) 

278 except StopValidation as e: 

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

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

281 return True 

282 except ValidationError as e: 

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

284 

285 return False 

286 

287 def pre_validate(self, form): 

288 """ 

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

290 validators. 

291 

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

293 """ 

294 pass 

295 

296 def post_validate(self, form, validation_stopped): 

297 """ 

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

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

300 

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

302 :param validation_stopped: 

303 `True` if any validator raised StopValidation. 

304 """ 

305 pass 

306 

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

308 """ 

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

310 and run filters. 

311 

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

313 default. 

314 

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

316 process_formdata and process_data methods. Only override this for 

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

318 inputs. 

319 

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

321 """ 

322 self.process_errors = [] 

323 if data is unset_value: 

324 try: 

325 data = self.default() 

326 except TypeError: 

327 data = self.default 

328 

329 self.object_data = data 

330 

331 try: 

332 self.process_data(data) 

333 except ValueError as e: 

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

335 

336 if formdata is not None: 

337 if self.name in formdata: 

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

339 else: 

340 self.raw_data = [] 

341 

342 try: 

343 self.process_formdata(self.raw_data) 

344 except ValueError as e: 

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

346 

347 try: 

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

349 self.data = filter(self.data) 

350 except ValueError as e: 

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

352 

353 def post_process(self): 

354 """Hook called after every field in the enclosing form has been processed. 

355 

356 Override this when a field needs to read other fields' processed data, 

357 for example to resolve dynamic choices that depend on the form state. 

358 The base implementation resolves any inline :class:`~wtforms.DataList` 

359 attached to the field. 

360 """ 

361 if self._datalist is not None and not isinstance(self._datalist, str): 

362 self._datalist._resolve(self) 

363 

364 def process_data(self, value): 

365 """ 

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

367 

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

369 `obj` argument. 

370 

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

372 """ 

373 self.data = value 

374 

375 def process_formdata(self, valuelist): 

376 """ 

377 Process data received over the wire from a form. 

378 

379 This will be called during form construction with data supplied 

380 through the `formdata` argument. 

381 

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

383 """ 

384 if valuelist: 

385 self.data = valuelist[0] 

386 

387 def populate_obj(self, obj, name): 

388 """ 

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

390 

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

392 it will be overridden. Use with caution. 

393 """ 

394 setattr(obj, name, self.data) 

395 

396 

397class UnboundField: 

398 _formfield = True 

399 creation_counter = 0 

400 

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

402 UnboundField.creation_counter += 1 

403 self.field_class = field_class 

404 self.args = args 

405 self.name = name 

406 self.kwargs = kwargs 

407 self.creation_counter = UnboundField.creation_counter 

408 validators = kwargs.get("validators") 

409 if validators: 

410 self.field_class.check_validators(validators) 

411 

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

413 kw = dict( 

414 self.kwargs, 

415 name=name, 

416 _form=form, 

417 _prefix=prefix, 

418 _translations=translations, 

419 **kwargs, 

420 ) 

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

422 

423 def __repr__(self): 

424 return ( 

425 "<UnboundField(" 

426 f"{self.field_class.__name__}, {self.args!r}, {self.kwargs!r}" 

427 ")>" 

428 ) 

429 

430 

431class Flags: 

432 """ 

433 Holds a set of flags as attributes. 

434 

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

436 """ 

437 

438 def __getattr__(self, name): 

439 if name.startswith("_"): 

440 return super().__getattr__(name) 

441 return None 

442 

443 def __contains__(self, name): 

444 return getattr(self, name) 

445 

446 def __repr__(self): 

447 flags = ( 

448 f"{name}={getattr(self, name)}" 

449 for name in dir(self) 

450 if not name.startswith("_") 

451 ) 

452 flags = ", ".join(flags) 

453 return f"<wtforms.fields.Flags: {{{flags}}}>" 

454 

455 

456class Label: 

457 """ 

458 An HTML :mdn-tag:`label`. 

459 """ 

460 

461 def __init__(self, field_id, text): 

462 self.field_id = field_id 

463 self.text = text 

464 

465 def __str__(self): 

466 return self() 

467 

468 def __html__(self): 

469 return self() 

470 

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

472 if "for_" in kwargs: 

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

474 else: 

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

476 

477 attributes = widgets.html_params(**kwargs) 

478 attributes = f" {attributes}" if attributes else "" 

479 text = escape(text or self.text) 

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

481 

482 def __repr__(self): 

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