Skip to content

Commit d787b3b

Browse files
kkresscopybara-github
authored andcommitted
Add a new optional param to fail() to allow pretty-printing user errors without a stack trace.
This allows a new fail(stack_trace = False, 'Message') which will not include a stack trace. This allows an intentional failure that is user facing to be a bit more user friendly since they're unlikely to want to see the stack trace. A flag --force_eval_stack_trace can be used to unconditionally include the stack trace for debugging. RELNOTES: Added an optional parameter to allow eliding the stack trace when calling fail(). PiperOrigin-RevId: 834908566 Change-Id: Ia7fd1673ec90e068b1a23578bd1094c662b94312
1 parent ade92cf commit d787b3b

File tree

7 files changed

+90
-15
lines changed

7 files changed

+90
-15
lines changed

src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,17 @@ public Converter() {
845845
+ " the BUILD file they are ultimately loaded from.")
846846
public boolean incompatibleResolveSelectKeysEagerly;
847847

848+
@Option(
849+
name = "force_starlark_stack_trace",
850+
defaultValue = "false",
851+
documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
852+
effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
853+
help =
854+
"If --force_starlark_stack_trace=true, Starlark stace traces will always be printed from"
855+
+ " calls to fail(), including those normally supressed with fail(..., stack_trace ="
856+
+ " False)")
857+
public boolean forceStarlarkStackTrace;
858+
848859
/**
849860
* An interner to reduce the number of StarlarkSemantics instances. A single Blaze instance should
850861
* never accumulate a large number of these and being able to shortcut on object identity makes a
@@ -956,7 +967,8 @@ private void setFlags(FlagConsumer consumer) {
956967
.setBool(
957968
StarlarkSemantics.INTERNAL_BAZEL_ONLY_UTF_8_BYTE_STRINGS,
958969
internalStarlarkUtf8ByteStrings)
959-
.setBool(EXPERIMENTAL_REPOSITORY_CTX_EXECUTE_WASM, repositoryCtxExecuteWasm);
970+
.setBool(EXPERIMENTAL_REPOSITORY_CTX_EXECUTE_WASM, repositoryCtxExecuteWasm)
971+
.setBool(StarlarkSemantics.FORCE_STARLARK_STACK_TRACE, forceStarlarkStackTrace);
960972
}
961973

962974
/** Constructs a {@link StarlarkSemantics} object corresponding to this set of option values. */
@@ -1130,6 +1142,7 @@ public FlagConsumer setBool(String key, boolean ignored) {
11301142
"-experimental_repository_ctx_execute_wasm";
11311143
public static final String INCOMPATIBLE_RESOLVE_SELECT_KEYS_EAGERLY =
11321144
"-incompatible_resolve_select_keys_eagerly";
1145+
11331146
// non-booleans
11341147
public static final StarlarkSemantics.Key<List<String>> INCOMPATIBLE_DISABLE_TRANSITIONS_OPTIONS =
11351148
new StarlarkSemantics.Key<>("incompatible_disable_transitions_on", ImmutableList.of());

src/main/java/net/starlark/java/eval/EvalException.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,39 @@ public class EvalException extends Exception {
3737
// stack when popping a frame. Thus an exception newly created by a
3838
// built-in function has no stack until it is thrown out of a function call.
3939
@Nullable private ImmutableList<StarlarkThread.CallStackEntry> callstack;
40+
private final boolean includeStackTrace;
4041

4142
/** Constructs an EvalException. Use {@link Starlark#errorf} if you want string formatting. */
4243
public EvalException(String message) {
43-
this(message, /* cause= */ (Throwable) null);
44+
this(message, /* cause= */ (Throwable) null, /* includeStackTrace= */ true);
4445
}
4546

4647
/**
47-
* Constructs an EvalException with a message and optional cause.
48+
* Constructs an EvalException with a message and optional cause and bool indicating if the error
49+
* should contain a stack trace.
4850
*
4951
* <p>The cause does not affect the error message, so callers should incorporate {@code
5052
* cause.getMessage()} into {@code message} if desired, or call {@code EvalException(Throwable)}.
5153
*/
52-
public EvalException(String message, @Nullable Throwable cause) {
54+
public EvalException(String message, @Nullable Throwable cause, boolean includeStackTrace) {
5355
super(checkNotNull(message), cause);
56+
this.includeStackTrace = includeStackTrace;
57+
}
58+
59+
/**
60+
* Constructs an EvalException with a message and optional cause and defaulting stack trace to
61+
* true.
62+
*
63+
* <p>The cause does not affect the error message, so callers should incorporate {@code
64+
* cause.getMessage()} into {@code message} if desired, or call {@code EvalException(Throwable)}.
65+
*/
66+
public EvalException(String message, @Nullable Throwable cause) {
67+
this(checkNotNull(message), cause, /* includeStackTrace= */ true);
5468
}
5569

5670
/** Constructs an EvalException using the same message as the cause exception. */
5771
public EvalException(Throwable cause) {
58-
super(getCauseMessage(cause), cause);
72+
this(getCauseMessage(cause), cause, /* includeStackTrace= */ true);
5973
}
6074

6175
/**
@@ -135,7 +149,7 @@ public final String getMessageWithStack() {
135149
* source line for each stack frame is obtained from the provided SourceReader.
136150
*/
137151
public final String getMessageWithStack(SourceReader src) {
138-
if (callstack != null) {
152+
if (includeStackTrace && callstack != null) {
139153
return formatCallStack(callstack, getMessage(), src);
140154
}
141155
return getMessage();

src/main/java/net/starlark/java/eval/MethodLibrary.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,13 @@ public StarlarkList<String> dir(Object object, StarlarkThread thread) throws Eva
869869
defaultValue = "\" \"",
870870
named = true,
871871
positional = false,
872-
doc = "The separator string between the objects, default is space (\" \").")
872+
doc = "The separator string between the objects, default is space (\" \")."),
873+
@Param(
874+
name = "stack_trace",
875+
doc = "If False stack trace is elided from failure for friendlier user messages",
876+
defaultValue = "True",
877+
positional = false,
878+
named = true),
873879
},
874880
extraPositionals =
875881
@Param(
@@ -879,8 +885,11 @@ public StarlarkList<String> dir(Object object, StarlarkThread thread) throws Eva
879885
+ " default) and joined with sep (defaults to \" \"), that appear in the"
880886
+ " error message."),
881887
useStarlarkThread = true)
882-
public void fail(Object msg, Object attr, String sep, Tuple args, StarlarkThread thread)
888+
public void fail(
889+
Object msg, Object attr, String sep, Boolean stackTrace, Tuple args, StarlarkThread thread)
883890
throws EvalException {
891+
boolean includeStack =
892+
stackTrace || thread.getSemantics().getBool(StarlarkSemantics.FORCE_STARLARK_STACK_TRACE);
884893
Printer printer = new Printer();
885894
boolean needSeparator = false;
886895
if (attr != Starlark.NONE) {
@@ -902,7 +911,7 @@ public void fail(Object msg, Object attr, String sep, Tuple args, StarlarkThread
902911
printer.debugPrint(arg, thread);
903912
needSeparator = true;
904913
}
905-
throw Starlark.errorf("%s", printer.toString());
914+
throw new EvalException(printer.toString(), null, includeStack);
906915
}
907916

908917
@StarlarkMethod(

src/main/java/net/starlark/java/eval/StarlarkSemantics.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,4 +266,7 @@ public final String toString() {
266266
/** Whether the Starlark interpreter uses UTF-8 byte strings instead of UTF-16 strings. */
267267
public static final String INTERNAL_BAZEL_ONLY_UTF_8_BYTE_STRINGS =
268268
"-internal_bazel_only_utf_8_byte_strings";
269+
270+
/** Globally Override fail(stack_trace=) to true. Flag default is false. */
271+
public static final String FORCE_STARLARK_STACK_TRACE = "-force_starlark_stack_trace";
269272
}

src/test/java/com/google/devtools/build/lib/packages/semantics/ConsistencyTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ private static BuildLanguageOptions buildRandomOptions(Random rand) throws Excep
139139
"--experimental_cc_shared_library=" + rand.nextBoolean(),
140140
"--experimental_repo_remote_exec=" + rand.nextBoolean(),
141141
"--experimental_dormant_deps=" + rand.nextBoolean(),
142+
"--force_starlark_stack_trace=" + rand.nextBoolean(),
142143
"--incompatible_always_check_depset_elements=" + rand.nextBoolean(),
143144
"--incompatible_disallow_empty_glob=" + rand.nextBoolean(),
144145
"--incompatible_do_not_split_linking_cmdline=" + rand.nextBoolean(),
@@ -185,6 +186,7 @@ private static StarlarkSemantics buildRandomSemantics(Random rand) {
185186
.setBool(BuildLanguageOptions.EXPERIMENTAL_CC_SHARED_LIBRARY, rand.nextBoolean())
186187
.setBool(BuildLanguageOptions.EXPERIMENTAL_REPO_REMOTE_EXEC, rand.nextBoolean())
187188
.setBool(BuildLanguageOptions.EXPERIMENTAL_DORMANT_DEPS, rand.nextBoolean())
189+
.setBool(StarlarkSemantics.FORCE_STARLARK_STACK_TRACE, rand.nextBoolean())
188190
.setBool(BuildLanguageOptions.INCOMPATIBLE_ALWAYS_CHECK_DEPSET_ELEMENTS, rand.nextBoolean())
189191
.setBool(BuildLanguageOptions.INCOMPATIBLE_DISALLOW_EMPTY_GLOB, rand.nextBoolean())
190192
.setBool(BuildLanguageOptions.INCOMPATIBLE_DO_NOT_SPLIT_LINKING_CMDLINE, rand.nextBoolean())

src/test/java/net/starlark/java/eval/EvaluationTestCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class EvaluationTestCase {
4242
* Updates the semantics used to filter predeclared bindings, and carried by subsequently created
4343
* threads. Causes a new StarlarkThread and Module to be created when next needed.
4444
*/
45-
private final void setSemantics(StarlarkSemantics semantics) {
45+
public final void setSemantics(StarlarkSemantics semantics) {
4646
this.semantics = semantics;
4747

4848
// Re-initialize the thread and module with the new semantics when needed.

src/test/java/net/starlark/java/eval/MethodLibraryTest.java

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -708,8 +708,8 @@ ev.new Scenario()
708708
* string and chars argument. If chars is null no argument is passed.
709709
*/
710710
private void checkStrip(
711-
String input, Object chars,
712-
String expLeft, String expRight, String expBoth) throws Exception {
711+
String input, Object chars, String expLeft, String expRight, String expBoth)
712+
throws Exception {
713713
if (chars == null) {
714714
ev.new Scenario()
715715
.update("s", input)
@@ -739,9 +739,7 @@ public void testStrip() throws Exception {
739739
checkStrip(" a b c ", Starlark.NONE, "a b c ", " a b c", "a b c");
740740
// Default whitespace with full range of Latin-1 whitespace chars.
741741
String whitespace = "\u0009\n\u000B\u000C\r\u001C\u001D\u001E\u001F\u0020\u0085\u00A0";
742-
checkStrip(
743-
whitespace + "a" + whitespace, null,
744-
"a" + whitespace, whitespace + "a", "a");
742+
checkStrip(whitespace + "a" + whitespace, null, "a" + whitespace, whitespace + "a", "a");
745743
checkStrip(
746744
whitespace + "a" + whitespace, Starlark.NONE, "a" + whitespace, whitespace + "a", "a");
747745
// Empty cases.
@@ -762,6 +760,42 @@ ev.new Scenario()
762760
.testIfErrorContains("0 1 2 3", "fail(1, 2, 3, msg=0)"); // deprecated
763761
}
764762

763+
@Test
764+
public void testFail_stackTrace() throws Exception {
765+
final StarlarkSemantics withoutForce =
766+
StarlarkSemantics.DEFAULT.toBuilder()
767+
.setBool(StarlarkSemantics.FORCE_STARLARK_STACK_TRACE, false)
768+
.build();
769+
770+
final StarlarkSemantics withForce =
771+
StarlarkSemantics.DEFAULT.toBuilder()
772+
.setBool(StarlarkSemantics.FORCE_STARLARK_STACK_TRACE, true)
773+
.build();
774+
775+
EvaluationTestCase customSemanticsEv = new EvaluationTestCase();
776+
777+
try {
778+
customSemanticsEv.setSemantics(withoutForce);
779+
customSemanticsEv.eval("fail('with_a_trace')");
780+
} catch (EvalException e) {
781+
assertThat(e.getMessageWithStack()).contains("Traceback (most recent call last)");
782+
}
783+
784+
try {
785+
customSemanticsEv.setSemantics(withoutForce);
786+
customSemanticsEv.eval("fail('without_a_trace', stack_trace=False)");
787+
} catch (EvalException e) {
788+
assertThat(e.getMessageWithStack()).doesNotContain("Traceback (most recent call last)");
789+
}
790+
791+
try {
792+
customSemanticsEv.setSemantics(withForce);
793+
customSemanticsEv.eval("fail('without_a_trace', stack_trace=False)");
794+
} catch (EvalException e) {
795+
assertThat(e.getMessageWithStack()).contains("Traceback (most recent call last)");
796+
}
797+
}
798+
765799
@Test
766800
public void testTupleCoercion() throws Exception {
767801
ev.new Scenario()

0 commit comments

Comments
 (0)