Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/modules/java-binding/pages/pkl-config-java.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ Similar methods exist for sets, maps, and other generic types.

A `ConfigEvaluator` caches module sources and evaluation results.
To clear the cache, for example to evaluate the same module again, close the evaluator and create a new one.
When only external properties need to vary per call, use the `evaluate`, `evaluateOutputValue`, or
`evaluateExpression` overloads that accept a `Map<String, String>` of external properties.
Those properties override the evaluator's configured external properties for that evaluation only,
and the call does not reuse cached module or resource evaluation results that could contain stale
`read("prop:...")` values.

For a ready-to-go example with full source code,
see link:{uri-config-java-example}[config-java] in the _pkl-jvm-examples_ repository.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,7 @@
*/
package org.pkl.config.java;

import java.util.Map;
import org.pkl.config.java.mapper.ValueMapper;
import org.pkl.core.ModuleSource;

Expand All @@ -41,12 +42,52 @@ static ConfigEvaluator preconfigured() {
/** Evaluates the given module source into a {@link Config} tree. */
Config evaluate(ModuleSource moduleSource);

/**
* Evaluates the given module source into a {@link Config} tree with the given external properties
* overlaid onto the evaluator's configured external properties for this evaluation only.
*
* <p>To avoid stale {@code read("prop:...")} values, this evaluation does not reuse the
* evaluator's module and resource evaluation caches.
*/
default Config evaluate(ModuleSource moduleSource, Map<String, String> externalProperties) {
throw new UnsupportedOperationException(
"Per-evaluation external properties are not supported by this evaluator.");
}

/** Evaluates the given module's {@code output.value} property into a {@link Config} tree. */
Config evaluateOutputValue(ModuleSource moduleSource);

/**
* Evaluates the given module's {@code output.value} property into a {@link Config} tree with the
* given external properties overlaid onto the evaluator's configured external properties for this
* evaluation only.
*
* <p>To avoid stale {@code read("prop:...")} values, this evaluation does not reuse the
* evaluator's module and resource evaluation caches.
*/
default Config evaluateOutputValue(
ModuleSource moduleSource, Map<String, String> externalProperties) {
throw new UnsupportedOperationException(
"Per-evaluation external properties are not supported by this evaluator.");
}

/** Evaluates the Pkl expression represented as {@code expression} into a {@link Config} tree. */
Config evaluateExpression(ModuleSource moduleSource, String expression);

/**
* Evaluates the Pkl expression represented as {@code expression} into a {@link Config} tree with
* the given external properties overlaid onto the evaluator's configured external properties for
* this evaluation only.
*
* <p>To avoid stale {@code read("prop:...")} values, this evaluation does not reuse the
* evaluator's module and resource evaluation caches.
*/
default Config evaluateExpression(
ModuleSource moduleSource, String expression, Map<String, String> externalProperties) {
throw new UnsupportedOperationException(
"Per-evaluation external properties are not supported by this evaluator.");
}

/**
* Releases all resources held by this evaluator. If an {@code evaluate} method is currently
* executing, this method blocks until cancellation of that execution has completed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static org.pkl.config.java.ConfigUtils.createConfig;

import java.util.Map;
import org.pkl.config.java.mapper.ValueMapper;
import org.pkl.core.Evaluator;
import org.pkl.core.ModuleSource;
Expand All @@ -36,18 +37,38 @@ public Config evaluate(ModuleSource moduleSource) {
return new CompositeConfig("", mapper, module);
}

@Override
public Config evaluate(ModuleSource moduleSource, Map<String, String> externalProperties) {
var module = evaluator.evaluate(moduleSource, externalProperties);
return new CompositeConfig("", mapper, module);
}

@Override
public Config evaluateOutputValue(ModuleSource moduleSource) {
var value = evaluator.evaluateOutputValue(moduleSource);
return createConfig(value, mapper);
}

@Override
public Config evaluateOutputValue(
ModuleSource moduleSource, Map<String, String> externalProperties) {
var value = evaluator.evaluateOutputValue(moduleSource, externalProperties);
return createConfig(value, mapper);
}

@Override
public Config evaluateExpression(ModuleSource moduleSource, String expression) {
var value = evaluator.evaluateExpression(moduleSource, expression);
return createConfig(value, mapper);
}

@Override
public Config evaluateExpression(
ModuleSource moduleSource, String expression, Map<String, String> externalProperties) {
var value = evaluator.evaluateExpression(moduleSource, expression, externalProperties);
return createConfig(value, mapper);
}

@Override
public ValueMapper getValueMapper() {
return mapper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.net.URI;
import java.util.Map;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.pkl.core.ModuleSource;
Expand Down Expand Up @@ -50,4 +52,43 @@ public void evaluateExpression() {
var address = addressConfig.as(Address.class);
assertThat(address.street).isEqualTo("Fuzzy St.");
}

@Test
public void evaluateWithPerEvaluationExternalProperties() {
var source =
ModuleSource.create(
URI.create("file:///config-evaluator-external-properties.pkl"),
"""
configured = read("prop:configured")
request = read("prop:request")
output {
value {
configured = read("prop:configured")
request = read("prop:request")
}
}
""");

try (var evaluator =
ConfigEvaluatorBuilder.preconfigured()
.addExternalProperty("configured", "configured")
.addExternalProperty("request", "default")
.build()) {
var first = evaluator.evaluate(source, Map.of("request", "one"));
assertThat(first.get("configured").as(String.class)).isEqualTo("configured");
assertThat(first.get("request").as(String.class)).isEqualTo("one");

var second = evaluator.evaluate(source, Map.of("request", "two"));
assertThat(second.get("request").as(String.class)).isEqualTo("two");

var unscoped = evaluator.evaluate(source);
assertThat(unscoped.get("request").as(String.class)).isEqualTo("default");

var outputValue = evaluator.evaluateOutputValue(source, Map.of("request", "three"));
assertThat(outputValue.get("request").as(String.class)).isEqualTo("three");

var expression = evaluator.evaluateExpression(source, "request", Map.of("request", "four"));
assertThat(expression.as(String.class)).isEqualTo("four");
}
}
}
48 changes: 48 additions & 0 deletions pkl-core/src/main/java/org/pkl/core/Evaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ static Evaluator preconfigured() {
*/
PModule evaluate(ModuleSource moduleSource);

/**
* Evaluates the module with the given external properties overlaid onto the evaluator's
* configured external properties for this evaluation only.
*
* <p>To avoid stale {@code read("prop:...")} values, this evaluation does not reuse the
* evaluator's module and resource evaluation caches.
*
* @throws PklException if an error occurs during evaluation
* @throws IllegalStateException if this evaluator has already been closed
*/
default PModule evaluate(ModuleSource moduleSource, Map<String, String> externalProperties) {
throw new UnsupportedOperationException(
"Per-evaluation external properties are not supported by this evaluator.");
}

/**
* Evaluates a module's {@code output.text} property.
*
Expand All @@ -72,6 +87,22 @@ static Evaluator preconfigured() {
*/
Object evaluateOutputValue(ModuleSource moduleSource);

/**
* Evaluates a module's {@code output.value} property with the given external properties overlaid
* onto the evaluator's configured external properties for this evaluation only.
*
* <p>To avoid stale {@code read("prop:...")} values, this evaluation does not reuse the
* evaluator's module and resource evaluation caches.
*
* @throws PklException if an error occurs during evaluation
* @throws IllegalStateException if this evaluator has already been closed
*/
default Object evaluateOutputValue(
ModuleSource moduleSource, Map<String, String> externalProperties) {
throw new UnsupportedOperationException(
"Per-evaluation external properties are not supported by this evaluator.");
}

/**
* Evaluates a module's {@code output.files} property.
*
Expand Down Expand Up @@ -174,6 +205,23 @@ static Evaluator preconfigured() {
*/
Object evaluateExpression(ModuleSource moduleSource, String expression);

/**
* Evaluates the Pkl expression represented as {@code expression} with the given external
* properties overlaid onto the evaluator's configured external properties for this evaluation
* only.
*
* <p>To avoid stale {@code read("prop:...")} values, this evaluation does not reuse the
* evaluator's module and resource evaluation caches.
*
* @throws PklException if an error occurs during evaluation
* @throws IllegalStateException if this evaluator has already been closed
*/
default Object evaluateExpression(
ModuleSource moduleSource, String expression, Map<String, String> externalProperties) {
throw new UnsupportedOperationException(
"Per-evaluation external properties are not supported by this evaluator.");
}

/**
* Evaluates the Pkl expression represented as {@code expression}, returning a byte array of the
* <code>pkl-binary</code>-encoded representation of the result.
Expand Down
Loading
Loading