using System; using System.Drawing; using System.IO; using System.Reflection; using System.Text; using System.Threading; using HeuristicLab.Common; using HeuristicLab.Common.Resources; using HeuristicLab.Core; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.Scripting.Python { [Item("Python Script", "An empty python script.")] [Creatable("Scripts")] [StorableClass] public sealed class PythonScript : NamedItem, IStorableContent { #region Constants private const string CodeTemplate = @"# adding imports from some HeuristicLab namespaces from HeuristicLab.Common import * from HeuristicLab.Core import * from HeuristicLab.Data import * from HeuristicLab.MainForm import MainFormManager # add paths to IronPython/Lib and further libraries # import sys # sys.path.append(r""path/to/lib"") # use 'vars' to access global variables in the variable store "; #endregion #region Fields & Properties public string Filename { get; set; } public static new Image StaticItemImage { get { return VSImageLibrary.Script; } } [Storable] private VariableStore variableStore; public VariableStore VariableStore { get { return variableStore; } } [Storable] private string code; public string Code { get { return code; } set { if (value == code) return; code = value; OnCodeChanged(); } } #endregion [StorableConstructor] private PythonScript(bool deserializing) : base(deserializing) { } private PythonScript(PythonScript original, Cloner cloner) : base(original, cloner) { code = original.code; variableStore = new VariableStore(); } public PythonScript() { name = ItemName; description = ItemDescription; code = CodeTemplate; variableStore = new VariableStore(); } public override IDeepCloneable Clone(Cloner cloner) { return new PythonScript(this, cloner); } private Thread scriptThread; public void Execute() { scriptThread = new Thread(() => { Exception ex = null; try { var pyEngine = IronPython.Hosting.Python.CreateEngine(); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) pyEngine.Runtime.LoadAssembly(assembly); using (var outputStream = new PyConsoleStream()) { outputStream.TextAdded += OutputStreamOnStreamChanged; pyEngine.Runtime.IO.SetOutput(outputStream, Encoding.UTF8); var pyScope = pyEngine.CreateScope(); pyScope.SetVariable("vars", (dynamic)(new Variables(variableStore))); OnScriptExecutionStarted(); pyEngine.CreateScriptSourceFromString(code).Compile().Execute(pyScope); outputStream.Close(); } } catch (ThreadAbortException) { // the execution was cancelled by the user } catch (TargetInvocationException e) { ex = e.InnerException; } catch (Exception e) { ex = e; } finally { OnScriptExecutionFinished(ex); } }); scriptThread.Start(); } private void OutputStreamOnStreamChanged(object sender, EventArgs args) { OnConsoleOutputChanged(args.Value); } public void Kill() { if (scriptThread != null && scriptThread.IsAlive) scriptThread.Abort(); } public event EventHandler CodeChanged; private void OnCodeChanged() { var handler = CodeChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler ScriptExecutionStarted; private void OnScriptExecutionStarted() { var handler = ScriptExecutionStarted; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler> ScriptExecutionFinished; private void OnScriptExecutionFinished(Exception e) { var handler = ScriptExecutionFinished; if (handler != null) handler(this, new EventArgs(e)); } public event EventHandler> ConsoleOutputChanged; private void OnConsoleOutputChanged(string args) { var handler = ConsoleOutputChanged; if (handler != null) handler(null, new EventArgs(args)); } class PyConsoleStream : Stream { public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { OnStreamChanged(Encoding.UTF8.GetString(buffer, offset, count)); } public override bool CanRead { get { return false; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return true; } } public override long Length { get { return 0; } } public override long Position { get { return 0; } set { } } public event EventHandler> TextAdded; private void OnStreamChanged(string text) { var handler = TextAdded; if (handler != null) handler(this, new EventArgs(text)); } } } }