-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
need API to enumerate names/bindings of a Function's local variables #538
Comments
Builtins are not associated with any module, and do not have special access to any variables that were not passed to them as arguments; this is as it should be. It is possible (from Go) to set a thread-local value that affects the behavior of certain built-ins called from that thread. In this way, built-ins can communicate with each other through Go data structures, and communicate with the host application as a whole. It is also possible to use the debug interface to walk up the call stack to find any enclosing active calls to Starlark functions, and find the their associated modules and global variables ( Why not give the |
Thank you very much for your answer! let name = 'hungtcs'
let message = `hello ${ name }` I'm aiming to implement something similar in starlark name = 'hungtcs'
message = format("${ name }") # format is a starlark.NewBuiltin Although starlark already provides functions such as Thanks for the |
Sorry I just realized that I can't get local variables inside functions this way. name = "hungtcs"
def test():
foo = "abc"
print(format("hello {name},, {foo}"))
test() var predeclared = starlark.StringDict{
"format": starlark.NewBuiltin("format", func(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var globals = thread.DebugFrame(1).Callable().(*starlark.Function).Globals()
var string = args[0].(starlark.String)
var format *starlark.Builtin
if val, err := string.Attr("format"); err != nil {
return nil, err
} else {
format = val.(*starlark.Builtin)
}
var formatKwargs = make([]starlark.Tuple, 0)
for key, val := range globals {
formatKwargs = append(formatKwargs, starlark.Tuple{starlark.String(key), val})
}
var result, err = format.CallInternal(thread, starlark.Tuple{}, formatKwargs)
if err != nil {
return nil, err
}
return result, nil
}),
} |
It's possible to access the values of local variables through the debug interface: |
@adonovan Yes, thanks for your perspective, I added a line of code to Line 730 in 3f0a370
func (fn *Function) Locals() []compile.Binding { return fn.funcode.Locals } But how to get for idx, local := range locals {
fmt.Println(thread.DebugFrame(1).Local(idx))
} Here's my current var predeclared = starlark.StringDict{
"format": starlark.NewBuiltin("format", func(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var function = thread.DebugFrame(1).Callable().(*starlark.Function)
var locals = function.Locals()
var globals = function.Globals()
var formatArgs = make(map[string]starlark.Value)
for key, val := range globals {
formatArgs[key] = val
}
for idx, local := range locals {
formatArgs[local.Name] = thread.DebugFrame(1).Local(idx)
}
var string = args[0].(starlark.String)
var format *starlark.Builtin
if val, err := string.Attr("format"); err != nil {
return nil, err
} else {
format = val.(*starlark.Builtin)
}
var formatKwargs = make([]starlark.Tuple, 0)
for key, val := range formatArgs {
formatKwargs = append(formatKwargs, starlark.Tuple{starlark.String(key), val})
}
var result, err = format.CallInternal(thread, starlark.Tuple{}, formatKwargs)
if err != nil {
return nil, err
}
return result, nil
}),
} |
You need to handle the case where the Callable is something other than a *Function.
Again here you need to handle a type error.
This can be a panic, since we know that every string has a format method.
Not needed. val is fine.
key is already a string.
Never call CallInternal directly. Use Call. You can pass nil for the Tuple. You can The main problem is that Function.Locals isn't public API. We could add it (or something like it), but we'd need to be very careful not to expose anything about the representation of compiled programs. |
Thanks @adonovan |
This change adds the following new API to allow debugging tools and built-in functions access to the internals of Function values and call frames: package starlark type Binding struct { Name string Pos syntax.Position } func (fr *frame) NumLocals() int func (fr *frame) Local(i int) (Binding, Value) type DebugFrame interface { ... NumLocals() int Local(i int) (Binding, Value) } func (fn *Function) NumFreeVars() int func (fn *Function) FreeVar(i int) (Binding, Value) This is strictly a breaking change, but the changed functions (the Local methods) were previously documented as experimental. The fix is straightforward. Also, a test of DebugFrame to write an 'env' function in a similar vein to Python's 'dir' function. Fixes #538
This change adds the following new API to allow debugging tools and built-in functions access to the internals of Function values and call frames: package starlark type Binding struct { Name string Pos syntax.Position } func (fr *frame) NumLocals() int func (fr *frame) Local(i int) (Binding, Value) type DebugFrame interface { ... NumLocals() int Local(i int) (Binding, Value) } func (fn *Function) NumFreeVars() int func (fn *Function) FreeVar(i int) (Binding, Value) This is strictly a breaking change, but the changed functions (the Local methods) were previously documented as experimental. The fix is straightforward. Also, a test of DebugFrame to write an 'env' function in a similar vein to Python's 'dir' function. Fixes #538
Hi all,
I am trying to access the context variable that calls the function in a custom built-in function, can I do it?
My idea was to implement code similar to the following, but using go to writing the
hello
method:The text was updated successfully, but these errors were encountered: