Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/template/defaultfilters.py: 28%

507 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1"""Default variable filters.""" 

2import random as random_module 

3import re 

4import types 

5from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation 

6from functools import wraps 

7from operator import itemgetter 

8from pprint import pformat 

9from urllib.parse import quote 

10 

11from django.utils import formats 

12from django.utils.dateformat import format, time_format 

13from django.utils.encoding import iri_to_uri 

14from django.utils.html import avoid_wrapping, conditional_escape, escape, escapejs 

15from django.utils.html import json_script as _json_script 

16from django.utils.html import linebreaks, strip_tags 

17from django.utils.html import urlize as _urlize 

18from django.utils.safestring import SafeData, mark_safe 

19from django.utils.text import Truncator, normalize_newlines, phone2numeric 

20from django.utils.text import slugify as _slugify 

21from django.utils.text import wrap 

22from django.utils.timesince import timesince, timeuntil 

23from django.utils.translation import gettext, ngettext 

24 

25from .base import VARIABLE_ATTRIBUTE_SEPARATOR 

26from .library import Library 

27 

28register = Library() 

29 

30 

31####################### 

32# STRING DECORATOR # 

33####################### 

34 

35 

36def stringfilter(func): 

37 """ 

38 Decorator for filters which should only receive strings. The object 

39 passed as the first positional argument will be converted to a string. 

40 """ 

41 

42 def _dec(*args, **kwargs): 

43 args = list(args) 

44 args[0] = str(args[0]) 

45 if isinstance(args[0], SafeData) and getattr( 

46 _dec._decorated_function, "is_safe", False 

47 ): 

48 return mark_safe(func(*args, **kwargs)) 

49 return func(*args, **kwargs) 

50 

51 # Include a reference to the real function (used to check original 

52 # arguments by the template parser, and to bear the 'is_safe' attribute 

53 # when multiple decorators are applied). 

54 _dec._decorated_function = getattr(func, "_decorated_function", func) 

55 

56 return wraps(func)(_dec) 

57 

58 

59################### 

60# STRINGS # 

61################### 

62 

63 

64@register.filter(is_safe=True) 

65@stringfilter 

66def addslashes(value): 

67 """ 

68 Add slashes before quotes. Useful for escaping strings in CSV, for 

69 example. Less useful for escaping JavaScript; use the ``escapejs`` 

70 filter instead. 

71 """ 

72 return value.replace("\\", "\\\\").replace('"', '\\"').replace("'", "\\'") 

73 

74 

75@register.filter(is_safe=True) 

76@stringfilter 

77def capfirst(value): 

78 """Capitalize the first character of the value.""" 

79 return value and value[0].upper() + value[1:] 

80 

81 

82@register.filter("escapejs") 

83@stringfilter 

84def escapejs_filter(value): 

85 """Hex encode characters for use in JavaScript strings.""" 

86 return escapejs(value) 

87 

88 

89@register.filter(is_safe=True) 

90def json_script(value, element_id): 

91 """ 

92 Output value JSON-encoded, wrapped in a <script type="application/json"> 

93 tag. 

94 """ 

95 return _json_script(value, element_id) 

96 

97 

98@register.filter(is_safe=True) 

99def floatformat(text, arg=-1): 

100 """ 

101 Display a float to a specified number of decimal places. 

102 

103 If called without an argument, display the floating point number with one 

104 decimal place -- but only if there's a decimal place to be displayed: 

105 

106 * num1 = 34.23234 

107 * num2 = 34.00000 

108 * num3 = 34.26000 

109 * {{ num1|floatformat }} displays "34.2" 

110 * {{ num2|floatformat }} displays "34" 

111 * {{ num3|floatformat }} displays "34.3" 

112 

113 If arg is positive, always display exactly arg number of decimal places: 

114 

115 * {{ num1|floatformat:3 }} displays "34.232" 

116 * {{ num2|floatformat:3 }} displays "34.000" 

117 * {{ num3|floatformat:3 }} displays "34.260" 

118 

119 If arg is negative, display arg number of decimal places -- but only if 

120 there are places to be displayed: 

121 

122 * {{ num1|floatformat:"-3" }} displays "34.232" 

123 * {{ num2|floatformat:"-3" }} displays "34" 

124 * {{ num3|floatformat:"-3" }} displays "34.260" 

125 

126 If arg has the 'g' suffix, force the result to be grouped by the 

127 THOUSAND_SEPARATOR for the active locale. When the active locale is 

128 en (English): 

129 

130 * {{ 6666.6666|floatformat:"2g" }} displays "6,666.67" 

131 * {{ 10000|floatformat:"g" }} displays "10,000" 

132 

133 If arg has the 'u' suffix, force the result to be unlocalized. When the 

134 active locale is pl (Polish): 

135 

136 * {{ 66666.6666|floatformat:"2" }} displays "66666,67" 

137 * {{ 66666.6666|floatformat:"2u" }} displays "66666.67" 

138 

139 If the input float is infinity or NaN, display the string representation 

140 of that value. 

141 """ 

142 force_grouping = False 

143 use_l10n = True 

144 if isinstance(arg, str): 

145 last_char = arg[-1] 

146 if arg[-2:] in {"gu", "ug"}: 

147 force_grouping = True 

148 use_l10n = False 

149 arg = arg[:-2] or -1 

150 elif last_char == "g": 

151 force_grouping = True 

152 arg = arg[:-1] or -1 

153 elif last_char == "u": 

154 use_l10n = False 

155 arg = arg[:-1] or -1 

156 try: 

157 input_val = repr(text) 

158 d = Decimal(input_val) 

159 except InvalidOperation: 

160 try: 

161 d = Decimal(str(float(text))) 

162 except (ValueError, InvalidOperation, TypeError): 

163 return "" 

164 try: 

165 p = int(arg) 

166 except ValueError: 

167 return input_val 

168 

169 try: 

170 m = int(d) - d 

171 except (ValueError, OverflowError, InvalidOperation): 

172 return input_val 

173 

174 if not m and p < 0: 

175 return mark_safe( 

176 formats.number_format( 

177 "%d" % (int(d)), 

178 0, 

179 use_l10n=use_l10n, 

180 force_grouping=force_grouping, 

181 ) 

182 ) 

183 

184 exp = Decimal(1).scaleb(-abs(p)) 

185 # Set the precision high enough to avoid an exception (#15789). 

186 tupl = d.as_tuple() 

187 units = len(tupl[1]) 

188 units += -tupl[2] if m else tupl[2] 

189 prec = abs(p) + units + 1 

190 

191 # Avoid conversion to scientific notation by accessing `sign`, `digits`, 

192 # and `exponent` from Decimal.as_tuple() directly. 

193 rounded_d = d.quantize(exp, ROUND_HALF_UP, Context(prec=prec)) 

194 sign, digits, exponent = rounded_d.as_tuple() 

195 digits = [str(digit) for digit in reversed(digits)] 

196 while len(digits) <= abs(exponent): 

197 digits.append("0") 

198 digits.insert(-exponent, ".") 

199 if sign and rounded_d: 

200 digits.append("-") 

201 number = "".join(reversed(digits)) 

202 return mark_safe( 

203 formats.number_format( 

204 number, 

205 abs(p), 

206 use_l10n=use_l10n, 

207 force_grouping=force_grouping, 

208 ) 

209 ) 

210 

211 

212@register.filter(is_safe=True) 

213@stringfilter 

214def iriencode(value): 

215 """Escape an IRI value for use in a URL.""" 

216 return iri_to_uri(value) 

217 

218 

219@register.filter(is_safe=True, needs_autoescape=True) 

220@stringfilter 

221def linenumbers(value, autoescape=True): 

222 """Display text with line numbers.""" 

223 lines = value.split("\n") 

224 # Find the maximum width of the line count, for use with zero padding 

225 # string format command 

226 width = str(len(str(len(lines)))) 

227 if not autoescape or isinstance(value, SafeData): 

228 for i, line in enumerate(lines): 

229 lines[i] = ("%0" + width + "d. %s") % (i + 1, line) 

230 else: 

231 for i, line in enumerate(lines): 

232 lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) 

233 return mark_safe("\n".join(lines)) 

234 

235 

236@register.filter(is_safe=True) 

237@stringfilter 

238def lower(value): 

239 """Convert a string into all lowercase.""" 

240 return value.lower() 

241 

242 

243@register.filter(is_safe=False) 

244@stringfilter 

245def make_list(value): 

246 """ 

247 Return the value turned into a list. 

248 

249 For an integer, it's a list of digits. 

250 For a string, it's a list of characters. 

251 """ 

252 return list(value) 

253 

254 

255@register.filter(is_safe=True) 

256@stringfilter 

257def slugify(value): 

258 """ 

259 Convert to ASCII. Convert spaces to hyphens. Remove characters that aren't 

260 alphanumerics, underscores, or hyphens. Convert to lowercase. Also strip 

261 leading and trailing whitespace. 

262 """ 

263 return _slugify(value) 

264 

265 

266@register.filter(is_safe=True) 

267def stringformat(value, arg): 

268 """ 

269 Format the variable according to the arg, a string formatting specifier. 

270 

271 This specifier uses Python string formatting syntax, with the exception 

272 that the leading "%" is dropped. 

273 

274 See https://docs.python.org/library/stdtypes.html#printf-style-string-formatting 

275 for documentation of Python string formatting. 

276 """ 

277 if isinstance(value, tuple): 

278 value = str(value) 

279 try: 

280 return ("%" + str(arg)) % value 

281 except (ValueError, TypeError): 

282 return "" 

283 

284 

285@register.filter(is_safe=True) 

286@stringfilter 

287def title(value): 

288 """Convert a string into titlecase.""" 

289 t = re.sub("([a-z])'([A-Z])", lambda m: m[0].lower(), value.title()) 

290 return re.sub(r"\d([A-Z])", lambda m: m[0].lower(), t) 

291 

292 

293@register.filter(is_safe=True) 

294@stringfilter 

295def truncatechars(value, arg): 

296 """Truncate a string after `arg` number of characters.""" 

297 try: 

298 length = int(arg) 

299 except ValueError: # Invalid literal for int(). 

300 return value # Fail silently. 

301 return Truncator(value).chars(length) 

302 

303 

304@register.filter(is_safe=True) 

305@stringfilter 

306def truncatechars_html(value, arg): 

307 """ 

308 Truncate HTML after `arg` number of chars. 

309 Preserve newlines in the HTML. 

310 """ 

311 try: 

312 length = int(arg) 

313 except ValueError: # invalid literal for int() 

314 return value # Fail silently. 

315 return Truncator(value).chars(length, html=True) 

316 

317 

318@register.filter(is_safe=True) 

319@stringfilter 

320def truncatewords(value, arg): 

321 """ 

322 Truncate a string after `arg` number of words. 

323 Remove newlines within the string. 

324 """ 

325 try: 

326 length = int(arg) 

327 except ValueError: # Invalid literal for int(). 

328 return value # Fail silently. 

329 return Truncator(value).words(length, truncate=" …") 

330 

331 

332@register.filter(is_safe=True) 

333@stringfilter 

334def truncatewords_html(value, arg): 

335 """ 

336 Truncate HTML after `arg` number of words. 

337 Preserve newlines in the HTML. 

338 """ 

339 try: 

340 length = int(arg) 

341 except ValueError: # invalid literal for int() 

342 return value # Fail silently. 

343 return Truncator(value).words(length, html=True, truncate=" …") 

344 

345 

346@register.filter(is_safe=False) 

347@stringfilter 

348def upper(value): 

349 """Convert a string into all uppercase.""" 

350 return value.upper() 

351 

352 

353@register.filter(is_safe=False) 

354@stringfilter 

355def urlencode(value, safe=None): 

356 """ 

357 Escape a value for use in a URL. 

358 

359 The ``safe`` parameter determines the characters which should not be 

360 escaped by Python's quote() function. If not provided, use the default safe 

361 characters (but an empty string can be provided when *all* characters 

362 should be escaped). 

363 """ 

364 kwargs = {} 

365 if safe is not None: 

366 kwargs["safe"] = safe 

367 return quote(value, **kwargs) 

368 

369 

370@register.filter(is_safe=True, needs_autoescape=True) 

371@stringfilter 

372def urlize(value, autoescape=True): 

373 """Convert URLs in plain text into clickable links.""" 

374 return mark_safe(_urlize(value, nofollow=True, autoescape=autoescape)) 

375 

376 

377@register.filter(is_safe=True, needs_autoescape=True) 

378@stringfilter 

379def urlizetrunc(value, limit, autoescape=True): 

380 """ 

381 Convert URLs into clickable links, truncating URLs to the given character 

382 limit, and adding 'rel=nofollow' attribute to discourage spamming. 

383 

384 Argument: Length to truncate URLs to. 

385 """ 

386 return mark_safe( 

387 _urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape) 

388 ) 

389 

390 

391@register.filter(is_safe=False) 

392@stringfilter 

393def wordcount(value): 

394 """Return the number of words.""" 

395 return len(value.split()) 

396 

397 

398@register.filter(is_safe=True) 

399@stringfilter 

400def wordwrap(value, arg): 

401 """Wrap words at `arg` line length.""" 

402 return wrap(value, int(arg)) 

403 

404 

405@register.filter(is_safe=True) 

406@stringfilter 

407def ljust(value, arg): 

408 """Left-align the value in a field of a given width.""" 

409 return value.ljust(int(arg)) 

410 

411 

412@register.filter(is_safe=True) 

413@stringfilter 

414def rjust(value, arg): 

415 """Right-align the value in a field of a given width.""" 

416 return value.rjust(int(arg)) 

417 

418 

419@register.filter(is_safe=True) 

420@stringfilter 

421def center(value, arg): 

422 """Center the value in a field of a given width.""" 

423 return value.center(int(arg)) 

424 

425 

426@register.filter 

427@stringfilter 

428def cut(value, arg): 

429 """Remove all values of arg from the given string.""" 

430 safe = isinstance(value, SafeData) 

431 value = value.replace(arg, "") 

432 if safe and arg != ";": 

433 return mark_safe(value) 

434 return value 

435 

436 

437################### 

438# HTML STRINGS # 

439################### 

440 

441 

442@register.filter("escape", is_safe=True) 

443@stringfilter 

444def escape_filter(value): 

445 """Mark the value as a string that should be auto-escaped.""" 

446 return conditional_escape(value) 

447 

448 

449@register.filter(is_safe=True) 

450@stringfilter 

451def force_escape(value): 

452 """ 

453 Escape a string's HTML. Return a new string containing the escaped 

454 characters (as opposed to "escape", which marks the content for later 

455 possible escaping). 

456 """ 

457 return escape(value) 

458 

459 

460@register.filter("linebreaks", is_safe=True, needs_autoescape=True) 

461@stringfilter 

462def linebreaks_filter(value, autoescape=True): 

463 """ 

464 Replace line breaks in plain text with appropriate HTML; a single 

465 newline becomes an HTML line break (``<br>``) and a new line 

466 followed by a blank line becomes a paragraph break (``</p>``). 

467 """ 

468 autoescape = autoescape and not isinstance(value, SafeData) 

469 return mark_safe(linebreaks(value, autoescape)) 

470 

471 

472@register.filter(is_safe=True, needs_autoescape=True) 

473@stringfilter 

474def linebreaksbr(value, autoescape=True): 

475 """ 

476 Convert all newlines in a piece of plain text to HTML line breaks 

477 (``<br>``). 

478 """ 

479 autoescape = autoescape and not isinstance(value, SafeData) 

480 value = normalize_newlines(value) 

481 if autoescape: 

482 value = escape(value) 

483 return mark_safe(value.replace("\n", "<br>")) 

484 

485 

486@register.filter(is_safe=True) 

487@stringfilter 

488def safe(value): 

489 """Mark the value as a string that should not be auto-escaped.""" 

490 return mark_safe(value) 

491 

492 

493@register.filter(is_safe=True) 

494def safeseq(value): 

495 """ 

496 A "safe" filter for sequences. Mark each element in the sequence, 

497 individually, as safe, after converting them to strings. Return a list 

498 with the results. 

499 """ 

500 return [mark_safe(obj) for obj in value] 

501 

502 

503@register.filter(is_safe=True) 

504@stringfilter 

505def striptags(value): 

506 """Strip all [X]HTML tags.""" 

507 return strip_tags(value) 

508 

509 

510################### 

511# LISTS # 

512################### 

513 

514 

515def _property_resolver(arg): 

516 """ 

517 When arg is convertible to float, behave like operator.itemgetter(arg) 

518 Otherwise, chain __getitem__() and getattr(). 

519 

520 >>> _property_resolver(1)('abc') 

521 'b' 

522 >>> _property_resolver('1')('abc') 

523 Traceback (most recent call last): 

524 ... 

525 TypeError: string indices must be integers 

526 >>> class Foo: 

527 ... a = 42 

528 ... b = 3.14 

529 ... c = 'Hey!' 

530 >>> _property_resolver('b')(Foo()) 

531 3.14 

532 """ 

533 try: 

534 float(arg) 

535 except ValueError: 

536 if VARIABLE_ATTRIBUTE_SEPARATOR + "_" in arg or arg[0] == "_": 

537 raise AttributeError("Access to private variables is forbidden.") 

538 parts = arg.split(VARIABLE_ATTRIBUTE_SEPARATOR) 

539 

540 def resolve(value): 

541 for part in parts: 

542 try: 

543 value = value[part] 

544 except (AttributeError, IndexError, KeyError, TypeError, ValueError): 

545 value = getattr(value, part) 

546 return value 

547 

548 return resolve 

549 else: 

550 return itemgetter(arg) 

551 

552 

553@register.filter(is_safe=False) 

554def dictsort(value, arg): 

555 """ 

556 Given a list of dicts, return that list sorted by the property given in 

557 the argument. 

558 """ 

559 try: 

560 return sorted(value, key=_property_resolver(arg)) 

561 except (AttributeError, TypeError): 

562 return "" 

563 

564 

565@register.filter(is_safe=False) 

566def dictsortreversed(value, arg): 

567 """ 

568 Given a list of dicts, return that list sorted in reverse order by the 

569 property given in the argument. 

570 """ 

571 try: 

572 return sorted(value, key=_property_resolver(arg), reverse=True) 

573 except (AttributeError, TypeError): 

574 return "" 

575 

576 

577@register.filter(is_safe=False) 

578def first(value): 

579 """Return the first item in a list.""" 

580 try: 

581 return value[0] 

582 except IndexError: 

583 return "" 

584 

585 

586@register.filter(is_safe=True, needs_autoescape=True) 

587def join(value, arg, autoescape=True): 

588 """Join a list with a string, like Python's ``str.join(list)``.""" 

589 try: 

590 if autoescape: 

591 value = [conditional_escape(v) for v in value] 

592 data = conditional_escape(arg).join(value) 

593 except TypeError: # Fail silently if arg isn't iterable. 

594 return value 

595 return mark_safe(data) 

596 

597 

598@register.filter(is_safe=True) 

599def last(value): 

600 """Return the last item in a list.""" 

601 try: 

602 return value[-1] 

603 except IndexError: 

604 return "" 

605 

606 

607@register.filter(is_safe=False) 

608def length(value): 

609 """Return the length of the value - useful for lists.""" 

610 try: 

611 return len(value) 

612 except (ValueError, TypeError): 

613 return 0 

614 

615 

616@register.filter(is_safe=False) 

617def length_is(value, arg): 

618 """Return a boolean of whether the value's length is the argument.""" 

619 try: 

620 return len(value) == int(arg) 

621 except (ValueError, TypeError): 

622 return "" 

623 

624 

625@register.filter(is_safe=True) 

626def random(value): 

627 """Return a random item from the list.""" 

628 return random_module.choice(value) 

629 

630 

631@register.filter("slice", is_safe=True) 

632def slice_filter(value, arg): 

633 """ 

634 Return a slice of the list using the same syntax as Python's list slicing. 

635 """ 

636 try: 

637 bits = [] 

638 for x in str(arg).split(":"): 

639 if not x: 

640 bits.append(None) 

641 else: 

642 bits.append(int(x)) 

643 return value[slice(*bits)] 

644 

645 except (ValueError, TypeError): 

646 return value # Fail silently. 

647 

648 

649@register.filter(is_safe=True, needs_autoescape=True) 

650def unordered_list(value, autoescape=True): 

651 """ 

652 Recursively take a self-nested list and return an HTML unordered list -- 

653 WITHOUT opening and closing <ul> tags. 

654 

655 Assume the list is in the proper format. For example, if ``var`` contains: 

656 ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then 

657 ``{{ var|unordered_list }}`` returns:: 

658 

659 <li>States 

660 <ul> 

661 <li>Kansas 

662 <ul> 

663 <li>Lawrence</li> 

664 <li>Topeka</li> 

665 </ul> 

666 </li> 

667 <li>Illinois</li> 

668 </ul> 

669 </li> 

670 """ 

671 if autoescape: 

672 escaper = conditional_escape 

673 else: 

674 

675 def escaper(x): 

676 return x 

677 

678 def walk_items(item_list): 

679 item_iterator = iter(item_list) 

680 try: 

681 item = next(item_iterator) 

682 while True: 

683 try: 

684 next_item = next(item_iterator) 

685 except StopIteration: 

686 yield item, None 

687 break 

688 if isinstance(next_item, (list, tuple, types.GeneratorType)): 

689 try: 

690 iter(next_item) 

691 except TypeError: 

692 pass 

693 else: 

694 yield item, next_item 

695 item = next(item_iterator) 

696 continue 

697 yield item, None 

698 item = next_item 

699 except StopIteration: 

700 pass 

701 

702 def list_formatter(item_list, tabs=1): 

703 indent = "\t" * tabs 

704 output = [] 

705 for item, children in walk_items(item_list): 

706 sublist = "" 

707 if children: 

708 sublist = "\n%s<ul>\n%s\n%s</ul>\n%s" % ( 

709 indent, 

710 list_formatter(children, tabs + 1), 

711 indent, 

712 indent, 

713 ) 

714 output.append("%s<li>%s%s</li>" % (indent, escaper(item), sublist)) 

715 return "\n".join(output) 

716 

717 return mark_safe(list_formatter(value)) 

718 

719 

720################### 

721# INTEGERS # 

722################### 

723 

724 

725@register.filter(is_safe=False) 

726def add(value, arg): 

727 """Add the arg to the value.""" 

728 try: 

729 return int(value) + int(arg) 

730 except (ValueError, TypeError): 

731 try: 

732 return value + arg 

733 except Exception: 

734 return "" 

735 

736 

737@register.filter(is_safe=False) 

738def get_digit(value, arg): 

739 """ 

740 Given a whole number, return the requested digit of it, where 1 is the 

741 right-most digit, 2 is the second-right-most digit, etc. Return the 

742 original value for invalid input (if input or argument is not an integer, 

743 or if argument is less than 1). Otherwise, output is always an integer. 

744 """ 

745 try: 

746 arg = int(arg) 

747 value = int(value) 

748 except ValueError: 

749 return value # Fail silently for an invalid argument 

750 if arg < 1: 

751 return value 

752 try: 

753 return int(str(value)[-arg]) 

754 except IndexError: 

755 return 0 

756 

757 

758################### 

759# DATES # 

760################### 

761 

762 

763@register.filter(expects_localtime=True, is_safe=False) 

764def date(value, arg=None): 

765 """Format a date according to the given format.""" 

766 if value in (None, ""): 

767 return "" 

768 try: 

769 return formats.date_format(value, arg) 

770 except AttributeError: 

771 try: 

772 return format(value, arg) 

773 except AttributeError: 

774 return "" 

775 

776 

777@register.filter(expects_localtime=True, is_safe=False) 

778def time(value, arg=None): 

779 """Format a time according to the given format.""" 

780 if value in (None, ""): 

781 return "" 

782 try: 

783 return formats.time_format(value, arg) 

784 except (AttributeError, TypeError): 

785 try: 

786 return time_format(value, arg) 

787 except (AttributeError, TypeError): 

788 return "" 

789 

790 

791@register.filter("timesince", is_safe=False) 

792def timesince_filter(value, arg=None): 

793 """Format a date as the time since that date (i.e. "4 days, 6 hours").""" 

794 if not value: 

795 return "" 

796 try: 

797 if arg: 

798 return timesince(value, arg) 

799 return timesince(value) 

800 except (ValueError, TypeError): 

801 return "" 

802 

803 

804@register.filter("timeuntil", is_safe=False) 

805def timeuntil_filter(value, arg=None): 

806 """Format a date as the time until that date (i.e. "4 days, 6 hours").""" 

807 if not value: 

808 return "" 

809 try: 

810 return timeuntil(value, arg) 

811 except (ValueError, TypeError): 

812 return "" 

813 

814 

815################### 

816# LOGIC # 

817################### 

818 

819 

820@register.filter(is_safe=False) 

821def default(value, arg): 

822 """If value is unavailable, use given default.""" 

823 return value or arg 

824 

825 

826@register.filter(is_safe=False) 

827def default_if_none(value, arg): 

828 """If value is None, use given default.""" 

829 if value is None: 

830 return arg 

831 return value 

832 

833 

834@register.filter(is_safe=False) 

835def divisibleby(value, arg): 

836 """Return True if the value is divisible by the argument.""" 

837 return int(value) % int(arg) == 0 

838 

839 

840@register.filter(is_safe=False) 

841def yesno(value, arg=None): 

842 """ 

843 Given a string mapping values for true, false, and (optionally) None, 

844 return one of those strings according to the value: 

845 

846 ========== ====================== ================================== 

847 Value Argument Outputs 

848 ========== ====================== ================================== 

849 ``True`` ``"yeah,no,maybe"`` ``yeah`` 

850 ``False`` ``"yeah,no,maybe"`` ``no`` 

851 ``None`` ``"yeah,no,maybe"`` ``maybe`` 

852 ``None`` ``"yeah,no"`` ``"no"`` (converts None to False 

853 if no mapping for None is given. 

854 ========== ====================== ================================== 

855 """ 

856 if arg is None: 

857 # Translators: Please do not add spaces around commas. 

858 arg = gettext("yes,no,maybe") 

859 bits = arg.split(",") 

860 if len(bits) < 2: 

861 return value # Invalid arg. 

862 try: 

863 yes, no, maybe = bits 

864 except ValueError: 

865 # Unpack list of wrong size (no "maybe" value provided). 

866 yes, no, maybe = bits[0], bits[1], bits[1] 

867 if value is None: 

868 return maybe 

869 if value: 

870 return yes 

871 return no 

872 

873 

874################### 

875# MISC # 

876################### 

877 

878 

879@register.filter(is_safe=True) 

880def filesizeformat(bytes_): 

881 """ 

882 Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 

883 102 bytes, etc.). 

884 """ 

885 try: 

886 bytes_ = int(bytes_) 

887 except (TypeError, ValueError, UnicodeDecodeError): 

888 value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {"size": 0} 

889 return avoid_wrapping(value) 

890 

891 def filesize_number_format(value): 

892 return formats.number_format(round(value, 1), 1) 

893 

894 KB = 1 << 10 

895 MB = 1 << 20 

896 GB = 1 << 30 

897 TB = 1 << 40 

898 PB = 1 << 50 

899 

900 negative = bytes_ < 0 

901 if negative: 

902 bytes_ = -bytes_ # Allow formatting of negative numbers. 

903 

904 if bytes_ < KB: 

905 value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {"size": bytes_} 

906 elif bytes_ < MB: 

907 value = gettext("%s KB") % filesize_number_format(bytes_ / KB) 

908 elif bytes_ < GB: 

909 value = gettext("%s MB") % filesize_number_format(bytes_ / MB) 

910 elif bytes_ < TB: 

911 value = gettext("%s GB") % filesize_number_format(bytes_ / GB) 

912 elif bytes_ < PB: 

913 value = gettext("%s TB") % filesize_number_format(bytes_ / TB) 

914 else: 

915 value = gettext("%s PB") % filesize_number_format(bytes_ / PB) 

916 

917 if negative: 

918 value = "-%s" % value 

919 return avoid_wrapping(value) 

920 

921 

922@register.filter(is_safe=False) 

923def pluralize(value, arg="s"): 

924 """ 

925 Return a plural suffix if the value is not 1, '1', or an object of 

926 length 1. By default, use 's' as the suffix: 

927 

928 * If value is 0, vote{{ value|pluralize }} display "votes". 

929 * If value is 1, vote{{ value|pluralize }} display "vote". 

930 * If value is 2, vote{{ value|pluralize }} display "votes". 

931 

932 If an argument is provided, use that string instead: 

933 

934 * If value is 0, class{{ value|pluralize:"es" }} display "classes". 

935 * If value is 1, class{{ value|pluralize:"es" }} display "class". 

936 * If value is 2, class{{ value|pluralize:"es" }} display "classes". 

937 

938 If the provided argument contains a comma, use the text before the comma 

939 for the singular case and the text after the comma for the plural case: 

940 

941 * If value is 0, cand{{ value|pluralize:"y,ies" }} display "candies". 

942 * If value is 1, cand{{ value|pluralize:"y,ies" }} display "candy". 

943 * If value is 2, cand{{ value|pluralize:"y,ies" }} display "candies". 

944 """ 

945 if "," not in arg: 

946 arg = "," + arg 

947 bits = arg.split(",") 

948 if len(bits) > 2: 

949 return "" 

950 singular_suffix, plural_suffix = bits[:2] 

951 

952 try: 

953 return singular_suffix if float(value) == 1 else plural_suffix 

954 except ValueError: # Invalid string that's not a number. 

955 pass 

956 except TypeError: # Value isn't a string or a number; maybe it's a list? 

957 try: 

958 return singular_suffix if len(value) == 1 else plural_suffix 

959 except TypeError: # len() of unsized object. 

960 pass 

961 return "" 

962 

963 

964@register.filter("phone2numeric", is_safe=True) 

965def phone2numeric_filter(value): 

966 """Take a phone number and converts it in to its numerical equivalent.""" 

967 return phone2numeric(value) 

968 

969 

970@register.filter(is_safe=True) 

971def pprint(value): 

972 """A wrapper around pprint.pprint -- for debugging, really.""" 

973 try: 

974 return pformat(value) 

975 except Exception as e: 

976 return "Error in formatting: %s: %s" % (e.__class__.__name__, e)