存储为字符串的脚本的IronPython依赖项

本文关键字:依赖 IronPython 脚本 字符串 存储 | 更新日期: 2023-09-27 18:24:55

我有一个C#应用程序,它将python脚本文件(*.py)存储为字符串。我使用加载它们

scriptEngine.CreateScriptSourceFromString(code);

但现在我有多个脚本文件,它们之间有依赖关系(导入)。为了处理依赖关系,我可以将所有字符串保存回文件夹中的文件,并使用加载我想要执行的脚本

scriptEngine.CreateScriptSourceFromFile(filePath);

但这将使所有脚本文件都可见。有没有一种方法可以在内存中实现这一点,这样脚本文件就不会首先保存到磁盘上,而是直接从字符串中加载?

TL;DR:这可能是什么样子的示例:

myutils.py:

def SomeMethod(p):
    print ('SomeMethod(p=%s)' % p)

script1.py:

import myutils;
if __name__ == '__main__':
    myutils.SomeMethod('script1')

script2.py:

import myutils;
if __name__ == '__main__':
    myutils.SomeMethod('script2')

我的应用程序将脚本存储为字符串。类似的东西

Dictionary<string, string> filePathToContent = new Dictionary<string, string>();
filePathToContent["myutils.py"] = "..."; // The script file content.
filePathToContent["script1.py"] = "..."; // The script file content.
filePathToContent["script2.py"] = "..."; // The script file content.

我想调用script1.py,而不必首先将脚本保存到文件夹中。注意:该代码只是我所拥有内容的一个简化示例。

存储为字符串的脚本的IronPython依赖项

IronPython和Python中通常有几种自定义导入处理方法。大多数概念在PEP 0302(新进口挂钩)中有定义。

可以解决需求的两种python机制是meta_pathpath_hooks。两者都可以用Python或(在IronPython的情况下)C#/.NET实现。考虑到问题涉及从C#托管IronPython,实现导入基础设施可以采用任何一种方式。

使用meta_path

IronPython附带ResourceMetaPathImporter,它允许您将包含脚本的ZIP存档作为嵌入式资源。假设这样一个档案被称为scripts.zip,包含在当前执行的程序集中,所需的设置可能看起来像:

var engine = Python.CreateEngine();
var sysScope = engine.GetSysModule();
List metaPath = sysScope.GetVariable("meta_path");
var importer = new ResourceMetaPathImporter(Assembly.GetExecutingAssembly(), "scripts.zip");
metaPath.Add(importer);
sysScope.SetVariable("meta_path", metaPath);

如果程序集和脚本是已知的,并且ZIP打包不会干扰开发过程,那么这种方法效果很好。

使用path_hooks

路径挂钩包含一个导入器链,将查询sys.path中的所有项,以确定它们是否可以处理给定的路径。类似于zipimport.cs的导入程序,但负责DLL/EXE中的嵌入资源,而不是ZIP存档。这可以提供一种更通用的方法,只需在路径中添加一个DLL即可处理其他文件。

使用PlatformAdaptationLayer

第三种方法是提供一个PlatformAdaptationLayer,它是Microsoft.Scripting/IronPython的一部分。此答案显示了一个完整的平台自适应层工作示例,该层解析预定义程序集和程序包命名空间的嵌入资源。

一般说明:关于github的相关问题/讨论。

您可以将不同的脚本分别创建为一个函数,并根据给定的参数调用这些函数

ScriptScope scope = scriptEngine.CreateScope();
scope.SetVariable("language", "en");
scriptEngine.Execute(scope);

和python(我知道的愚蠢的例子):

def doEnStuff():
  print "english"
def doEsStuff():
  print "espagna"
if language == "en"
  doEnStuff()

我开发了以下解决方案,请记住将脚本文件存储在项目资源中。

using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Scripting.Hosting.Providers;
namespace JJMasterData.Core.DataDictionary
{
    /// <summary>
    /// Class responsible for handling Python 3.4 scripts
    /// Gustavo Barros 05/01/2022
    /// </summary>
    public class PythonScriptManager
    {
        private ScriptScope Scope;
        private ScriptEngine _engine;
        private ScriptEngine Engine
        {
            get
            {
                if (_engine == null)
                {
                    ///Creating the Python Engine
                    _engine = Python.CreateEngine();
                    ///Setup JJMasterData scripts
                    Scope = _engine.CreateScope();
                    ///Adding IronPython StdLib
                    ICollection<string> searchPaths = _engine.GetSearchPaths();
                    string user = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
                    searchPaths.Add(user + $"''.nuget''packages''ironpython.stdlib''3.4.0-alpha1''content''Lib");
                    _engine.SetSearchPaths(searchPaths);
                    ///Importing custom JJMasterData scripts
                    ImportScripts();
                }
                return _engine;
            }
        }
        protected void ImportScripts()
        {
            ///Repeat the same steps to import a new script
            string dataAccess = Encoding.UTF8.GetString(Properties.Resources.data_access);
            Engine.Execute(dataAccess, Scope);
            Scope.SetVariable("data_access", HostingHelpers.GetScope(Scope));
        }
        /// <summary>
        /// Executes a Python 3.4 script from a string.
        /// Gustavo Barros - 05/01/2022
        /// </summary>
        /// <param name="script">Python script to be executed.</param>
        /// <returns>Script dynamic return./returns>
        public object Run(string script) => Engine.CreateScriptSourceFromString(script).Execute(Scope);
    }
}