2

I have a Python script as follows:

def VSH(GR, GRsand, GRshale): 
    '''Calculates Vsh from gamma inputs'''
    value = (((GR-GRsand)/(GRshale-GRsand)))
    if (value > 100.0):
      value = 100.0
    elif (value < 0.0):
        value = 0.0
    return value

From C# I have a loop that in IronPython 2.2 would extract the 3 arguments.

foreach (string variableName in (PythonTuple)inputFunction.func_code.co_varnames)
{
    // do something here
}

Now in IronPython 2.7.5 I get the names of the 4 variables, which makes sense, but breaks the old code. From the watch I get:

co_varnames tuple, 4 items  IronPython.Runtime.PythonTuple
[0] "GR"    object {string}
[1] "GRsand"    object {string}
[2] "GRshale"   object {string}
[3] "value" object {string}

Looking at the object inputFunction.func_code in the debugger, I don't see anything that would return just the arguments. I do see the property co_argcount = 3. If I could be sure that the arguments were always first in the list of variables, then I could use it to filter out the local variables. Any suggestions?

1 Answer 1

1

Here's my solution:

// using System.Reflection;
dynamic func = scope.GetVariable("VSH");
var code = func.__code__;
var argNamesProperty = code.GetType().GetProperty("ArgNames", BindingFlags.NonPublic | BindingFlags.Instance);
string[] argNames = (string[])argNamesProperty.GetValue(code, null);
// argNames = ["GR", "GRsand", "GRshale"]

You were looking in the right place, but unfortunately the IronPython.Runtime.FunctionCode.ArgNames property is private. With reflection, we can ignore that and just get the argument names anyway.

Here's my complete test setup:

static void Main(string[] args)
{
    ScriptEngine engine = Python.CreateEngine();
    ScriptScope scope = engine.CreateScope();
    ObjectOperations ops = engine.CreateOperations();
    ScriptSource source = engine.CreateScriptSourceFromString(@"
def VSH(GR, GRsand, GRshale): 
    '''Calculates Vsh from gamma inputs'''
    value = (((GR-GRsand)/(GRshale-GRsand)))
    if (value > 100.0):
        value = 100.0
    elif (value < 0.0):
        value = 0.0
    return value");
    CompiledCode compiled = source.Compile();
    compiled.Execute(scope);

    dynamic func = scope.GetVariable("VSH");
    var code = func.__code__;
    var argNamesProperty = code.GetType().GetProperty("ArgNames", BindingFlags.NonPublic | BindingFlags.Instance);
    string[] argNames = (string[])argNamesProperty.GetValue(code, null);
    // argNames = ["GR", "GRsand", "GRshale"]
}

I'm sure that you can trim down everything before the dynamic func = ... line, because you probably have access to your VSH function already.

1
  • Thanks. That worked perfectly. I will have to remember that trick to get the private property.
    – spainchaud
    Commented Jul 27, 2015 at 18:21

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.