Skip to content

Commit 72d938f

Browse files
committed
Add feature to ignore parse errors when doing nested interpretation
1 parent 4f17919 commit 72d938f

4 files changed

Lines changed: 71 additions & 11 deletions

File tree

src/main/java/com/hubspot/jinjava/interpret/Context.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,23 @@ public TemporaryValueClosable<Boolean> withUnwrapRawOverride() {
819819
return temporaryValueClosable;
820820
}
821821

822+
public boolean isIgnoreParseErrors() {
823+
return contextConfiguration.isIgnoreParseErrors();
824+
}
825+
826+
private void setIgnoreParseErrors(boolean ignoreParseErrors) {
827+
contextConfiguration = contextConfiguration.withIgnoreParseErrors(ignoreParseErrors);
828+
}
829+
830+
public TemporaryValueClosable<Boolean> withIgnoreParseErrors() {
831+
TemporaryValueClosable<Boolean> temporaryValueClosable = new TemporaryValueClosable<>(
832+
isIgnoreParseErrors(),
833+
this::setIgnoreParseErrors
834+
);
835+
setIgnoreParseErrors(true);
836+
return temporaryValueClosable;
837+
}
838+
822839
public static class TemporaryValueClosable<T> implements AutoCloseable {
823840

824841
private final T previousValue;
@@ -829,10 +846,26 @@ private TemporaryValueClosable(T previousValue, Consumer<T> resetValueConsumer)
829846
this.resetValueConsumer = resetValueConsumer;
830847
}
831848

849+
public static <T> TemporaryValueClosable<T> noOp() {
850+
return new NoOpTemporaryValueClosable<>();
851+
}
852+
832853
@Override
833854
public void close() {
834855
resetValueConsumer.accept(previousValue);
835856
}
857+
858+
private static class NoOpTemporaryValueClosable<T> extends TemporaryValueClosable<T> {
859+
860+
private NoOpTemporaryValueClosable() {
861+
super(null, null);
862+
}
863+
864+
@Override
865+
public void close() {
866+
// No-op
867+
}
868+
}
836869
}
837870

838871
public Node getCurrentNode() {

src/main/java/com/hubspot/jinjava/interpret/ContextConfigurationIF.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,12 @@ default boolean isPartialMacroEvaluation() {
4747
default boolean isUnwrapRawOverride() {
4848
return false;
4949
}
50+
51+
/**
52+
* When trying nested interpretation, parsing errors are likely. This flag avoids propogating to differentiate between static and dynamic parsing errors.
53+
*/
54+
@Default
55+
default boolean isIgnoreParseErrors() {
56+
return false;
57+
}
5058
}

src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.hubspot.jinjava.el.ExpressionResolver;
3030
import com.hubspot.jinjava.el.ext.DeferredParsingException;
3131
import com.hubspot.jinjava.el.ext.ExtendedParser;
32+
import com.hubspot.jinjava.interpret.Context.TemporaryValueClosable;
3233
import com.hubspot.jinjava.interpret.TemplateError.ErrorItem;
3334
import com.hubspot.jinjava.interpret.TemplateError.ErrorReason;
3435
import com.hubspot.jinjava.interpret.TemplateError.ErrorType;
@@ -83,6 +84,8 @@ public class JinjavaInterpreter implements PyishSerializable {
8384

8485
public static final String OUTPUT_UNDEFINED_VARIABLES_ERROR =
8586
"OUTPUT_UNDEFINED_VARIABLES_ERROR";
87+
public static final String IGNORE_NESTED_INTERPRETATION_PARSE_ERRORS =
88+
"IGNORE_NESTED_INTERPRETATION_PARSE_ERRORS";
8689
private final Multimap<String, BlockInfo> blocks = ArrayListMultimap.create();
8790
private final LinkedList<Node> extendParentRoots = new LinkedList<>();
8891
private final Map<String, RevertibleObject> revertibleObjects = new HashMap<>();
@@ -259,7 +262,7 @@ public String renderFlat(String template) {
259262
public String renderFlat(String template, long renderLimit) {
260263
int depth = context.getRenderDepth();
261264

262-
try {
265+
try (TemporaryValueClosable<Boolean> c = ignoreParseErrorsIfActivated()) {
263266
if (depth > config.getMaxRenderDepth()) {
264267
ENGINE_LOG.warn("Max render depth exceeded: {}", Integer.toString(depth));
265268
return template;
@@ -272,6 +275,17 @@ public String renderFlat(String template, long renderLimit) {
272275
}
273276
}
274277

278+
private TemporaryValueClosable<Boolean> ignoreParseErrorsIfActivated() {
279+
return config
280+
.getFeatures()
281+
.getActivationStrategy(
282+
JinjavaInterpreter.IGNORE_NESTED_INTERPRETATION_PARSE_ERRORS
283+
)
284+
.isActive(context)
285+
? context.withIgnoreParseErrors()
286+
: TemporaryValueClosable.noOp();
287+
}
288+
275289
/**
276290
* Parse the given string into a root Node, and then renders it processing extend parents.
277291
*

src/main/java/com/hubspot/jinjava/tree/TreeParser.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public Node buildTree() {
8181

8282
do {
8383
if (parent != root) {
84-
interpreter.addError(
84+
maybeAddError(
8585
TemplateError.fromException(
8686
new MissingEndTagException(
8787
((TagNode) parent).getEndName(),
@@ -113,7 +113,7 @@ private Node nextNode() {
113113

114114
if (token.getType() == symbols.getFixed()) {
115115
if (token instanceof UnclosedToken) {
116-
interpreter.addError(
116+
maybeAddError(
117117
new TemplateError(
118118
ErrorType.WARNING,
119119
ErrorReason.SYNTAX_ERROR,
@@ -134,7 +134,7 @@ private Node nextNode() {
134134
} else if (token.getType() == symbols.getNote()) {
135135
String commentClosed = symbols.getClosingComment();
136136
if (!token.getImage().endsWith(commentClosed)) {
137-
interpreter.addError(
137+
maybeAddError(
138138
new TemplateError(
139139
ErrorType.WARNING,
140140
ErrorReason.SYNTAX_ERROR,
@@ -148,7 +148,7 @@ private Node nextNode() {
148148
);
149149
}
150150
} else {
151-
interpreter.addError(
151+
maybeAddError(
152152
TemplateError.fromException(
153153
new UnexpectedTokenException(
154154
token.getImage(),
@@ -237,13 +237,11 @@ private Node tag(TagToken tagToken) {
237237
try {
238238
tag = interpreter.getContext().getTag(tagToken.getTagName());
239239
if (tag == null) {
240-
interpreter.addError(
241-
TemplateError.fromException(new UnknownTagException(tagToken))
242-
);
240+
maybeAddError(TemplateError.fromException(new UnknownTagException(tagToken)));
243241
return null;
244242
}
245243
} catch (DisabledException e) {
246-
interpreter.addError(
244+
maybeAddError(
247245
new TemplateError(
248246
ErrorType.FATAL,
249247
ErrorReason.DISABLED,
@@ -292,7 +290,7 @@ private void endTag(Tag tag, TagToken tagToken) {
292290
hasMatchingStartTag = true;
293291
break;
294292
} else {
295-
interpreter.addError(
293+
maybeAddError(
296294
TemplateError.fromException(
297295
new TemplateSyntaxException(
298296
tagToken.getImage(),
@@ -305,7 +303,7 @@ private void endTag(Tag tag, TagToken tagToken) {
305303
}
306304
}
307305
if (!hasMatchingStartTag) {
308-
interpreter.addError(
306+
maybeAddError(
309307
new TemplateError(
310308
ErrorType.WARNING,
311309
ErrorReason.SYNTAX_ERROR,
@@ -326,4 +324,11 @@ private boolean isTrimmingEnabledForToken(Token token, JinjavaConfig jinjavaConf
326324
}
327325
return jinjavaConfig.getLegacyOverrides().isUseTrimmingForNotesAndExpressions();
328326
}
327+
328+
private void maybeAddError(TemplateError templateError) {
329+
if (interpreter.getContext().isIgnoreParseErrors()) {
330+
return;
331+
}
332+
interpreter.addError(templateError);
333+
}
329334
}

0 commit comments

Comments
 (0)