diff --git a/bench/src/jmh/java/org/pkl/core/ListSort.java b/bench/src/jmh/java/org/pkl/core/ListSort.java index 1324b53ba..3a2a32e3b 100644 --- a/bench/src/jmh/java/org/pkl/core/ListSort.java +++ b/bench/src/jmh/java/org/pkl/core/ListSort.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 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. @@ -51,7 +51,8 @@ public class ListSort { null, IoUtils.getCurrentWorkingDir(), StackFrameTransformers.defaultTransformer, - false); + false, + null); private static final List list = new ArrayList<>(100000); static { diff --git a/docs/src/test/kotlin/DocSnippetTests.kt b/docs/src/test/kotlin/DocSnippetTests.kt index b8e3f6a2b..c6f1cc8d3 100644 --- a/docs/src/test/kotlin/DocSnippetTests.kt +++ b/docs/src/test/kotlin/DocSnippetTests.kt @@ -97,6 +97,7 @@ class DocSnippetTestsEngine : HierarchicalTestEngine = mapOf(), + + /** Defines options for the formatting of calls to the trace() method. */ + val traceMode: TraceMode? = null, ) { companion object { diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseOptions.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseOptions.kt index a82416f42..ef4020e8a 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseOptions.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/BaseOptions.kt @@ -32,6 +32,7 @@ import org.pkl.commons.cli.CliException import org.pkl.commons.shlex import org.pkl.core.evaluatorSettings.Color import org.pkl.core.evaluatorSettings.PklEvaluatorSettings.ExternalReader +import org.pkl.core.evaluatorSettings.TraceMode import org.pkl.core.runtime.VmUtils import org.pkl.core.util.IoUtils @@ -255,6 +256,17 @@ class BaseOptions : OptionGroup() { .multiple() .toMap() + val traceMode: TraceMode by + option( + names = arrayOf("--trace-mode"), + metavar = "when", + help = + "Specifies how calls to trace() are formatted. Possible values of are 'default', 'pretty' and 'hidden'.", + ) + .enum { it.name.lowercase() } + .single() + .default(TraceMode.DEFAULT) + // hidden option used by native tests private val testPort: Int by option(names = arrayOf("--test-port"), help = "Internal test option", hidden = true) @@ -291,6 +303,7 @@ class BaseOptions : OptionGroup() { httpNoProxy = noProxy ?: emptyList(), externalModuleReaders = externalModuleReaders, externalResourceReaders = externalResourceReaders, + traceMode = traceMode, ) } } diff --git a/pkl-core/src/main/java/org/pkl/core/Analyzer.java b/pkl-core/src/main/java/org/pkl/core/Analyzer.java index 345e9e88a..2afc335c8 100644 --- a/pkl-core/src/main/java/org/pkl/core/Analyzer.java +++ b/pkl-core/src/main/java/org/pkl/core/Analyzer.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import org.graalvm.polyglot.Context; +import org.pkl.core.evaluatorSettings.TraceMode; import org.pkl.core.http.HttpClient; import org.pkl.core.http.HttpClientInitException; import org.pkl.core.module.ModuleKeyFactory; @@ -46,6 +47,7 @@ public class Analyzer { private final @Nullable DeclaredDependencies projectDependencies; private final ModuleResolver moduleResolver; private final HttpClient httpClient; + private final TraceMode traceMode; public Analyzer( StackFrameTransformer transformer, @@ -54,7 +56,8 @@ public Analyzer( Collection moduleKeyFactories, @Nullable Path moduleCacheDir, @Nullable DeclaredDependencies projectDependencies, - HttpClient httpClient) { + HttpClient httpClient, + @Nullable TraceMode traceMode) { this.transformer = transformer; this.color = color; this.securityManager = securityManager; @@ -62,6 +65,7 @@ public Analyzer( this.projectDependencies = projectDependencies; this.moduleResolver = new ModuleResolver(moduleKeyFactories); this.httpClient = httpClient; + this.traceMode = traceMode; } /** @@ -115,7 +119,8 @@ private Context createContext() { projectDependencies == null ? null : new ProjectDependenciesManager( - projectDependencies, moduleResolver, securityManager))); + projectDependencies, moduleResolver, securityManager), + traceMode)); }); } } diff --git a/pkl-core/src/main/java/org/pkl/core/EvaluatorBuilder.java b/pkl-core/src/main/java/org/pkl/core/EvaluatorBuilder.java index aa7ae8dd2..a867642fd 100644 --- a/pkl-core/src/main/java/org/pkl/core/EvaluatorBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/EvaluatorBuilder.java @@ -21,6 +21,7 @@ import java.util.regex.Pattern; import org.pkl.core.SecurityManagers.StandardBuilder; import org.pkl.core.evaluatorSettings.PklEvaluatorSettings.ExternalReader; +import org.pkl.core.evaluatorSettings.TraceMode; import org.pkl.core.externalreader.ExternalReaderProcess; import org.pkl.core.http.HttpClient; import org.pkl.core.module.ModuleKeyFactories; @@ -67,6 +68,8 @@ public final class EvaluatorBuilder { private @Nullable DeclaredDependencies dependencies; + private @Nullable TraceMode traceMode; + private EvaluatorBuilder() {} /** @@ -454,6 +457,17 @@ public EvaluatorBuilder setProjectDependencies(DeclaredDependencies dependencies return this.dependencies; } + /** Sets whether calls to trace() produce indented, multi-line output. */ + public EvaluatorBuilder setTraceMode(TraceMode traceMode) { + this.traceMode = traceMode; + return this; + } + + /** Returns whether calls to trace() produce indented, multi-line output. */ + public TraceMode getTraceMode() { + return this.traceMode; + } + /** * Given a project, sets its dependencies, and also applies any evaluator settings if set. * @@ -517,6 +531,9 @@ public EvaluatorBuilder applyFromProject(Project project) { procs.computeIfAbsent(entry.getValue(), ExternalReaderProcess::of))); } } + if (settings.traceMode() != null) { + setTraceMode(settings.traceMode()); + } return this; } @@ -543,6 +560,7 @@ public Evaluator build() { timeout, moduleCacheDir, dependencies, - outputFormat); + outputFormat, + traceMode); } } diff --git a/pkl-core/src/main/java/org/pkl/core/EvaluatorImpl.java b/pkl-core/src/main/java/org/pkl/core/EvaluatorImpl.java index 2d607e6c0..07f49e2f3 100644 --- a/pkl-core/src/main/java/org/pkl/core/EvaluatorImpl.java +++ b/pkl-core/src/main/java/org/pkl/core/EvaluatorImpl.java @@ -30,6 +30,7 @@ import org.graalvm.polyglot.Context; import org.pkl.core.ast.ConstantValueNode; import org.pkl.core.ast.internal.ToStringNodeGen; +import org.pkl.core.evaluatorSettings.TraceMode; import org.pkl.core.http.HttpClient; import org.pkl.core.module.ModuleKeyFactory; import org.pkl.core.module.ProjectDependenciesManager; @@ -80,7 +81,8 @@ public EvaluatorImpl( @Nullable Duration timeout, @Nullable Path moduleCacheDir, @Nullable DeclaredDependencies projectDependencies, - @Nullable String outputFormat) { + @Nullable String outputFormat, + @Nullable TraceMode traceMode) { securityManager = manager; frameTransformer = transformer; @@ -108,7 +110,8 @@ public EvaluatorImpl( projectDependencies == null ? null : new ProjectDependenciesManager( - projectDependencies, moduleResolver, securityManager))); + projectDependencies, moduleResolver, securityManager), + traceMode)); }); this.timeout = timeout; // NOTE: would probably make sense to share executor between evaluators diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/TraceNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/TraceNode.java index 649920ae4..e9c0ac3bc 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/TraceNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/TraceNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 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. @@ -19,12 +19,17 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.evaluatorSettings.TraceMode; import org.pkl.core.runtime.*; public final class TraceNode extends ExpressionNode { @Child private ExpressionNode valueNode; - private final VmValueRenderer renderer = VmValueRenderer.singleLine(1000000); + private static final int MAX_RENDERER_LENGTH = 1000000; + + private final VmValueRenderer singleLineRenderer = + VmValueRenderer.singleLine(MAX_RENDERER_LENGTH); + private final VmValueRenderer multiLineRenderer = VmValueRenderer.multiLine(MAX_RENDERER_LENGTH); public TraceNode(SourceSection sourceSection, ExpressionNode valueNode) { super(sourceSection); @@ -40,6 +45,10 @@ public Object executeGeneric(VirtualFrame frame) { @TruffleBoundary private void doTrace(Object value, VmContext context) { + // If traces are disabled, returns early. + if (context.getTraceMode() == TraceMode.HIDDEN) { + return; + } if (value instanceof VmObjectLike objectLike) { try { objectLike.force(true, true); @@ -48,7 +57,12 @@ private void doTrace(Object value, VmContext context) { } var sourceSection = valueNode.getSourceSection(); - var renderedValue = renderer.render(value); + String renderedValue; + if (context.getTraceMode() == TraceMode.PRETTY) { + renderedValue = multiLineRenderer.render(value); + } else { + renderedValue = singleLineRenderer.render(value); + } var message = (sourceSection.isAvailable() ? sourceSection.getCharacters() : " ExternalReader.parse(entry.getValue()))); var color = (String) pSettings.get("color"); + var traceMode = (String) pSettings.get("traceMode"); return new PklEvaluatorSettings( (Map) pSettings.get("externalProperties"), @@ -118,7 +120,8 @@ public static PklEvaluatorSettings parse( rootDir, Http.parse((Value) pSettings.get("http")), externalModuleReaders, - externalResourceReaders); + externalResourceReaders, + traceMode == null ? null : TraceMode.valueOf(traceMode.toUpperCase())); } public record Http(@Nullable Proxy proxy) { @@ -213,7 +216,8 @@ && arePatternsEqual(allowedResources, that.allowedResources) && Objects.equals(moduleCacheDir, that.moduleCacheDir) && Objects.equals(timeout, that.timeout) && Objects.equals(rootDir, that.rootDir) - && Objects.equals(http, that.http); + && Objects.equals(http, that.http) + && Objects.equals(traceMode, that.traceMode); } private int hashPatterns(@Nullable List patterns) { @@ -231,7 +235,15 @@ private int hashPatterns(@Nullable List patterns) { public int hashCode() { var result = Objects.hash( - externalProperties, env, color, noCache, moduleCacheDir, timeout, rootDir, http); + externalProperties, + env, + color, + noCache, + moduleCacheDir, + timeout, + rootDir, + http, + traceMode); result = 31 * result + hashPatterns(allowedModules); result = 31 * result + hashPatterns(allowedResources); return result; diff --git a/pkl-core/src/main/java/org/pkl/core/evaluatorSettings/TraceMode.java b/pkl-core/src/main/java/org/pkl/core/evaluatorSettings/TraceMode.java new file mode 100644 index 000000000..a984eb3d2 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/evaluatorSettings/TraceMode.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2025 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.evaluatorSettings; + +/** Dictates the rendering of calls to the trace() method within Pkl. */ +public enum TraceMode { + /** All trace() calls will not be emitted to stderr. */ + HIDDEN, + /** All structures passed to trace() will be emitted on a single line. */ + DEFAULT, + /** All structures passed to trace() will be indented and emitted across multiple lines. */ + PRETTY +} diff --git a/pkl-core/src/main/java/org/pkl/core/messaging/AbstractMessagePackEncoder.java b/pkl-core/src/main/java/org/pkl/core/messaging/AbstractMessagePackEncoder.java index 841ff50ba..33ab16d1c 100644 --- a/pkl-core/src/main/java/org/pkl/core/messaging/AbstractMessagePackEncoder.java +++ b/pkl-core/src/main/java/org/pkl/core/messaging/AbstractMessagePackEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 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. @@ -72,7 +72,8 @@ protected void packMapHeader( @Nullable Object valueC, @Nullable Object valueD, @Nullable Object valueE, - @Nullable Object valueF) + @Nullable Object valueF, + @Nullable Object valueG) throws IOException { packer.packMapHeader( size @@ -90,7 +91,8 @@ protected void packMapHeader( + (valueC != null ? 1 : 0) + (valueD != null ? 1 : 0) + (valueE != null ? 1 : 0) - + (valueF != null ? 1 : 0)); + + (valueF != null ? 1 : 0) + + (valueG != null ? 1 : 0)); } protected void packKeyValue(String name, @Nullable Integer value) throws IOException { diff --git a/pkl-core/src/main/java/org/pkl/core/project/Project.java b/pkl-core/src/main/java/org/pkl/core/project/Project.java index ab3ff31a0..5757b4398 100644 --- a/pkl-core/src/main/java/org/pkl/core/project/Project.java +++ b/pkl-core/src/main/java/org/pkl/core/project/Project.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 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. @@ -43,6 +43,7 @@ import org.pkl.core.Value; import org.pkl.core.Version; import org.pkl.core.evaluatorSettings.PklEvaluatorSettings; +import org.pkl.core.evaluatorSettings.TraceMode; import org.pkl.core.module.ModuleKeyFactories; import org.pkl.core.packages.Checksums; import org.pkl.core.packages.Dependency.RemoteDependency; @@ -197,7 +198,8 @@ private static List> findImportCycle(ModuleSource moduleSource) { builder.getModuleKeyFactories(), builder.getModuleCacheDir(), builder.getProjectDependencies(), - builder.getHttpClient()); + builder.getHttpClient(), + builder.getTraceMode()); var importGraph = analyzer.importGraph(moduleSource.getUri()); var ret = ImportGraphUtils.findImportCycles(importGraph); // we only care about cycles in the same scheme as `moduleSource` @@ -511,7 +513,8 @@ public EvaluatorSettings( @Nullable Path moduleCacheDir, @Nullable List modulePath, @Nullable Duration timeout, - @Nullable Path rootDir) { + @Nullable Path rootDir, + @Nullable TraceMode traceMode) { this.delegate = new PklEvaluatorSettings( externalProperties, @@ -526,7 +529,8 @@ public EvaluatorSettings( rootDir, null, null, - null); + null, + traceMode); } @Deprecated(forRemoval = true) @@ -610,6 +614,8 @@ public String toString() { + delegate.timeout() + ", rootDir=" + delegate.rootDir() + + ", traceMode=" + + delegate.traceMode() + '}'; } } diff --git a/pkl-core/src/main/java/org/pkl/core/repl/ReplServer.java b/pkl-core/src/main/java/org/pkl/core/repl/ReplServer.java index a117478d5..738f2c93b 100644 --- a/pkl-core/src/main/java/org/pkl/core/repl/ReplServer.java +++ b/pkl-core/src/main/java/org/pkl/core/repl/ReplServer.java @@ -32,6 +32,7 @@ import org.pkl.core.ast.member.*; import org.pkl.core.ast.repl.ResolveClassMemberNode; import org.pkl.core.ast.type.TypeNode; +import org.pkl.core.evaluatorSettings.TraceMode; import org.pkl.core.http.HttpClient; import org.pkl.core.module.*; import org.pkl.core.packages.PackageResolver; @@ -82,7 +83,8 @@ public ReplServer( @Nullable String outputFormat, Path workingDir, StackFrameTransformer frameTransformer, - boolean color) { + boolean color, + @Nullable TraceMode traceMode) { this.workingDir = workingDir; this.securityManager = securityManager; @@ -114,7 +116,8 @@ public ReplServer( moduleCacheDir, outputFormat, packageResolver, - projectDependenciesManager)); + projectDependenciesManager, + traceMode)); }); language = languageRef.get(); } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/StdLibModule.java b/pkl-core/src/main/java/org/pkl/core/runtime/StdLibModule.java index 28bfe592f..d0e8b8b95 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/StdLibModule.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/StdLibModule.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 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. @@ -49,6 +49,7 @@ protected static void loadModule(URI uri, VmTyped instance) { null, null, null, + null, null)); var language = VmLanguage.get(null); var moduleKey = ModuleKeys.standardLibrary(uri); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmContext.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmContext.java index b691de165..c80837d38 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmContext.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmContext.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 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. @@ -23,6 +23,7 @@ import org.pkl.core.Logger; import org.pkl.core.SecurityManager; import org.pkl.core.StackFrameTransformer; +import org.pkl.core.evaluatorSettings.TraceMode; import org.pkl.core.http.HttpClient; import org.pkl.core.module.ProjectDependenciesManager; import org.pkl.core.packages.PackageResolver; @@ -50,6 +51,7 @@ public static final class Holder { private final ModuleCache moduleCache; private final @Nullable PackageResolver packageResolver; private final @Nullable ProjectDependenciesManager projectDependenciesManager; + private final TraceMode traceMode; public Holder( StackFrameTransformer frameTransformer, @@ -63,7 +65,8 @@ public Holder( @Nullable Path moduleCacheDir, @Nullable String outputFormat, @Nullable PackageResolver packageResolver, - @Nullable ProjectDependenciesManager projectDependenciesManager) { + @Nullable ProjectDependenciesManager projectDependenciesManager, + @Nullable TraceMode traceMode) { this.frameTransformer = frameTransformer; this.securityManager = securityManager; @@ -84,6 +87,7 @@ public Holder( moduleCache = new ModuleCache(); this.packageResolver = packageResolver; this.projectDependenciesManager = projectDependenciesManager; + this.traceMode = traceMode; } } @@ -143,4 +147,8 @@ public Map getExternalProperties() { public @Nullable ProjectDependenciesManager getProjectDependenciesManager() { return holder.projectDependenciesManager; } + + public @Nullable TraceMode getTraceMode() { + return holder.traceMode; + } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/defaultTraceMode/PklProject b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/defaultTraceMode/PklProject new file mode 100644 index 000000000..b2cc53e6d --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/defaultTraceMode/PklProject @@ -0,0 +1,5 @@ +amends "pkl:Project" + +evaluatorSettings { + traceMode = "default" +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/defaultTraceMode/defaultTraceMode.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/defaultTraceMode/defaultTraceMode.pkl new file mode 100644 index 000000000..1088350a9 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/defaultTraceMode/defaultTraceMode.pkl @@ -0,0 +1,18 @@ +amends ".../snippetTest.pkl" + +examples { + ["traceMode = 'default' results in single-line trace() values"] { + trace(new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + }) + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/evaluatorSettings/nullTraceMode.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/evaluatorSettings/nullTraceMode.pkl new file mode 100644 index 000000000..8e4b8693b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/evaluatorSettings/nullTraceMode.pkl @@ -0,0 +1,18 @@ +amends ".../snippetTest.pkl" + +examples { + ["traceMode = null results in single-line trace() values"] { + trace(new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + }) + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/evaluatorSettings/prettyTraces.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/evaluatorSettings/prettyTraces.pkl new file mode 100644 index 000000000..dbf0a1723 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/evaluatorSettings/prettyTraces.pkl @@ -0,0 +1,18 @@ +amends ".../snippetTest.pkl" + +examples { + ["prettyTraces = true results in indented, multi-line trace values"] { + trace(new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + }) + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/hiddenTraceMode/PklProject b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/hiddenTraceMode/PklProject new file mode 100644 index 000000000..687d256a2 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/hiddenTraceMode/PklProject @@ -0,0 +1,5 @@ +amends "pkl:Project" + +evaluatorSettings { + traceMode = "hidden" +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/hiddenTraceMode/hiddenTraceMode.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/hiddenTraceMode/hiddenTraceMode.pkl new file mode 100644 index 000000000..813655b69 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/hiddenTraceMode/hiddenTraceMode.pkl @@ -0,0 +1,18 @@ +amends ".../snippetTest.pkl" + +examples { + ["traceMode = 'hidden' results in no trace output"] { + trace(new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + }) + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/prettyTraceMode/PklProject b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/prettyTraceMode/PklProject new file mode 100644 index 000000000..30bf1bcd7 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/prettyTraceMode/PklProject @@ -0,0 +1,5 @@ +amends "pkl:Project" + +evaluatorSettings { + traceMode = "pretty" +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/projects/prettyTraceMode/prettyTraceMode.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/prettyTraceMode/prettyTraceMode.pkl new file mode 100644 index 000000000..15523cdfb --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/projects/prettyTraceMode/prettyTraceMode.pkl @@ -0,0 +1,18 @@ +amends ".../snippetTest.pkl" + +examples { + ["traceMode = 'pretty' results in indented, multi-line output"] { + trace(new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + }) + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/projects/defaultTraceMode/defaultTraceMode.err b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/defaultTraceMode/defaultTraceMode.err new file mode 100644 index 000000000..f6f17aaa0 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/defaultTraceMode/defaultTraceMode.err @@ -0,0 +1,28 @@ +examples { + ["traceMode = 'default' results in single-line trace() values"] { + new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings { + kicked = true + ["fjordConfig"] { + isPining = true + } + } + } + } + } +} +pkl: TRACE: new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + } = new Dynamic { ["Parrot"] { name = "Parrot"; age = 1234; bucketSettings { kicked = true; ["fjordConfig"] { isPining = true } } } } (file:///$snippetsDir/input/projects/defaultTraceMode/defaultTraceMode.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/projects/evaluatorSettings/nullTraceMode.err b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/evaluatorSettings/nullTraceMode.err new file mode 100644 index 000000000..1c5bddea2 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/evaluatorSettings/nullTraceMode.err @@ -0,0 +1,28 @@ +examples { + ["traceMode = null results in single-line trace() values"] { + new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings { + kicked = true + ["fjordConfig"] { + isPining = true + } + } + } + } + } +} +pkl: TRACE: new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + } = new Dynamic { ["Parrot"] { name = "Parrot"; age = 1234; bucketSettings { kicked = true; ["fjordConfig"] { isPining = true } } } } (file:///$snippetsDir/input/projects/evaluatorSettings/nullTraceMode.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/projects/evaluatorSettings/prettyTraces.err b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/evaluatorSettings/prettyTraces.err new file mode 100644 index 000000000..e326fc565 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/evaluatorSettings/prettyTraces.err @@ -0,0 +1,39 @@ +examples { + ["prettyTraces = true results in indented, multi-line trace values"] { + new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings { + kicked = true + ["fjordConfig"] { + isPining = true + } + } + } + } + } +} +pkl: TRACE: new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + } = new Dynamic { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings { + kicked = true + ["fjordConfig"] { + isPining = true + } + } + } +} (file:///$snippetsDir/input/projects/evaluatorSettings/prettyTraces.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/projects/hiddenTraceMode/hiddenTraceMode.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/hiddenTraceMode/hiddenTraceMode.pcf new file mode 100644 index 000000000..04d2056a6 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/hiddenTraceMode/hiddenTraceMode.pcf @@ -0,0 +1,16 @@ +examples { + ["traceMode = 'hidden' results in no trace output"] { + new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings { + kicked = true + ["fjordConfig"] { + isPining = true + } + } + } + } + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/projects/prettyTraceMode/prettyTraceMode.err b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/prettyTraceMode/prettyTraceMode.err new file mode 100644 index 000000000..8d709286c --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/projects/prettyTraceMode/prettyTraceMode.err @@ -0,0 +1,39 @@ +examples { + ["traceMode = 'pretty' results in indented, multi-line output"] { + new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings { + kicked = true + ["fjordConfig"] { + isPining = true + } + } + } + } + } +} +pkl: TRACE: new { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings = new { + kicked = true + ["fjordConfig"] = new { + isPining = true + } + } + } + } = new Dynamic { + ["Parrot"] { + name = "Parrot" + age = 1234 + bucketSettings { + kicked = true + ["fjordConfig"] { + isPining = true + } + } + } +} (file:///$snippetsDir/input/projects/prettyTraceMode/prettyTraceMode.pkl) diff --git a/pkl-core/src/test/kotlin/org/pkl/core/AnalyzerTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/AnalyzerTest.kt index 687902f7c..caa2391ba 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/AnalyzerTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/AnalyzerTest.kt @@ -37,6 +37,7 @@ class AnalyzerTest { null, null, HttpClient.dummyClient(), + null, ) @Test @@ -115,6 +116,7 @@ class AnalyzerTest { tempDir.resolve("packages"), null, HttpClient.dummyClient(), + null, ) PackageServer.populateCacheDir(tempDir.resolve("packages")) val file1 = @@ -190,6 +192,7 @@ class AnalyzerTest { tempDir.resolve("packages"), project.dependencies, HttpClient.dummyClient(), + null, ) val file1 = tempDir @@ -302,6 +305,7 @@ class AnalyzerTest { tempDir.resolve("packages"), project.dependencies, HttpClient.dummyClient(), + null, ) val result = analyzer.importGraph(mainPkl.toUri()) val birdUri = URI("projectpackage://localhost:0/birds@1.0.0#/bird.pkl") diff --git a/pkl-core/src/test/kotlin/org/pkl/core/ReplServerTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/ReplServerTest.kt index 6eef30894..37b80c647 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/ReplServerTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/ReplServerTest.kt @@ -45,6 +45,7 @@ class ReplServerTest { "/".toPath(), StackFrameTransformers.defaultTransformer, false, + null, ) @Test diff --git a/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt index b8fc92cad..b228ff61a 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt @@ -74,6 +74,7 @@ class ProjectTest { null, null, null, + null, ) val expectedAnnotations = listOf( diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/task/BasePklTask.java b/pkl-gradle/src/main/java/org/pkl/gradle/task/BasePklTask.java index 0e23d4460..30571fd13 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/task/BasePklTask.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/task/BasePklTask.java @@ -196,7 +196,8 @@ protected CliBaseOptions getCliBaseOptions() { getHttpProxy().getOrNull(), getHttpNoProxy().getOrElse(List.of()), Map.of(), - Map.of()); + Map.of(), + null); } return cachedOptions; } diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/task/ModulesTask.java b/pkl-gradle/src/main/java/org/pkl/gradle/task/ModulesTask.java index 4ec124818..f54f36bd0 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/task/ModulesTask.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/task/ModulesTask.java @@ -164,7 +164,8 @@ protected CliBaseOptions getCliBaseOptions() { null, List.of(), Map.of(), - Map.of()); + Map.of(), + null); } return cachedOptions; } diff --git a/pkl-server/src/main/kotlin/org/pkl/server/BinaryEvaluator.kt b/pkl-server/src/main/kotlin/org/pkl/server/BinaryEvaluator.kt index f1cb88938..b509aaaec 100644 --- a/pkl-server/src/main/kotlin/org/pkl/server/BinaryEvaluator.kt +++ b/pkl-server/src/main/kotlin/org/pkl/server/BinaryEvaluator.kt @@ -20,6 +20,7 @@ import java.time.Duration import org.msgpack.core.MessagePacker import org.pkl.core.* import org.pkl.core.ast.member.ObjectMember +import org.pkl.core.evaluatorSettings.TraceMode import org.pkl.core.http.HttpClient import org.pkl.core.module.ModuleKeyFactory import org.pkl.core.project.DeclaredDependencies @@ -39,6 +40,7 @@ internal class BinaryEvaluator( moduleCacheDir: Path?, declaredDependencies: DeclaredDependencies?, outputFormat: String?, + traceMode: TraceMode?, ) : EvaluatorImpl( transformer, @@ -54,6 +56,7 @@ internal class BinaryEvaluator( moduleCacheDir, declaredDependencies, outputFormat, + traceMode, ) { fun evaluate(moduleSource: ModuleSource, expression: String?): ByteArray { return doEvaluate(moduleSource) { module -> diff --git a/pkl-server/src/main/kotlin/org/pkl/server/Server.kt b/pkl-server/src/main/kotlin/org/pkl/server/Server.kt index 7bc12f47a..99040806e 100644 --- a/pkl-server/src/main/kotlin/org/pkl/server/Server.kt +++ b/pkl-server/src/main/kotlin/org/pkl/server/Server.kt @@ -225,6 +225,7 @@ class Server(private val transport: MessageTransport) : AutoCloseable { cacheDir, dependencies, message.outputFormat, + message.traceMode, ) } diff --git a/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackDecoder.kt b/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackDecoder.kt index 62926a727..b4b846830 100644 --- a/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackDecoder.kt +++ b/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackDecoder.kt @@ -22,6 +22,7 @@ import java.time.Duration import org.msgpack.core.MessagePack import org.msgpack.core.MessageUnpacker import org.msgpack.value.Value +import org.pkl.core.evaluatorSettings.TraceMode import org.pkl.core.messaging.BaseMessagePackDecoder import org.pkl.core.messaging.Message import org.pkl.core.packages.Checksums @@ -50,6 +51,13 @@ class ServerMessagePackDecoder(unpacker: MessageUnpacker) : BaseMessagePackDecod map.unpackHttp(), unpackStringMapOrNull(map, "externalModuleReaders", ::unpackExternalReader), unpackStringMapOrNull(map, "externalResourceReaders", ::unpackExternalReader), + unpackStringOrNull( + map, + "traceMode", + fun(traceMode: String): TraceMode { + return TraceMode.valueOf(traceMode.uppercase()) + }, + ), ) Message.Type.CREATE_EVALUATOR_RESPONSE -> CreateEvaluatorResponse( diff --git a/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackEncoder.kt b/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackEncoder.kt index b613cd3f0..28e4b9e86 100644 --- a/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackEncoder.kt +++ b/pkl-server/src/main/kotlin/org/pkl/server/ServerMessagePackEncoder.kt @@ -102,6 +102,7 @@ class ServerMessagePackEncoder(packer: MessagePacker) : BaseMessagePackEncoder(p msg.http, msg.externalModuleReaders, msg.externalResourceReaders, + msg.traceMode, ) packKeyValue("requestId", msg.requestId()) packKeyValue("allowedModules", msg.allowedModules) @@ -151,6 +152,9 @@ class ServerMessagePackEncoder(packer: MessagePacker) : BaseMessagePackEncoder(p packExternalReader(spec) } } + if (msg.traceMode != null) { + packKeyValue("traceMode", msg.traceMode.toString()) + } return } Message.Type.CREATE_EVALUATOR_RESPONSE -> { diff --git a/pkl-server/src/main/kotlin/org/pkl/server/ServerMessages.kt b/pkl-server/src/main/kotlin/org/pkl/server/ServerMessages.kt index 5eb3fe645..ef417a557 100644 --- a/pkl-server/src/main/kotlin/org/pkl/server/ServerMessages.kt +++ b/pkl-server/src/main/kotlin/org/pkl/server/ServerMessages.kt @@ -19,6 +19,7 @@ import java.net.URI import java.nio.file.Path import java.time.Duration import java.util.* +import org.pkl.core.evaluatorSettings.TraceMode import org.pkl.core.messaging.Message import org.pkl.core.messaging.Messages import org.pkl.core.packages.Checksums @@ -40,6 +41,7 @@ data class CreateEvaluatorRequest( val http: Http?, val externalModuleReaders: Map?, val externalResourceReaders: Map?, + val traceMode: TraceMode?, ) : Message.Client.Request { override fun type(): Message.Type = Message.Type.CREATE_EVALUATOR_REQUEST diff --git a/pkl-server/src/test/kotlin/org/pkl/server/AbstractServerTest.kt b/pkl-server/src/test/kotlin/org/pkl/server/AbstractServerTest.kt index 6d644d9b1..b0646615b 100644 --- a/pkl-server/src/test/kotlin/org/pkl/server/AbstractServerTest.kt +++ b/pkl-server/src/test/kotlin/org/pkl/server/AbstractServerTest.kt @@ -961,6 +961,7 @@ abstract class AbstractServerTest { http, null, null, + null, ) send(message) diff --git a/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorSnippetTests.kt b/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorSnippetTests.kt index e8bfc77f9..f374a3b57 100644 --- a/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorSnippetTests.kt +++ b/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorSnippetTests.kt @@ -56,6 +56,7 @@ class BinaryEvaluatorSnippetTestEngine : InputOutputTestEngine() { null, null, null, + null, ) private fun String.stripFilePaths() = diff --git a/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorTest.kt b/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorTest.kt index fa0bbcbcb..9c38c8885 100644 --- a/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorTest.kt +++ b/pkl-server/src/test/kotlin/org/pkl/server/BinaryEvaluatorTest.kt @@ -45,6 +45,7 @@ class BinaryEvaluatorTest { null, null, null, + null, ) private fun evaluate(text: String, expression: String?) = diff --git a/pkl-server/src/test/kotlin/org/pkl/server/ServerMessagePackCodecTest.kt b/pkl-server/src/test/kotlin/org/pkl/server/ServerMessagePackCodecTest.kt index 8ecfd0cf9..35b991012 100644 --- a/pkl-server/src/test/kotlin/org/pkl/server/ServerMessagePackCodecTest.kt +++ b/pkl-server/src/test/kotlin/org/pkl/server/ServerMessagePackCodecTest.kt @@ -23,6 +23,7 @@ import java.time.Duration import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.msgpack.core.MessagePack +import org.pkl.core.evaluatorSettings.TraceMode import org.pkl.core.messaging.Message import org.pkl.core.messaging.MessageDecoder import org.pkl.core.messaging.MessageEncoder @@ -99,6 +100,7 @@ class ServerMessagePackCodecTest { ), externalModuleReaders = mapOf("external" to externalReader, "external2" to externalReader), externalResourceReaders = mapOf("external" to externalReader), + traceMode = TraceMode.PRETTY, ) ) } diff --git a/stdlib/EvaluatorSettings.pkl b/stdlib/EvaluatorSettings.pkl index 539c5f8e1..5cd86335d 100644 --- a/stdlib/EvaluatorSettings.pkl +++ b/stdlib/EvaluatorSettings.pkl @@ -109,6 +109,16 @@ externalModuleReaders: Mapping? @Since { version = "0.27.0" } externalResourceReaders: Mapping? +/// Defines options for the formatting of calls to the trace() method. +/// +/// Possible values: +/// +/// - `"hidden"`: All trace() calls will not be emitted to stderr. +/// - `"default"`: All structures passed to trace() will be emitted on a single line. +/// - `"pretty"`: All structures passed to trace() will be indented and emitted across multiple lines. +@Since { version = "0.30.0" } +traceMode: ("hidden"|"default"|"pretty")? + /// Settings that control how Pkl talks to HTTP(S) servers. class Http { /// Configuration of the HTTP proxy to use.