从模块中访问脚本范围变量

3

我们在开源项目中使用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;
}

https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.Core/ScriptEngine/Python/PythonScriptEngine.cs#L267

我可以使用主脚本中的全局变量,但是任何已加载的模块都将失败。该如何解决?
更新:给定此模块 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));
    });
}

https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.Core/ScriptEngine/Python/PythonScriptEngine.cs#L163

from mymodule import * #this will load the moduel and it fails with

enter image description here

编辑:回应 @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));

现在导入代理触发了,但是在第一行崩溃,并出现“全局名称'mouse'未定义”的错误,该模块未使用mouse。当我将自定义全局变量添加到BuiltinModule时,似乎会产生混淆。

请问您能否提供一个更完整的示例(例如,针对该范围执行了什么操作以及它是如何失败的)? - Simon Opelt
2个回答

2
据我所知,导入某些模块会创建一个新的作用域。因此,通过from ... import ...创建PythonModule实例时,它们有自己的作用域。在这个新的作用域中,您的公共变量是不可用的。如果我说错了,请纠正我。
解决方法:
您可以创建一些静态类来保存这些值。这样,您就可以确保始终拥有它们。例如:
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));

接下来覆盖 import 函数:

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);

恐怕这不是一个可靠的解决方案。我们有一个插件结构,每个插件都在作用域上注册了一个名称,因此您可以像 mouse.deltaX 或 keyboard.getKey(Key.A) 一样访问它。 - Anders
用一些静态的DynamicObject怎么样? - BendEg
但如果你这样写“from someNS import SomeClass”,它真的会起作用吗?这种方式在子模块中能够正常工作吗?我不想让用户被迫编写导入语句。 - Anders
1
@Anders,我有一个更好的解决方案给你,请等待2分钟 :) - BendEg
1
只是想深入了解IronPython核心,我们公司使用IronPython,所以目前有很多事情要做 :) - BendEg
显示剩余14条评论

2
这可能不是正确的答案,因为它仍然无法在主IronPython脚本中共享定义的变量与导入的模块,但这是向前迈出的一步。
这种方法允许在引擎级别而不是脚本级别设置变量,并且它们将在每个导入的模块中可用。
engine = Python.CreateEngine();
engine.Runtime.Globals.SetVariable("test_global", "This is a test global variable.");

然后,在任何IronPython脚本中,都可以使用import访问它:
import test_global
print(test_global)

与ScriptScope不同,那些全局变量需要被导入才能直接使用。
原文章: https://ludovic.chabant.com/devblog/2009/12/24/exposing-global-variables-in-ironpython/ 免责声明: 我添加了这个答案,因为我一直在努力寻找关于这个话题的资料,除了这个SO问答以外,所以我发布这个可能的解决方法来帮助未来有困难的读者(就像我自己)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接