code: func (intr *Interpreter) LoadModule(ctx context.Context, pkg, path string) (starlark.StringDict, error) {
  	key, err := makeModuleKey(fmt.Sprintf("@%s//%s", pkg, path), nil)
  	if err != nil {
  		return nil, err
  	}
  
  	// If the module has been 'exec'-ed previously it is not allowed to be loaded.
  	// Modules are either 'library-like' or 'script-like', not both.
  	if _, yes := intr.execed[key]; yes {
  		return nil, errors.New("the module has been exec'ed before and therefore is not loadable")
  	}
  
  	switch m, ok := intr.modules[key]; {
  	case m != nil: // already loaded or attempted and failed
  		return m.dict, m.err
  	case ok:
  		// This module is being loaded right now, Starlark stack trace will show
  		// the sequence of load(...) calls that led to this cycle.
  		return nil, errors.New("cycle in the module dependency graph")
  	}
  
  	// Add a placeholder to indicate we are loading this module to detect cycles.
  	intr.modules[key] = nil
  
  	m := &loadedModule{
  		err: fmt.Errorf("panic when loading %q", key), // overwritten on non-panic
  	}
  	defer func() { intr.modules[key] = m }()
  
  	m.dict, m.err = intr.runModule(ctx, key, ThreadLoading)
  	return m.dict, m.err
  }
docs: // LoadModule finds and loads a starlark module, returning its dict.
  //
  // This is similar to load(...) statement: caches the result of the execution.
  // Modules are always loaded only once.
  //
  // 'pkg' is a package alias, it will be used to lookup the package loader in
  // intr.Packages. 'path' is a module path (without leading '//') within
  // the package.
  //
  // The context ends up available to builtins through Context(...).