Import 自定义IronPython导入解析
我正在从数据库加载一个IronPython脚本并执行它。这对于简单的脚本很好,但是导入是一个问题。如何拦截这些导入调用,然后从数据库加载适当的脚本 编辑:我的主要应用程序是用C语言编写的,我希望在不编辑Python脚本的情况下拦截C端的调用Import 自定义IronPython导入解析,import,ironpython,Import,Ironpython,我正在从数据库加载一个IronPython脚本并执行它。这对于简单的脚本很好,但是导入是一个问题。如何拦截这些导入调用,然后从数据库加载适当的脚本 编辑:我的主要应用程序是用C语言编写的,我希望在不编辑Python脚本的情况下拦截C端的调用 编辑:根据我所做的研究,创建自己的PlatformAdaptionLayer似乎是实现这一点的方式,但在这种情况下它不起作用。我创建了自己的PAL,在测试中,脚本中的每个导入都会调用我的FileExsists方法。但出于某种原因,它从不调用OpenInput
编辑:根据我所做的研究,创建自己的PlatformAdaptionLayer似乎是实现这一点的方式,但在这种情况下它不起作用。我创建了自己的PAL,在测试中,脚本中的每个导入都会调用我的
FileExsists
方法。但出于某种原因,它从不调用OpenInputFileStream
方法的任何重载。翻阅IronPython源代码,一旦FileExists返回true,它就会尝试在路径上定位文件本身。所以这看起来像一条死胡同。您需要实现导入挂钩。这里有一个关于指针的问题:您可以使用PlatformAdaptionLayer将所有I/O重新定向到数据库。为此,您需要实现一个提供PAL的ScriptHost。然后,当您创建ScriptRuntime时,您将HostType设置为您的主机类型,并将其用于运行时。然后在PAL上覆盖OpenInputFileStream并返回一个包含数据库内容的stream对象(从DB读取后,您可以在这里使用MemoryStream)
如果您仍然想提供对文件I/O的访问,您可以随时使用FileStream来查找找不到的“文件”。经过大量的尝试和错误,我找到了一个解决方案。我从未设法使
platformadaptionlayer
方法正常工作。在尝试加载模块时,它从未回调PAL
因此,我决定使用SetVariable方法替换内置的导入函数,如下所示(引擎和作用域是受保护的成员,为父脚本公开ScriptEngine
和ScriptScope
):
希望这对别人有帮助 我只是想做同样的事情,只是想将脚本存储为嵌入式资源。我正在创建一个混合了C#和IronPython的库,并希望将其作为单个dll分发。我编写了一个PlatformAdaptionLayer,它首先在资源中查找正在加载的脚本,然后返回到在文件系统中查找的基本实现。本报告由三部分组成: 第1部分,定制平台AdaptionLayer
namespace ZenCoding.Hosting
{
internal class ResourceAwarePlatformAdaptationLayer : PlatformAdaptationLayer
{
private readonly Dictionary<string, string> _resourceFiles = new Dictionary<string, string>();
private static readonly char Seperator = Path.DirectorySeparatorChar;
private const string ResourceScriptsPrefix = "ZenCoding.python.";
public ResourceAwarePlatformAdaptationLayer()
{
CreateResourceFileSystemEntries();
}
#region Private methods
private void CreateResourceFileSystemEntries()
{
foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
if (!name.EndsWith(".py"))
{
continue;
}
string filename = name.Substring(ResourceScriptsPrefix.Length);
filename = filename.Substring(0, filename.Length - 3); //Remove .py
filename = filename.Replace('.', Seperator);
_resourceFiles.Add(filename + ".py", name);
}
}
private Stream OpenResourceInputStream(string path)
{
string resourceName;
if (_resourceFiles.TryGetValue(RemoveCurrentDir(path), out resourceName))
{
return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
}
return null;
}
private bool ResourceDirectoryExists(string path)
{
return _resourceFiles.Keys.Any(f => f.StartsWith(RemoveCurrentDir(path) + Seperator));
}
private bool ResourceFileExists(string path)
{
return _resourceFiles.ContainsKey(RemoveCurrentDir(path));
}
private static string RemoveCurrentDir(string path)
{
return path.Replace(Directory.GetCurrentDirectory() + Seperator, "").Replace("." + Seperator, "");
}
#endregion
#region Overrides from PlatformAdaptationLayer
public override bool FileExists(string path)
{
return ResourceFileExists(path) || base.FileExists(path);
}
public override string[] GetFileSystemEntries(string path, string searchPattern, bool includeFiles, bool includeDirectories)
{
string fullPath = Path.Combine(path, searchPattern);
if (ResourceFileExists(fullPath) || ResourceDirectoryExists(fullPath))
{
return new[] { fullPath };
}
if (!ResourceDirectoryExists(path))
{
return base.GetFileSystemEntries(path, searchPattern, includeFiles, includeDirectories);
}
return new string[0];
}
public override bool DirectoryExists(string path)
{
return ResourceDirectoryExists(path) || base.DirectoryExists(path);
}
public override Stream OpenInputFileStream(string path)
{
return OpenResourceInputStream(path) ?? base.OpenInputFileStream(path);
}
public override Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share)
{
return OpenResourceInputStream(path) ?? base.OpenInputFileStream(path, mode, access, share);
}
public override Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
{
return OpenResourceInputStream(path) ?? base.OpenInputFileStream(path, mode, access, share, bufferSize);
}
#endregion
}
}
第3部分,最后,如何使用自定义内容获得Python引擎:
namespace ZenCoding.Hosting
{
internal static class ResourceAwareScriptEngineSetup
{
public static ScriptEngine CreateResourceAwareEngine()
{
var setup = Python.CreateRuntimeSetup(null);
setup.HostType = typeof(ResourceAwareScriptHost);
var runtime = new ScriptRuntime(setup);
return runtime.GetEngineByTypeName(typeof(PythonContext).AssemblyQualifiedName);
}
}
}
将其更改为从其他位置(如数据库)加载脚本将很容易。只需更改OpenResourceStream、ResourceFileExists和ResourceDirectoryExists方法
希望这能有所帮助。我不确定我最初对这个问题的措辞是否足够清楚。调用Python脚本的应用程序是用C#编写的,我希望尽可能将Python脚本视为一个黑匣子,因此如果可能的话,我希望能够在C#端拦截导入。我对原始问题进行了编辑,以反映这些附加信息。请参见上文。这看起来应该行得通,但行不通。也许IronPython导入实现打破了标准方法?我正在开发一个以此为起点的解决方案(我发现一些有用的东西散落在周围(和)。当前的问题似乎是如何破解各种平台适应层方法的调用。我似乎根本找不到任何关于这方面的文档。如果您想查看现有的实现,Silverlight主机可能是一个合理的示例。有关此应用程序的一个相当简单的自定义平台适应层,请参阅我的答案如果您只想将文件存储在预期文件系统位置以外的其他位置,roach似乎很有用,但如果您想(比如)预编译它们,roach就不行了。谢谢!我有与您完全相同的用例。您能添加这是用于哪个版本的IronPython吗?我想这是用于比IronPython 2.7更旧的版本?是的,这是两个版本ars之前,所以无论哪个版本是当前版本。可能是2.6,但我不确定。2.7+中有更好的方法吗?顺便说一句,上面的方法仍然有效。@simonjpascoe,我试图在中解释其他两种方法。
namespace ZenCoding.Hosting
{
internal class ResourceAwareScriptHost : ScriptHost
{
private readonly PlatformAdaptationLayer _layer = new ResourceAwarePlatformAdaptationLayer();
public override PlatformAdaptationLayer PlatformAdaptationLayer
{
get { return _layer; }
}
}
}
namespace ZenCoding.Hosting
{
internal static class ResourceAwareScriptEngineSetup
{
public static ScriptEngine CreateResourceAwareEngine()
{
var setup = Python.CreateRuntimeSetup(null);
setup.HostType = typeof(ResourceAwareScriptHost);
var runtime = new ScriptRuntime(setup);
return runtime.GetEngineByTypeName(typeof(PythonContext).AssemblyQualifiedName);
}
}
}