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 | kr...@wintellect.com
www.wintellect.com

-----Original Message-----
From: Doug Blank [mailto:doug.bl...@gmail.com] 
Sent: Thursday, September 20, 2012 6:44 AM
To: Keith Rome
Cc: ironpython-users@python.org
Subject: Re: [Ironpython-users] Separating return values from printed values?

On Thu, Sep 20, 2012 at 1:37 AM, Keith Rome <r...@wintellect.com> 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 | kr...@wintellect.com www.wintellect.com
>
> -----Original Message-----
> From: Ironpython-users 
> [mailto:ironpython-users-bounces+rome=wintellect....@python.org] On 
> Behalf Of Doug Blank
> Sent: Thursday, September 20, 2012 12:43 AM
> To: ironpython-users@python.org
> 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
> Ironpython-users@python.org
> http://mail.python.org/mailman/listinfo/ironpython-users
>
>


_______________________________________________
Ironpython-users mailing list
Ironpython-users@python.org
http://mail.python.org/mailman/listinfo/ironpython-users

Reply via email to