diff --git a/appveyor.yml b/appveyor.yml index b20604dce..6bf286fbd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ install: - ps: (new-object net.webclient).DownloadFile('https://raw.github.com/pypa/pip/master/contrib/get-pip.py', 'C:\get-pip.py') # appveyor has python 2.7.6 x86 preinstalled, but in the wrong directory, this works around this - ps: if ($env:pythonurl -eq "http://www.python.org/ftp/python/2.7.6/python-2.7.6.msi") {mi c:\python27 c:\python} + - set PATH=C:\Python;%PATH% - C:\Python\python.exe c:\get-pip.py - C:\Python\Scripts\pip.exe install wheel diff --git a/pythonnet/src/console/pythonconsole.cs b/pythonnet/src/console/pythonconsole.cs index 2184c8730..26fc756d5 100644 --- a/pythonnet/src/console/pythonconsole.cs +++ b/pythonnet/src/console/pythonconsole.cs @@ -9,6 +9,7 @@ using System; using System.Reflection; +using System.Collections.Generic; using Python.Runtime; namespace Python.Runtime { @@ -19,6 +20,9 @@ private PythonConsole() {} [STAThread] public static int Main(string[] args) { + // reference the static assemblyLoader to stop it being optimized away + AssemblyLoader a = assemblyLoader; + string [] cmd = Environment.GetCommandLineArgs(); PythonEngine.Initialize(); @@ -31,16 +35,27 @@ public static int Main(string[] args) { // Register a callback function to load embedded assmeblies. // (Python.Runtime.dll is included as a resource) private sealed class AssemblyLoader { + Dictionary loadedAssemblies; + public AssemblyLoader() { + loadedAssemblies = new Dictionary(); + AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { - String resourceName = new AssemblyName(args.Name).Name + ".dll"; + string shortName = args.Name.Split(',')[0]; + String resourceName = shortName + ".dll"; + + if (loadedAssemblies.ContainsKey(resourceName)) { + return loadedAssemblies[resourceName]; + } // looks for the assembly from the resources and load it using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { if (stream != null) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); - return Assembly.Load(assemblyData); + Assembly assembly = Assembly.Load(assemblyData); + loadedAssemblies[resourceName] = assembly; + return assembly; } } diff --git a/pythonnet/src/runtime/assemblymanager.cs b/pythonnet/src/runtime/assemblymanager.cs index b07cde1c2..291301466 100644 --- a/pythonnet/src/runtime/assemblymanager.cs +++ b/pythonnet/src/runtime/assemblymanager.cs @@ -30,6 +30,7 @@ internal class AssemblyManager { static ResolveEventHandler rhandler; static Dictionary probed; static List assemblies; + static Dictionary loadedAssemblies; internal static List pypath; private AssemblyManager() {} @@ -46,6 +47,7 @@ internal static void Initialize() { probed = new Dictionary(32); //generics = new Dictionary>(); assemblies = new List(16); + loadedAssemblies = new Dictionary(); pypath = new List(16); AppDomain domain = AppDomain.CurrentDomain; @@ -202,7 +204,19 @@ public static Assembly LoadAssemblyPath(string name) { string path = FindAssembly(name); Assembly assembly = null; if (path != null) { - try { assembly = Assembly.LoadFrom(path); } + if (loadedAssemblies.ContainsKey(path)) { + return loadedAssemblies[path]; + } + // Avoid using Assembly.LoadFrom as referenced assemblies that exist + // in the same path will be loaded directly from there, rather than + // using other versions already loaded. This is a problem if there + // is a Python.Runtime.dll in the same folder as the assembly being + // loaded, as that will result in two instances being loaded. + try { + byte[] bytes = System.IO.File.ReadAllBytes(path); + assembly = Assembly.Load(bytes); + loadedAssemblies[path] = assembly; + } catch {} } return assembly; diff --git a/pythonnet/src/testing/threadtest.cs b/pythonnet/src/testing/threadtest.cs index a31fd9768..caad9fcf4 100644 --- a/pythonnet/src/testing/threadtest.cs +++ b/pythonnet/src/testing/threadtest.cs @@ -39,35 +39,43 @@ public class ThreadTest { public static string CallEchoString(string arg) { IntPtr gs = PythonEngine.AcquireLock(); - if (module == null) { - module = PythonEngine.ModuleFromString("tt", testmod); + try { + if (module == null) { + module = PythonEngine.ModuleFromString("tt", testmod); + } + PyObject func = module.GetAttr("echostring"); + PyString parg = new PyString(arg); + PyObject temp = func.Invoke(parg); + string result = (string)temp.AsManagedObject(typeof(String)); + func.Dispose(); + parg.Dispose(); + temp.Dispose(); + return result; + } + finally { + PythonEngine.ReleaseLock(gs); } - PyObject func = module.GetAttr("echostring"); - PyString parg = new PyString(arg); - PyObject temp = func.Invoke(parg); - string result = (string)temp.AsManagedObject(typeof(String)); - func.Dispose(); - parg.Dispose(); - temp.Dispose(); - PythonEngine.ReleaseLock(gs); - return result; } public static string CallEchoString2(string arg) { IntPtr gs = PythonEngine.AcquireLock(); - if (module == null) { - module = PythonEngine.ModuleFromString("tt", testmod); - } + try { + if (module == null) { + module = PythonEngine.ModuleFromString("tt", testmod); + } - PyObject func = module.GetAttr("echostring2"); - PyString parg = new PyString(arg); - PyObject temp = func.Invoke(parg); - string result = (string)temp.AsManagedObject(typeof(String)); - func.Dispose(); - parg.Dispose(); - temp.Dispose(); - PythonEngine.ReleaseLock(gs); - return result; + PyObject func = module.GetAttr("echostring2"); + PyString parg = new PyString(arg); + PyObject temp = func.Invoke(parg); + string result = (string)temp.AsManagedObject(typeof(String)); + func.Dispose(); + parg.Dispose(); + temp.Dispose(); + return result; + } + finally { + PythonEngine.ReleaseLock(gs); + } }