Skip to content
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

Change the stdout dynamically within the same interpreter instance? #1631

Closed
rcoreilly opened this issue May 12, 2024 · 7 comments
Closed

Change the stdout dynamically within the same interpreter instance? #1631

rcoreilly opened this issue May 12, 2024 · 7 comments
Labels
question Further information is requested

Comments

@rcoreilly
Copy link

rcoreilly commented May 12, 2024

Proposal

It would be nice if we could change the default stdout etc used by fmt.Print* within the same running interpreter instance. For example we're writing a shell using yaegi (which is working really well and would not have been possible without this amazing package!), and we want to be able to dynamically redirect output based on standard shell > kind of syntax.

Background

In fixStdlib in interp/use.go, it looks like you replace these methods with functions that use local copies of the interp.std* variables. Thus, even if you had a way of updating those interp.std* values, it wouldn't actually update the Print* functions, which seem to be permanently tied to these initial values.

Thus, one simple change would be to change this code to use interp.std* and then add a SetStdIO or similar such method that sets those values. Or a SetOptions to use the Options values to set them, but that might be more problematic in terms of making the new settings take effect everywhere.

e.g., the new code would be, on line 168:

	p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(interp.stdout, a...) })

would you accept a PR along these lines? Any thoughts about the SetStdIO function name? Separate SetStdout etc instead?

Workarounds

for now, we will try to revert the "fixed" mapTypes to use the original os.Stdout functions so we can control by changing os.Stdout.

@rcoreilly
Copy link
Author

Unfortunately, it looks like the Use function always calls fixStdlib so it is impossible to overcome this problem.

@rcoreilly
Copy link
Author

Sorry for the churn here: the proper workaround here is to give yaegi a set of wrappers that we can then redirect ourselves.

@ldez
Copy link
Member

ldez commented May 12, 2024

Hello, have you tried interp.Options.Stdout/interp.Options.Stderr?

yaegi/interp/interp.go

Lines 301 to 304 in 381e045

// Standard input, output and error streams.
// They default to os.Stdin, os.Stdout and os.Stderr respectively.
Stdin io.Reader
Stdout, Stderr io.Writer

@rcoreilly
Copy link
Author

Yes, but I need to be able to change those after creating the interpreter!

@ldez
Copy link
Member

ldez commented May 12, 2024

You can use a writer wrapper, something like that:

https://go.dev/play/p/sxFkJGLgmH1

You wrap the writer inside another writer and you can change the writer when you want.
But take care of a concurrency problem, a lock can be useful.

@ldez ldez added the question Further information is requested label May 12, 2024
@ldez
Copy link
Member

ldez commented May 12, 2024

After more thinking, your suggestion can lead to concurrency problems.
I think the best approach is the writer wrapper with a lock inside.
WDTY?

@rcoreilly
Copy link
Author

Yep that sounds good -- I implemented the wrapper and it works well. It seems that some writers are already concurrency-safe and others are not, so a lock is a good idea. I will go ahead and close this issue and anyone else looking for this functionality could presumably find this ticket. You could also maybe add a note in the relevant docs at some point.. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants