I was just trying to do the same thing, except I wanted to store my scripts as embedded resources. I'm creating a library that is a mixture of C# and IronPython and wanted to distribute it as a single dll. I wrote a PlatformAdaptationLayer that works, it first looks in the resources for the script that's being loaded, but then falls back to the base implementation which looks in the filesystem. Three parts to this:
Part 1, The custom PlatformAdaptationLayer
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
}
}
You would need to change the constant ResourceScriptsPrefix to whatever your base namespace is where you stored the python scripts.
Part 2, The custom ScriptHost
namespace ZenCoding.Hosting
{
internal class ResourceAwareScriptHost : ScriptHost
{
private readonly PlatformAdaptationLayer _layer = new ResourceAwarePlatformAdaptationLayer();
public override PlatformAdaptationLayer PlatformAdaptationLayer
{
get { return _layer; }
}
}
}
Part 3, finally, how to get a Python engine using your custom stuff:
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);
}
}
}
It would be easy to change this to load scripts from some other location, like a database. Just change the OpenResourceStream, ResourceFileExists and ResourceDirectoryExists methods.
Hope this helps.