Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/template/defaultfilters.py: 33%

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

517 statements  

1"""Default variable filters.""" 

2 

3import random as random_module 

4import re 

5import types 

6from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation, getcontext 

7from functools import wraps 

8from inspect import unwrap 

9from operator import itemgetter 

10from pprint import pformat 

11from urllib.parse import quote 

12 

13from django.utils import formats 

14from django.utils.dateformat import format, time_format 

15from django.utils.encoding import iri_to_uri 

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

17from django.utils.html import json_script as _json_script 

18from django.utils.html import linebreaks, strip_tags 

19from django.utils.html import urlize as _urlize 

20from django.utils.safestring import SafeData, mark_safe 

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

22from django.utils.text import slugify as _slugify 

23from django.utils.text import wrap 

24from django.utils.timesince import timesince, timeuntil 

25from django.utils.translation import gettext, ngettext 

26 

27from .base import VARIABLE_ATTRIBUTE_SEPARATOR 

28from .library import Library 

29 

30register = Library() 

31 

32 

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

34# STRING DECORATOR # 

35####################### 

36 

37 

38def stringfilter(func): 

39 """ 

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

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

42 """ 

43 

44 @wraps(func) 

45 def _dec(first, *args, **kwargs): 

46 first = str(first) 

47 result = func(first, *args, **kwargs) 

48 if isinstance(first, SafeData) and getattr(unwrap(func), "is_safe", False): 

49 result = mark_safe(result) 

50 return result 

51 

52 return _dec 

53 

54 

55################### 

56# STRINGS # 

57################### 

58 

59 

60@register.filter(is_safe=True) 

61@stringfilter 

62def addslashes(value): 

63 """ 

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

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

66 filter instead. 

67 """ 

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

69 

70 

71@register.filter(is_safe=True) 

72@stringfilter 

73def capfirst(value): 

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

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

76 

77 

78@register.filter("escapejs") 

79@stringfilter 

80def escapejs_filter(value): 

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

82 return escapejs(value) 

83 

84 

85@register.filter(is_safe=True) 

86def json_script(value, element_id=None): 

87 """ 

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

89 tag (with an optional id). 

90 """ 

91 return _json_script(value, element_id) 

92 

93 

94@register.filter(is_safe=True) 

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

96 """ 

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

98 

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

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

101 

102 * num1 = 34.23234 

103 * num2 = 34.00000 

104 * num3 = 34.26000 

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

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

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

108 

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

110 

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

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

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

114 

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

116 there are places to be displayed: 

117 

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

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

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

121 

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

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

124 en (English): 

125 

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

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

128 

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

130 active locale is pl (Polish): 

131 

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

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

134 

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

136 of that value. 

137 """ 

138 force_grouping = False 

139 use_l10n = True 

140 if isinstance(arg, str): 

141 last_char = arg[-1] 

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

143 force_grouping = True 

144 use_l10n = False 

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

146 elif last_char == "g": 

147 force_grouping = True 

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

149 elif last_char == "u": 

150 use_l10n = False 

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

152 try: 

153 input_val = str(text) 

154 d = Decimal(input_val) 

155 except InvalidOperation: 

156 try: 

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

158 except (ValueError, InvalidOperation, TypeError): 

159 return "" 

160 try: 

161 p = int(arg) 

162 except ValueError: 

163 return input_val 

164 

165 _, digits, exponent = d.as_tuple() 

166 try: 

167 number_of_digits_and_exponent_sum = len(digits) + abs(exponent) 

168 except TypeError: 

169 # Exponent values can be "F", "n", "N". 

170 number_of_digits_and_exponent_sum = 0 

171 

172 # Values with more than 200 digits, or with a large exponent, are returned "as is" 

173 # to avoid high memory consumption and potential denial-of-service attacks. 

174 # The cut-off of 200 is consistent with django.utils.numberformat.floatformat(). 

175 if number_of_digits_and_exponent_sum > 200: 

176 return input_val 

177 

178 try: 

179 m = int(d) - d 

180 except (ValueError, OverflowError, InvalidOperation): 

181 return input_val 

182 

183 if not m and p <= 0: 

184 return mark_safe( 

185 formats.number_format( 

186 "%d" % (int(d)), 

187 0, 

188 use_l10n=use_l10n, 

189 force_grouping=force_grouping, 

190 ) 

191 ) 

192 

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

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

195 tupl = d.as_tuple() 

196 units = len(tupl[1]) 

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

198 prec = abs(p) + units + 1 

199 prec = max(getcontext().prec, prec) 

200 

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

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

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

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

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

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

207 digits.append("0") 

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

209 if sign and rounded_d: 

210 digits.append("-") 

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

212 return mark_safe( 

213 formats.number_format( 

214 number, 

215 abs(p), 

216 use_l10n=use_l10n, 

217 force_grouping=force_grouping, 

218 ) 

219 ) 

220 

221 

222@register.filter(is_safe=True) 

223@stringfilter 

224def iriencode(value): 

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

226 return iri_to_uri(value) 

227 

228 

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

230@stringfilter 

231def linenumbers(value, autoescape=True): 

232 """Display text with line numbers.""" 

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

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

235 # string format command 

236 width = str(len(str(len(lines)))) 

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

238 for i, line in enumerate(lines): 

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

240 else: 

241 for i, line in enumerate(lines): 

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

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

244 

245 

246@register.filter(is_safe=True) 

247@stringfilter 

248def lower(value): 

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

250 return value.lower() 

251 

252 

253@register.filter(is_safe=False) 

254@stringfilter 

255def make_list(value): 

256 """ 

257 Return the value turned into a list. 

258 

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

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

261 """ 

262 return list(value) 

263 

264 

265@register.filter(is_safe=True) 

266@stringfilter 

267def slugify(value): 

268 """ 

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

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

271 leading and trailing whitespace. 

272 """ 

273 return _slugify(value) 

274 

275 

276@register.filter(is_safe=True) 

277def stringformat(value, arg): 

278 """ 

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

280 

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

282 that the leading "%" is dropped. 

283 

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

285 for documentation of Python string formatting. 

286 """ 

287 if isinstance(value, tuple): 

288 value = str(value) 

289 try: 

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

291 except (ValueError, TypeError): 

292 return "" 

293 

294 

295@register.filter(is_safe=True) 

296@stringfilter 

297def title(value): 

298 """Convert a string into titlecase.""" 

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

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

301 

302 

303@register.filter(is_safe=True) 

304@stringfilter 

305def truncatechars(value, arg): 

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

307 try: 

308 length = int(arg) 

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

310 return value # Fail silently. 

311 return Truncator(value).chars(length) 

312 

313 

314@register.filter(is_safe=True) 

315@stringfilter 

316def truncatechars_html(value, arg): 

317 """ 

318 Truncate HTML after `arg` number of chars. 

319 Preserve newlines in the HTML. 

320 """ 

321 try: 

322 length = int(arg) 

323 except ValueError: # invalid literal for int() 

324 return value # Fail silently. 

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

326 

327 

328@register.filter(is_safe=True) 

329@stringfilter 

330def truncatewords(value, arg): 

331 """ 

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

333 Remove newlines within the string. 

334 """ 

335 try: 

336 length = int(arg) 

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

338 return value # Fail silently. 

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

340 

341 

342@register.filter(is_safe=True) 

343@stringfilter 

344def truncatewords_html(value, arg): 

345 """ 

346 Truncate HTML after `arg` number of words. 

347 Preserve newlines in the HTML. 

348 """ 

349 try: 

350 length = int(arg) 

351 except ValueError: # invalid literal for int() 

352 return value # Fail silently. 

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

354 

355 

356@register.filter(is_safe=False) 

357@stringfilter 

358def upper(value): 

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

360 return value.upper() 

361 

362 

363@register.filter(is_safe=False) 

364@stringfilter 

365def urlencode(value, safe=None): 

366 """ 

367 Escape a value for use in a URL. 

368 

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

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

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

372 should be escaped). 

373 """ 

374 kwargs = {} 

375 if safe is not None: 

376 kwargs["safe"] = safe 

377 return quote(value, **kwargs) 

378 

379 

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

381@stringfilter 

382def urlize(value, autoescape=True): 

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

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

385 

386 

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

388@stringfilter 

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

390 """ 

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

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

393 

394 Argument: Length to truncate URLs to. 

395 """ 

396 return mark_safe( 

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

398 ) 

399 

400 

401@register.filter(is_safe=False) 

402@stringfilter 

403def wordcount(value): 

404 """Return the number of words.""" 

405 return len(value.split()) 

406 

407 

408@register.filter(is_safe=True) 

409@stringfilter 

410def wordwrap(value, arg): 

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

412 return wrap(value, int(arg)) 

413 

414 

415@register.filter(is_safe=True) 

416@stringfilter 

417def ljust(value, arg): 

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

419 return value.ljust(int(arg)) 

420 

421 

422@register.filter(is_safe=True) 

423@stringfilter 

424def rjust(value, arg): 

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

426 return value.rjust(int(arg)) 

427 

428 

429@register.filter(is_safe=True) 

430@stringfilter 

431def center(value, arg): 

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

433 return value.center(int(arg)) 

434 

435 

436@register.filter 

437@stringfilter 

438def cut(value, arg): 

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

440 safe = isinstance(value, SafeData) 

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

442 if safe and arg != ";": 

443 return mark_safe(value) 

444 return value 

445 

446 

447################### 

448# HTML STRINGS # 

449################### 

450 

451 

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

453@stringfilter 

454def escape_filter(value): 

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

456 return conditional_escape(value) 

457 

458 

459@register.filter(is_safe=True) 

460def escapeseq(value): 

461 """ 

462 An "escape" filter for sequences. Mark each element in the sequence, 

463 individually, as a string that should be auto-escaped. Return a list with 

464 the results. 

465 """ 

466 return [conditional_escape(obj) for obj in value] 

467 

468 

469@register.filter(is_safe=True) 

470@stringfilter 

471def force_escape(value): 

472 """ 

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

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

475 possible escaping). 

476 """ 

477 return escape(value) 

478 

479 

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

481@stringfilter 

482def linebreaks_filter(value, autoescape=True): 

483 """ 

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

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

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

487 """ 

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

489 return mark_safe(linebreaks(value, autoescape)) 

490 

491 

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

493@stringfilter 

494def linebreaksbr(value, autoescape=True): 

495 """ 

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

497 (``<br>``). 

498 """ 

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

500 value = normalize_newlines(value) 

501 if autoescape: 

502 value = escape(value) 

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

504 

505 

506@register.filter(is_safe=True) 

507@stringfilter 

508def safe(value): 

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

510 return mark_safe(value) 

511 

512 

513@register.filter(is_safe=True) 

514def safeseq(value): 

515 """ 

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

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

518 with the results. 

519 """ 

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

521 

522 

523@register.filter(is_safe=True) 

524@stringfilter 

525def striptags(value): 

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

527 return strip_tags(value) 

528 

529 

530################### 

531# LISTS # 

532################### 

533 

534 

535def _property_resolver(arg): 

536 """ 

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

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

539 

540 >>> _property_resolver(1)('abc') 

541 'b' 

542 >>> _property_resolver('1')('abc') 

543 Traceback (most recent call last): 

544 ... 

545 TypeError: string indices must be integers 

546 >>> class Foo: 

547 ... a = 42 

548 ... b = 3.14 

549 ... c = 'Hey!' 

550 >>> _property_resolver('b')(Foo()) 

551 3.14 

552 """ 

553 try: 

554 float(arg) 

555 except ValueError: 

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

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

558 parts = arg.split(VARIABLE_ATTRIBUTE_SEPARATOR) 

559 

560 def resolve(value): 

561 for part in parts: 

562 try: 

563 value = value[part] 

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

565 value = getattr(value, part) 

566 return value 

567 

568 return resolve 

569 else: 

570 return itemgetter(arg) 

571 

572 

573@register.filter(is_safe=False) 

574def dictsort(value, arg): 

575 """ 

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

577 the argument. 

578 """ 

579 try: 

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

581 except (AttributeError, TypeError): 

582 return "" 

583 

584 

585@register.filter(is_safe=False) 

586def dictsortreversed(value, arg): 

587 """ 

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

589 property given in the argument. 

590 """ 

591 try: 

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

593 except (AttributeError, TypeError): 

594 return "" 

595 

596 

597@register.filter(is_safe=False) 

598def first(value): 

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

600 try: 

601 return value[0] 

602 except IndexError: 

603 return "" 

604 

605 

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

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

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

609 try: 

610 if autoescape: 

611 data = conditional_escape(arg).join([conditional_escape(v) for v in value]) 

612 else: 

613 data = arg.join(value) 

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

615 return value 

616 return mark_safe(data) 

617 

618 

619@register.filter(is_safe=True) 

620def last(value): 

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

622 try: 

623 return value[-1] 

624 except IndexError: 

625 return "" 

626 

627 

628@register.filter(is_safe=False) 

629def length(value): 

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

631 try: 

632 return len(value) 

633 except (ValueError, TypeError): 

634 return 0 

635 

636 

637@register.filter(is_safe=True) 

638def random(value): 

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

640 try: 

641 return random_module.choice(value) 

642 except IndexError: 

643 return "" 

644 

645 

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

647def slice_filter(value, arg): 

648 """ 

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

650 """ 

651 try: 

652 bits = [] 

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

654 if not x: 

655 bits.append(None) 

656 else: 

657 bits.append(int(x)) 

658 return value[slice(*bits)] 

659 

660 except (ValueError, TypeError, KeyError): 

661 return value # Fail silently. 

662 

663 

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

665def unordered_list(value, autoescape=True): 

666 """ 

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

668 WITHOUT opening and closing <ul> tags. 

669 

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

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

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

673 

674 <li>States 

675 <ul> 

676 <li>Kansas 

677 <ul> 

678 <li>Lawrence</li> 

679 <li>Topeka</li> 

680 </ul> 

681 </li> 

682 <li>Illinois</li> 

683 </ul> 

684 </li> 

685 """ 

686 if autoescape: 

687 escaper = conditional_escape 

688 else: 

689 

690 def escaper(x): 

691 return x 

692 

693 def walk_items(item_list): 

694 item_iterator = iter(item_list) 

695 try: 

696 item = next(item_iterator) 

697 while True: 

698 try: 

699 next_item = next(item_iterator) 

700 except StopIteration: 

701 yield item, None 

702 break 

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

704 try: 

705 iter(next_item) 

706 except TypeError: 

707 pass 

708 else: 

709 yield item, next_item 

710 item = next(item_iterator) 

711 continue 

712 yield item, None 

713 item = next_item 

714 except StopIteration: 

715 pass 

716 

717 def list_formatter(item_list, tabs=1): 

718 indent = "\t" * tabs 

719 output = [] 

720 for item, children in walk_items(item_list): 

721 sublist = "" 

722 if children: 

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

724 indent, 

725 list_formatter(children, tabs + 1), 

726 indent, 

727 indent, 

728 ) 

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

730 return "\n".join(output) 

731 

732 return mark_safe(list_formatter(value)) 

733 

734 

735################### 

736# INTEGERS # 

737################### 

738 

739 

740@register.filter(is_safe=False) 

741def add(value, arg): 

742 """Add the arg to the value.""" 

743 try: 

744 return int(value) + int(arg) 

745 except (ValueError, TypeError): 

746 try: 

747 return value + arg 

748 except Exception: 

749 return "" 

750 

751 

752@register.filter(is_safe=False) 

753def get_digit(value, arg): 

754 """ 

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

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

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

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

759 """ 

760 try: 

761 arg = int(arg) 

762 value = int(value) 

763 except ValueError: 

764 return value # Fail silently for an invalid argument 

765 if arg < 1: 

766 return value 

767 try: 

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

769 except IndexError: 

770 return 0 

771 

772 

773################### 

774# DATES # 

775################### 

776 

777 

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

779def date(value, arg=None): 

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

781 if value in (None, ""): 

782 return "" 

783 try: 

784 return formats.date_format(value, arg) 

785 except AttributeError: 

786 try: 

787 return format(value, arg) 

788 except AttributeError: 

789 return "" 

790 

791 

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

793def time(value, arg=None): 

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

795 if value in (None, ""): 

796 return "" 

797 try: 

798 return formats.time_format(value, arg) 

799 except (AttributeError, TypeError): 

800 try: 

801 return time_format(value, arg) 

802 except (AttributeError, TypeError): 

803 return "" 

804 

805 

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

807def timesince_filter(value, arg=None): 

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

809 if not value: 

810 return "" 

811 try: 

812 if arg: 

813 return timesince(value, arg) 

814 return timesince(value) 

815 except (ValueError, TypeError): 

816 return "" 

817 

818 

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

820def timeuntil_filter(value, arg=None): 

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

822 if not value: 

823 return "" 

824 try: 

825 return timeuntil(value, arg) 

826 except (ValueError, TypeError): 

827 return "" 

828 

829 

830################### 

831# LOGIC # 

832################### 

833 

834 

835@register.filter(is_safe=False) 

836def default(value, arg): 

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

838 return value or arg 

839 

840 

841@register.filter(is_safe=False) 

842def default_if_none(value, arg): 

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

844 if value is None: 

845 return arg 

846 return value 

847 

848 

849@register.filter(is_safe=False) 

850def divisibleby(value, arg): 

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

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

853 

854 

855@register.filter(is_safe=False) 

856def yesno(value, arg=None): 

857 """ 

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

859 return one of those strings according to the value: 

860 

861 ========== ====================== ================================== 

862 Value Argument Outputs 

863 ========== ====================== ================================== 

864 ``True`` ``"yeah,no,maybe"`` ``yeah`` 

865 ``False`` ``"yeah,no,maybe"`` ``no`` 

866 ``None`` ``"yeah,no,maybe"`` ``maybe`` 

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

868 if no mapping for None is given. 

869 ========== ====================== ================================== 

870 """ 

871 if arg is None: 

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

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

874 bits = arg.split(",") 

875 if len(bits) < 2: 

876 return value # Invalid arg. 

877 try: 

878 yes, no, maybe = bits 

879 except ValueError: 

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

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

882 if value is None: 

883 return maybe 

884 if value: 

885 return yes 

886 return no 

887 

888 

889################### 

890# MISC # 

891################### 

892 

893 

894@register.filter(is_safe=True) 

895def filesizeformat(bytes_): 

896 """ 

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

898 102 bytes, etc.). 

899 """ 

900 try: 

901 bytes_ = int(bytes_) 

902 except (TypeError, ValueError, UnicodeDecodeError): 

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

904 return avoid_wrapping(value) 

905 

906 def filesize_number_format(value): 

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

908 

909 KB = 1 << 10 

910 MB = 1 << 20 

911 GB = 1 << 30 

912 TB = 1 << 40 

913 PB = 1 << 50 

914 

915 negative = bytes_ < 0 

916 if negative: 

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

918 

919 if bytes_ < KB: 

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

921 elif bytes_ < MB: 

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

923 elif bytes_ < GB: 

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

925 elif bytes_ < TB: 

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

927 elif bytes_ < PB: 

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

929 else: 

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

931 

932 if negative: 

933 value = "-%s" % value 

934 return avoid_wrapping(value) 

935 

936 

937@register.filter(is_safe=False) 

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

939 """ 

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

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

942 

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

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

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

946 

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

948 

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

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

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

952 

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

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

955 

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

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

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

959 """ 

960 if "," not in arg: 

961 arg = "," + arg 

962 bits = arg.split(",") 

963 if len(bits) > 2: 

964 return "" 

965 singular_suffix, plural_suffix = bits[:2] 

966 

967 try: 

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

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

970 pass 

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

972 try: 

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

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

975 pass 

976 return "" 

977 

978 

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

980def phone2numeric_filter(value): 

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

982 return phone2numeric(value) 

983 

984 

985@register.filter(is_safe=True) 

986def pprint(value): 

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

988 try: 

989 return pformat(value) 

990 except Exception as e: 

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