IronLanguages/main — pull request #63 by KeithJRome (2012-04-06 20:52:42+00:00)
Title: Implementation of resource-based meta_path importer

PEP 302 provides for python hosts to be able to hook the importer protocol prior to processing the sys.path list (and any importer path hooks such as zipimport). The three primary differences between a meta_path importer and a normal path importer is that 1) a normal importer seems to need to be in an actual Module, whereas a meta_path importer does not need to be in a Module. Also, 2) meta_path importer generally follows a pattern of a singleton per hosting context, while a normal path importer is generally instantiated upon each use. Lastly, 3) a meta_path importer is generally injected at the discretion of the host application.

Meta_path importers are useful for scenarios such as single-file deployments (where you don't want to or cannot deploy external .py libraries), especially in side-by-side versioning situations or where library tampering might be a concern. In my case, we need to ensure side-by-side versioning stability and also support dual build targets for WPF and Silverlight platforms. I have been using a custom Platform Adaptation Layer using embedded (uncompressed) .py library files until now, but this meta_path implementation is a far superior solution.

This implementation borrows heavily on the existing zipimporter module, but is an "opt-in" feature. You need to instantiate the importer, giving it an assembly and resource name that contains the ZIP'd import library, and then attach it to sys.meta_path such as in the following code (which assumes "python_27_lib.zip" has been included in the console application as an embedded resource):

class Program
{
static void Main(string[] args)
{
var assembly = typeof (Program).Assembly;
const string resourceName = "python_27_lib.zip";
var importer = new IronPython.Modules.ResourceMetaPathImporter(assembly, resourceName);

var engine = IronPython.Hosting.Python.CreateEngine();
dynamic sys = IronPython.Hosting.Python.GetSysModule(engine);
sys.meta_path.append(importer);

var script = "import os" + Environment.NewLine + "os.name";
var result = engine.Execute(script);

Console.WriteLine("os.name: {0}", result);
Console.ReadKey();
}
}

This implementation should also work fine using multiple embedded ZIP libraries, although I have not tested that scenario.

My hope is that this will be accepted back into the official repo so that going forward we don't have to maintain this part of our custom DLR implementation. It also seems to be a somewhat frequently requested feature from other IronPython users.