Documentation
¶
Overview ¶
Package scriptdialect is the Starlark dialect every managed script is parsed, resolved and executed under, in one place, so the run, the validator and every reader of a script's syntax tree (the flow graph, internal/platform/scriptflow) read one language.
Index ¶
- Constants
- Variables
- func EntryPoint(file *syntax.File) *syntax.DefStmt
- func Exec(thread *starlark.Thread, name, source string, env starlark.StringDict, ...) (starlark.StringDict, error)
- func IsTest(s syntax.Stmt) bool
- func Parse(source string, predeclared func(string) bool) (*syntax.File, error)
- func Tests(file *syntax.File) []string
- type Coverage
- type Hooks
Constants ¶
const CoverStepsPerStatement = 4
CoverStepsPerStatement is the interpreter steps one inserted __cover__ call costs (load the builtin, load the index, call, pop the result), which a test run adds to its step cap for each call made so the instrumentation never fails a test that would fit uninstrumented (#1940).
const EntryPointName = "main"
EntryPointName is the function the platform calls after a script's module is loaded (#1944).
const TestPrefix = "test_"
TestPrefix starts the name of a test (#1939): a top-level def whose name begins with it is one of the script's tests, run by the test runner and never by a run.
Variables ¶
var Options = &syntax.FileOptions{ Set: true, While: false, TopLevelControl: true, GlobalReassign: true, LoadBindsGlobally: false, Recursion: false, }
Options is the dialect every managed script is parsed and resolved under.
while and recursion are OFF. Both are unbounded control flow whose cost cannot be read off the source, and a script that needs either is doing computation that belongs in SQL. This is the deliberate restrictiveness of the feature, not an oversight, and it is the only pair of switches here that is about safety.
TopLevelControl and GlobalReassign are ON, and both defaults are inverted on purpose. Starlark's defaults come from Bazel, where a .bzl file is a DECLARATION loaded by other files: top-level control flow and rebinding a top-level name would make what a file declares depend on evaluation order. A managed script is the opposite — a procedure executed once, top to bottom, by one runner, loaded by nobody. Under the Bazel defaults an author could not write `total = 0` and then accumulate into it inside a loop without wrapping the whole script in a function, which is friction that buys no safety and no determinism: neither switch has anything to do with either. `load` stays file-local (and there is nothing to load).
A script created since #1944 is held to more than the dialect: its work is in main(), which the platform calls (EntryPoint), and its top level declares. That is a rule of the authoring gates, checked on save, not a switch here, so a script saved before it keeps parsing and running as it did.
Functions ¶
func EntryPoint ¶ added in v1.138.0
EntryPoint returns the main() the platform calls for file, or nil when it calls none: main is not defined at the top level, takes parameters, or is already called by the top level itself. The last case is a script written in the Python habit (def main(), then main() at the bottom); calling it again would run its work twice, so the script's own call is the one that runs.
The run, the flow graph and the lint all ask this, so a script's main() is either called by all three or by none.
func Exec ¶ added in v1.138.0
func Exec(thread *starlark.Thread, name, source string, env starlark.StringDict, hooks Hooks) (starlark.StringDict, error)
Exec loads a script's module and then calls its main() when the platform owns that call (EntryPoint, #1944), or the function hooks.Entry names. What the function returns is not the run's result; platform.result is the one way a script reports one. The module's globals are returned whatever happened, since a caller that measures the run walks them.
func Parse ¶
Parse parses and resolves source under Options, keeping its comments, for a reader that walks the syntax tree and must see the tree a run sees, with every identifier's binding set by the resolver. predeclared reports whether a name is part of the script environment. The error is the parser's or the resolver's.
Types ¶
type Coverage ¶ added in v1.138.0
type Coverage struct {
// contains filtered or unexported fields
}
Coverage records which of a script's statements ran (#1940).
A statement is one that compiles to code: every statement but pass and a docstring, in every statement list, function bodies included, outside the script's tests. Coverage is per statement, so the branches inside one expression (a conditional expression, and/or, a comprehension's if) are not measured. One Coverage is shared by every test of one source: the table is built by the first run, and the statements are numbered in walk order, which is the same order for the same source every time.
func NewCoverage ¶ added in v1.138.0
func NewCoverage() *Coverage
NewCoverage returns an empty Coverage.
func (*Coverage) Calls ¶ added in v1.138.0
Calls is how many instrumented statements ran, across every run the Coverage was handed to.
func (*Coverage) MissedLines ¶ added in v1.138.0
MissedLines is the lines holding a statement that never ran, ascending and each once.
type Hooks ¶ added in v1.138.0
type Hooks struct {
// AtMainEnd, when not nil, is called on the thread as main() finishes, by
// whichever caller called it, while main's frame is still live: at each
// return and at the end of its body. It is how a caller measures what main
// holds when it ends, which is gone by the time Exec returns; an error from
// it fails the run at that point.
AtMainEnd func(*starlark.Thread) error
// Entry, when not empty, is the top-level function called after the module
// loads in place of main(): a test (#1939). It takes no arguments.
Entry string
// Cover, when not nil, is told each statement of the script as it is about
// to run (#1940); see Instrument.
Cover *Coverage
}
Hooks is what a caller asks Exec to do beyond loading a script's module and calling its main(). The zero value asks for nothing more.