5
votes

I'm calling an IronPython function from C#. It seems that on definition, this function captures its original scope. When I later call it without an explicit scope, it can still access values from that original scope. Even if I change scope values, it correctly reads the new values. Take a look at this example:

using IronPython.Hosting;
using IronPython.Runtime;
using IronPython.Runtime.Operations;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

namespace IronPythonFuncTest {
    class Program {
        static void Main() {
            ScriptEngine scriptEngine = Python.CreateEngine();

            // Create scope with a global value for the script to use
            ScriptScope scriptScope = scriptEngine.CreateScope();
            scriptScope.SetVariable("someGlobalValue", 10);

            // Execute script defining function foo(x)
            string script = "def foo(): print(someGlobalValue)";
            ScriptSource scriptSource = scriptEngine.
                CreateScriptSourceFromString(script, SourceCodeKind.Statements);
            scriptSource.Execute(scriptScope);

            // Extract foo from the scope
            PythonFunction foo = scriptScope.GetVariable<PythonFunction>("foo");

            // Change someGlobalValue
            scriptScope.SetVariable("someGlobalValue", 42);

            // Call foo. This prints 42, not 10 (or nothing).
            PythonCalls.Call(foo);
        }
    }
}

Now I'm wondering: Most overloads of PythonCalls.Call() expect a CodeContext object (which, if I understand correctly, mainly represents a scope). Am I losing something if I call an IronPython function like above, without passing a code context? Given that the function apparently captured its original scope on creation, there doesn't seem to be any point in passing an additional code context. Are there situations where it makes a difference whether I do?

1

1 Answers

0
votes

Why do you call foo over PythonCall.Call? Try to do it like this: scriptScope.Host.ScriptEngine.Operations.InvokeMember(CLASS-Instance, METHOD-Name, ARGUMENTS); than Ironpython will correctly handle the CodeContext on it's own. You can found a sample implementation here: DlrClass/InvokeMember.

As far as i know, the CodeContext is more than just the scope, just take a look at the definition:

/// <summary>
/// Captures and flows the state of executing code from the generated 
/// Python code into the IronPython runtime.
/// </summary>    
[DebuggerTypeProxy(typeof(CodeContext.DebugProxy)), DebuggerDisplay("module: {ModuleName}", Type="module")]
public sealed class CodeContext { // ... }

Hope this helps!