I use the DLLs IronPython.dll and IronPython.Wpf.dll for running Iron Python in C# code using ScriptEngine and ScriptScope
I am running Iron Python script from the WPF c# code as
public static ScriptEngine engine = Python.CreateEngine();
public static ScriptScope scope = engine.CreateScope();
ScriptSource source = engine.CreateScriptSourceFromFile(Path.Combine(currentPath, temp), Encoding.ASCII, SourceCodeKind.File);
source.Execute(scope);
The script in the Source file has several methods and could be called as
Func<object> func = scope.GetVariable("scriptMethodName");
func();
I found that even after closing the application, there is still some memory not released and found in the diagnostic tool the following item
IronPython.Runtime.CodeContext <0x2890FCC> [Static variable temp$2.$constant1]
The static variables of the IronPython runtime still hold some memory as given in https://github.com/IronLanguages/main/pull/1250. This actually causes many bigger objects to still stay in memory
Is there any fix for this ?
I tried the following, but no improvement in fixing this
I included the options dictionary to
Dictionary<string, object> options = new Dictionary<string, object>();
options["Debug"] = true;
options["LightweightScopes"] = true;
Also tried
engine.Runtime.Shutdown();
scope.Engine.Runtime.Shutdown();
This issue is causing heavy memory leak,Is there a possibility to get hold of the memory used by the mainwindow and clear it explicitly
Related
Right now I can use following code to access my c# types in IronPython as follow
import clr
clr.AddReference('myDLL.dll')
import myType
obj = myType()
however I don't want script developers to have clr.AddReference('myDLL.dll') line in Python source code, and inject myDLL.dll (and/or a c# class) directly from c# into ScriptEngine so the previous code will be something similar to:
import myType
obj = myType()
how can I achieve this?
You can solve this problem using following solution :
ScriptRuntime runtime = Python.CreateRuntime();
runtime.LoadAssembly(Assembly.GetAssembly(typeof(MyNameSpace.MyClass)));
ScriptEngine eng = runtime.GetEngine("py");
ScriptScope scope = eng.CreateScope();
ScriptSource src = eng.CreateScriptSourceFromString(MySource, SourceCodeKind.Statements);
var result = src.Execute(scope);
Now, in python script you can write:
from MyNameSpace import *
n=MyClass()
print n.DoSomeThing()
So I am having some troubles importing Pyro or Pyro4 while using IronPython as a C# ScriptEngine.
The import works fine if run directly in the ipy interpreter, but I receive the following error when ipy is initialized as a script engine from a c# script.
TypeErrorException: sequence item 0: expected bytes or byte array, str found
IronPython.Runtime.Operations.ByteOps.AppendJoin (System.Object value, Int32 index, System.Collections.Generic.List`1 byteList)
IronPython.Runtime.Bytes.join (System.Object sequence)
Microsoft.Scripting.Interpreter.FuncCallInstruction`3[IronPython.Runtime.Bytes,System.Object,IronPython.Runtime.Bytes].Run (Microsoft.Scripting.Interpreter.InterpretedFrame frame)
Microsoft.Scripting.Interpreter.Interpreter.Run (Microsoft.Scripting.Interpreter.InterpretedFrame frame)
I am passing the -X:FullFrames and -X:Frames parameters to ipy at it's initialization already, so I believe I have ruled that out. In fact, this error happens whether or not I initialize with this parameters.
Here is my code in c#:
// Set the fullframes parameter in a dict
var options = new Dictionary<string, object>();
options["Frames"] = true;
options["FullFrames"] = true;
// create the engine
var ScriptEngine = IronPython.Hosting.Python.CreateEngine(options);
// and the scope (ie, the python namespace)
var ScriptScope = ScriptEngine.CreateScope();
//set the python file to execute
string scriptPath = "Assets\\test.py";
var ScriptSource = ScriptEngine.CreateScriptSourceFromFile(scriptPath);
And, of course, in the python script, I am receiving this error upon import of Pyro.
import Pyro
This error is typically obtuse for working with IPY, and I am stumped.
Any insight at all, including any techniques to get a little more traceback would be of great help.
Thanks in advance!
The issue was I was using dlls from IPY 2.7.4 and the site-lib modules for 2.7.7.
One step further, and I discovered that 2.7.4 has issues with Pyro as described here:
http://pyro-core.narkive.com/0G88Usma/bug-typeerror-expected-pointer-got-int64
If you can see your full traceback from IPY, you can see the line in Pyro that causes the issue, and a hacky fix is to comment out the method.
I just found out our old IronPython DLLs are not compatible with .NET 4.0. I have removed the old references from our project and added these references from 2.7.4
IronPython
IronPython.Modules
Microsoft.Dynamic
Microsoft.Scripting
Microsoft.Scripting.Metadata
I now have a problem with older C# code that creates a CodeContext object.
private ScriptEngine engine;
private PythonFunction currentFunction;
ArrayList parameters = new ArrayList();
LanguageContext cxt = Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetLanguageContext(engine);
CodeContext context = new CodeContext(new Scope(), cxt);
object result = currentFunction.__call__(context, parameters.ToArray());
return result.ToString();
The CodeContext constructor wants a PythonDictionary and a ModuleContext as arguments, instead of what I am giving it. It looks like the API may have changed over the years. I am hoping that I am just missing the correct reference to allow this old code to compile. I know nothing about Python. Perhaps a simple code change could make this work. I could repalce the new Scope() with new PythonDictionary(), but I am not sure how to get a ModuleContext. Any help would be appreciated.
I have tried the obvious:
var appDomain = AppDomain.CreateDomain("New Domain");
var engine = IronPython.Hosting.Python.CreateEngine(appDomain); // boom!
But I am getting the following error message: Type is not resolved for member 'Microsoft.Scripting.Hosting.ScriptRuntimeSetup,Microsoft.Scripting, Version=0.9.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
Googling for this error has not proved fruitful sofar...
EDIT #1:
I tried to create a minimal reproducing project by copying the relevant stuff to a new Console Application:
using System;
using Microsoft.Scripting;
namespace PythonHostSamle
{
class Program
{
static void Main(string[] args)
{
AppDomain sandbox = AppDomain.CreateDomain("sandbox");
var engine = IronPython.Hosting.Python.CreateEngine(sandbox);
var searchPaths = engine.GetSearchPaths();
searchPaths.Add(#"C:\Python25\Lib");
searchPaths.Add(#"C:\RevitPythonShell");
engine.SetSearchPaths(searchPaths);
var scope = engine.CreateScope();
//scope.SetVariable("revit", _application);
//engine.Runtime.IO.SetOutput(new ScriptOutputStream(_instance), Encoding.UTF8);
//engine.Runtime.IO.SetErrorOutput(new ScriptOutputStream(_instance), Encoding.UTF8);
var script = engine.CreateScriptSourceFromString("print 'hello, world!'", SourceCodeKind.Statements);
script.Execute(scope);
Console.ReadKey();
}
}
}
This works as expected!
I am thus left to conclude that the error I am getting is related to one of the lines I commented out: The scope added to the engine contains an object I have little control over - a reference to a plugin host this software is intended to run in (Autodesk Revit Architecture 2010).
Maybe trying to pass that is what is creating the error?
Is there a way to pass a proxy instead? (will have to look up .NET remoting...)
EDIT #2:
I have whittled the problem down to passing an object via the scope that does cannot be proxied to the other AppDomain: All objects added to the scope of an IronPython interpreter running in a different AppDomain will have to be marshaled somehow and must thus either extend MarshalByRefObject or be Serializable.
Just create your own bootstrapping class that will run in a new AppDomain and will do the initialization of IronPyton there, will it solve the prob?
Thanks to suggestions from a previous question, I'm busy trying out IronPython, IronRuby and Boo to create a DSL for my C# app. Step one is IronPython, due to the larger user and knowledge base. If I can get something to work well here, I can just stop.
Here is my problem:
I want my IronPython script to have access to the functions in a class called Lib. Right now I can add the assembly to the IronPython runtime and import the class by executing the statement in the scope I created:
// load 'ScriptLib' assembly
Assembly libraryAssembly = Assembly.LoadFile(libraryPath);
_runtime.LoadAssembly(libraryAssembly);
// import 'Lib' class from 'ScriptLib'
ScriptSource imports = _engine.CreateScriptSourceFromString("from ScriptLib import Lib", SourceCodeKind.Statements);
imports.Execute(_scope);
// run .py script:
ScriptSource script = _engine.CreateScriptSourceFromFile(scriptPath);
script.Execute(_scope);
If I want to run Lib::PrintHello, which is just a hello world style statement, my Python script contains:
Lib.PrintHello()
or (if it's not static):
library = new Lib()
library.PrintHello()
How can I change my environment so that I can just have basic statments in the Python script like this:
PrintHello
TurnOnPower
VerifyFrequency
TurnOffPower
etc...
I want these scripts to be simple for a non-programmer to write. I don't want them to have to know what a class is or how it works. IronPython is really just there so that some basic operations like for, do, if, and a basic function definition don't require my writing a compiler for my DSL.
You should be able to do something like:
var objOps = _engine.Operations;
var lib = new Lib();
foreach (string memberName in objOps.GetMemberNames(lib)) {
_scope.SetVariable(memberName, objOps.GetMember(lib, memberName));
}
This will get all of the members from the lib objec and then inject them into the ScriptScope. This is done w/ the Python ObjectOperations class so that the members you get off will be Python members. So if you then do something similar w/ IronRuby the same code should basically work.