Opened 10 years ago
Last modified 15 months ago
#23424 assigned Bug
Verbatim tag fails to render curly braces
Reported by: | stephband | Owned by: | Atul Bhouraskar |
---|---|---|---|
Component: | Template system | Version: | dev |
Severity: | Normal | Keywords: | verbatim template tag |
Cc: | Chris Jerdonek, Sarah Boyce | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
The {% verbatim %} tag fails to render curly braces under certain conditions - notably when there are two verbatim tags on the same line.
This works:
{% verbatim %}{{{% endverbatim %} – correctly renders as – {{ {% verbatim %}}}{% endverbatim %} – correctly renders as – }} {% verbatim %}{ {% endverbatim %}text{% verbatim %} }{% endverbatim %} – correctly renders as – { text }
However, this fails:
{% verbatim %}{{% endverbatim %}text{% verbatim %}}{% endverbatim %} – incorrectly renders as – {{% endverbatim %}text{% verbatim %}} {% verbatim %}{{{% endverbatim %}text{% verbatim %}}}{% endverbatim %} – incorrectly renders as – {{{% endverbatim %}text{% verbatim %}}} {% verbatim %}{{ {% endverbatim %}text{% verbatim %} }}{% endverbatim %} – incorrectly renders as – {{ {% endverbatim %}text{% verbatim %} }}
Change History (19)
comment:1 by , 10 years ago
Description: | modified (diff) |
---|
comment:2 by , 10 years ago
Description: | modified (diff) |
---|
comment:3 by , 10 years ago
Description: | modified (diff) |
---|
comment:4 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|---|
Version: | 1.7-rc-2 → master |
comment:5 by , 10 years ago
This occurs because the {{ and }} are the starting and ending delimiters for variables. This also affects block tag delimters, {% and %}, and comment tag delimiters, {# and #}. In all cases they must be matching.
comment:6 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:7 by , 10 years ago
Possibly a better example why this is a really huge headache is this
{%verbatim%} {{ {%endverbatim%} computer.{{field}} {%verbatim%} }} {%endverbatim%}
From some auditing software where we are using django views to render lists of fields , which however are being fetched via AngularJS from a third party REST source. We simply can't find a simple way around this. :(
An alternative might even be some simple tags that provide the missing character Ie {% special left_double_brace %} or whatever.
Whilst documentation would at least warn the bug is there, this is a show-stopper error for a lot of use cases.
For the purposes of anyone coming across this ticket and pulling their hair out over it, heres a template tag that might ease the pain;-
from django import template register = template.Library() @register.filter(name='specialbracket') def specialbracket(value): if value == 'left': return "{{" elif value == 'right': return "}}" else: return "??"
And the above example solved with this tag;-
<td class="field-{{ident}}">{{ 'left'|specialbracket }} computer.{{field}} {{ 'right'|specialbracket }} </td>
comment:9 by , 10 years ago
Oh wow. I didn't even realise that existed. Thanks! Still I do think that allowing multiple verbatim/endverbatim instances on a line would solve a lot of headaches.
comment:10 by , 10 years ago
This may require switching to a proper lexer that understands literals instead of the current regex-based implementation.
comment:11 by , 5 years ago
Probably related:
{% verbatim %}{%{% endverbatim %}
With curly bracket and percentage sign (open block tag) template throws exception of unclosed verbatim tag. Django 1.11.22
comment:12 by , 3 years ago
Cc: | added |
---|---|
Owner: | removed |
Status: | assigned → new |
comment:13 by , 3 years ago
Has patch: | set |
---|---|
Owner: | set to |
Status: | new → assigned |
I've created a pull request that allows all the examples in this ticket to be correctly rendered - https://github.com/django/django/pull/14786
Test cases for the examples have been added and pass. More test cases (for examples not yet reported) may need to be added.
Note that this pull request is based off my proposed fix for #23356 where all verbatim tag logic is isolated into a Parser.parse_verbatim()
method and removes all special case logic currently in the Lexer.create_token()
.
comment:15 by , 3 years ago
The way I would try solving this today is, after PR #14739 is merged, make Lexer.create_token() raise a custom exception called something like VerbatimTagStarting
with the end string as the argument if a verbatim tag is encountered. (The line where this happens is here.) Then, in Lexer.tokenize(), handle VerbatimTagStarting
being raised by searching for the new end string, and then resuming the tag_re.split()
iteration at the new location in the template string afterwards. This will be much easier to implement now that ticket #33002 is resolved. One side benefit of this is that Lexer.create_token()
should become simpler (though the complexity will move to tokenize()
). However, Lexer.create_token()
probably shouldn't be having much logic anyways (nor state, which the changes I'm suggesting would also remove).
follow-up: 18 comment:16 by , 3 years ago
Here are a few comments to add to my previous comment, after thinking a little more about this:
I believe that finding the end tag for a custom verbatim
tag can be done efficiently and without having to dynamically compile any new regular expressions. (The regex pattern I have in mind here would be one that ends with endverbatim
.)
I also think this could be done somewhat more efficiently if Django didn't have to support pathological custom closing verbatim tags like this:
{% verbatim {% %} Avoid template rendering via the {% verbatim %}{% endverbatim %} block. {% endverbatim {% %}
(In the above, {%
is being used as the custom myblock
string below.)
{% verbatim myblock %} Avoid template rendering via the {% verbatim %}{% endverbatim %} block. {% endverbatim myblock %}
If closing tags like this didn't have to be supported, the approach I have in mind could I think be done with fewer operations since the regex pattern could end with %}
instead of endverbatim
. (The Python regex
module only supports searching for non-overlapping patterns, and whether closing tags like this are allowed affects whether the regex pattern used to search for a closing verbatim tag will be overlapping or not.) But as I said, I believe it can still be done efficiently even if they are allowed.
By the way, would I be allowed to work on this? Atul, I know you assigned yourself, but I'm not sure if that was only on the assumption that this ticket would be resolved using the PR you posted for #23356.
PS - one other thing I noticed is that care needs to be taken to ensure that the regex will match {% endverbatim
with any Unicode whitespace between {%
and endverbatim
(so not just ASCII whitespace), but not \n
. That means the regex sequence \s
can't be used as is since it includes \n
.
comment:17 by , 3 years ago
Hi Chris and Mariusz,
Sorry for the delay in responding, was a bit too busy at work.
Let me add in a few general comments about this ticket:
- The
{% verbatim %}
tags are only found in a very small subset of django templates (I could be very wrong here but I've never actually come across usage out in the wild yet). - The conditions in this ticket are only found in a small subset of all verbatim usages - ie combined with the above this is an extremely small use case.
- Every example provided here can be implemented with either the
templatetag tag
as pointed out in https://code.djangoproject.com/ticket/23424#comment:8 or via a custom tag. As older comments have said we could note this in the documentation. - Adding complexity to
create_token()
and/ortokenise()
- both of which are in the critical path for all template tags (ie. every single template out there is impacted), does not seem (IMHO) a good enough tradeoff to fix this. - My strong feeling is that any solution to this ticket could always either be considered inefficient or have special cases/ limitations which will need to be spelt out in documentation anyway.
- It would be best to simply acknowledge the current behaviour in the documentation and provide workarounds there - happy to create a PR.
Now coming to my proposed fix (if we are to proceed with the fixing anyway):
- The multiple re-tokenisation only happens if the conditions for this ticket are met (multiple verbatim tags on the same line containing matched var/tag braces within).
- As I've said it is only a proof of concept fix and of course the regex etc definitely can improve.
- The inefficiency will only be encountered if the template being parsed contains a verbatim tag (as
parse_verbatim()
will never get called otherwise). - The inefficiency is restricted to the parsing stage which should only ever happen once per template as long as the cached loader is used (which is the default). Users concerned with performance should really not opt out of this.
To summarise:
- I would strongly support leaving the current behaviour as-is and updating the documentation as simple workarounds exist.
PS. On the other hand there is no workaround for creating "verbatim like" tags (#23356) and the proposed fix there has zero impact on all other template tags (and actually reduces complexity in create_token()
with a possible small boost in performance).
comment:18 by , 3 years ago
Replying to Chris Jerdonek:
By the way, would I be allowed to work on this? Atul, I know you assigned yourself, but I'm not sure if that was only on the assumption that this ticket would be resolved using the PR you posted for #23356.
Sure thing :)
I can de-assign myself if you want.
comment:19 by , 15 months ago
Cc: | added |
---|
Not sure if it will be easy/possible to fix. If not, we can document the limitation.