Opened 13 years ago
Closed 4 years ago
#20296 closed New feature (fixed)
django.utils.safestring.mark_safe forces evaluation of lazy objects
| Reported by: | Baptiste Mispelon | Owned by: | Theofilos Alexiou |
|---|---|---|---|
| Component: | Utilities | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | bmispelon@… | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Consider the following example:
from django.utils.safestring import mark_safe from django.utils.translation import activate, ugettext_lazy as _ s = mark_safe(_("username")) tpl = Template('{{ s }}') activate('fr') print(tpl.render(Context({'s': s})))
I would expect this to output nom d'utilisateur (which is the french translation of username) but what happens instead is that it outputs username.
The reason for this is that mark_safe will force the evaluation of the lazy string provided by ugettext_lazy when it's called.
Unfortunately, the solution to this it trickier than simply wrapping mark_safe with django.utils.functional.allow_lazy, because mark_safe can operate both on bytes and text (and allow_lazy needs to know the type of object return by the wrapped function).
I wrote some tests and a proposed solution on my branch: https://github.com/bmispelon/django/compare/lazy-safedata
Change History (14)
comment:1 by , 12 years ago
| Cc: | added |
|---|
comment:2 by , 12 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:3 by , 12 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
I verified the problem exists.
The patch fixes the problem, and has tests.
comment:4 by , 12 years ago
Since it might not be clear, I'd like to point that the reason we can't simply decorate mark_safe with allow_lazy is that mark_safe can return either bytes or text.
The allow_lazy decorator cannot handle this case (there are specific checks in the code for it). [1]
[1] https://github.com/django/django/blob/master/django/utils/functional.py#L106
comment:5 by , 12 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
comment:7 by , 12 years ago
| Resolution: | fixed |
|---|---|
| Status: | closed → new |
| Triage Stage: | Ready for checkin → Accepted |
A better fix for the issue is here: https://github.com/django/django/pull/2234
comment:9 by , 11 years ago
| Has patch: | unset |
|---|---|
| Patch needs improvement: | unset |
I closed the PR (it is still there for anyone who'd like to see how it looked like).
If I have some time, I'll try to see if the approach still works and I'll reopen it.
Thanks for the ping.
comment:10 by , 9 years ago
In the steps to reproduce, should mark_safe() be inside ugettext_lazy() as in #27803 instead of the other way around? If so, maybe this is a wontfix, assuming the documentation is clear about proper usage.
comment:11 by , 4 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
This should be an easy fix now I believe. mark_safe no longer operates on both bytes and text, so wrapping it with keep_lazy (the new allow_lazy) should solve the issue.
comment:13 by , 4 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
I created a pull request: https://github.com/django/django/pull/1093