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

211 statements  

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

1from markupsafe import escape 

2from markupsafe import Markup 

3 

4__all__ = ( 

5 "CheckboxInput", 

6 "ColorInput", 

7 "DateInput", 

8 "DateTimeInput", 

9 "DateTimeLocalInput", 

10 "EmailInput", 

11 "FileInput", 

12 "HiddenInput", 

13 "ListWidget", 

14 "MonthInput", 

15 "NumberInput", 

16 "Option", 

17 "PasswordInput", 

18 "RadioInput", 

19 "RangeInput", 

20 "SearchInput", 

21 "Select", 

22 "SubmitInput", 

23 "TableWidget", 

24 "TextArea", 

25 "TextInput", 

26 "TelInput", 

27 "TimeInput", 

28 "URLInput", 

29 "WeekInput", 

30) 

31 

32 

33def clean_key(key): 

34 key = key.rstrip("_") 

35 if key.startswith("data_") or key.startswith("aria_"): 

36 key = key.replace("_", "-") 

37 return key 

38 

39 

40def html_params(**kwargs): 

41 """ 

42 Generate HTML attribute syntax from inputted keyword arguments. 

43 

44 The output value is sorted by the passed keys, to provide consistent output 

45 each time this function is called with the same parameters. Because of the 

46 frequent use of the normally reserved keywords `class` and `for`, suffixing 

47 these with an underscore will allow them to be used. 

48 

49 In order to facilitate the use of ``data-`` and ``aria-`` attributes, if the 

50 name of the attribute begins with ``data_`` or ``aria_``, then every 

51 underscore will be replaced with a hyphen in the generated attribute. 

52 

53 >>> html_params(data_attr='user.name', aria_labeledby='name') 

54 'data-attr="user.name" aria-labeledby="name"' 

55 

56 In addition, the values ``True`` and ``False`` are special: 

57 * ``attr=True`` generates the HTML compact output of a boolean attribute, 

58 e.g. ``checked=True`` will generate simply ``checked`` 

59 * ``attr=False`` will be ignored and generate no output. 

60 

61 >>> html_params(name='text1', id='f', class_='text') 

62 'class="text" id="f" name="text1"' 

63 >>> html_params(checked=True, readonly=False, name="text1", abc="hello") 

64 'abc="hello" checked name="text1"' 

65 

66 .. versionchanged:: 3.0 

67 ``aria_`` args convert underscores to hyphens like ``data_`` 

68 args. 

69 

70 .. versionchanged:: 2.2 

71 ``data_`` args convert all underscores to hyphens, instead of 

72 only the first one. 

73 """ 

74 params = [] 

75 for k, v in sorted(kwargs.items()): 

76 k = clean_key(k) 

77 if v is True: 

78 params.append(k) 

79 elif v is False: 

80 pass 

81 else: 

82 params.append(f'{str(k)}="{escape(v)}"') 

83 return " ".join(params) 

84 

85 

86class ListWidget: 

87 """ 

88 Renders a list of fields as a `ul` or `ol` list. 

89 

90 This is used for fields which encapsulate many inner fields as subfields. 

91 The widget will try to iterate the field to get access to the subfields and 

92 call them to render them. 

93 

94 If `prefix_label` is set, the subfield's label is printed before the field, 

95 otherwise afterwards. The latter is useful for iterating radios or 

96 checkboxes. 

97 """ 

98 

99 def __init__(self, html_tag="ul", prefix_label=True): 

100 assert html_tag in ("ol", "ul") 

101 self.html_tag = html_tag 

102 self.prefix_label = prefix_label 

103 

104 def __call__(self, field, **kwargs): 

105 kwargs.setdefault("id", field.id) 

106 html = [f"<{self.html_tag} {html_params(**kwargs)}>"] 

107 for subfield in field: 

108 if self.prefix_label: 

109 html.append(f"<li>{subfield.label} {subfield()}</li>") 

110 else: 

111 html.append(f"<li>{subfield()} {subfield.label}</li>") 

112 html.append("</%s>" % self.html_tag) 

113 return Markup("".join(html)) 

114 

115 

116class TableWidget: 

117 """ 

118 Renders a list of fields as a set of table rows with th/td pairs. 

119 

120 If `with_table_tag` is True, then an enclosing <table> is placed around the 

121 rows. 

122 

123 Hidden fields will not be displayed with a row, instead the field will be 

124 pushed into a subsequent table row to ensure XHTML validity. Hidden fields 

125 at the end of the field list will appear outside the table. 

126 """ 

127 

128 def __init__(self, with_table_tag=True): 

129 self.with_table_tag = with_table_tag 

130 

131 def __call__(self, field, **kwargs): 

132 html = [] 

133 if self.with_table_tag: 

134 kwargs.setdefault("id", field.id) 

135 html.append("<table %s>" % html_params(**kwargs)) 

136 hidden = "" 

137 for subfield in field: 

138 if subfield.type in ("HiddenField", "CSRFTokenField"): 

139 hidden += str(subfield) 

140 else: 

141 html.append( 

142 "<tr><th>%s</th><td>%s%s</td></tr>" 

143 % (str(subfield.label), hidden, str(subfield)) 

144 ) 

145 hidden = "" 

146 if self.with_table_tag: 

147 html.append("</table>") 

148 if hidden: 

149 html.append(hidden) 

150 return Markup("".join(html)) 

151 

152 

153class Input: 

154 """ 

155 Render a basic ``<input>`` field. 

156 

157 This is used as the basis for most of the other input fields. 

158 

159 By default, the `_value()` method will be called upon the associated field 

160 to provide the ``value=`` HTML attribute. 

161 """ 

162 

163 html_params = staticmethod(html_params) 

164 validation_attrs = ["required"] 

165 

166 def __init__(self, input_type=None): 

167 if input_type is not None: 

168 self.input_type = input_type 

169 

170 def __call__(self, field, **kwargs): 

171 kwargs.setdefault("id", field.id) 

172 kwargs.setdefault("type", self.input_type) 

173 if "value" not in kwargs: 

174 kwargs["value"] = field._value() 

175 flags = getattr(field, "flags", {}) 

176 for k in dir(flags): 

177 if k in self.validation_attrs and k not in kwargs: 

178 kwargs[k] = getattr(flags, k) 

179 return Markup("<input %s>" % self.html_params(name=field.name, **kwargs)) 

180 

181 

182class TextInput(Input): 

183 """ 

184 Render a single-line text input. 

185 """ 

186 

187 input_type = "text" 

188 validation_attrs = ["required", "maxlength", "minlength", "pattern"] 

189 

190 

191class PasswordInput(Input): 

192 """ 

193 Render a password input. 

194 

195 For security purposes, this field will not reproduce the value on a form 

196 submit by default. To have the value filled in, set `hide_value` to 

197 `False`. 

198 """ 

199 

200 input_type = "password" 

201 validation_attrs = ["required", "maxlength", "minlength", "pattern"] 

202 

203 def __init__(self, hide_value=True): 

204 self.hide_value = hide_value 

205 

206 def __call__(self, field, **kwargs): 

207 if self.hide_value: 

208 kwargs["value"] = "" 

209 return super().__call__(field, **kwargs) 

210 

211 

212class HiddenInput(Input): 

213 """ 

214 Render a hidden input. 

215 """ 

216 

217 input_type = "hidden" 

218 

219 def __init__(self, *args, **kwargs): 

220 super().__init__(*args, **kwargs) 

221 self.field_flags = {"hidden": True} 

222 

223 

224class CheckboxInput(Input): 

225 """ 

226 Render a checkbox. 

227 

228 The ``checked`` HTML attribute is set if the field's data is a non-false value. 

229 """ 

230 

231 input_type = "checkbox" 

232 

233 def __call__(self, field, **kwargs): 

234 if getattr(field, "checked", field.data): 

235 kwargs["checked"] = True 

236 return super().__call__(field, **kwargs) 

237 

238 

239class RadioInput(Input): 

240 """ 

241 Render a single radio button. 

242 

243 This widget is most commonly used in conjunction with ListWidget or some 

244 other listing, as singular radio buttons are not very useful. 

245 """ 

246 

247 input_type = "radio" 

248 

249 def __call__(self, field, **kwargs): 

250 if field.checked: 

251 kwargs["checked"] = True 

252 return super().__call__(field, **kwargs) 

253 

254 

255class FileInput(Input): 

256 """Render a file chooser input. 

257 

258 :param multiple: allow choosing multiple files 

259 """ 

260 

261 input_type = "file" 

262 validation_attrs = ["required", "accept"] 

263 

264 def __init__(self, multiple=False): 

265 super().__init__() 

266 self.multiple = multiple 

267 

268 def __call__(self, field, **kwargs): 

269 # browser ignores value of file input for security 

270 kwargs["value"] = False 

271 

272 if self.multiple: 

273 kwargs["multiple"] = True 

274 

275 return super().__call__(field, **kwargs) 

276 

277 

278class SubmitInput(Input): 

279 """ 

280 Renders a submit button. 

281 

282 The field's label is used as the text of the submit button instead of the 

283 data on the field. 

284 """ 

285 

286 input_type = "submit" 

287 

288 def __call__(self, field, **kwargs): 

289 kwargs.setdefault("value", field.label.text) 

290 return super().__call__(field, **kwargs) 

291 

292 

293class TextArea: 

294 """ 

295 Renders a multi-line text area. 

296 

297 `rows` and `cols` ought to be passed as keyword args when rendering. 

298 """ 

299 

300 validation_attrs = ["required", "maxlength", "minlength"] 

301 

302 def __call__(self, field, **kwargs): 

303 kwargs.setdefault("id", field.id) 

304 flags = getattr(field, "flags", {}) 

305 for k in dir(flags): 

306 if k in self.validation_attrs and k not in kwargs: 

307 kwargs[k] = getattr(flags, k) 

308 return Markup( 

309 "<textarea %s>\r\n%s</textarea>" 

310 % (html_params(name=field.name, **kwargs), escape(field._value())) 

311 ) 

312 

313 

314class Select: 

315 """ 

316 Renders a select field. 

317 

318 If `multiple` is True, then the `size` property should be specified on 

319 rendering to make the field useful. 

320 

321 The field must provide an `iter_choices()` method which the widget will 

322 call on rendering; this method must yield tuples of 

323 `(value, label, selected)`. 

324 It also must provide a `has_groups()` method which tells whether choices 

325 are divided into groups, and if they do, the field must have an 

326 `iter_groups()` method that yields tuples of `(label, choices)`, where 

327 `choices` is a iterable of `(value, label, selected)` tuples. 

328 """ 

329 

330 validation_attrs = ["required"] 

331 

332 def __init__(self, multiple=False): 

333 self.multiple = multiple 

334 

335 def __call__(self, field, **kwargs): 

336 kwargs.setdefault("id", field.id) 

337 if self.multiple: 

338 kwargs["multiple"] = True 

339 flags = getattr(field, "flags", {}) 

340 for k in dir(flags): 

341 if k in self.validation_attrs and k not in kwargs: 

342 kwargs[k] = getattr(flags, k) 

343 html = ["<select %s>" % html_params(name=field.name, **kwargs)] 

344 if field.has_groups(): 

345 for group, choices in field.iter_groups(): 

346 html.append("<optgroup %s>" % html_params(label=group)) 

347 for val, label, selected in choices: 

348 html.append(self.render_option(val, label, selected)) 

349 html.append("</optgroup>") 

350 else: 

351 for val, label, selected in field.iter_choices(): 

352 html.append(self.render_option(val, label, selected)) 

353 html.append("</select>") 

354 return Markup("".join(html)) 

355 

356 @classmethod 

357 def render_option(cls, value, label, selected, **kwargs): 

358 if value is True: 

359 # Handle the special case of a 'True' value. 

360 value = str(value) 

361 

362 options = dict(kwargs, value=value) 

363 if selected: 

364 options["selected"] = True 

365 return Markup(f"<option {html_params(**options)}>{escape(label)}</option>") 

366 

367 

368class Option: 

369 """ 

370 Renders the individual option from a select field. 

371 

372 This is just a convenience for various custom rendering situations, and an 

373 option by itself does not constitute an entire field. 

374 """ 

375 

376 def __call__(self, field, **kwargs): 

377 return Select.render_option( 

378 field._value(), field.label.text, field.checked, **kwargs 

379 ) 

380 

381 

382class SearchInput(Input): 

383 """ 

384 Renders an input with type "search". 

385 """ 

386 

387 input_type = "search" 

388 validation_attrs = ["required", "maxlength", "minlength", "pattern"] 

389 

390 

391class TelInput(Input): 

392 """ 

393 Renders an input with type "tel". 

394 """ 

395 

396 input_type = "tel" 

397 validation_attrs = ["required", "maxlength", "minlength", "pattern"] 

398 

399 

400class URLInput(Input): 

401 """ 

402 Renders an input with type "url". 

403 """ 

404 

405 input_type = "url" 

406 validation_attrs = ["required", "maxlength", "minlength", "pattern"] 

407 

408 

409class EmailInput(Input): 

410 """ 

411 Renders an input with type "email". 

412 """ 

413 

414 input_type = "email" 

415 validation_attrs = ["required", "maxlength", "minlength", "pattern"] 

416 

417 

418class DateTimeInput(Input): 

419 """ 

420 Renders an input with type "datetime". 

421 """ 

422 

423 input_type = "datetime" 

424 validation_attrs = ["required", "max", "min", "step"] 

425 

426 

427class DateInput(Input): 

428 """ 

429 Renders an input with type "date". 

430 """ 

431 

432 input_type = "date" 

433 validation_attrs = ["required", "max", "min", "step"] 

434 

435 

436class MonthInput(Input): 

437 """ 

438 Renders an input with type "month". 

439 """ 

440 

441 input_type = "month" 

442 validation_attrs = ["required", "max", "min", "step"] 

443 

444 

445class WeekInput(Input): 

446 """ 

447 Renders an input with type "week". 

448 """ 

449 

450 input_type = "week" 

451 validation_attrs = ["required", "max", "min", "step"] 

452 

453 

454class TimeInput(Input): 

455 """ 

456 Renders an input with type "time". 

457 """ 

458 

459 input_type = "time" 

460 validation_attrs = ["required", "max", "min", "step"] 

461 

462 

463class DateTimeLocalInput(Input): 

464 """ 

465 Renders an input with type "datetime-local". 

466 """ 

467 

468 input_type = "datetime-local" 

469 validation_attrs = ["required", "max", "min", "step"] 

470 

471 

472class NumberInput(Input): 

473 """ 

474 Renders an input with type "number". 

475 """ 

476 

477 input_type = "number" 

478 validation_attrs = ["required", "max", "min", "step"] 

479 

480 def __init__(self, step=None, min=None, max=None): 

481 self.step = step 

482 self.min = min 

483 self.max = max 

484 

485 def __call__(self, field, **kwargs): 

486 if self.step is not None: 

487 kwargs.setdefault("step", self.step) 

488 if self.min is not None: 

489 kwargs.setdefault("min", self.min) 

490 if self.max is not None: 

491 kwargs.setdefault("max", self.max) 

492 return super().__call__(field, **kwargs) 

493 

494 

495class RangeInput(Input): 

496 """ 

497 Renders an input with type "range". 

498 """ 

499 

500 input_type = "range" 

501 validation_attrs = ["required", "max", "min", "step"] 

502 

503 def __init__(self, step=None): 

504 self.step = step 

505 

506 def __call__(self, field, **kwargs): 

507 if self.step is not None: 

508 kwargs.setdefault("step", self.step) 

509 return super().__call__(field, **kwargs) 

510 

511 

512class ColorInput(Input): 

513 """ 

514 Renders an input with type "color". 

515 """ 

516 

517 input_type = "color"