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

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

179 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 widget=None, 

43 render_kw=None, 

44 name=None, 

45 _form=None, 

46 _prefix="", 

47 _translations=None, 

48 _meta=None, 

49 ): 

50 """ 

51 Construct a new field. 

52 

53 :param label: 

54 The label of the field. 

55 :param validators: 

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

57 :param filters: 

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

59 to filter or transform the input data. For example 

60 ``StringForm(filters=[str.strip, str.upper])``. 

61 Note that filters are applied after processing the default and 

62 incoming data, but before validation. 

63 :param description: 

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

65 :param id: 

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

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

68 :param default: 

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

70 input is provided. May be a callable. 

71 :param widget: 

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

73 :param dict render_kw: 

74 If provided, a dictionary which provides default keywords that 

75 will be given to the widget at render time. 

76 :param name: 

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

78 attribute name. 

79 :param _form: 

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

81 construction. You should never pass this value yourself. 

82 :param _prefix: 

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

84 the enclosing form during construction. 

85 :param _translations: 

86 A translations object providing message translations. Usually 

87 passed by the enclosing form during construction. See 

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

89 :param _meta: 

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

91 don't pass this yourself. 

92 

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

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

95 a name to construct the field. 

96 """ 

97 if _translations is not None: 

98 self._translations = _translations 

99 

100 if _meta is not None: 

101 self.meta = _meta 

102 elif _form is not None: 

103 self.meta = _form.meta 

104 else: 

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

106 

107 self.default = default 

108 self.description = description 

109 self.render_kw = render_kw 

110 self.filters = filters 

111 self.flags = Flags() 

112 self.name = _prefix + name 

113 self.short_name = name 

114 self.type = type(self).__name__ 

115 

116 self.check_validators(validators) 

117 self.validators = validators or self.validators 

118 

119 self.id = id or self.name 

120 self.label = Label( 

121 self.id, 

122 label 

123 if label is not None 

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

125 ) 

126 

127 if widget is not None: 

128 self.widget = widget 

129 

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

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

132 

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

134 setattr(self.flags, k, v) 

135 

136 def __str__(self): 

137 """ 

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

139 see the `__call__` method. 

140 """ 

141 return self() 

142 

143 def __html__(self): 

144 """ 

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

146 see the :meth:`__call__` method. 

147 """ 

148 return self() 

149 

150 def __call__(self, **kwargs): 

151 """ 

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

153 

154 This delegates rendering to 

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

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

157 keyword arguments from this call along to the widget. 

158 

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

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

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

162 even do anything related to HTML. 

163 """ 

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

165 

166 @classmethod 

167 def check_validators(cls, validators): 

168 if validators is not None: 

169 for validator in validators: 

170 if not callable(validator): 

171 raise TypeError( 

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

173 "callable" 

174 ) 

175 

176 if inspect.isclass(validator): 

177 raise TypeError( 

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

179 "it should be an instance" 

180 ) 

181 

182 def gettext(self, string): 

183 """ 

184 Get a translation for the given message. 

185 

186 This proxies for the internal translations object. 

187 

188 :param string: A string to be translated. 

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

190 """ 

191 return self._translations.gettext(string) 

192 

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

194 """ 

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

196 

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

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

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

200 """ 

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

202 

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

204 """ 

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

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

207 called by `Form.validate`. 

208 

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

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

211 

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

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

214 """ 

215 self.errors = list(self.process_errors) 

216 stop_validation = False 

217 

218 # Check the type of extra_validators 

219 self.check_validators(extra_validators) 

220 

221 # Call pre_validate 

222 try: 

223 self.pre_validate(form) 

224 except StopValidation as e: 

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

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

227 stop_validation = True 

228 except ValidationError as e: 

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

230 

231 # Run validators 

232 if not stop_validation: 

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

234 stop_validation = self._run_validation_chain(form, chain) 

235 

236 # Call post_validate 

237 try: 

238 self.post_validate(form, stop_validation) 

239 except ValidationError as e: 

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

241 

242 return len(self.errors) == 0 

243 

244 def _run_validation_chain(self, form, validators): 

245 """ 

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

247 

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

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

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

251 """ 

252 for validator in validators: 

253 try: 

254 validator(form, self) 

255 except StopValidation as e: 

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

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

258 return True 

259 except ValidationError as e: 

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

261 

262 return False 

263 

264 def pre_validate(self, form): 

265 """ 

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

267 validators. 

268 

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

270 """ 

271 pass 

272 

273 def post_validate(self, form, validation_stopped): 

274 """ 

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

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

277 

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

279 :param validation_stopped: 

280 `True` if any validator raised StopValidation. 

281 """ 

282 pass 

283 

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

285 """ 

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

287 and run filters. 

288 

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

290 default. 

291 

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

293 process_formdata and process_data methods. Only override this for 

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

295 inputs. 

296 

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

298 """ 

299 self.process_errors = [] 

300 if data is unset_value: 

301 try: 

302 data = self.default() 

303 except TypeError: 

304 data = self.default 

305 

306 self.object_data = data 

307 

308 try: 

309 self.process_data(data) 

310 except ValueError as e: 

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

312 

313 if formdata is not None: 

314 if self.name in formdata: 

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

316 else: 

317 self.raw_data = [] 

318 

319 try: 

320 self.process_formdata(self.raw_data) 

321 except ValueError as e: 

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

323 

324 try: 

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

326 self.data = filter(self.data) 

327 except ValueError as e: 

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

329 

330 def process_data(self, value): 

331 """ 

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

333 

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

335 `obj` argument. 

336 

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

338 """ 

339 self.data = value 

340 

341 def process_formdata(self, valuelist): 

342 """ 

343 Process data received over the wire from a form. 

344 

345 This will be called during form construction with data supplied 

346 through the `formdata` argument. 

347 

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

349 """ 

350 if valuelist: 

351 self.data = valuelist[0] 

352 

353 def populate_obj(self, obj, name): 

354 """ 

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

356 

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

358 it will be overridden. Use with caution. 

359 """ 

360 setattr(obj, name, self.data) 

361 

362 

363class UnboundField: 

364 _formfield = True 

365 creation_counter = 0 

366 

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

368 UnboundField.creation_counter += 1 

369 self.field_class = field_class 

370 self.args = args 

371 self.name = name 

372 self.kwargs = kwargs 

373 self.creation_counter = UnboundField.creation_counter 

374 validators = kwargs.get("validators") 

375 if validators: 

376 self.field_class.check_validators(validators) 

377 

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

379 kw = dict( 

380 self.kwargs, 

381 name=name, 

382 _form=form, 

383 _prefix=prefix, 

384 _translations=translations, 

385 **kwargs, 

386 ) 

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

388 

389 def __repr__(self): 

390 return ( 

391 "<UnboundField(" 

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

393 ")>" 

394 ) 

395 

396 

397class Flags: 

398 """ 

399 Holds a set of flags as attributes. 

400 

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

402 """ 

403 

404 def __getattr__(self, name): 

405 if name.startswith("_"): 

406 return super().__getattr__(name) 

407 return None 

408 

409 def __contains__(self, name): 

410 return getattr(self, name) 

411 

412 def __repr__(self): 

413 flags = ( 

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

415 for name in dir(self) 

416 if not name.startswith("_") 

417 ) 

418 flags = ", ".join(flags) 

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

420 

421 

422class Label: 

423 """ 

424 An HTML form label. 

425 """ 

426 

427 def __init__(self, field_id, text): 

428 self.field_id = field_id 

429 self.text = text 

430 

431 def __str__(self): 

432 return self() 

433 

434 def __html__(self): 

435 return self() 

436 

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

438 if "for_" in kwargs: 

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

440 else: 

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

442 

443 attributes = widgets.html_params(**kwargs) 

444 text = escape(text or self.text) 

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

446 

447 def __repr__(self): 

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