存储为字符串的脚本的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和Python中通常有几种自定义导入处理方法。大多数概念在PEP 0302(新进口挂钩)中有定义。
可以解决需求的两种python机制是meta_path和path_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);
}
}