Freeing script variables from modules
We are using IronPython in our open source project. I have a problem with variables added to the script scope like
private ScriptScope CreateScope(IDictionary<string, object> globals)
{
globals.Add("starting", true);
globals.Add("stopping", false);
var scope = Engine.CreateScope(globals);
scope.ImportModule("math");
return scope;
}
I can use globals from the main script, but any loaded module will fail. How can this be fixed?
update: given this module mymodule.py
if starting: #starting is defined on the scope
...
From the main script executed using this code
void RunLoop(string script, ScriptScope scope)
{
ExecuteSafe(() =>
{
var compiled = Engine.CreateScriptSourceFromString(script).Compile();
while (!stopRequested)
{
usedPlugins.ForEach(p => p.DoBeforeNextExecute());
CatchThreadAbortedException(() => compiled.Execute(scope));
scope.SetVariable("starting", false);
threadTimingFactory.Get().Wait();
}
scope.SetVariable("stopping", true);
CatchThreadAbortedException(() => compiled.Execute(scope));
});
}
from mymodule import * #this will load the moduel and it fails with
edit: In response to @ BendEg's answer
I tried this
scope.SetVariable("__import__", new Func<CodeContext, string, PythonDictionary, PythonDictionary, PythonTuple, object>(ResolveImport));
ImportDelegate
is undefined so tried to use Func instead, ResolveImport method never gets run and I get the same exception name is undefined
edit: I changed the creation of the area to
var scope = Engine.GetBuiltinModule();
globals.ForEach(g => scope.SetVariable(g.Key, g.Value));
Now the import delegate starts, but the first line fails with global name 'mouse' is not defined
, mouse is not used from the module. It seems to be confusing when I add my custom globals toBuiltinModule
source to share
As far as I know, importing some module will create a new scope. So when instantiated PythonModule
through from ... import ...
it has its own scope. In this new scope, your public variables are not available. Please correct me if I am wrong.
Workaround:
You can create some static class that stores values. Than you can be sure, you always have them. For example:
namespace someNS
{
public static class SomeClass
{
public static bool Start { get; set; }
}
}
And than in your IP code:
from someNS import SomeClass
# Now you can access the member
yourVal = SomeClass.Start
Perhaps this is what you can use. You don't need to specify it as a variable in scope.
EDIT
Maybe it works for you. In the module, I override the import module and try to set the global vars:
The first thing you need is to provide the IronPython delegate with some delegate to import the module:
# Scope should be your default scope
scope.SetVariable("__import__", new ImportDelegate(ResolveImport));
Then override the import function:
private object ResolveImport(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple fromlist)
{
// Do default import but set module
var builtin = IronPython.Modules.Builtin.__import__(context, moduleName, globals, locals, fromlist, 0);
context.ModuleContext.Module.__setattr__(context, "some_global", "Hello World");
return builtin;
}
EDIT
Definition ImportDelegate
delegate object ImportDelegate(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple fromlist);
source to share