go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/starlark/interpreter/interpreter.go (about) 1 // Copyright 2018 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package interpreter contains customized Starlark interpreter. 16 // 17 // It is an opinionated wrapper around the basic single-file interpreter 18 // provided by go.starlark.net library. It implements 'load' and a new built-in 19 // 'exec' in a specific way to support running Starlark programs that consist of 20 // many files that reference each other, thus allowing decomposing code into 21 // small logical chunks and enabling code reuse. 22 // 23 // # Modules and packages 24 // 25 // Two main new concepts are modules and packages. A package is a collection 26 // of Starlark files living under the same root. A module is just one such 27 // Starlark file. Furthermore, modules can either be "library-like" (executed 28 // via 'load' statement) or "script-like" (executed via 'exec' function). 29 // 30 // Library-like modules can load other library-like modules via 'load', but may 31 // not call 'exec'. Script-like modules may use both 'load' and 'exec'. 32 // 33 // Modules within a single package can refer to each other (in 'load' and 34 // 'exec') using their relative or absolute (if start with "//") paths. 35 // 36 // Packages also have identifiers, though they are local to the interpreter 37 // (e.g. not defined anywhere in the package itself). For that reason they are 38 // called "package aliases". 39 // 40 // Modules from one package may refer to modules from another package by using 41 // the following syntax: 42 // 43 // load("@<package alias>//<path within the package>", ...) 44 // 45 // Presently the mapping between a package alias (e.g. 'stdlib') and package's 46 // source code (a collection of *.star files under a single root) is static and 47 // supplied by Go code that uses the interpreter. This may change in the future 48 // to allow loading packages dynamically, e.g. over the network. 49 // 50 // # Special packages 51 // 52 // There are two special package aliases recognized natively by the interpreter: 53 // '__main__' and 'stdlib'. 54 // 55 // '__main__' is a conventional name of the package with top level user-supplied 56 // code. When printing module paths in stack traces and error message, 57 // "@__main__//<path>" are shown as simply "//<path>" to avoid confusing users 58 // who don't know what "@<alias>//" might mean. There's no other special 59 // handling. 60 // 61 // Module '@stdlib//builtins.star' (if present) is loaded before any other code. 62 // All its exported symbols that do not start with '_' are made available in the 63 // global namespace of all other modules (except ones loaded by 64 // '@stdlib//builtins.star' itself). This allows to implement in Starlark 65 // built-in global functions exposed by the interpreter. 66 // 67 // Exec'ing modules 68 // 69 // The built-in function 'exec' can be used to execute a module for its side 70 // effects and get its global dict as a return value. It works similarly to 71 // 'load', except it doesn't import any symbols into the caller's namespace, and 72 // it is not idempotent: calling 'exec' on already executed module is an error. 73 // 74 // Each module is executed with its own instance of starlark.Thread. Modules 75 // are either loaded as libraries (via 'load' call or LoadModule) or executed 76 // as scripts (via 'exec' or ExecModule), but not both. Attempting to load 77 // a module that was previous exec'ed (and vice versa) is an error. 78 // 79 // Consequently, each Starlark thread created by the nterpreter is either 80 // executing some 'load' or some 'exec'. This distinction is available to 81 // builtins through GetThreadKind function. Some builtins may check it. For 82 // example, a builtin that mutates a global state may return an error when it is 83 // called from a 'load' thread. 84 // 85 // Dicts of modules loaded via 'load' are reused, e.g. if two different scripts 86 // load the exact same module, they'll get the exact same symbols as a result. 87 // The loaded code always executes only once. The interpreter MAY load modules 88 // in parallel in the future, libraries must not rely on their loading order and 89 // must not have side effects. 90 // 91 // On the other hand, modules executed via 'exec' are guaranteed to be executed 92 // sequentially, and only once. Thus 'exec'-ed scripts essentially form a tree, 93 // traversed exactly once in the depth first order. 94 // 95 // # Built-in symbols 96 // 97 // In addition to 'exec' builtin implemented by the interpreter itself, users 98 // of the interpreter can also supply arbitrary "predeclared" symbols they want 99 // to be available in the global scope of all modules. Predeclared symbols and 100 // '@stdlib//builtins.star' module explained above, are the primary mechanisms 101 // of making the interpreter do something useful. 102 package interpreter 103 104 import ( 105 "context" 106 "errors" 107 "fmt" 108 "os" 109 "path" 110 "strings" 111 112 "go.starlark.net/starlark" 113 "go.starlark.net/starlarkstruct" 114 ) 115 116 var ( 117 // ErrNoModule is returned by loaders when they can't find a source code of 118 // the requested module. It is also returned by LoadModule when it can't find 119 // a module within an existing package. 120 ErrNoModule = errors.New("no such module") 121 122 // ErrNoPackage is returned by LoadModule when it can't find a package. 123 ErrNoPackage = errors.New("no such package") 124 ) 125 126 // ThreadKind is enumeration describing possible uses of a thread. 127 type ThreadKind int 128 129 const ( 130 // ThreadUnknown indicates a thread used in some custom way, not via 131 // LoadModule or ExecModule. 132 ThreadUnknown ThreadKind = iota 133 134 // ThreadLoading indicates a thread that is evaluating a module referred to by 135 // some load(...) statement. 136 ThreadLoading 137 138 // ThreadExecing indicates a thread that is evaluating a module referred to by 139 // some exec(...) statement. 140 ThreadExecing 141 ) 142 143 const ( 144 // MainPkg is an alias of the package with user-supplied code. 145 MainPkg = "__main__" 146 // StdlibPkg is an alias of the package with the standard library. 147 StdlibPkg = "stdlib" 148 ) 149 150 const ( 151 // Key of context.Context inside starlark.Thread's local store. 152 threadCtxKey = "interpreter.Context" 153 // Key with *Interpreter that created the thread. 154 threadIntrKey = "interpreter.Interpreter" 155 // Key with TheadKind of the thread. 156 threadKindKey = "interpreter.ThreadKind" 157 // Key with the ModuleKey of the currently executing module. 158 threadModKey = "interpreter.ModuleKey" 159 ) 160 161 // Context returns a context of the thread created through Interpreter. 162 // 163 // Panics if the Starlark thread wasn't created through Interpreter.Thread(). 164 func Context(th *starlark.Thread) context.Context { 165 if ctx := th.Local(threadCtxKey); ctx != nil { 166 return ctx.(context.Context) 167 } 168 panic("not an Interpreter thread, no context in its locals") 169 } 170 171 // GetThreadInterpreter returns Interpreter that created the Starlark thread. 172 // 173 // Panics if the Starlark thread wasn't created through Interpreter.Thread(). 174 func GetThreadInterpreter(th *starlark.Thread) *Interpreter { 175 if intr := th.Local(threadIntrKey); intr != nil { 176 return intr.(*Interpreter) 177 } 178 panic("not an Interpreter thread, no Interpreter in its locals") 179 } 180 181 // GetThreadKind tells what sort of thread 'th' is: it is either inside some 182 // load(...), some exec(...) or some custom call (probably a callback called 183 // from native code). 184 // 185 // Panics if the Starlark thread wasn't created through Interpreter.Thread(). 186 func GetThreadKind(th *starlark.Thread) ThreadKind { 187 if k := th.Local(threadKindKey); k != nil { 188 return k.(ThreadKind) 189 } 190 panic("not an Interpreter thread, no ThreadKind in its locals") 191 } 192 193 // GetThreadModuleKey returns a ModuleKey with the location of the module being 194 // processed by a current load(...) or exec(...) statement. 195 // 196 // It has no relation to the module that holds the top-level stack frame. For 197 // example, if a currently loading module 'A' calls a function in module 'B' and 198 // this function calls GetThreadModuleKey, it will see module 'A' as the result, 199 // even though the call goes through code in module 'B'. 200 // 201 // Returns nil if the current thread is not executing any load(...) or 202 // exec(...), i.e. it has ThreadUnknown kind. 203 func GetThreadModuleKey(th *starlark.Thread) *ModuleKey { 204 if modKey, ok := th.Local(threadModKey).(ModuleKey); ok { 205 return &modKey 206 } 207 return nil 208 } 209 210 // Loader knows how to load modules of some concrete package. 211 // 212 // It takes a module path relative to the package and returns either module's 213 // dict (e.g. for go native modules) or module's source code, to be interpreted. 214 // 215 // Returns ErrNoModule if there's no such module in the package. 216 // 217 // The source code is returned as 'string' to guarantee immutability and 218 // allowing efficient use of string Go constants. 219 type Loader func(path string) (dict starlark.StringDict, src string, err error) 220 221 // Interpreter knows how to execute starlark modules that can load or execute 222 // other starlark modules. 223 type Interpreter struct { 224 // Predeclared is a dict with predeclared symbols that are available globally. 225 // 226 // They are available when loading stdlib and when executing user modules. Can 227 // be used to extend the interpreter with native Go functions. 228 Predeclared starlark.StringDict 229 230 // Packages is a mapping from a package alias to a loader with package code. 231 // 232 // Users of Interpreter are expected to supply a loader for at least __main__ 233 // package. 234 Packages map[string]Loader 235 236 // Logger is called by Starlark's print(...) function. 237 // 238 // The callback takes the position in the starlark source code where 239 // print(...) was called and the message it received. 240 // 241 // The default implementation just prints the message to stderr. 242 Logger func(file string, line int, message string) 243 244 // ThreadModifier is called whenever interpreter makes a new starlark.Thread. 245 // 246 // It can inject additional state into thread locals. Useful when hooking up 247 // a thread to starlarktest's reporter in unit tests. 248 ThreadModifier func(th *starlark.Thread) 249 250 // PreExec is called before launching code through some 'exec' or ExecModule. 251 // 252 // It may modify the thread or some other global state in preparation for 253 // executing a script. 254 // 255 // 'load' calls do not trigger PreExec/PostExec hooks. 256 PreExec func(th *starlark.Thread, module ModuleKey) 257 258 // PostExec is called after finishing running code through some 'exec' or 259 // ExecModule. 260 // 261 // It is always called, even if the 'exec' failed. May restore the state 262 // modified by PreExec. Note that PreExec/PostExec calls can nest (if an 263 // 'exec'-ed script calls 'exec' itself). 264 // 265 // 'load' calls do not trigger PreExec/PostExec hooks. 266 PostExec func(th *starlark.Thread, module ModuleKey) 267 268 modules map[ModuleKey]*loadedModule // cache of the loaded modules 269 execed map[ModuleKey]struct{} // a set of modules that were ever exec'ed 270 visited []ModuleKey // all modules, in order of visits 271 globals starlark.StringDict // global symbols exposed to all modules 272 } 273 274 // ModuleKey is a key of a module within a cache of loaded modules. 275 // 276 // It identifies a package and a file within the package. 277 type ModuleKey struct { 278 Package string // a package name, e.g. "stdlib" 279 Path string // path within the package, e.g. "abc/script.star" 280 } 281 282 // String returns a fully-qualified module name to use in error messages. 283 // 284 // If is either "@pkg//path" or just "//path" if pkg is "__main__". We omit the 285 // name of the top-level package with user-supplied code ("__main__") to avoid 286 // confusing users who are oblivious of packages. 287 func (key ModuleKey) String() string { 288 pkg := "" 289 if key.Package != MainPkg { 290 pkg = "@" + key.Package 291 } 292 return pkg + "//" + key.Path 293 } 294 295 // MakeModuleKey takes '[@pkg]//<path>' or '<path>', parses and normalizes it. 296 // 297 // Converts the path to be relative to the package root. Does some light 298 // validation, in particular checking the resulting path doesn't start with 299 // '../'. Module loaders are expected to validate module paths more rigorously 300 // (since they interpret them anyway). 301 // 302 // 'th' is used to get the name of the currently executing package and a path to 303 // the currently executing module within it. It is required if 'ref' is not 304 // given as an absolute path (i.e. does NOT look like '@pkg//path'). 305 func MakeModuleKey(th *starlark.Thread, ref string) (key ModuleKey, err error) { 306 defer func() { 307 if err == nil && (strings.HasPrefix(key.Path, "../") || key.Path == "..") { 308 err = errors.New("outside the package root") 309 } 310 }() 311 312 // 'th' can be nil here if MakeModuleKey is called by LoadModule or 313 // ExecModule: they are entry points into Starlark code, there's no thread 314 // yet when they start. 315 var current *ModuleKey 316 if th != nil { 317 current = GetThreadModuleKey(th) 318 } 319 320 // Absolute paths start with '//' or '@'. Everything else is a relative path. 321 hasPkg := strings.HasPrefix(ref, "@") 322 isAbs := hasPkg || strings.HasPrefix(ref, "//") 323 324 if !isAbs { 325 if current == nil { 326 err = errors.New("can't resolve relative module path: no current module information in thread locals") 327 } else { 328 key = ModuleKey{ 329 Package: current.Package, 330 Path: path.Join(path.Dir(current.Path), ref), 331 } 332 } 333 return 334 } 335 336 idx := strings.Index(ref, "//") 337 if idx == -1 || (!hasPkg && idx != 0) { 338 err = errors.New("a module path should be either '//<path>', '<path>' or '@<package>//<path>'") 339 return 340 } 341 342 if hasPkg { 343 if key.Package = ref[1:idx]; key.Package == "" { 344 err = errors.New("a package alias can't be empty") 345 return 346 } 347 } 348 key.Path = path.Clean(ref[idx+2:]) 349 350 // Grab the package name from thread locals, if given. 351 if !hasPkg { 352 if current == nil { 353 err = errors.New("no current package name in thread locals") 354 } else { 355 key.Package = current.Package 356 } 357 } 358 return 359 } 360 361 // loadedModule represents an executed starlark module. 362 type loadedModule struct { 363 dict starlark.StringDict // global dict of the module after we executed it 364 err error // non-nil if this module could not be loaded 365 } 366 367 // Init initializes the interpreter and loads '@stdlib//builtins.star'. 368 // 369 // Registers whatever was passed via Predeclared plus 'exec'. Then loads 370 // '@stdlib//builtins.star', which may define more symbols or override already 371 // defined ones. Whatever symbols not starting with '_' end up in the global 372 // dict of '@stdlib//builtins.star' module will become available as global 373 // symbols in all modules. 374 // 375 // The context ends up available to builtins through Context(...). 376 func (intr *Interpreter) Init(ctx context.Context) error { 377 intr.modules = map[ModuleKey]*loadedModule{} 378 intr.execed = map[ModuleKey]struct{}{} 379 380 intr.globals = make(starlark.StringDict, len(intr.Predeclared)+1) 381 for k, v := range intr.Predeclared { 382 intr.globals[k] = v 383 } 384 intr.globals["exec"] = intr.execBuiltin() 385 386 // Load the stdlib, if any. 387 top, err := intr.LoadModule(ctx, StdlibPkg, "builtins.star") 388 if err != nil && err != ErrNoModule && err != ErrNoPackage { 389 return err 390 } 391 for k, v := range top { 392 if !strings.HasPrefix(k, "_") { 393 intr.globals[k] = v 394 } 395 } 396 return nil 397 } 398 399 // LoadModule finds and loads a starlark module, returning its dict. 400 // 401 // This is similar to load(...) statement: caches the result of the execution. 402 // Modules are always loaded only once. 403 // 404 // 'pkg' is a package alias, it will be used to lookup the package loader in 405 // intr.Packages. 'path' is a module path (without leading '//') within 406 // the package. 407 // 408 // The context ends up available to builtins through Context(...). 409 func (intr *Interpreter) LoadModule(ctx context.Context, pkg, path string) (starlark.StringDict, error) { 410 key, err := MakeModuleKey(nil, fmt.Sprintf("@%s//%s", pkg, path)) 411 if err != nil { 412 return nil, err 413 } 414 415 // If the module has been 'exec'-ed previously it is not allowed to be loaded. 416 // Modules are either 'library-like' or 'script-like', not both. 417 if _, yes := intr.execed[key]; yes { 418 return nil, errors.New("the module has been exec'ed before and therefore is not loadable") 419 } 420 421 switch m, ok := intr.modules[key]; { 422 case m != nil: // already loaded or attempted and failed 423 return m.dict, m.err 424 case ok: 425 // This module is being loaded right now, Starlark stack trace will show 426 // the sequence of load(...) calls that led to this cycle. 427 return nil, errors.New("cycle in the module dependency graph") 428 } 429 430 // Add a placeholder to indicate we are loading this module to detect cycles. 431 intr.modules[key] = nil 432 433 m := &loadedModule{ 434 err: fmt.Errorf("panic when loading %q", key), // overwritten on non-panic 435 } 436 defer func() { intr.modules[key] = m }() 437 438 m.dict, m.err = intr.runModule(ctx, key, ThreadLoading) 439 return m.dict, m.err 440 } 441 442 // ExecModule finds and executes a starlark module, returning its dict. 443 // 444 // This is similar to exec(...) statement: always executes the module. 445 // 446 // 'pkg' is a package alias, it will be used to lookup the package loader in 447 // intr.Packages. 'path' is a module path (without leading '//') within 448 // the package. 449 // 450 // The context ends up available to builtins through Context(...). 451 func (intr *Interpreter) ExecModule(ctx context.Context, pkg, path string) (starlark.StringDict, error) { 452 key, err := MakeModuleKey(nil, fmt.Sprintf("@%s//%s", pkg, path)) 453 if err != nil { 454 return nil, err 455 } 456 457 // If the module has been loaded previously it is not allowed to be 'exec'-ed. 458 // Modules are either 'library-like' or 'script-like', not both. 459 if _, yes := intr.modules[key]; yes { 460 return nil, errors.New("the module has been loaded before and therefore is not executable") 461 } 462 463 // Reexecing a module is forbidden. 464 if _, yes := intr.execed[key]; yes { 465 return nil, errors.New("the module has already been executed, 'exec'-ing same code twice is forbidden") 466 } 467 intr.execed[key] = struct{}{} 468 469 // Actually execute the code. 470 return intr.runModule(ctx, key, ThreadExecing) 471 } 472 473 // Visited returns a list of modules visited by the interpreter. 474 // 475 // Includes both loaded and execed modules, successfully or not. 476 func (intr *Interpreter) Visited() []ModuleKey { 477 return intr.visited 478 } 479 480 // LoadSource returns a body of a file inside a package. 481 // 482 // It doesn't have to be a Starlark file, can be any text file as long as 483 // the corresponding package Loader can find it. 484 // 485 // 'ref' is either an absolute reference to the file, in the same format as 486 // accepted by 'load' and 'exec' (i.e. "[@pkg]//path"), or a path relative to 487 // the currently executing module (i.e. just "path"). 488 // 489 // Only Starlark threads started via LoadModule or ExecModule can be used with 490 // this function. Other threads don't have enough context to resolve paths 491 // correctly. 492 func (intr *Interpreter) LoadSource(th *starlark.Thread, ref string) (string, error) { 493 if kind := GetThreadKind(th); kind != ThreadLoading && kind != ThreadExecing { 494 return "", errors.New("wrong kind of thread (not enough information to " + 495 "resolve the file reference), only threads that do 'load' and 'exec' can call this function") 496 } 497 498 target, err := MakeModuleKey(th, ref) 499 if err != nil { 500 return "", err 501 } 502 503 dict, src, err := intr.invokeLoader(target) 504 if err == nil && dict != nil { 505 err = fmt.Errorf("it is a native Go module") 506 } 507 if err != nil { 508 // Avoid term "module" since LoadSource is often used to load non-star 509 // files. 510 if err == ErrNoModule { 511 err = fmt.Errorf("no such file") 512 } 513 return "", fmt.Errorf("cannot load %s: %s", target, err) 514 } 515 return src, nil 516 } 517 518 // Thread creates a new Starlark thread associated with the given context. 519 // 520 // Thread() can be used, for example, to invoke callbacks registered by the 521 // loaded Starlark code. 522 // 523 // The context ends up available to builtins through Context(...). 524 // 525 // The returned thread has no implementation of load(...) or exec(...). Use 526 // LoadModule or ExecModule to load top-level Starlark code instead. Note that 527 // load(...) statements are forbidden inside Starlark functions anyway. 528 func (intr *Interpreter) Thread(ctx context.Context) *starlark.Thread { 529 th := &starlark.Thread{ 530 Print: func(th *starlark.Thread, msg string) { 531 position := th.CallFrame(1).Pos 532 if intr.Logger != nil { 533 intr.Logger(position.Filename(), int(position.Line), msg) 534 } else { 535 fmt.Fprintf(os.Stderr, "[%s:%d] %s\n", position.Filename(), position.Line, msg) 536 } 537 }, 538 } 539 th.SetLocal(threadCtxKey, ctx) 540 th.SetLocal(threadIntrKey, intr) 541 th.SetLocal(threadKindKey, ThreadUnknown) 542 if intr.ThreadModifier != nil { 543 intr.ThreadModifier(th) 544 } 545 return th 546 } 547 548 // execBuiltin returns exec(...) builtin symbol. 549 func (intr *Interpreter) execBuiltin() *starlark.Builtin { 550 return starlark.NewBuiltin("exec", func(th *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 551 var module starlark.String 552 if err := starlark.UnpackArgs("exec", args, kwargs, "module", &module); err != nil { 553 return nil, err 554 } 555 mod := module.GoString() 556 557 // Only threads started via 'exec' (or equivalently ExecModule) can exec 558 // other scripts. Modules that are loaded via load(...), or custom callbacks 559 // from native code aren't allowed to call exec, since exec's impurity may 560 // lead to unexpected results. 561 if GetThreadKind(th) != ThreadExecing { 562 return nil, fmt.Errorf("exec %s: forbidden in this context, only exec'ed scripts can exec other scripts", mod) 563 } 564 565 // See also th.Load in runModule. 566 key, err := MakeModuleKey(th, mod) 567 if err != nil { 568 return nil, err 569 } 570 dict, err := intr.ExecModule(Context(th), key.Package, key.Path) 571 if err != nil { 572 // Starlark stringifies errors using Error(). For EvalError, Error() 573 // string is just a root cause, it does not include the backtrace where 574 // the execution failed. Preserve it explicitly by sticking the backtrace 575 // into the error message. 576 // 577 // Note that returning 'err' as is will preserve the backtrace inside 578 // the execed module, but we'll lose the backtrace of the 'exec' call 579 // itself, since EvalError object will just bubble to the top of the Go 580 // call stack unmodified. 581 if evalErr, ok := err.(*starlark.EvalError); ok { 582 return nil, fmt.Errorf("exec %s failed: %s", mod, evalErr.Backtrace()) 583 } 584 return nil, fmt.Errorf("cannot exec %s: %s", mod, err) 585 } 586 587 return starlarkstruct.FromStringDict(starlark.String("execed"), dict), nil 588 }) 589 } 590 591 // invokeLoader loads the module via the loader associated with the package. 592 func (intr *Interpreter) invokeLoader(key ModuleKey) (dict starlark.StringDict, src string, err error) { 593 loader, ok := intr.Packages[key.Package] 594 if !ok { 595 return nil, "", ErrNoPackage 596 } 597 return loader(key.Path) 598 } 599 600 // runModule really loads and executes the module, used by both LoadModule and 601 // ExecModule. 602 func (intr *Interpreter) runModule(ctx context.Context, key ModuleKey, kind ThreadKind) (starlark.StringDict, error) { 603 // Grab the source code. 604 dict, src, err := intr.invokeLoader(key) 605 switch { 606 case err != nil: 607 return nil, err 608 case dict != nil: 609 // This is a native module constructed in Go, no need to interpret it. 610 return dict, nil 611 } 612 613 // Otherwise make a thread for executing the code. We do not reuse threads 614 // between modules. All global state is passed through intr.globals, 615 // intr.modules and ctx. 616 th := intr.Thread(ctx) 617 th.Load = func(th *starlark.Thread, module string) (starlark.StringDict, error) { 618 key, err := MakeModuleKey(th, module) 619 if err != nil { 620 return nil, err 621 } 622 dict, err := intr.LoadModule(ctx, key.Package, key.Path) 623 // See comment in execBuiltin about why we extract EvalError backtrace into 624 // new error. 625 if evalErr, ok := err.(*starlark.EvalError); ok { 626 err = fmt.Errorf("%s", evalErr.Backtrace()) 627 } 628 return dict, err 629 } 630 631 // Let builtins know what this thread is doing. Some calls (most notably Exec 632 // itself) are allowed only from exec'ing threads, not from load'ing ones. 633 th.SetLocal(threadKindKey, kind) 634 // Let builtins (and in particular MakeModuleKey and LoadSource) know the 635 // package and the module that the thread executes. 636 th.SetLocal(threadModKey, key) 637 638 if kind == ThreadExecing { 639 if intr.PreExec != nil { 640 intr.PreExec(th, key) 641 } 642 if intr.PostExec != nil { 643 defer intr.PostExec(th, key) 644 } 645 } 646 647 // Record we've been here. 648 intr.visited = append(intr.visited, key) 649 650 // Execute the module. It may 'load' or 'exec' other modules inside, which 651 // will either call LoadModule or ExecModule. 652 // 653 // Use user-friendly module name (with omitted "@__main__") for error messages 654 // and stack traces to avoid confusing the user. 655 return starlark.ExecFile(th, key.String(), src, intr.globals) 656 }