代码之家  ›  专栏  ›  技术社区  ›  Dan

自定义IronPython导入解析

  •  8
  • Dan  · 技术社区  · 14 年前

    我正在从数据库加载一个IronPython脚本并执行它。对于简单的脚本来说,这很好,但是导入是个问题。如何截获这些导入调用,然后从数据库加载适当的脚本?

    编辑:从我做的研究来看,创建自己的platformadaptionlayer就像 想象上的 去实现它,但在这种情况下不起作用。我创造了自己的朋友,在我的测试中 FileExsists OpenInputFileStream 方法。通过IrpPython源挖掘,一旦文件存在返回true,它就尝试在路径上查找文件本身。所以这看起来是个死胡同。

    4 回复  |  直到 14 年前
        1
  •  10
  •   Bort    10 年前

    经过多次反复试验,我终于找到了解决办法。我从来没有设法得到 PlatformAdaptationLayer 正确工作的方法。在尝试加载模块时,它从不回叫PAL。

    所以我决定用如下所示的SetVariable方法替换内置的import函数(引擎和作用域是公开 ScriptEngine ScriptScope 对于父脚本):

    delegate object ImportDelegate(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple tuple);
    
    protected void OverrideImport()
    {
        ScriptScope scope = IronPython.Hosting.Python.GetBuiltinModule(Engine);
        scope.SetVariable("__import__", new ImportDelegate(DoDatabaseImport));
    }
    
    protected object DoDatabaseImport(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple tuple)
    {
        if (ScriptExistsInDb(moduleName))
        {
            string rawScript = GetScriptFromDb(moduleName);
            ScriptSource source = Engine.CreateScriptSourceFromString(rawScript);
            ScriptScope scope = Engine.CreateScope();
            Engine.Execute(rawScript, scope);
            Microsoft.Scripting.Runtime.Scope ret = Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetScope(scope);
            Scope.SetVariable(moduleName, ret);
            return ret;
         }
         else
         {   // fall back on the built-in method
             return IronPython.Modules.Builtin.__import__(context, moduleName);
         }
    }
    

    希望这能帮助别人!

        2
  •  9
  •   Einar Egilsson    14 年前

    我只是想做同样的事情,只是我想把我的脚本存储为嵌入式资源。我正在创建一个库,它是C和IronPython的混合体,并希望将其作为单个dll分发。我编写了一个工作的platformadaptionlayer,它首先在参考资料中查找正在加载的脚本,然后返回到在文件系统中查找的基本实现。三个部分:

    第一部分,自定义平台应用层

    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
        }
    }
    

    第2部分,自定义脚本宿主

    namespace ZenCoding.Hosting
    {
        internal class ResourceAwareScriptHost : ScriptHost
        {
            private readonly PlatformAdaptationLayer _layer = new ResourceAwarePlatformAdaptationLayer();
            public override PlatformAdaptationLayer PlatformAdaptationLayer
            {
                get { return _layer; }
            }
        }
    }
    

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

    很容易将其更改为从其他位置(如数据库)加载脚本。只需改变OpenRealStestRestRM,RealCurryFipe就存在,资源目录就存在方法。

    希望这有帮助。

        3
  •  1
  •   Dino Viehland    14 年前

    如果您仍然想提供对文件I/O的访问,那么您可以返回到您找不到的“文件”的文件流。