4.3.6 Starlark REPL / Playground
extraStarlark REPLs and playgrounds are scratchpads for language questions. They are useful when the question is "what does this Starlark expression or helper function do?" They are not a miniature Bazel server, a macro-expansion viewer, or a way to inspect configured targets.
The boundary matters because Starlark has Python-inspired syntax, but Bazel-specific semantics and file contexts. The official language page points out that Starlark behavior can differ from Python, that .bzl files and BUILD files have different roles, and that BUILD files have extra syntactic restrictions such as no function declarations and no *args or **kwargs arguments.1 A REPL can help you explore the language. It cannot reproduce every context where Bazel evaluates that language.
What A REPL Is Good For
Use a REPL or playground for small, pure Starlark experiments:
def unique(values):
seen = {}
result = []
for value in values:
if value not in seen:
seen[value] = True
result.append(value)
return result
unique(["fast", "dbg", "fast"])
That kind of snippet is a good fit because it does not depend on a package, a label, a configured target, a ctx object, or Bazel's native rule APIs. You are checking language shape: list and dict behavior, function control flow, equality, ordering, string representation, or whether a Python habit is also legal Starlark.
The official language reference is especially useful for deciding what to test. It documents mutability and freezing: lists and dicts are mutable only for objects created in the current context, and values become immutable after their context finishes.2 It also lists Python differences such as no recursion, no while, no exceptions, no import, and BUILD-file restrictions.3 Those are the kinds of rules a scratchpad can make concrete before you put the code inside a rule or macro.
What It Cannot Prove
The starlark-go map is a useful portability check
because upstream explicitly documents
dialect differences
from Bazel's Java implementation. Its
example_test.go
is executable documentation for embedding APIs such as Thread.Load. Those are
Go host APIs, not names available in Bazel BUILD or .bzl evaluation.
A REPL does not know what Bazel will load for a package. It does not expand macros, resolve labels through a caller's BUILD file, apply select(), construct ctx, return providers, or declare actions. If the code mentions native.package_name(), Label(), ctx.actions, DefaultInfo, a toolchain, or a provider from another rule, the interesting behavior is not just Starlark syntax anymore. It is Bazel evaluation.
For those questions, switch back to the section's Bazel-aware tools. Use 4.3.2 Macro Expansion Inspection when you need to see the rules a macro emitted. Use 4.3.1 Print Debugging when you need a value from loading or analysis. Use 4.3.5 Starlark Debugger when control flow is too complex for snapshots and you need breakpoints.
That is also why exact launch commands should be treated as environment-specific. A Starlark REPL sits alongside the VS Code Starlark debugger as a tool for cases where macro code itself fails.4 In practice, do not build your ruleset workflow around a universal bazel starlark command. Bazel's stable proof loop for repository behavior is still build, query, cquery, aquery, targeted tests, and the debugger when you need it.
A Useful Workflow
The best REPL workflow is short:
- Extract the pure helper you want to reason about.
- Remove Bazel-only objects from the experiment.
- Test a few concrete inputs.
- Move the helper back into
.bzlcode. - Prove the Bazel-facing behavior with the right tool.
For example, a helper that normalizes a list of mode strings can be explored interactively. The rule implementation that reads ctx.attr.mode, chooses outputs, and registers an action cannot. After the helper returns to the rule, use print() for a temporary analysis-time value, bazel query --output=build for macro output, or the query family from 5.2 Query when the question is about the target graph.
Keep The Tool In Its Lane
REPLs are most valuable because they are small. They remove the package graph, configuration, action graph, and remote execution from the feedback loop. That makes them fast and clear for language semantics, but it also means they are deliberately blind to the parts of Bazel that make a rule or macro useful.
Treat the REPL as an extra tool after formatting, editor feedback, print(), query output, and the debugger. If a snippet only uses Starlark values, try it interactively. If the snippet needs Bazel to answer, let Bazel answer.
A Starlark REPL or playground is for pure language experiments. It helps you test syntax, functions, and small data transformations before putting them into .bzl files.
It does not validate macro expansion, label resolution, configured targets, providers, toolchains, or actions. Use Bazel-aware tools for those.
Check your understanding · 3 questions
1.Which question is a good fit for a Starlark REPL or playground?
Select one answer
2.Which topics should send you back to Bazel-aware debugging tools instead of a standalone REPL?
Select all that apply
3.True or false: limits of using a Starlark REPL or playground.
Choose True or False for each sentence
bazel starlark should be treated as a portable, cross-version workflow dependency.load() chain works inside a real BUILD package.select(), ctx, providers, or toolchain resolution, switch back to Bazel-aware tools instead of a REPL.Footnotes
-
Starlark Language — syntax overview, BUILD versus
.bzlfile roles, and BUILD-file restrictions. ↩ -
Starlark Language — mutability model and freezing after an evaluation context finishes. ↩
-
Starlark Language — differences from Python, including unsupported language features. ↩
-
Sponsored Session: Enough Bazel to Be Dangerous: A Debugging Cookbook - Instructor: Alejandro Gomez — REPL and Starlark debugger as tools for macro-code failures. ↩