That should be fine. My example was the simplest way possible to spin up a
script, so I didn't bother with an explicit scope. But that doesn't change
anything really. Also if you re-use the same engine many times, you only really
need to wire up the custom output writer once - after initial engine creation.
You can also make the TextWriter slightly smarter and ensure that it only
colorizes text that comes from the print function. There isn't a
straightforward way to do that, but you can cheat and check the call stack. As
long as your code is full-trust then this shouldn't be an issue.
This version might be a little more suitable and complete after seeing your
example. I still had to make some assumptions about how your Execute is being
handled, but this should at least be fairly close:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using IronPython.Compiler;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
var scripting = new Scripting();
scripting.RunScript("22");
scripting.RunScript("print(23)");
Console.ReadKey();
}
}
internal class Scripting
{
private readonly ScriptEngine _engine;
private readonly ScriptScope _scope;
private readonly CompilerOptions _options;
private readonly ConsoleWriter _green;
private readonly PythonFile _stderr;
private readonly PythonFile _stdin;
private readonly PythonFile _stdout;
public Scripting()
{
_engine = Python.CreateEngine();
var io = _engine.Runtime.IO;
_green = new ConsoleWriter(io.OutputWriter, ConsoleColor.Green);
io.SetOutput(io.OutputStream, _green);
_scope = _engine.CreateScope();
_options = new PythonCompilerOptions(ModuleOptions.None);
var sys = _engine.GetSysModule();
_stderr = sys.GetVariable<PythonFile>("stderr");
_stdin = sys.GetVariable<PythonFile>("stdin");
_stdout = sys.GetVariable<PythonFile>("stdout");
}
public bool RunScript(string text)
{
var sctype = SourceCodeKind.InteractiveCode;
var source = _engine.CreateScriptSourceFromString(text, sctype);
try
{
source.Compile(_options);
}
catch
{
sctype = SourceCodeKind.Statements;
source = _engine.CreateScriptSourceFromString(text, sctype);
try
{
source.Compile(_options);
}
catch (Exception e)
{
var eo = _engine.GetService<ExceptionOperations>();
PrintLine(eo.FormatException(e));
return false;
}
}
try
{
_green.IsEnabled = true;
try
{
source.Execute(_scope);
}
finally
{
_green.IsEnabled = false;
}
}
catch (Exception e)
{
if (e.Message.Contains("Thread was being aborted"))
{
_stderr.write("[Script stopped----------]\n");
}
else
{
var eo = _engine.GetService<ExceptionOperations>();
PrintLine(eo.FormatException(e));
}
return false;
}
if (_stderr != null)
PrintLine("Ok");
return true;
}
void PrintLine(string msg)
{
_stdout.write(msg + Environment.NewLine);
}
}
class ConsoleWriter : TextWriter
{
private readonly TextWriter _inner;
public bool IsEnabled { get; set; }
public ConsoleColor Color { get; set; }
public ConsoleWriter(TextWriter inner, ConsoleColor color)
{
_inner = inner;
Color = color;
}
public override void Write(string value)
{
var enabled = IsEnabled;
if (enabled)
{
var stack = (new StackTrace()).GetFrames() ?? new StackFrame[0];
var indexed =
stack.Select((f, ix) => new {mInfo = f.GetMethod(), ix});
var printFrameQuery =
from frame in indexed
where
frame.mInfo.Name == "Print"
&& frame.mInfo.DeclaringType == typeof
(IronPython.Runtime.Operations.PythonOps)
select frame;
var printFrame = printFrameQuery.FirstOrDefault();
if (printFrame != null)
{
var printFrameCallerIx = printFrame.ix + 1;
var caller = stack[printFrameCallerIx].GetMethod();
var isHook =
caller.DeclaringType == typeof
(IronPython.Modules.SysModule)
&& caller.Name == "displayhookImpl";
// only apply color if this is a message being written by
the print() function
// but not if print() was called via internal sys
displayhook - which should
// leave only explicit print() calls
enabled = !isHook;
}
}
if (!enabled)
{
_inner.Write(value);
return;
}
var original = Console.ForegroundColor;
Console.ForegroundColor = Color;
_inner.Write(value);
Console.ForegroundColor = original;
}
public override Encoding Encoding
{
get { return _inner.Encoding; }
}
}
}
Keith Rome
Senior Consultant and Architect
MCPD-EAD, MCSD, MCDBA, MCTS-WPF, MCTS-TFS, MCTS-WSS
Wintellect | 770.617.4016 | [email protected]
www.wintellect.com
-----Original Message-----
From: Doug Blank [mailto:[email protected]]
Sent: Thursday, September 20, 2012 6:44 AM
To: Keith Rome
Cc: [email protected]
Subject: Re: [Ironpython-users] Separating return values from printed values?
On Thu, Sep 20, 2012 at 1:37 AM, Keith Rome <[email protected]> wrote:
> This might give you what you need (hooking in via the runtime output writer,
> which is where the print function routes to), it seems to work for my quick
> test:
>
> using System;
> using System.IO;
> using System.Text;
> using IronPython.Hosting;
>
> namespace ConsoleApplication3
> {
> class Program
> {
> static void Main(string[] args)
> {
> RunScript("22");
> RunScript("print(23)");
> Console.ReadKey();
> }
>
> static void RunScript(string script)
> {
> var engine = Python.CreateEngine();
> var io = engine.Runtime.IO;
> io.SetOutput(io.OutputStream, new GreenWriter(io.OutputWriter));
> var result = engine.Execute(script);
> if (result != null)
> Console.WriteLine(result);
> }
> }
>
> class GreenWriter : TextWriter
> {
> private readonly TextWriter _inner;
> public GreenWriter(TextWriter inner)
> {
> _inner = inner;
> }
> public override void Write(string value)
> {
> var original = Console.ForegroundColor;
> Console.ForegroundColor = ConsoleColor.Green;
> _inner.Write(value);
> Console.ForegroundColor = original;
> }
> public override Encoding Encoding
> {
> get { return _inner.Encoding; }
> }
> }
> }
>
Thanks for the example! I guess we also have some other issues which may cause
use to be unable to use that interface, or maybe our code is overly
complicated? We:
* use a shared scope, if available so other DLR languages can interoperate
(manager.scope below)
* compile the code, perhaps with options (for speed and feedback, I guess)
* make a distinction between different SourceCodeKinds (for the compiler, I
guess)
* thus, we don't have a return value; the system automatically prints it (?)
* we try/catch each step to give feedback to the student
Here is our Execute:
sctype = Microsoft.Scripting.SourceCodeKind.InteractiveCode;
source = engine.CreateScriptSourceFromString(text, sctype);
try {
if (compiler_options != null) {
source.Compile(compiler_options);
} else {
source.Compile();
}
} catch {
sctype = Microsoft.Scripting.SourceCodeKind.Statements;
source = engine.CreateScriptSourceFromString(text, sctype);
try {
if (compiler_options != null) {
source.Compile(compiler_options);
} else {
source.Compile();
}
} catch (Exception e) {
eo =
engine.GetService<Microsoft.Scripting.Hosting.ExceptionOperations>();
PrintLine(eo.FormatException(e));
return false;
}
}
try {
if (manager != null && manager.UseSharedScope)
source.Execute(manager.scope);
else
source.Execute(scope);
} catch (Exception e) {
if (e.Message.Contains("Thread was being aborted")) {
manager.stderr.Print("[Script stopped----------]\n");
} else {
eo =
engine.GetService<Microsoft.Scripting.Hosting.ExceptionOperations>();
PrintLine(eo.FormatException(e));
}
return false;
}
if (manager.stderr != null)
PrintLine(Tag.Info, "Ok");
Is there a way using this interface, or is our code too much, and we could get
by with less?
-Doug
> Keith Rome
> Senior Consultant and Architect
> MCPD-EAD, MCSD, MCDBA, MCTS-WPF, MCTS-TFS, MCTS-WSS Wintellect |
> 770.617.4016 | [email protected] www.wintellect.com
>
> -----Original Message-----
> From: Ironpython-users
> [mailto:[email protected]] On
> Behalf Of Doug Blank
> Sent: Thursday, September 20, 2012 12:43 AM
> To: [email protected]
> Subject: [Ironpython-users] Separating return values from printed values?
>
> IronPython users,
>
> Interesting question: we have IronPython embedded in a nice little
> educational GUI, and have the DLR output sent to our text output window where
> we can control the font color, etc.
>
> In teaching CS, it is always hard to try to get across the difference between
> a "return value" and a "displayed string". For example:
>
>>>> 22
> 22
>>>> print(23)
> 23
>>>> function()
> 22
>
> where 22 is just a value, and 23 is a displayed string (a side-effect for you
> functional types). These GUIs don't make it easy to see the difference.
>
> One thing we've thought about is making a distinction between them with
> color, such that 22 might be blue, and 23 would be green.
>
> Can you think of an easy way to do that with IronPython and the DLR?
> Anything we could tap into to send evaluations one way, and displayed values
> another?
>
> Thanks for any suggestions,
>
> -Doug
> _______________________________________________
> Ironpython-users mailing list
> [email protected]
> http://mail.python.org/mailman/listinfo/ironpython-users
>
>
_______________________________________________
Ironpython-users mailing list
[email protected]
http://mail.python.org/mailman/listinfo/ironpython-users