Wie benutze ich das Ergebnis eines Ausdrucks als Argument zu einem Namenknoten programmgesteuert?
Posted: 09 Apr 2025, 05:08
Im Prozess der Entwicklung einer benutzerdefinierten Jinja2 -Erweiterung, die Namespaces mit dynamisch bewerteten Namen erstellt, muss ich das Ergebnis der Bewertung eines Template -Expression als Argument für einen -Namen verwenden Knoten. AST -Knoten. Das Problem ist jedoch, dass der AST aus der statischen Bewertung resultiert, während der Ausdruck eine Laufzeitbewertung erfordert (da Ausdrücke Variablen enthalten können, deren Wert zum Zeitpunkt der Analyse nicht bekannt ist). Gibt es eine Möglichkeit, diese Lücke zu überbrücken?
Natürlich kann ich den Namen problemlos analysieren und das Ergebnis seiner Bewertung mit einem Ausgang Knoten. Ich kann auch Namespaces mit vordefinierten wörtlichen Namen erstellen: < /p>
Natürlich werden Ausdrücke standardmäßig mit einem "Laden" -Kontext analysiert und wir benötigen einen Namen Knoten mit einem "Speicher" -Kontext.
Wie wäre es mit dem Namen Ausdruck im 'Load' Kontext in einem Namen Knoten mit einem "Speicher" -Kontext?
Code: Select all
{% set my_name = 'my_namespace' %}
{% namespace my_name %}
{# A namespace named 'my_namespace' should now exists #}
Code: Select all
from jinja2 import nodes
from jinja2.ext import Extension
class NamespaceExtension(Extension):
tags = {"namespace"}
def __init__(self, environment):
super().__init__(environment)
def parse(self, parser):
lineno = next(parser.stream).lineno
eval_context = nodes.EvalContext(self.environment)
name = parser.parse_expression()
##
## This obviously works as expected and outputs the result
## of evaluating `name` as an expression.
##
return nodes.Output([name]).set_lineno(lineno)
##
## This below also works — as a proof of concept — but we
## need the namespace name to be evaluated dynamically.
##
# return nodes.Assign(
# nodes.Name('my_namespace', 'store'),
# nodes.Call(nodes.Name('namespace', 'load'), [], [], None, None)
# ).set_lineno(lineno)
namespace = NamespaceExtension
< /code>
Die folgenden Versuche funktionieren jedoch nicht (ich hatte tatsächlich nur Hoffnung im ersten zu längeren Versuch, also nicht allzu überrascht in den meisten anderen, aber hier sind sie dennoch, wenn auch nur aus Vollständigkeit und Demonstration). < /p>
return nodes.Assign(
name,
nodes.Call(nodes.Name('namespace', 'load'), [], [], None, None)
).set_lineno(lineno)
# SyntaxError: cannot assign to function call
< /code>
Richtig, wir brauchen den Wert des Ausdrucks. < /p>
return nodes.Assign(
name.as_const(),
nodes.Call(nodes.Name('namespace', 'load'), [], [], None, None)
).set_lineno(lineno)
# RuntimeError: if no eval context is passed, the node must have an attached environment.
< /code>
Nun, ok, einfach genug. < /p>
return nodes.Assign(
name.as_const(eval_context),
nodes.Call(nodes.Name('namespace', 'load'), [], [], None, None)
).set_lineno(lineno)
# jinja2.nodes.Impossible
< /code>
Möglicherweise haben Sie ein besseres Glück, die Umgebung anzubringen. < /p>
name.set_environment(self.environment)
return nodes.Assign(
name.as_const(),
nodes.Call(nodes.Name('namespace', 'load'), [], [], None, None)
).set_lineno(lineno)
# jinja2.nodes.Impossible
Code: Select all
name.set_ctx('store')
return nodes.Assign(
name,
nodes.Call(nodes.Name('namespace', 'load'), [], [], None, None)
).set_lineno(lineno)
# SyntaxError: cannot assign to function call
Code: Select all
return nodes.Assign(
nodes.Name(name, 'store'),
nodes.Call(nodes.Name('namespace', 'load'), [], [], None, None)
).set_lineno(lineno)
# TypeError: '