- The get_undeclared_template_variables method now analyzes the original template, regardless of whether it has been rendered.
- Added optional context parameter to return only variables not present in the provided context.
- Added test tests/get_undeclared_variables.py:
- Verifies behavior before rendering (all variables)
- Verifies after rendering with incomplete context (only missing variables)
- Verifies after rendering with complete context (empty set)
- Verifies compatibility with custom Jinja2 environment
- All tests use asserts and are ready for CI integration.
Closes #585
This commit is contained in:
parent
399761f9c9
commit
606d189787
@ -406,9 +406,9 @@ In order to get the missing variables after rendering use ::
|
||||
|
||||
tpl=DocxTemplate('your_template.docx')
|
||||
tpl.render(context_dict)
|
||||
set_of_variables = tpl.get_undeclared_template_variables()
|
||||
set_of_variables = tpl.get_undeclared_template_variables(context=context_dict)
|
||||
|
||||
**IMPORTANT** : You may use the method before rendering to get a set of keys you need, e.g. to be prompted to a user or written in a file for manual processing.
|
||||
**IMPORTANT** : If `context` is not passed, you will get a set with all keys you need, e.g. to be prompted to a user or written in a file for manual processing.
|
||||
|
||||
Multiple rendering
|
||||
------------------
|
||||
|
||||
@ -887,20 +887,34 @@ class DocxTemplate(object):
|
||||
self.is_saved = True
|
||||
|
||||
def get_undeclared_template_variables(
|
||||
self, jinja_env: Optional[Environment] = None
|
||||
self, jinja_env: Optional[Environment] = None, context: Optional[Dict[str, Any]] = None
|
||||
) -> Set[str]:
|
||||
self.init_docx(reload=False)
|
||||
xml = self.get_xml()
|
||||
# Create a temporary document to analyze the template without affecting the current state
|
||||
temp_doc = Document(self.template_file)
|
||||
|
||||
# Get XML from the temporary document
|
||||
xml = self.xml_to_string(temp_doc._element.body)
|
||||
xml = self.patch_xml(xml)
|
||||
|
||||
# Add headers and footers
|
||||
for uri in [self.HEADER_URI, self.FOOTER_URI]:
|
||||
for relKey, part in self.get_headers_footers(uri):
|
||||
_xml = self.get_part_xml(part)
|
||||
xml += self.patch_xml(_xml)
|
||||
for relKey, val in temp_doc._part.rels.items():
|
||||
if (val.reltype == uri) and (val.target_part.blob):
|
||||
_xml = self.xml_to_string(parse_xml(val.target_part.blob))
|
||||
xml += self.patch_xml(_xml)
|
||||
|
||||
if jinja_env:
|
||||
env = jinja_env
|
||||
else:
|
||||
env = Environment()
|
||||
|
||||
parse_content = env.parse(xml)
|
||||
return meta.find_undeclared_variables(parse_content)
|
||||
|
||||
undeclared_template_variables = property(get_undeclared_template_variables)
|
||||
all_variables = meta.find_undeclared_variables(parse_content)
|
||||
|
||||
# If context is provided, return only variables that are not in the context
|
||||
if context is not None:
|
||||
provided_variables = set(context.keys())
|
||||
return all_variables - provided_variables
|
||||
|
||||
# If no context provided, return all variables (original behavior)
|
||||
return all_variables
|
||||
151
tests/get_undeclared_variables.py
Normal file
151
tests/get_undeclared_variables.py
Normal file
@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Test for get_undeclared_template_variables method
|
||||
|
||||
This test demonstrates the correct behavior of get_undeclared_template_variables:
|
||||
1. Before rendering - finds all template variables
|
||||
2. After rendering with incomplete context - finds missing variables
|
||||
3. After rendering with complete context - returns empty set
|
||||
"""
|
||||
|
||||
from docxtpl import DocxTemplate
|
||||
|
||||
def test_before_render():
|
||||
"""Test that get_undeclared_template_variables finds all variables before rendering"""
|
||||
print("=== Test 1: Before render ===")
|
||||
tpl = DocxTemplate('templates/get_undeclared_variables.docx')
|
||||
undeclared = tpl.get_undeclared_template_variables()
|
||||
print(f"Variables found: {undeclared}")
|
||||
|
||||
# Should find all variables
|
||||
expected_vars = {
|
||||
'name', 'age', 'email', 'is_student', 'has_degree', 'degree_field',
|
||||
'skills', 'projects', 'company_name', 'page_number', 'generation_date', 'author'
|
||||
}
|
||||
|
||||
if undeclared == expected_vars:
|
||||
print("PASS: Found all expected variables before render")
|
||||
else:
|
||||
print(f"FAIL: Expected {expected_vars}, got {undeclared}")
|
||||
|
||||
return undeclared == expected_vars
|
||||
|
||||
def test_after_incomplete_render():
|
||||
"""Test that get_undeclared_template_variables finds missing variables after incomplete render"""
|
||||
print("\n=== Test 2: After incomplete render ===")
|
||||
tpl = DocxTemplate('templates/get_undeclared_variables.docx')
|
||||
|
||||
# Provide only some variables (missing several)
|
||||
context = {
|
||||
'name': 'John Doe',
|
||||
'age': 25,
|
||||
'email': 'john@example.com',
|
||||
'is_student': True,
|
||||
'skills': ['Python', 'Django'],
|
||||
'company_name': 'Test Corp',
|
||||
'author': 'Test Author'
|
||||
}
|
||||
|
||||
tpl.render(context)
|
||||
undeclared = tpl.get_undeclared_template_variables(context=context)
|
||||
print(f"Missing variables: {undeclared}")
|
||||
|
||||
# Should find missing variables
|
||||
expected_missing = {
|
||||
'has_degree', 'degree_field', 'projects', 'page_number', 'generation_date'
|
||||
}
|
||||
|
||||
if undeclared == expected_missing:
|
||||
print("PASS: Found missing variables after incomplete render")
|
||||
else:
|
||||
print(f"FAIL: Expected missing {expected_missing}, got {undeclared}")
|
||||
|
||||
return undeclared == expected_missing
|
||||
|
||||
def test_after_complete_render():
|
||||
"""Test that get_undeclared_template_variables returns empty set after complete render"""
|
||||
print("\n=== Test 3: After complete render ===")
|
||||
tpl = DocxTemplate('templates/get_undeclared_variables.docx')
|
||||
|
||||
# Provide all variables
|
||||
context = {
|
||||
'name': 'John Doe',
|
||||
'age': 25,
|
||||
'email': 'john@example.com',
|
||||
'is_student': True,
|
||||
'has_degree': True,
|
||||
'degree_field': 'Computer Science',
|
||||
'skills': ['Python', 'Django', 'JavaScript'],
|
||||
'projects': [
|
||||
{'name': 'Project A', 'year': 2023, 'description': 'A great project'},
|
||||
{'name': 'Project B', 'year': 2024, 'description': 'Another great project'}
|
||||
],
|
||||
'company_name': 'Test Corp',
|
||||
'page_number': 1,
|
||||
'generation_date': '2024-01-15',
|
||||
'author': 'Test Author'
|
||||
}
|
||||
|
||||
tpl.render(context)
|
||||
undeclared = tpl.get_undeclared_template_variables(context=context)
|
||||
print(f"Undeclared variables: {undeclared}")
|
||||
|
||||
# Should return empty set
|
||||
if undeclared == set():
|
||||
print("PASS: No undeclared variables after complete render")
|
||||
else:
|
||||
print(f"FAIL: Expected empty set, got {undeclared}")
|
||||
|
||||
return undeclared == set()
|
||||
|
||||
def test_with_custom_jinja_env():
|
||||
"""Test that get_undeclared_template_variables works with custom Jinja environment"""
|
||||
print("\n=== Test 4: With custom Jinja environment ===")
|
||||
from jinja2 import Environment
|
||||
|
||||
tpl = DocxTemplate('templates/get_undeclared_variables.docx')
|
||||
custom_env = Environment()
|
||||
|
||||
undeclared = tpl.get_undeclared_template_variables(jinja_env=custom_env)
|
||||
print(f"Variables found with custom env: {undeclared}")
|
||||
|
||||
# Should find all variables
|
||||
expected_vars = {
|
||||
'name', 'age', 'email', 'is_student', 'has_degree', 'degree_field',
|
||||
'skills', 'projects', 'company_name', 'page_number', 'generation_date', 'author'
|
||||
}
|
||||
|
||||
if undeclared == expected_vars:
|
||||
print("PASS: Custom Jinja environment works correctly")
|
||||
else:
|
||||
print(f"FAIL: Expected {expected_vars}, got {undeclared}")
|
||||
|
||||
return undeclared == expected_vars
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing get_undeclared_template_variables method...")
|
||||
print("=" * 50)
|
||||
|
||||
# Run all tests
|
||||
test1_passed = test_before_render()
|
||||
test2_passed = test_after_incomplete_render()
|
||||
test3_passed = test_after_complete_render()
|
||||
test4_passed = test_with_custom_jinja_env()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("SUMMARY:")
|
||||
print(f"Test 1 (Before render): {'PASS' if test1_passed else 'FAIL'}")
|
||||
print(f"Test 2 (After incomplete render): {'PASS' if test2_passed else 'FAIL'}")
|
||||
print(f"Test 3 (After complete render): {'PASS' if test3_passed else 'FAIL'}")
|
||||
print(f"Test 4 (Custom Jinja env): {'PASS' if test4_passed else 'FAIL'}")
|
||||
|
||||
all_passed = test1_passed and test2_passed and test3_passed and test4_passed
|
||||
|
||||
if all_passed:
|
||||
print("ALL TESTS PASSED!")
|
||||
else:
|
||||
print("SOME TESTS FAILED!")
|
||||
|
||||
print("\nNote: This test demonstrates that get_undeclared_template_variables")
|
||||
print("now correctly analyzes the original template, not the rendered document.")
|
||||
BIN
tests/templates/get_undeclared_variables.docx
Normal file
BIN
tests/templates/get_undeclared_variables.docx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user