diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc668b0c..a95a9aa81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ## [unreleased][] ### Added +- Optimized implicit assembly loading on module import, PythonEngine.ImplicitAssemblyLoading event added. - Added support for embedding python into dotnet core 2.0 (NetStandard 2.0) - Added new build system (pythonnet.15.sln) based on dotnetcore-sdk/xplat(crossplatform msbuild). Currently there two side-by-side build systems that produces the same output (net40) from the same sources. diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs index d63930a58..59afc0e90 100644 --- a/src/runtime/assemblymanager.cs +++ b/src/runtime/assemblymanager.cs @@ -196,7 +196,17 @@ public static Assembly LoadAssembly(string name) Assembly assembly = null; try { - assembly = Assembly.Load(name); + var importEvent = new ImplicitAssemblyLoadingEventArgs(name); + if (importEvent.SkipAssemblyLoad) + { + return null; + } + + PythonEngine.RaiseAssemblyAsModuleImportingEvent(importEvent); + if (!importEvent.SkipAssemblyLoad) + { + assembly = Assembly.Load(name); + } } catch (Exception) { @@ -343,8 +353,17 @@ internal static void ScanAssembly(Assembly assembly) // A couple of things we want to do here: first, we want to // gather a list of all of the namespaces contributed to by // the assembly. + Type[] types = new Type[0]; + try + { + types = assembly.IsDynamic ? assembly.GetTypes():assembly.GetExportedTypes(); + } + catch(TypeLoadException) + { + // Do nothing. + // This problem usually occurs when transitive dependencies have references to older packages than main application. + } - Type[] types = assembly.GetTypes(); foreach (Type t in types) { string ns = t.Namespace ?? ""; @@ -419,12 +438,15 @@ public static List GetNames(string nsname) { foreach (Assembly a in namespaces[nsname].Keys) { - Type[] types = a.GetTypes(); + Type[] types = a.IsDynamic ? a.GetTypes(): a.GetExportedTypes(); foreach (Type t in types) { if ((t.Namespace ?? "") == nsname) { - names.Add(t.Name); + if (!t.IsNested) + { + names.Add(t.Name); + } } } } diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index a23c7ac79..0c212de6b 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -130,6 +130,11 @@ public static string Compiler get { return Marshal.PtrToStringAnsi(Runtime.Py_GetCompiler()); } } + /// + /// Fires when python engines importing module and probably tries to load an assembly. + /// + public static event EventHandler ImplicitAssemblyLoading; + public static int RunSimpleString(string code) { return Runtime.PyRun_SimpleString(code); @@ -520,6 +525,29 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals, } } } + + internal static void RaiseAssemblyAsModuleImportingEvent(ImplicitAssemblyLoadingEventArgs e) + { + ImplicitAssemblyLoading?.Invoke(null, e); + } + } + + public class ImplicitAssemblyLoadingEventArgs: EventArgs + { + public ImplicitAssemblyLoadingEventArgs(string moduleName) + { + ModuleName = moduleName; + } + + /// + /// The name of the module to import that is probably assembly name. + /// + public string ModuleName { get; } + + /// + /// Set it to true, if you know that is not an assembly to import. + /// + public bool SkipAssemblyLoad { get; set; } } public enum RunFlagType