-
Notifications
You must be signed in to change notification settings - Fork 12
Implement upper filter #81
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
Changes from 1 commit
75884bb
8d7e898
ec05922
41ed1a4
8e04e53
0577760
3b89e91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -8,7 +8,7 @@ use pyo3::types::PyType; | |||||||
|
||||||||
use crate::filters::{ | ||||||||
AddFilter, AddSlashesFilter, CapfirstFilter, DefaultFilter, EscapeFilter, ExternalFilter, | ||||||||
FilterType, LowerFilter, SafeFilter, SlugifyFilter, | ||||||||
FilterType, LowerFilter, SafeFilter, SlugifyFilter, UpperFilter, | ||||||||
}; | ||||||||
use crate::parse::Filter; | ||||||||
use crate::render::types::{Content, Context}; | ||||||||
|
@@ -72,6 +72,7 @@ impl Resolve for Filter { | |||||||
FilterType::Lower(filter) => filter.resolve(left, py, template, context), | ||||||||
FilterType::Safe(filter) => filter.resolve(left, py, template, context), | ||||||||
FilterType::Slugify(filter) => filter.resolve(left, py, template, context), | ||||||||
FilterType::Upper(filter) => filter.resolve(left, py, template, context), | ||||||||
}; | ||||||||
result | ||||||||
} | ||||||||
|
@@ -322,10 +323,30 @@ impl ResolveFilter for SlugifyFilter { | |||||||
} | ||||||||
} | ||||||||
|
||||||||
impl ResolveFilter for UpperFilter { | ||||||||
fn resolve<'t, 'py>( | ||||||||
&self, | ||||||||
variable: Option<Content<'t, 'py>>, | ||||||||
_py: Python<'py>, | ||||||||
_template: TemplateString<'t>, | ||||||||
context: &mut Context, | ||||||||
) -> ResolveResult<'t, 'py> { | ||||||||
let content = match variable { | ||||||||
Some(content) => Some( | ||||||||
content | ||||||||
.resolve_string(context)? | ||||||||
.map_content(|content| Cow::Owned(content.to_uppercase())), | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
To handle this correctly here, we'll need to replace
Suggested change
We'll also need a test to demonstrate we follow Django's behaviour here. (I'd add it in the Python tests.) This test should include a html tag which will become html-escaped as well as uppercased in the expected output. For more information about this quirk, see my blog post or the ticket and Django PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Django case is really interesting. I've been trying to use upper filter for HTML text in Django, and I'm not sure about the proposed solution. Django uses uppercase to encode text before encoding HTML characters. In this case, the I added test cases, especially the last one which I feel is not solvable without change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a very good point - I think this is essentially a bug in the existing Rust implementation that's been exposed by your work here. I've had a play around with your idea of introducing a |
||||||||
), | ||||||||
None => "".as_content(), | ||||||||
}; | ||||||||
Ok(content) | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
#[cfg(test)] | ||||||||
mod tests { | ||||||||
use super::*; | ||||||||
use crate::filters::{AddSlashesFilter, DefaultFilter, LowerFilter}; | ||||||||
use crate::filters::{AddSlashesFilter, DefaultFilter, LowerFilter, UpperFilter}; | ||||||||
use crate::parse::TagElement; | ||||||||
use crate::render::Render; | ||||||||
use crate::template::django_rusty_templates::{EngineData, Template}; | ||||||||
|
@@ -800,4 +821,53 @@ mod tests { | |||||||
assert_eq!(rendered, "bryony"); | ||||||||
}) | ||||||||
} | ||||||||
|
||||||||
#[test] | ||||||||
fn test_render_filter_upper() { | ||||||||
pyo3::prepare_freethreaded_python(); | ||||||||
|
||||||||
Python::with_gil(|py| { | ||||||||
let name = PyString::new(py, "Foo").into_any(); | ||||||||
let context = HashMap::from([("name".to_string(), name.unbind())]); | ||||||||
let mut context = Context { | ||||||||
context, | ||||||||
request: None, | ||||||||
autoescape: false, | ||||||||
}; | ||||||||
let template = TemplateString("{{ name|upper }}"); | ||||||||
let variable = Variable::new((3, 4)); | ||||||||
let filter = Filter { | ||||||||
at: (8, 5), | ||||||||
left: TagElement::Variable(variable), | ||||||||
filter: FilterType::Upper(UpperFilter), | ||||||||
}; | ||||||||
|
||||||||
let rendered = filter.render(py, template, &mut context).unwrap(); | ||||||||
assert_eq!(rendered, "FOO"); | ||||||||
}) | ||||||||
} | ||||||||
|
||||||||
#[test] | ||||||||
fn test_render_filter_upper_missing_left() { | ||||||||
pyo3::prepare_freethreaded_python(); | ||||||||
|
||||||||
Python::with_gil(|py| { | ||||||||
let context = HashMap::new(); | ||||||||
let mut context = Context { | ||||||||
context, | ||||||||
request: None, | ||||||||
autoescape: false, | ||||||||
}; | ||||||||
let template = TemplateString("{{ name|upper }}"); | ||||||||
let variable = Variable::new((3, 4)); | ||||||||
let filter = Filter { | ||||||||
at: (8, 5), | ||||||||
left: TagElement::Variable(variable), | ||||||||
filter: FilterType::Upper(UpperFilter), | ||||||||
}; | ||||||||
|
||||||||
let rendered = filter.render(py, template, &mut context).unwrap(); | ||||||||
assert_eq!(rendered, ""); | ||||||||
}) | ||||||||
} | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from django.template import engines | ||
|
||
|
||
def test_upper_string(): | ||
template = "{{ var|upper }}" | ||
django_template = engines["django"].from_string(template) | ||
rust_template = engines["rusty"].from_string(template) | ||
|
||
var = "foo" | ||
uppered = "FOO" | ||
assert django_template.render({"var": var}) == uppered | ||
assert rust_template.render({"var": var}) == uppered | ||
|
||
def test_upper_undefined(): | ||
template = "{{ var|upper }}" | ||
django_template = engines["django"].from_string(template) | ||
rust_template = engines["rusty"].from_string(template) | ||
|
||
assert django_template.render() == "" | ||
assert rust_template.render() == "" | ||
|
||
def test_upper_integer(): | ||
template = "{{ var|upper }}" | ||
django_template = engines["django"].from_string(template) | ||
rust_template = engines["rusty"].from_string(template) | ||
|
||
var = "3" | ||
uppered = "3" | ||
assert django_template.render({"var": var}) == uppered | ||
assert rust_template.render({"var": var}) == uppered |
Uh oh!
There was an error while loading. Please reload this page.