5

I'm working on providing an extensibility mechanism for my C# mapmaking application through IronPython. Everything works fine, but I have a specific requirement which I'm having trouble implementing: I want the user to be able to specify two things:

  1. A file name of the Python script to be loaded
  2. A one-liner string containing Python script which would typically be a call of a function from that Python file (example getTextLabel(element))

These two settings must be separate, but I don't know if it is possible to do this using PythonScript and related classes.

I'm a newbie in Python, perhaps there is another way to achieve this? For performance reasons I want to avoid loading and compiling the Python script file several times (since there could be several above mentioned different "function call" settings and I want to reuse the CompiledCode instance for the file if possible).

UPDATE: @digEmAll gave the correct answer to my question, so I'm accepting it as a valid answer. But if you are concerned with performance, you should also check out my own answer.

2 Answers 2

5

You can do something like this:

string importScript = "import sys" + Environment.NewLine +
                      "sys.path.append( r\"{0}\" )" + Environment.NewLine +
                      "from {1} import *";

// python script to load
string fullPath = @"c:\path\mymodule.py";

var engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();

// import the module
string scriptStr = string.Format(importScript,
                                 Path.GetDirectoryName(fullPath),
                                 Path.GetFileNameWithoutExtension(fullPath));
var importSrc = engine.CreateScriptSourceFromString(scriptStr,Microsoft.Scripting.SourceCodeKind.File);
importSrc.Execute(scope);

// now you ca execute one-line expressions on the scope e.g.
string expr = "functionOfMyModule()";
var result = engine.Execute(expr, scope);

As long as you keep the scope where the module is loaded, you can call functions of the module without reloading it.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I'll do some testing and if this works OK, I'll accept your answer. BTW is there a way to clone the scope? For example to avoid one function call to affect the execution of subsequent function calls?
Mmh, I don't think it's possible... and if it was, it wouldn't be much faster than create a new scope and re-load the desired modules...
your code runs, thanks for your help. Also check out my own answer.
5

I've done some testing of @digEmAll's code. First I must say it runs correctly and does what I asked in the question. But I was concerned with the fact that you have to call

string expr = "functionOfMyModule()";
var result = engine.Execute(expr, scope);

every time you want to evaluate a user-defined expression. My concern was that the code is not pre-compiled and has to be reparsed on each execution, which could have a serious effect on the performance of my application (these kinds of expressions could be called hundreds of thousands if not millions of times, so every millisecond counts).

I tried a different solution: simply pasting the user-defined expression at the end of the Python module (I'm not saying this works in all situations!):

def simpleFunc(x):
    return x + 2;

# this is where the pasting occurs:
simpleFunc(x)

What I did then is to compile this code:

 ScriptSource source = engine.CreateScriptSourceFromString(myCode);
 CompiledCode compiledCode = source.Compile();

... create a scope and run it:

 ScriptScope scope = engine.CreateScope();
 scope.SetVariable ("x", 10);
 int result = compiledCode.Execute<int>(scope);

Now I executed both solutions (digEmAll's and my own) on the same piece of code and the same expression 10,000 times and here are the results:

  • engine.Execute(expr, scope): 0.29 ms / run
  • compiledCode.Execute(scope): 0.01 ms / run

So I guess I'll try to use my own solution, unless the code pasting proves to be a problem.

2 Comments

Yes, that's perfectly working too. Of course compiling the code (and saving a call to Execute) it's faster, I didn't think to it ;)
Guys, what if on C# side you do not know parameter/variable names and want to substitute parameters by a real values, like A if B > C else D so given expression how would you replace such parameters A,B,C,D by the real numbers? I've only such expression string. Thanks in advance

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.