moonsharp icon indicating copy to clipboard operation
moonsharp copied to clipboard

io.write() does not print standard console

Open rmcrackan opened this issue 9 years ago • 10 comments

io.write does not output to console. It has a green/done status on the StdLib PDF (http://www.moonsharp.org/MoonSharpStdLib.pdf)

        Console.WriteLine("Console.WriteLine 1");
        Script.RunString("print('lua print')");
        Script.RunString("io.write('lua io.write')");
        Console.WriteLine("Console.WriteLine 2");

output: Console.WriteLine 1 lua print Console.WriteLine 2

Windows 7 .NET 4.6.1 MoonSharp 1.8.0.0

rmcrackan avatar Oct 13 '16 15:10 rmcrackan

This is because the StreamWriter instance is not being flushed when io.write is called.

Call io.flush() to flush stdout. The messages will be displayed.

It is possible to fix this using AutoFlush on the stream, but I strongly oppose this approach as this behavior is not necessarily correct. Not only does it affect all streams, it could also change behavior when the process' stdout is being redirected. I'm for closing this issue.

In StandardIOFileUserDataBase:25

f.Initialize(stream, null, new StreamWriter(stream) { AutoFlush = true });

bplu4t2f avatar Jan 29 '17 18:01 bplu4t2f

I am unable to make io.flush() affect output

rmcrackan avatar Jan 30 '17 20:01 rmcrackan

Be aware that Moonsharp currently re-initializes the core modules every time a new Script is instanciated.

If you do

Script.RunString("io.write('lua io.write'); io.flush()");

rather than

Script.RunString("io.write('lua io.write')");
Script.RunString("io.flush()");

you should see the message. You could argue that this behavior is intentional if you think about the StreamWriter's (and its internal buffer's) lifetime being tied to the running Script. On the other hand, you'd probably expect the StreamWriter to be disposed of properly (and possibly even flushed) when the script's lifetime ends (which currently isn't the case, which is strictly speaking a resource leak).

bplu4t2f avatar Feb 03 '17 19:02 bplu4t2f

No dice:

Script script = new Script();

script.DoString("print 'print works'");

// doesn't work; with or without flush
script.DoString("io.write('io.write does not work'); io.flush()");
Script.RunString("io.write('lua io.write'); io.flush()");

// hack: assign print to io.write since i can't figure out how to redirect io.write
script.DoString(@"
    local _io_write = io.write;
    io.write = print;");
script.DoString("io.write('io.write hack works')");

output: print works io.write hack works

rmcrackan avatar Feb 03 '17 20:02 rmcrackan

OK that worked on my machine, so it could be an issue with your particular setup of stdout. Can you try running

var stream = Console.OpenStandardOutput();
var writer = new StreamWriter(stream);
writer.Write("TEST TEST TEST");
writer.Flush();

, which is the equivalent of what Lua does when you call io.write(); io.flush() in a new Script instance?

E: Also keep in mind that io.write() does NOT append a newline. Maybe your environment is expecting an \n for some reason.

bplu4t2f avatar Feb 03 '17 20:02 bplu4t2f

This must just be an idiosyncrasy of Visual Studio's Test Explorer:

Console.WriteLine("begin 1");
Console.Out.WriteLine("begin 2");

var stream = Console.OpenStandardOutput();
var writer = new System.IO.StreamWriter(stream);
writer.Write("TEST TEST TEST");
writer.Flush();

Console.WriteLine("end 1");
Console.Out.WriteLine("end 2");

Standard Output: begin 1 begin 2 end 1 end 2

rmcrackan avatar Feb 03 '17 20:02 rmcrackan

Well, not exactly.

If you are using visual studio as a host process, then visual studio will most likely want to redirect standard output in some way (for one, to display it in the output window).

The OpenStandardOutput method specifically gets the actual stdout stream (of visual studio), which probably isn't displayed anywhere. If you start devenv.exe from a command prompt, you should see those messages.

@xanathar Maybe it is better to use Console.Out rather than Console.OpenStandardOut() to acquire the application's current stdout when initializing the IO module. Otherwise, Lua is implicitly able to override the host environment's stdout setting (via Console.SetOut()) and access the original stdin, stdout and stderr streams, which is very likely to be unintended (potentially even dangerous if the host's original stdout is being processed further).

bplu4t2f avatar Feb 03 '17 21:02 bplu4t2f

This appears to be idiosyncratic/unclear behavior in Visual Studio's Test Explorer, not a Moon# bug. Feel free to close my issue.

These all write to my window in a normal console app. But the first method does not print to "standard output" in VS Test Explorer:

var stream = Console.OpenStandardOutput();
var writer = new System.IO.StreamWriter(stream);
writer.WriteLine("TEST 1 OpenStandardOutput");
writer.Flush();

Console.WriteLine("TEST 2 Console.WriteLine");

Console.Out.WriteLine("TEST 3 Console.Out.WriteLine");

rmcrackan avatar Feb 06 '17 17:02 rmcrackan

By the way, theoretically it should have been possible to set a Script's output stream via

var s = new Sript();
s.Options.Stdout = someStream;

However in practice this doesn't work for 2 reasons:

  1. The core modules are initialized in the Script constructor before you have the chance to modify the ScriptOptions. I personally find the ScriptOptions very awkward and I'm wondering why they aren't immutable and passed into the Script constructor. The way it is currently is just asking for problems.

  2. ScriptOptions.StdOut (and the other streams) are Stream, not TextWriter/TextReader. Console.Out, which is what you'd need to get the same behavior as Console.WriteLine is TextWriter (which is notably different from Console.OpenStandardOutput()). Interestingly, the current architecture of the Lua IO module is so that the Stream from ScriptOptions.StdOut is immediately wrapped by a TextWriter anyway, so it would have been feasible to use TextWriter all along for the ScriptOptions.StdOut property.

If you really don't consider this a bug, I don't know what else to call it. ScriptOptions.StdOut is currently completely broken.

bplu4t2f avatar Feb 06 '17 18:02 bplu4t2f

Thank you for the info on Stdout not working, saved me a world of head-hurt!

For any one else trying to get around the problem, you can as a workaround update the default ScriptOptions before instantiating the script to get it to recognize the setting.

// This won't work, stdout will not be routed to your stream
var s = new Script();
s.Options.Stdout = myStream;
s.DoString("io.stdout.write('foo'); io.stdout.flush();");
// This will work, stdout will now be routed to your stream
MoonSharp.Interpreter.Script.DefaultOptions.Stdout = myStream;
var s = new Script();
s.DoString("io.stdout.write('foo'); io.stdout.flush();");

I personally don't like working with static variables but as long as you only instantiate scripts in a single thread you should be fine.