Comments (4)
Yes, the render
method in both StandaloneTag
and ContainerTag
can indeed return any object, not just strings. When using the assignment feature with the as
keyword, the result is treated the same as any regular variable in the context.
Example:
from jinja2_simple_tags import StandaloneTag
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}, {self.age} years old"
def greet(self, other_person):
return f"Hello, {other_person}! My name is {self.name}."
class PersonTag(StandaloneTag):
tags = {"person"}
def render(self, name, age):
return Person(name, age)
{% person 'Kevin', 12 as user %}
Name: {{ user.name }}
Age: {{ user.age }}
Greeting: {{ user.greet("Mike") }}
Result:
Name: Kevin
Age: 12
Greeting: Hello, Mike! My name is Kevin.
from jinja2-simple-tags.
Hi @pix666, thanks so much for your reply! The above example works perfectly for the StandaloneTag
. However, I kept getting the following error while using the ContainerTag
. Could you please give me some suggestions? Thanks!
TypeError: sequence item 0: expected str instance, Person found
Here is my PersonTag class:
class PersonTag(ContainerTag):
tags = {"person"}
def render(self, name, age, caller=None):
return Person(name, age)
Jinja template:
{% person 'Kevin', 12 as user %}{% endperson %}
Name: {{ user.name }}
Age: {{ user.age }}
Greeting: {{ user.greet("Mike") }}
from jinja2-simple-tags.
Oh, yes, you are right. I discovered that there is a significant difference between the nodes.Assign
and nodes.AssignBlock
(which is used in ContainerTag
) in Jinja2. nodes.AssignBlock
uses the concat
function, which is a synonym for "".join()
. Therefore, block tags MUST return a string value, and I don't see a simple way to bypass this limitation.
However, if you really need to achieve the desired behavior, you can create a custom subclass of CodeGenerator
. This is a hacky way to change the behavior of the Jinja2 compiler. Here's an example:
from jinja2 import Environment, nodes
from jinja2.compiler import CodeGenerator, Frame
from jinja2_simple_tags import ContainerTag
class CustomCodeGenerator(CodeGenerator):
def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
self.push_assign_tracking()
block_frame = frame.inner()
# This is a special case. Since a set block always captures we
# will disable output checks. This way one can use set blocks
# toplevel even in extended templates.
block_frame.require_output_check = False
block_frame.symbols.analyze_node(node)
self.enter_frame(block_frame)
self.buffer(block_frame)
self.blockvisit(node.body, block_frame)
self.newline(node)
self.writeline(f"if len({block_frame.buffer}) == 1 and not isinstance({block_frame.buffer}[0], str):")
self.indent()
self.newline()
self.visit(node.target, frame)
self.write(f" = {block_frame.buffer}[0]")
self.outdent()
self.writeline("else:")
self.indent()
self.newline()
self.visit(node.target, frame)
self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
if node.filter is not None:
self.visit_Filter(node.filter, block_frame)
else:
self.write(f"concat({block_frame.buffer})")
self.write(")")
self.outdent()
self.pop_assign_tracking(frame)
self.leave_frame(block_frame)
class Person:
def __init__(self, name: str, age: int, bio: str = ""):
self.name = name
self.age = age
self.bio = bio
def greet(self, other_person):
return f"Hello, {other_person}! My name is {self.name}."
class PersonTag(ContainerTag):
tags = {"person"}
def render(self, name, age, caller=None):
bio = caller().strip()
return Person(name, age, bio)
class TestContainerTagAssignment:
def setup_method(self):
self.env = Environment(
extensions=[PersonTag],
)
self.env.code_generator_class = CustomCodeGenerator
def test_output(self):
template = self.env.from_string(
"{% person 'Kevin', 12 as person %}"
" I love playing soccer with my friends."
"{% endperson %}\n"
""
"Name: {{ person.name }}\n"
"Age: {{ person.age }}\n"
"Greeting: {{ person.greet(\"Mike\") }}\n"
"Bio: {{ person.bio }}\n"
)
assert template.render({}).strip() == (
"Name: Kevin\n"
"Age: 12\n"
"Greeting: Hello, Mike! My name is Kevin.\n"
"Bio: I love playing soccer with my friends."
)
from jinja2-simple-tags.
It works! Thanks so much for your help!
from jinja2-simple-tags.
Related Issues (7)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jinja2-simple-tags.