Skip to content

About

🌟 Refined legendary Go wrapper for Google's Starlark

Topics

Resources

Security policy

Stars

7 stars

Watchers

0 watching

Forks

Β 
Β 

Repository files navigation

🌟 Starlight Enhanced - Bridging Go and Starlark

Go Reference codecov codacy codeclimate goreportcard

Starlight runs Starlark scripts from Go and bridges Go values and functions into them. Use Eval for one-off scripts, Cache for file-backed scripts and modules, or the convert package directly.

Installation

go get github.com/1set/starlight

The module's Go floor is 1.19. Its interpreter requirement is go.starlark.net v0.0.0-20260324133313-ffb3f39dd27a. Another dependency can select a newer interpreter; check your application's selection with go list -m go.starlark.net. Build production applications with a supported Go toolchain containing current security fixes.

This README and usage examples describe this checkout. The published API reference may show a different released version; the current runner and conversion APIs are in this source tree. In your application module, inspect the selected version and its local documentation with:

go list -m github.com/1set/starlight
go doc github.com/1set/starlight
go doc github.com/1set/starlight/convert

Use host-selected, reviewed source and modules. The retained interpreter has a known parser recursion limitation, and this package does not provide process or resource isolation. See SECURITY.md for the deployment boundary.

Quick start

This complete program calls a Go function and updates an exported Go struct field through a pointer:

package main

import (
	"fmt"

	"github.com/1set/starlight"
)

type Contact struct {
	Name string
}

func main() {
	contact := Contact{Name: "Ada"}
	globals := map[string]interface{}{
		"contact": &contact,
		"greet": func(name string) string {
			return "Hello, " + name + "!"
		},
	}
	result, err := starlight.Eval([]byte(`
contact.Name = "Lin"
text = greet(contact.Name)
count = 2 + 3
`), globals, nil)
	if err != nil {
		fmt.Println("script error:", err)
		return
	}
	fmt.Println(result["text"]) // Hello, Lin!
	fmt.Println(contact.Name)   // Lin
	count := result["count"]
	fmt.Printf("%v (%T)\n", count, count) // 5 (int64)
}

Script inputs and results

Eval(src, globals, load) executes a whole script, including statements and function definitions.

Argument Meaning
src as string A filename, not source text.
src as []byte or io.Reader Script source text.
globals Predeclared input names and Go values; nil means none.
load Resolver for load(); nil leaves module loading unavailable.

The returned map[string]interface{} contains bindings defined by the script, converted through convert.FromValue. Input names are not automatically copied into the result. Functions and custom values without a native Go conversion remain starlark.Value objects. Input conversion, parsing, loading and execution failures return an error; host changes made before an error are not rolled back. Eval compiles on every call and does not use Cache automatically. See result-conversion policies for conversions that have no error return.

Files, modules and caching

cache := starlight.New("./scripts")
result, err := cache.Run("main.star", map[string]interface{}{"name": "Ada"})

New searches directories in order and panics if none are supplied. Run caches compiled scripts by filename and input-name set, then executes the script on each call with the current input values. Its load() calls use the same directory search; loaded modules cache their initialized exports or errors. Editing files does not automatically invalidate either cache.

cache.Forget("main.star") invalidates that filename's script/module entries. It does not invalidate modules that already loaded it; use cache.Reset() to refresh the whole cache. For one-off execution with the same loader, pass cache.Load as the third argument to Eval.

WithGlobals(moduleGlobals, dirs...) returns a cache with predeclared values for loaded modules. They are separate from the per-call globals supplied to Run; pass values to both when needed. Unlike New, it returns an error for an empty directory list. See the complete file and module example, including both script files.

Conversion and host data

convert.ToValue returns (starlark.Value, error); convert.ToValueWithTag also selects the struct-field tag name. The direct conversion example shows custom field tags and a callable Go method. convert.FromValue returns the corresponding Go value:

Starlark value Go result
None, bool, float, string nil, bool, float64, string
Integer int64, then uint64, then *big.Int, by magnitude
List / tuple []interface{}
Bytes []byte
Dict map[interface{}]interface{}
Set map[interface{}]bool (FromSetToSlice returns an insertion-ordered slice)
time.time / time.duration time.Time / time.Duration
Go wrapper Its underlying Go value

Conversion is not a JSON encoding or a guarantee of identical round-trip types. Go maps, slices and structs use live wrappers: scripts can change shared Go data where it is writable. Pass a struct pointer to allow exported-field writes; Go arrays are copied to slices. A Go []byte is a slice wrapper, not native Starlark bytes. Wrapper Freeze is shallow; it neither copies host data nor synchronizes host access. The host must coordinate concurrent access. See the slice update example for explicit write-back when the host needs the new length.

GoStruct and GoInterface expose the Go methods available on the wrapped value; GoMap and GoSlice expose their mapping/sequence builtins instead. Field tags rename or hide fields; they do not hide methods. Neither tags nor wrapper Freeze restrict the effects of a Go method, which may still change host state.

Channels, complex numbers, unsafe.Pointer and uintptr have no direct conversion. Statically unsupported collection element types are rejected; unsupported dynamic values inside interface collections can remain opaque wrappers. Existing starlark.Value objects pass through, except typed-nil pointers become None.

Wrapped Go functions accept positional arguments only, including variadics. Arguments and collection writes use checked conversions: overflow, fractional float-to-integer conversion and None for a non-nullable target are errors. A non-nil final error becomes a script error; after removing that error result, zero, one or multiple return values become None, one value or a tuple.

Contributing and license

Open an issue before substantial changes to avoid duplicate work. Contributions are welcome under the MIT License.

This is a fork of Starlight, created by Nate Finch and its contributors. README-old.md preserves the upstream documentation as historical reference.

About

🌟 Refined legendary Go wrapper for Google's Starlark

Topics

Resources

Security policy

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages