从模块访问脚本范围变量
Accesing script scope variables from modules
我们在我们的开源项目中使用 IronPython。我在访问添加到脚本范围的变量时遇到问题,例如
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;
}
我可以使用主脚本中的全局变量,但加载的任何模块都会失败。如何解决?
更新:给定这个模块mymodule.py
if starting: #starting is defined on the scope
...
来自使用此代码执行的主脚本
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
编辑:回应@BendEg 的回答
我试过了
scope.SetVariable("__import__", new Func<CodeContext, string, PythonDictionary, PythonDictionary, PythonTuple, object>(ResolveImport));
ImportDelegate
未定义,因此尝试使用 Func 代替,ResolveImport 方法永远不会触发,我得到相同的异常,名称未定义
编辑:我将范围创建更改为
var scope = Engine.GetBuiltinModule();
globals.ForEach(g => scope.SetVariable(g.Key, g.Value));
现在导入委托触发,但它在 global name 'mouse' is not defined
的第一行崩溃,模块中未使用鼠标。当我将自定义全局变量添加到 BuiltinModule
时,它似乎很困惑
据我所知,导入一些模块会创建一个新的作用域。因此,当通过 from ... import ...
创建 PythonModule
的实例时,它们有自己的范围。在这个新范围内,您的 public 变量不可用。如有错误请指正
解决方法:
您可以创建一些静态的 class,用于保存这些值。你可以肯定,你总是拥有它们。例如:
namespace someNS
{
public static class SomeClass
{
public static bool Start { get; set; }
}
}
并且比在您的 IP 代码中:
from someNS import SomeClass
# Now you can access the member
yourVal = SomeClass.Start
也许这是您可以使用的东西。您的事件不需要将其设置为范围内的变量。
编辑
也许这对您有用。在代码中,我重写了模块导入并尝试设置全局变量:
您需要做的第一件事是,给 IronPython 一些委托,用于模块导入:
# Scope should be your default scope
scope.SetVariable("__import__", new ImportDelegate(ResolveImport));
然后覆盖导入函数:
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;
}
编辑
ImportDelegate
的定义
delegate object ImportDelegate(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple fromlist);
这可能不是正确的答案,因为仍然不允许与导入的模块共享主 IronPhyton 脚本中定义的变量,但这是向前迈出的一步。
这种方法允许在引擎级别而不是脚本级别设置变量,并且它们将在每个导入的模块中可用。
engine = Python.CreateEngine();
engine.Runtime.Globals.SetVariable("test_global", "This is a test global variable.");
然后,在任何 IronPhyton 脚本中都可以使用导入访问它:
import test_global
print(test_global)
与使它们直接可用的 ScriptScope 不同,这些全局变量需要导入。
原创文章
https://ludovic.chabant.com/devblog/2009/12/24/exposing-global-variables-in-ironpython/
免责声明
我添加了这个答案,因为除了这个 SO 问答之外,我一直在努力寻找关于这个主题的任何 material,所以我发布了这个可能的解决方法来帮助未来遇到麻烦的读者(比如我自己)
我们在我们的开源项目中使用 IronPython。我在访问添加到脚本范围的变量时遇到问题,例如
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;
}
我可以使用主脚本中的全局变量,但加载的任何模块都会失败。如何解决?
更新:给定这个模块mymodule.py
if starting: #starting is defined on the scope
...
来自使用此代码执行的主脚本
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
编辑:回应@BendEg 的回答
我试过了
scope.SetVariable("__import__", new Func<CodeContext, string, PythonDictionary, PythonDictionary, PythonTuple, object>(ResolveImport));
ImportDelegate
未定义,因此尝试使用 Func 代替,ResolveImport 方法永远不会触发,我得到相同的异常,名称未定义
编辑:我将范围创建更改为
var scope = Engine.GetBuiltinModule();
globals.ForEach(g => scope.SetVariable(g.Key, g.Value));
现在导入委托触发,但它在 global name 'mouse' is not defined
的第一行崩溃,模块中未使用鼠标。当我将自定义全局变量添加到 BuiltinModule
据我所知,导入一些模块会创建一个新的作用域。因此,当通过 from ... import ...
创建 PythonModule
的实例时,它们有自己的范围。在这个新范围内,您的 public 变量不可用。如有错误请指正
解决方法:
您可以创建一些静态的 class,用于保存这些值。你可以肯定,你总是拥有它们。例如:
namespace someNS
{
public static class SomeClass
{
public static bool Start { get; set; }
}
}
并且比在您的 IP 代码中:
from someNS import SomeClass
# Now you can access the member
yourVal = SomeClass.Start
也许这是您可以使用的东西。您的事件不需要将其设置为范围内的变量。
编辑
也许这对您有用。在代码中,我重写了模块导入并尝试设置全局变量:
您需要做的第一件事是,给 IronPython 一些委托,用于模块导入:
# Scope should be your default scope
scope.SetVariable("__import__", new ImportDelegate(ResolveImport));
然后覆盖导入函数:
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;
}
编辑
ImportDelegate
delegate object ImportDelegate(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple fromlist);
这可能不是正确的答案,因为仍然不允许与导入的模块共享主 IronPhyton 脚本中定义的变量,但这是向前迈出的一步。
这种方法允许在引擎级别而不是脚本级别设置变量,并且它们将在每个导入的模块中可用。
engine = Python.CreateEngine();
engine.Runtime.Globals.SetVariable("test_global", "This is a test global variable.");
然后,在任何 IronPhyton 脚本中都可以使用导入访问它:
import test_global
print(test_global)
与使它们直接可用的 ScriptScope 不同,这些全局变量需要导入。
原创文章
https://ludovic.chabant.com/devblog/2009/12/24/exposing-global-variables-in-ironpython/
免责声明
我添加了这个答案,因为除了这个 SO 问答之外,我一直在努力寻找关于这个主题的任何 material,所以我发布了这个可能的解决方法来帮助未来遇到麻烦的读者(比如我自己)