123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 |
- """Default variable filters."""
- import random as random_module
- import re
- import types
- from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation
- from functools import wraps
- from operator import itemgetter
- from pprint import pformat
- from urllib.parse import quote
- from django.utils import formats
- from django.utils.dateformat import format, time_format
- from django.utils.encoding import iri_to_uri
- from django.utils.html import (
- avoid_wrapping, conditional_escape, escape, escapejs,
- json_script as _json_script, linebreaks, strip_tags, urlize as _urlize,
- )
- from django.utils.safestring import SafeData, mark_safe
- from django.utils.text import (
- Truncator, normalize_newlines, phone2numeric, slugify as _slugify, wrap,
- )
- from django.utils.timesince import timesince, timeuntil
- from django.utils.translation import gettext, ngettext
- from .base import Variable, VariableDoesNotExist
- from .library import Library
- register = Library()
- #######################
- # STRING DECORATOR #
- #######################
- def stringfilter(func):
- """
- Decorator for filters which should only receive strings. The object
- passed as the first positional argument will be converted to a string.
- """
- def _dec(*args, **kwargs):
- args = list(args)
- args[0] = str(args[0])
- if (isinstance(args[0], SafeData) and
- getattr(_dec._decorated_function, 'is_safe', False)):
- return mark_safe(func(*args, **kwargs))
- return func(*args, **kwargs)
- # Include a reference to the real function (used to check original
- # arguments by the template parser, and to bear the 'is_safe' attribute
- # when multiple decorators are applied).
- _dec._decorated_function = getattr(func, '_decorated_function', func)
- return wraps(func)(_dec)
- ###################
- # STRINGS #
- ###################
- @register.filter(is_safe=True)
- @stringfilter
- def addslashes(value):
- """
- Add slashes before quotes. Useful for escaping strings in CSV, for
- example. Less useful for escaping JavaScript; use the ``escapejs``
- filter instead.
- """
- return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
- @register.filter(is_safe=True)
- @stringfilter
- def capfirst(value):
- """Capitalize the first character of the value."""
- return value and value[0].upper() + value[1:]
- @register.filter("escapejs")
- @stringfilter
- def escapejs_filter(value):
- """Hex encode characters for use in JavaScript strings."""
- return escapejs(value)
- @register.filter(is_safe=True)
- def json_script(value, element_id):
- """
- Output value JSON-encoded, wrapped in a <script type="application/json">
- tag.
- """
- return _json_script(value, element_id)
- @register.filter(is_safe=True)
- def floatformat(text, arg=-1):
- """
- Display a float to a specified number of decimal places.
- If called without an argument, display the floating point number with one
- decimal place -- but only if there's a decimal place to be displayed:
- * num1 = 34.23234
- * num2 = 34.00000
- * num3 = 34.26000
- * {{ num1|floatformat }} displays "34.2"
- * {{ num2|floatformat }} displays "34"
- * {{ num3|floatformat }} displays "34.3"
- If arg is positive, always display exactly arg number of decimal places:
- * {{ num1|floatformat:3 }} displays "34.232"
- * {{ num2|floatformat:3 }} displays "34.000"
- * {{ num3|floatformat:3 }} displays "34.260"
- If arg is negative, display arg number of decimal places -- but only if
- there are places to be displayed:
- * {{ num1|floatformat:"-3" }} displays "34.232"
- * {{ num2|floatformat:"-3" }} displays "34"
- * {{ num3|floatformat:"-3" }} displays "34.260"
- If arg has the 'g' suffix, force the result to be grouped by the
- THOUSAND_SEPARATOR for the active locale. When the active locale is
- en (English):
- * {{ 6666.6666|floatformat:"2g" }} displays "6,666.67"
- * {{ 10000|floatformat:"g" }} displays "10,000"
- If arg has the 'u' suffix, force the result to be unlocalized. When the
- active locale is pl (Polish):
- * {{ 66666.6666|floatformat:"2" }} displays "66666,67"
- * {{ 66666.6666|floatformat:"2u" }} displays "66666.67"
- If the input float is infinity or NaN, display the string representation
- of that value.
- """
- force_grouping = False
- use_l10n = True
- if isinstance(arg, str):
- last_char = arg[-1]
- if arg[-2:] in {'gu', 'ug'}:
- force_grouping = True
- use_l10n = False
- arg = arg[:-2] or -1
- elif last_char == 'g':
- force_grouping = True
- arg = arg[:-1] or -1
- elif last_char == 'u':
- use_l10n = False
- arg = arg[:-1] or -1
- try:
- input_val = repr(text)
- d = Decimal(input_val)
- except InvalidOperation:
- try:
- d = Decimal(str(float(text)))
- except (ValueError, InvalidOperation, TypeError):
- return ''
- try:
- p = int(arg)
- except ValueError:
- return input_val
- try:
- m = int(d) - d
- except (ValueError, OverflowError, InvalidOperation):
- return input_val
- if not m and p < 0:
- return mark_safe(formats.number_format(
- '%d' % (int(d)),
- 0,
- use_l10n=use_l10n,
- force_grouping=force_grouping,
- ))
- exp = Decimal(1).scaleb(-abs(p))
- # Set the precision high enough to avoid an exception (#15789).
- tupl = d.as_tuple()
- units = len(tupl[1])
- units += -tupl[2] if m else tupl[2]
- prec = abs(p) + units + 1
- # Avoid conversion to scientific notation by accessing `sign`, `digits`,
- # and `exponent` from Decimal.as_tuple() directly.
- rounded_d = d.quantize(exp, ROUND_HALF_UP, Context(prec=prec))
- sign, digits, exponent = rounded_d.as_tuple()
- digits = [str(digit) for digit in reversed(digits)]
- while len(digits) <= abs(exponent):
- digits.append('0')
- digits.insert(-exponent, '.')
- if sign and rounded_d:
- digits.append('-')
- number = ''.join(reversed(digits))
- return mark_safe(formats.number_format(
- number,
- abs(p),
- use_l10n=use_l10n,
- force_grouping=force_grouping,
- ))
- @register.filter(is_safe=True)
- @stringfilter
- def iriencode(value):
- """Escape an IRI value for use in a URL."""
- return iri_to_uri(value)
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def linenumbers(value, autoescape=True):
- """Display text with line numbers."""
- lines = value.split('\n')
- # Find the maximum width of the line count, for use with zero padding
- # string format command
- width = str(len(str(len(lines))))
- if not autoescape or isinstance(value, SafeData):
- for i, line in enumerate(lines):
- lines[i] = ("%0" + width + "d. %s") % (i + 1, line)
- else:
- for i, line in enumerate(lines):
- lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
- return mark_safe('\n'.join(lines))
- @register.filter(is_safe=True)
- @stringfilter
- def lower(value):
- """Convert a string into all lowercase."""
- return value.lower()
- @register.filter(is_safe=False)
- @stringfilter
- def make_list(value):
- """
- Return the value turned into a list.
- For an integer, it's a list of digits.
- For a string, it's a list of characters.
- """
- return list(value)
- @register.filter(is_safe=True)
- @stringfilter
- def slugify(value):
- """
- Convert to ASCII. Convert spaces to hyphens. Remove characters that aren't
- alphanumerics, underscores, or hyphens. Convert to lowercase. Also strip
- leading and trailing whitespace.
- """
- return _slugify(value)
- @register.filter(is_safe=True)
- def stringformat(value, arg):
- """
- Format the variable according to the arg, a string formatting specifier.
- This specifier uses Python string formatting syntax, with the exception
- that the leading "%" is dropped.
- See https://docs.python.org/library/stdtypes.html#printf-style-string-formatting
- for documentation of Python string formatting.
- """
- if isinstance(value, tuple):
- value = str(value)
- try:
- return ("%" + str(arg)) % value
- except (ValueError, TypeError):
- return ""
- @register.filter(is_safe=True)
- @stringfilter
- def title(value):
- """Convert a string into titlecase."""
- t = re.sub("([a-z])'([A-Z])", lambda m: m[0].lower(), value.title())
- return re.sub(r'\d([A-Z])', lambda m: m[0].lower(), t)
- @register.filter(is_safe=True)
- @stringfilter
- def truncatechars(value, arg):
- """Truncate a string after `arg` number of characters."""
- try:
- length = int(arg)
- except ValueError: # Invalid literal for int().
- return value # Fail silently.
- return Truncator(value).chars(length)
- @register.filter(is_safe=True)
- @stringfilter
- def truncatechars_html(value, arg):
- """
- Truncate HTML after `arg` number of chars.
- Preserve newlines in the HTML.
- """
- try:
- length = int(arg)
- except ValueError: # invalid literal for int()
- return value # Fail silently.
- return Truncator(value).chars(length, html=True)
- @register.filter(is_safe=True)
- @stringfilter
- def truncatewords(value, arg):
- """
- Truncate a string after `arg` number of words.
- Remove newlines within the string.
- """
- try:
- length = int(arg)
- except ValueError: # Invalid literal for int().
- return value # Fail silently.
- return Truncator(value).words(length, truncate=' …')
- @register.filter(is_safe=True)
- @stringfilter
- def truncatewords_html(value, arg):
- """
- Truncate HTML after `arg` number of words.
- Preserve newlines in the HTML.
- """
- try:
- length = int(arg)
- except ValueError: # invalid literal for int()
- return value # Fail silently.
- return Truncator(value).words(length, html=True, truncate=' …')
- @register.filter(is_safe=False)
- @stringfilter
- def upper(value):
- """Convert a string into all uppercase."""
- return value.upper()
- @register.filter(is_safe=False)
- @stringfilter
- def urlencode(value, safe=None):
- """
- Escape a value for use in a URL.
- The ``safe`` parameter determines the characters which should not be
- escaped by Python's quote() function. If not provided, use the default safe
- characters (but an empty string can be provided when *all* characters
- should be escaped).
- """
- kwargs = {}
- if safe is not None:
- kwargs['safe'] = safe
- return quote(value, **kwargs)
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def urlize(value, autoescape=True):
- """Convert URLs in plain text into clickable links."""
- return mark_safe(_urlize(value, nofollow=True, autoescape=autoescape))
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def urlizetrunc(value, limit, autoescape=True):
- """
- Convert URLs into clickable links, truncating URLs to the given character
- limit, and adding 'rel=nofollow' attribute to discourage spamming.
- Argument: Length to truncate URLs to.
- """
- return mark_safe(_urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape))
- @register.filter(is_safe=False)
- @stringfilter
- def wordcount(value):
- """Return the number of words."""
- return len(value.split())
- @register.filter(is_safe=True)
- @stringfilter
- def wordwrap(value, arg):
- """Wrap words at `arg` line length."""
- return wrap(value, int(arg))
- @register.filter(is_safe=True)
- @stringfilter
- def ljust(value, arg):
- """Left-align the value in a field of a given width."""
- return value.ljust(int(arg))
- @register.filter(is_safe=True)
- @stringfilter
- def rjust(value, arg):
- """Right-align the value in a field of a given width."""
- return value.rjust(int(arg))
- @register.filter(is_safe=True)
- @stringfilter
- def center(value, arg):
- """Center the value in a field of a given width."""
- return value.center(int(arg))
- @register.filter
- @stringfilter
- def cut(value, arg):
- """Remove all values of arg from the given string."""
- safe = isinstance(value, SafeData)
- value = value.replace(arg, '')
- if safe and arg != ';':
- return mark_safe(value)
- return value
- ###################
- # HTML STRINGS #
- ###################
- @register.filter("escape", is_safe=True)
- @stringfilter
- def escape_filter(value):
- """Mark the value as a string that should be auto-escaped."""
- return conditional_escape(value)
- @register.filter(is_safe=True)
- @stringfilter
- def force_escape(value):
- """
- Escape a string's HTML. Return a new string containing the escaped
- characters (as opposed to "escape", which marks the content for later
- possible escaping).
- """
- return escape(value)
- @register.filter("linebreaks", is_safe=True, needs_autoescape=True)
- @stringfilter
- def linebreaks_filter(value, autoescape=True):
- """
- Replace line breaks in plain text with appropriate HTML; a single
- newline becomes an HTML line break (``<br>``) and a new line
- followed by a blank line becomes a paragraph break (``</p>``).
- """
- autoescape = autoescape and not isinstance(value, SafeData)
- return mark_safe(linebreaks(value, autoescape))
- @register.filter(is_safe=True, needs_autoescape=True)
- @stringfilter
- def linebreaksbr(value, autoescape=True):
- """
- Convert all newlines in a piece of plain text to HTML line breaks
- (``<br>``).
- """
- autoescape = autoescape and not isinstance(value, SafeData)
- value = normalize_newlines(value)
- if autoescape:
- value = escape(value)
- return mark_safe(value.replace('\n', '<br>'))
- @register.filter(is_safe=True)
- @stringfilter
- def safe(value):
- """Mark the value as a string that should not be auto-escaped."""
- return mark_safe(value)
- @register.filter(is_safe=True)
- def safeseq(value):
- """
- A "safe" filter for sequences. Mark each element in the sequence,
- individually, as safe, after converting them to strings. Return a list
- with the results.
- """
- return [mark_safe(obj) for obj in value]
- @register.filter(is_safe=True)
- @stringfilter
- def striptags(value):
- """Strip all [X]HTML tags."""
- return strip_tags(value)
- ###################
- # LISTS #
- ###################
- def _property_resolver(arg):
- """
- When arg is convertible to float, behave like operator.itemgetter(arg)
- Otherwise, behave like Variable(arg).resolve
- >>> _property_resolver(1)('abc')
- 'b'
- >>> _property_resolver('1')('abc')
- Traceback (most recent call last):
- ...
- TypeError: string indices must be integers
- >>> class Foo:
- ... a = 42
- ... b = 3.14
- ... c = 'Hey!'
- >>> _property_resolver('b')(Foo())
- 3.14
- """
- try:
- float(arg)
- except ValueError:
- return Variable(arg).resolve
- else:
- return itemgetter(arg)
- @register.filter(is_safe=False)
- def dictsort(value, arg):
- """
- Given a list of dicts, return that list sorted by the property given in
- the argument.
- """
- try:
- return sorted(value, key=_property_resolver(arg))
- except (TypeError, VariableDoesNotExist):
- return ''
- @register.filter(is_safe=False)
- def dictsortreversed(value, arg):
- """
- Given a list of dicts, return that list sorted in reverse order by the
- property given in the argument.
- """
- try:
- return sorted(value, key=_property_resolver(arg), reverse=True)
- except (TypeError, VariableDoesNotExist):
- return ''
- @register.filter(is_safe=False)
- def first(value):
- """Return the first item in a list."""
- try:
- return value[0]
- except IndexError:
- return ''
- @register.filter(is_safe=True, needs_autoescape=True)
- def join(value, arg, autoescape=True):
- """Join a list with a string, like Python's ``str.join(list)``."""
- try:
- if autoescape:
- value = [conditional_escape(v) for v in value]
- data = conditional_escape(arg).join(value)
- except TypeError: # Fail silently if arg isn't iterable.
- return value
- return mark_safe(data)
- @register.filter(is_safe=True)
- def last(value):
- """Return the last item in a list."""
- try:
- return value[-1]
- except IndexError:
- return ''
- @register.filter(is_safe=False)
- def length(value):
- """Return the length of the value - useful for lists."""
- try:
- return len(value)
- except (ValueError, TypeError):
- return 0
- @register.filter(is_safe=False)
- def length_is(value, arg):
- """Return a boolean of whether the value's length is the argument."""
- try:
- return len(value) == int(arg)
- except (ValueError, TypeError):
- return ''
- @register.filter(is_safe=True)
- def random(value):
- """Return a random item from the list."""
- return random_module.choice(value)
- @register.filter("slice", is_safe=True)
- def slice_filter(value, arg):
- """
- Return a slice of the list using the same syntax as Python's list slicing.
- """
- try:
- bits = []
- for x in str(arg).split(':'):
- if not x:
- bits.append(None)
- else:
- bits.append(int(x))
- return value[slice(*bits)]
- except (ValueError, TypeError):
- return value # Fail silently.
- @register.filter(is_safe=True, needs_autoescape=True)
- def unordered_list(value, autoescape=True):
- """
- Recursively take a self-nested list and return an HTML unordered list --
- WITHOUT opening and closing <ul> tags.
- Assume the list is in the proper format. For example, if ``var`` contains:
- ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
- ``{{ var|unordered_list }}`` returns::
- <li>States
- <ul>
- <li>Kansas
- <ul>
- <li>Lawrence</li>
- <li>Topeka</li>
- </ul>
- </li>
- <li>Illinois</li>
- </ul>
- </li>
- """
- if autoescape:
- escaper = conditional_escape
- else:
- def escaper(x):
- return x
- def walk_items(item_list):
- item_iterator = iter(item_list)
- try:
- item = next(item_iterator)
- while True:
- try:
- next_item = next(item_iterator)
- except StopIteration:
- yield item, None
- break
- if isinstance(next_item, (list, tuple, types.GeneratorType)):
- try:
- iter(next_item)
- except TypeError:
- pass
- else:
- yield item, next_item
- item = next(item_iterator)
- continue
- yield item, None
- item = next_item
- except StopIteration:
- pass
- def list_formatter(item_list, tabs=1):
- indent = '\t' * tabs
- output = []
- for item, children in walk_items(item_list):
- sublist = ''
- if children:
- sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (
- indent, list_formatter(children, tabs + 1), indent, indent)
- output.append('%s<li>%s%s</li>' % (
- indent, escaper(item), sublist))
- return '\n'.join(output)
- return mark_safe(list_formatter(value))
- ###################
- # INTEGERS #
- ###################
- @register.filter(is_safe=False)
- def add(value, arg):
- """Add the arg to the value."""
- try:
- return int(value) + int(arg)
- except (ValueError, TypeError):
- try:
- return value + arg
- except Exception:
- return ''
- @register.filter(is_safe=False)
- def get_digit(value, arg):
- """
- Given a whole number, return the requested digit of it, where 1 is the
- right-most digit, 2 is the second-right-most digit, etc. Return the
- original value for invalid input (if input or argument is not an integer,
- or if argument is less than 1). Otherwise, output is always an integer.
- """
- try:
- arg = int(arg)
- value = int(value)
- except ValueError:
- return value # Fail silently for an invalid argument
- if arg < 1:
- return value
- try:
- return int(str(value)[-arg])
- except IndexError:
- return 0
- ###################
- # DATES #
- ###################
- @register.filter(expects_localtime=True, is_safe=False)
- def date(value, arg=None):
- """Format a date according to the given format."""
- if value in (None, ''):
- return ''
- try:
- return formats.date_format(value, arg)
- except AttributeError:
- try:
- return format(value, arg)
- except AttributeError:
- return ''
- @register.filter(expects_localtime=True, is_safe=False)
- def time(value, arg=None):
- """Format a time according to the given format."""
- if value in (None, ''):
- return ''
- try:
- return formats.time_format(value, arg)
- except (AttributeError, TypeError):
- try:
- return time_format(value, arg)
- except (AttributeError, TypeError):
- return ''
- @register.filter("timesince", is_safe=False)
- def timesince_filter(value, arg=None):
- """Format a date as the time since that date (i.e. "4 days, 6 hours")."""
- if not value:
- return ''
- try:
- if arg:
- return timesince(value, arg)
- return timesince(value)
- except (ValueError, TypeError):
- return ''
- @register.filter("timeuntil", is_safe=False)
- def timeuntil_filter(value, arg=None):
- """Format a date as the time until that date (i.e. "4 days, 6 hours")."""
- if not value:
- return ''
- try:
- return timeuntil(value, arg)
- except (ValueError, TypeError):
- return ''
- ###################
- # LOGIC #
- ###################
- @register.filter(is_safe=False)
- def default(value, arg):
- """If value is unavailable, use given default."""
- return value or arg
- @register.filter(is_safe=False)
- def default_if_none(value, arg):
- """If value is None, use given default."""
- if value is None:
- return arg
- return value
- @register.filter(is_safe=False)
- def divisibleby(value, arg):
- """Return True if the value is divisible by the argument."""
- return int(value) % int(arg) == 0
- @register.filter(is_safe=False)
- def yesno(value, arg=None):
- """
- Given a string mapping values for true, false, and (optionally) None,
- return one of those strings according to the value:
- ========== ====================== ==================================
- Value Argument Outputs
- ========== ====================== ==================================
- ``True`` ``"yeah,no,maybe"`` ``yeah``
- ``False`` ``"yeah,no,maybe"`` ``no``
- ``None`` ``"yeah,no,maybe"`` ``maybe``
- ``None`` ``"yeah,no"`` ``"no"`` (converts None to False
- if no mapping for None is given.
- ========== ====================== ==================================
- """
- if arg is None:
- # Translators: Please do not add spaces around commas.
- arg = gettext('yes,no,maybe')
- bits = arg.split(',')
- if len(bits) < 2:
- return value # Invalid arg.
- try:
- yes, no, maybe = bits
- except ValueError:
- # Unpack list of wrong size (no "maybe" value provided).
- yes, no, maybe = bits[0], bits[1], bits[1]
- if value is None:
- return maybe
- if value:
- return yes
- return no
- ###################
- # MISC #
- ###################
- @register.filter(is_safe=True)
- def filesizeformat(bytes_):
- """
- Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
- 102 bytes, etc.).
- """
- try:
- bytes_ = int(bytes_)
- except (TypeError, ValueError, UnicodeDecodeError):
- value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
- return avoid_wrapping(value)
- def filesize_number_format(value):
- return formats.number_format(round(value, 1), 1)
- KB = 1 << 10
- MB = 1 << 20
- GB = 1 << 30
- TB = 1 << 40
- PB = 1 << 50
- negative = bytes_ < 0
- if negative:
- bytes_ = -bytes_ # Allow formatting of negative numbers.
- if bytes_ < KB:
- value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {'size': bytes_}
- elif bytes_ < MB:
- value = gettext("%s KB") % filesize_number_format(bytes_ / KB)
- elif bytes_ < GB:
- value = gettext("%s MB") % filesize_number_format(bytes_ / MB)
- elif bytes_ < TB:
- value = gettext("%s GB") % filesize_number_format(bytes_ / GB)
- elif bytes_ < PB:
- value = gettext("%s TB") % filesize_number_format(bytes_ / TB)
- else:
- value = gettext("%s PB") % filesize_number_format(bytes_ / PB)
- if negative:
- value = "-%s" % value
- return avoid_wrapping(value)
- @register.filter(is_safe=False)
- def pluralize(value, arg='s'):
- """
- Return a plural suffix if the value is not 1, '1', or an object of
- length 1. By default, use 's' as the suffix:
- * If value is 0, vote{{ value|pluralize }} display "votes".
- * If value is 1, vote{{ value|pluralize }} display "vote".
- * If value is 2, vote{{ value|pluralize }} display "votes".
- If an argument is provided, use that string instead:
- * If value is 0, class{{ value|pluralize:"es" }} display "classes".
- * If value is 1, class{{ value|pluralize:"es" }} display "class".
- * If value is 2, class{{ value|pluralize:"es" }} display "classes".
- If the provided argument contains a comma, use the text before the comma
- for the singular case and the text after the comma for the plural case:
- * If value is 0, cand{{ value|pluralize:"y,ies" }} display "candies".
- * If value is 1, cand{{ value|pluralize:"y,ies" }} display "candy".
- * If value is 2, cand{{ value|pluralize:"y,ies" }} display "candies".
- """
- if ',' not in arg:
- arg = ',' + arg
- bits = arg.split(',')
- if len(bits) > 2:
- return ''
- singular_suffix, plural_suffix = bits[:2]
- try:
- return singular_suffix if float(value) == 1 else plural_suffix
- except ValueError: # Invalid string that's not a number.
- pass
- except TypeError: # Value isn't a string or a number; maybe it's a list?
- try:
- return singular_suffix if len(value) == 1 else plural_suffix
- except TypeError: # len() of unsized object.
- pass
- return ''
- @register.filter("phone2numeric", is_safe=True)
- def phone2numeric_filter(value):
- """Take a phone number and converts it in to its numerical equivalent."""
- return phone2numeric(value)
- @register.filter(is_safe=True)
- def pprint(value):
- """A wrapper around pprint.pprint -- for debugging, really."""
- try:
- return pformat(value)
- except Exception as e:
- return "Error in formatting: %s: %s" % (e.__class__.__name__, e)
|