Skip to content

Commit 2cd7fdb

Browse files
committed
Fix usage of template origins in {% extends %} tags
1 parent f6a198c commit 2cd7fdb

7 files changed

Lines changed: 60 additions & 51 deletions

File tree

src/loaders.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ pub struct Origin {
2222
pub loader: Option<usize>,
2323
}
2424

25+
impl Origin {
26+
pub fn as_name(&self) -> &str {
27+
match &self.template_name {
28+
Some(name) => name,
29+
None => &self.name,
30+
}
31+
}
32+
}
33+
2534
#[derive(Clone, Debug, PartialEq, Eq)]
2635
pub struct LoaderError {
2736
pub tried: Vec<(String, String)>,
@@ -122,7 +131,14 @@ fn get_template(
122131
))));
123132
}
124133
return Ok(
125-
match Template::new(py, &contents, path, template_name, engine) {
134+
match Template::new(
135+
py,
136+
&contents,
137+
path,
138+
template_name,
139+
engine,
140+
Some(this_origin.clone()),
141+
) {
126142
Ok(template) => Ok((template, this_origin)),
127143
Err(error) => Err(error),
128144
},
@@ -325,6 +341,7 @@ impl LocMemLoader {
325341
PathBuf::from(template_name),
326342
template_name,
327343
engine,
344+
Some(this_origin.clone()),
328345
) {
329346
Ok(template) => Ok((template, this_origin)),
330347
Err(error) => Err(error),

src/parse.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,7 +2077,7 @@ impl<'t, 'py> Parser<'t, 'py> {
20772077
let template_name = match parse_extends_template_token(token, self)? {
20782078
IncludeTemplateName::Text(Text { at }) => {
20792079
let template_path = self.template.content(at);
2080-
let origin_name = self.origin.as_ref().map(|origin| origin.name.as_str());
2080+
let origin_name = self.origin.as_ref().map(|origin| origin.as_name());
20812081
match construct_relative_path(template_path, origin_name, at)
20822082
.map_err(ParseError::from)?
20832083
{
@@ -2160,7 +2160,7 @@ impl<'t, 'py> Parser<'t, 'py> {
21602160
let template_name = match parse_include_template_token(template_token, self)? {
21612161
IncludeTemplateName::Text(Text { at }) => {
21622162
let template_path = self.template.content(at);
2163-
let origin_name = self.origin.as_ref().map(|origin| origin.name.as_str());
2163+
let origin_name = self.origin.as_ref().map(|origin| origin.as_name());
21642164
match construct_relative_path(template_path, origin_name, at)? {
21652165
Some(path) => IncludeTemplateName::Relative(RelativePath {
21662166
path: path.into_owned(),
@@ -2214,7 +2214,7 @@ impl<'t, 'py> Parser<'t, 'py> {
22142214
return Err(ParseError::MissingKeywordArgument { at: with_at.into() });
22152215
}
22162216
}
2217-
let origin = self.origin.as_ref().map(|origin| origin.name.clone());
2217+
let origin = self.origin.as_ref().map(|origin| origin.as_name().to_string());
22182218
let include = Include {
22192219
template_name,
22202220
origin,

src/render/tags.rs

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ use crate::parse::{
2626
};
2727
use crate::path::construct_relative_path;
2828
use crate::template::django_rusty_templates::{
29-
NoReverseMatch, Template, TemplateDoesNotExist, TemplateSyntaxError, WithSourceCode,
30-
get_template,
29+
NoReverseMatch, Template, TemplateDoesNotExist, TemplateSyntaxError, get_template,
3130
};
3231
use crate::types::Variable;
3332
use crate::utils::PyResultMethods;
@@ -1102,39 +1101,6 @@ impl Extends {
11021101
}
11031102
}
11041103

1105-
fn get_template_from_string<'t, 'py>(
1106-
&self,
1107-
content: &Bound<'py, PyString>,
1108-
template: TemplateString<'t>,
1109-
context: &mut Context,
1110-
) -> Result<Template, PyErr> {
1111-
let py = content.py();
1112-
let template_path = content
1113-
.extract()
1114-
.expect("PyString should be compatible with Cow<str>");
1115-
let origin_name = match &self.origin {
1116-
Some(origin) => origin.template_name.as_deref(),
1117-
None => None,
1118-
};
1119-
let relative_path =
1120-
construct_relative_path(template_path, origin_name, template_at(&self.template_name));
1121-
let template_path =
1122-
match relative_path {
1123-
Err(error) => {
1124-
return Err(TemplateDoesNotExist::with_source_code(
1125-
error.into(),
1126-
template.to_string(),
1127-
));
1128-
}
1129-
Ok(Some(_)) => {
1130-
return Err(TemplateDoesNotExist::new_err((template_path.to_string(),))
1131-
.annotate(py, template_at(&self.template_name), "here", template));
1132-
}
1133-
Ok(None) => template_path.to_string(),
1134-
};
1135-
self.load_template(py, template_path, template, context)
1136-
}
1137-
11381104
fn get_template<'t, 'py>(
11391105
&self,
11401106
template_name: Content<'t, 'py>,
@@ -1150,13 +1116,19 @@ impl Extends {
11501116
if let Ok(parent) = content.extract::<Template>() {
11511117
Ok(parent)
11521118
} else if let Ok(content) = content.cast::<PyString>() {
1153-
self.get_template_from_string(content, template, context)
1119+
let template_path = content
1120+
.extract()
1121+
.expect("PyString should be compatible with Cow<str>");
1122+
self.load_template(py, template_path, template, context)
11541123
} else {
11551124
let promise = PROMISE.import(py, "django.utils.functional", "Promise")?;
11561125
let path_like = PATH_LIKE.import(py, "os", "PathLike")?;
11571126
if content.is_instance(promise)? || content.is_instance(path_like)? {
11581127
let content = content.str()?;
1159-
self.get_template_from_string(&content, template, context)
1128+
let template_path = content
1129+
.extract()
1130+
.expect("PyString should be compatible with Cow<str>");
1131+
self.load_template(py, template_path, template, context)
11601132
} else {
11611133
return Err(invalid_template_name(
11621134
py,

src/template.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -521,11 +521,15 @@ pub mod django_rusty_templates {
521521
filename: PathBuf,
522522
template_name: &str,
523523
engine: Arc<Engine>,
524+
origin: Option<Origin>,
524525
) -> PyResult<Self> {
525-
let origin = Origin {
526-
name: template_name.to_string(),
527-
template_name: None,
528-
loader: None,
526+
let origin = match origin {
527+
Some(origin) => origin,
528+
None => Origin {
529+
name: template_name.to_string(),
530+
template_name: None,
531+
loader: None,
532+
},
529533
};
530534
let mut parser =
531535
Parser::new(py, TemplateString(template), engine.clone(), Some(origin));
@@ -740,8 +744,15 @@ mod tests {
740744
let engine = Arc::new(Engine::empty());
741745
let template_string = std::fs::read_to_string(&filename).unwrap();
742746
let error = temp_env::with_var("NO_COLOR", Some("1"), || {
743-
Template::new(py, &template_string, filename, "parse_error.txt", engine)
744-
.unwrap_err()
747+
Template::new(
748+
py,
749+
&template_string,
750+
filename,
751+
"parse_error.txt",
752+
engine,
753+
None,
754+
)
755+
.unwrap_err()
745756
});
746757

747758
let error_string = format!("{error}");

tests/tags/test_extends.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ def test_extends_content_outside_blocks(assert_render):
158158
)
159159

160160

161+
def test_extends_relative_template_variable_parent(assert_render):
162+
template = "{% extends 'extends_extends_variable.txt' %}"
163+
assert_render(
164+
template=template,
165+
context={"parent": "./basic.txt", "user": "Lily"},
166+
expected="Hello Lily!\n",
167+
)
168+
169+
161170
def test_extends_after_variable(assert_parse_error):
162171
template = "{{ variable }} {% extends 'base.txt' %}{% block body %}Some content{% endblock body %}"
163172
django_message = snapshot(
@@ -553,8 +562,7 @@ def test_extends_variable_relative_path(assert_render_error):
553562
template = "{% extends 'nested/extends_variable.txt' %}"
554563
django_message = snapshot("../base.txt")
555564
rusty_message = snapshot("""\
556-
× The relative path '../base.txt' cannot be evaluated due to an unknown
557-
│ template origin.
565+
× ../base.txt
558566
╭─[1:12]
559567
1 │ {% extends parent %}
560568
· ───┬──
@@ -575,8 +583,7 @@ def test_extends_variable_relative_path_deep(assert_render_error):
575583
template = "{% extends 'nested/extends_variable.txt' %}"
576584
django_message = snapshot("../../missing.txt")
577585
rusty_message = snapshot("""\
578-
× The relative path '../../missing.txt' cannot be evaluated due to an
579-
│ unknown template origin.
586+
× ../../missing.txt
580587
╭─[1:12]
581588
1 │ {% extends parent %}
582589
· ───┬──
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{% extends "extends_variable.txt" %}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{% extends parent %}

0 commit comments

Comments
 (0)