Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

please add a "pyformat" filter #70

Closed
mfrasca opened this issue Oct 27, 2011 · 8 comments
Closed

please add a "pyformat" filter #70

mfrasca opened this issue Oct 27, 2011 · 8 comments

Comments

@mfrasca
Copy link

mfrasca commented Oct 27, 2011

the current implementation of filters allows us to pipe one value through filters and combine the effect of each step.

I see the "format" filter as an exception.

it filters a format according to values, instead of filtering a value according to a format.

I think the following filter would be more useful:

def do_pyformat(value, format):
    """return float according to format
    """

    return soft_unicode("%" + format) % value

you would use it like this:

...] {{ length | float | pyformat("0.1f") }} m [...

this follows the documentation of the old format filter, as I found it on http://wsgiarea.pocoo.org/jinja/docs/filters.html#format

@snoack
Copy link
Contributor

snoack commented Oct 27, 2011

The format filter as implemented in jinja2 is redundant anyway. If I could, I would deprecate and finally remove it, but mitsuhiko would not let me break backwards compatibility. Whenever I have to format a string in a template, I use the modulo operator inline.

{{ '%d' % x }}
{{ '%s:%d' % (x, y) }}
{{ '%(foo)s' % {'foo': 'bar'} }}

Written that way, the code is less, simpler and more readable. Also note that using the modulo operator instead of the format filter results in more efficient Python code, when the template is compiled.

>>> print env.compile("{{ '%d'|format(x) }}", raw=True)
from __future__ import division
from jinja2.runtime import LoopContext, TemplateReference, Macro, Markup, TemplateRuntimeError, missing, concat, escape, markup_join, unicode_join, to_string, identity, TemplateNotFound
name = None

def root(context, environment=environment):
    l_x = context.resolve('x')
    t_1 = environment.filters['format']
    if 0: yield None
    yield to_string(t_1('%d', l_x))

blocks = {}
debug_info = '1=9'
>>> print env.compile("{{ '%d' % x }}", raw=True)
from __future__ import division
from jinja2.runtime import LoopContext, TemplateReference, Macro, Markup, TemplateRuntimeError, missing, concat, escape, markup_join, unicode_join, to_string, identity, TemplateNotFound
name = None

def root(context, environment=environment):
    l_x = context.resolve('x')
    if 0: yield None
    yield to_string(('%d' % l_x))

blocks = {}
debug_info = '1=8'

Therefor I don't like the idea of adding another useless filter for formatting values, as that can also be accomplished the same, more elegant way by using the modulo operator.

@mfrasca
Copy link
Author

mfrasca commented Oct 27, 2011

imagine the situation:
a value that I hope it's a number, but it could be None.

vvv | float | pyformat("0.2f")

how would you do this, using the modulo operator?

@snoack
Copy link
Contributor

snoack commented Oct 27, 2011

{{ '%0.2f' % vvv|float }}

... or if you prefer the format filter syntax ...

{{ '%0.2f'|format(vvv|float) }}

@mfrasca
Copy link
Author

mfrasca commented Oct 28, 2011

but this way the flow of information goes less linearly, instead of left to right (1, 2, 3) it goes middle to right to left (2, 3, 1). I understand your argument for speed, but readability is also worth considering...

@snoack
Copy link
Contributor

snoack commented Oct 29, 2011

In my opinion the modulo operator variant is more readable than using the built-in format or your pyformat filter. That's because of it requires less code to read (and write). Also imagine a more sophisticated example, where you need to format a string with multiple values:

{{ '%i points (%0.2f%%)' % (points, total / points * 100) }}

Using pyformat that code would look like that:

{{ points|pyformat('i') + 'points (' + (total / points * 100)|pyformat('0.2f') + '%)' }}

Do you still think that this it more readable?

I agree that the built-in format filter breaks the information flow concept of filters. And also it is less efficient and requires more code to write compared to the module operator. But the modulo operator is not a filter, but an operator. So nobody expects that the information flow works like it does for filters.

The problems with adding another filter for string formatting, that works like your pyformat filter, are:

  • It is less flexible than the built-in format filter and the modulo operator.
  • It is just as inefficient as the built-in format filter.
  • Having three ways (pyformat, format, modulo operator) to do the same thing is even worth than having two ways.
  • Users might be confused about the presence of two filters for string formatting, in particular because of the name "pyformat" is anything but self-explanatory, in my opinion. Also note that a lot of template authors, don't know anything about Python.

@mfrasca
Copy link
Author

mfrasca commented Oct 31, 2011

well, I would write it like this:

... {{ points|pyformat('i') }} points ({{ total / points * 100)|pyformat('0.2f')}}%) ...

but I agree, in this case the single use of % operator makes it more readable.

@chris-hailstorm
Copy link

Here's a simple approach with a user-defined filter. If the overall filter behavior of this appeals to you, one of us could easily add to Jinja standard filters and make a pull request. Your thoughts on this?

(1) example of a message to be interpolated -- a custom flask-security error message:

SECURITY_MSG_CONFIRMATION_REQUIRED = ('This email address has not been confirmed. '\
    'Please click on the link in the email we sent you. To receive the confirmation '\
    'email again, <a href="{0.url_root}confirm">click here</a>.', 'error')

(2) custom filter defined in a flask app, for example:

def _jinja_interp(text, obj):
    return text.format(obj)

app.jinja_env.filters['interp'] = _jinja_interp

(3) filter as used used in a Jinja template:

{% for error in field.errors %}
    ... other stuff ....
    {{ error | interp(request) | safe }}
{% endfor %}

This allows string interpolation via a filter, given a dictionary to interpolate from -- exact analog of 'string {placeholder}'.format(**some_dict) in typical Python. Can be filter-piped on to subsequent filters.

@jeffwidman
Copy link
Contributor

I think the pyformat filter is unlikely to get implemented/merged.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 13, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants