diff --git a/CHANGELOG.md b/CHANGELOG.md index d27c136a3..8f6185b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,10 @@ details about the cause of the failure - Fixed a bug where indexers could not be used if they were inherited - Made it possible to use `__len__` also on `ICollection<>` interface objects +### Removed + +- implicit assembly loading (you have to explicitly `clr.AddReference` before doing import) + ## [2.5.0][] - 2020-06-14 This version improves performance on benchmarks significantly compared to 2.3. diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs index ba6faa076..01ccf6957 100644 --- a/src/runtime/assemblymanager.cs +++ b/src/runtime/assemblymanager.cs @@ -252,75 +252,6 @@ public static Assembly FindLoadedAssembly(string name) return null; } - /// - /// Given a qualified name of the form A.B.C.D, attempt to load - /// an assembly named after each of A.B.C.D, A.B.C, A.B, A. This - /// will only actually probe for the assembly once for each unique - /// namespace. Returns true if any assemblies were loaded. - /// - /// - /// TODO item 3 "* Deprecate implicit loading of assemblies": - /// Set the fromFile flag if the name of the loaded assembly matches - /// the fully qualified name that was requested if the framework - /// actually loads an assembly. - /// Call ONLY for namespaces that HAVE NOT been cached yet. - /// - public static bool LoadImplicit(string name, Action assemblyLoadErrorHandler, bool warn = true) - { - string[] names = name.Split('.'); - var loaded = false; - var s = ""; - Assembly lastAssembly = null; - HashSet assembliesSet = null; - for (var i = 0; i < names.Length; i++) - { - s = i == 0 ? names[0] : s + "." + names[i]; - if (!probed.ContainsKey(s)) - { - if (assembliesSet == null) - { - assembliesSet = new HashSet(AppDomain.CurrentDomain.GetAssemblies()); - } - Assembly a = FindLoadedAssembly(s); - try - { - if (a == null) - { - a = LoadAssemblyPath(s); - } - - if (a == null) - { - a = LoadAssembly(s); - } - } - catch (FileLoadException e) { assemblyLoadErrorHandler(e); } - catch (BadImageFormatException e) { assemblyLoadErrorHandler(e); } - catch (System.Security.SecurityException e) { assemblyLoadErrorHandler(e); } - catch (PathTooLongException e) { assemblyLoadErrorHandler(e); } - - if (a != null && !assembliesSet.Contains(a)) - { - loaded = true; - lastAssembly = a; - } - probed[s] = 1; - } - } - - // Deprecation warning - if (warn && loaded) - { - string location = Path.GetFileNameWithoutExtension(lastAssembly.Location); - string deprWarning = "The module was found, but not in a referenced namespace.\n" + - $"Implicit loading is deprecated. Please use clr.AddReference('{location}')."; - Exceptions.deprecation(deprWarning); - } - - return loaded; - } - - /// /// Scans an assembly for exported namespaces, adding them to the /// mapping of valid namespaces. Note that for a given namespace diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs index e2795e4e3..3f318fd1c 100644 --- a/src/runtime/importhook.cs +++ b/src/runtime/importhook.cs @@ -310,38 +310,6 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) string[] names = realname.Split('.'); - // Now we need to decide if the name refers to a CLR module, - // and may have to do an implicit load (for b/w compatibility) - // using the AssemblyManager. The assembly manager tries - // really hard not to use Python objects or APIs, because - // parts of it can run recursively and on strange threads. - // - // It does need an opportunity from time to time to check to - // see if sys.path has changed, in a context that is safe. Here - // we know we have the GIL, so we'll let it update if needed. - - AssemblyManager.UpdatePath(); - if (!AssemblyManager.IsValidNamespace(realname)) - { - var loadExceptions = new List(); - if (!AssemblyManager.LoadImplicit(realname, assemblyLoadErrorHandler: loadExceptions.Add)) - { - // May be called when a module being imported imports a module. - // In particular, I've seen decimal import copy import org.python.core - IntPtr importResult = Runtime.PyObject_Call(py_import, args, kw); - // TODO: use ModuleNotFoundError in Python 3.6+ - if (importResult == IntPtr.Zero && loadExceptions.Count > 0 - && Exceptions.ExceptionMatches(Exceptions.ImportError)) - { - loadExceptions.Add(new PythonException()); - var importError = new PyObject(new BorrowedReference(Exceptions.ImportError)); - importError.SetAttr("__cause__", new AggregateException(loadExceptions).ToPython()); - Runtime.PyErr_SetObject(new BorrowedReference(Exceptions.ImportError), importError.Reference); - } - return importResult; - } - } - // See if sys.modules for this interpreter already has the // requested module. If so, just return the existing module. IntPtr modules = Runtime.PyImport_GetModuleDict(); diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs index c7085d18a..334c5c2f3 100644 --- a/src/runtime/moduleobject.cs +++ b/src/runtime/moduleobject.cs @@ -118,30 +118,6 @@ public ManagedType GetAttribute(string name, bool guess) return c; } - // This is a little repetitive, but it ensures that the right - // thing happens with implicit assembly loading at a reasonable - // cost. Ask the AssemblyManager to do implicit loading for each - // of the steps in the qualified name, then try it again. - bool ignore = name.StartsWith("__"); - if (AssemblyManager.LoadImplicit(qname, assemblyLoadErrorHandler: ImportWarning, !ignore)) - { - if (AssemblyManager.IsValidNamespace(qname)) - { - m = new ModuleObject(qname); - StoreAttribute(name, m); - m.DecrRefCount(); - return m; - } - - type = AssemblyManager.LookupTypes(qname).FirstOrDefault(t => t.IsPublic); - if (type != null) - { - c = ClassManager.GetClass(type); - StoreAttribute(name, c); - return c; - } - } - // We didn't find the name, so we may need to see if there is a // generic type with this base name. If so, we'll go ahead and // return it. Note that we store the mapping of the unmangled diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs index 893bd9491..1e10967d7 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -250,6 +250,7 @@ public static bool Matches(IntPtr ob) return Runtime.PyErr_ExceptionMatches(ob) != 0; } + [System.Diagnostics.DebuggerHidden] public static void ThrowIfIsNull(IntPtr ob) { if (ob == IntPtr.Zero) @@ -258,6 +259,7 @@ public static void ThrowIfIsNull(IntPtr ob) } } + [System.Diagnostics.DebuggerHidden] internal static void ThrowIfIsNull(BorrowedReference reference) { if (reference.IsNull) @@ -266,6 +268,7 @@ internal static void ThrowIfIsNull(BorrowedReference reference) } } + [System.Diagnostics.DebuggerHidden] public static void ThrowIfIsNotZero(int value) { if (value != 0) diff --git a/src/tests/test_array.py b/src/tests/test_array.py index 015b5bdde..428bb2065 100644 --- a/src/tests/test_array.py +++ b/src/tests/test_array.py @@ -2,6 +2,7 @@ """Test support for managed arrays.""" +import clr import Python.Test as Test import System import pytest @@ -1143,6 +1144,8 @@ def test_boxed_value_type_mutation_result(): # to accidentally write code like the following which is not really # mutating value types in-place but changing boxed copies. + clr.AddReference('System.Drawing') + from System.Drawing import Point from System import Array diff --git a/src/tests/test_class.py b/src/tests/test_class.py index c24d788df..4666631f7 100644 --- a/src/tests/test_class.py +++ b/src/tests/test_class.py @@ -3,6 +3,7 @@ """Test CLR class support.""" +import clr import Python.Test as Test import System import pytest @@ -124,6 +125,9 @@ def __init__(self, v): def test_struct_construction(): """Test construction of structs.""" + + clr.AddReference('System.Drawing') + from System.Drawing import Point p = Point() diff --git a/src/tests/test_compat.py b/src/tests/test_compat.py index 1c9f80e65..ec36e3be0 100644 --- a/src/tests/test_compat.py +++ b/src/tests/test_compat.py @@ -3,6 +3,7 @@ """Backward-compatibility tests for deprecated features.""" +import clr import types import pytest @@ -137,6 +138,8 @@ def test_dotted_name_import_from_with_alias(): def test_from_module_import_star(): """Test from module import * behavior.""" + clr.AddReference('System.Management') + count = len(locals().keys()) m = __import__('CLR.System.Management', globals(), locals(), ['*']) assert m.__name__ == 'System.Management' diff --git a/src/tests/test_module.py b/src/tests/test_module.py index 2b1a9e4ec..dcdb0248e 100644 --- a/src/tests/test_module.py +++ b/src/tests/test_module.py @@ -192,6 +192,8 @@ def test_dotted_name_import_from_with_alias(): def test_from_module_import_star(): """Test from module import * behavior.""" + clr.AddReference('System.Xml') + count = len(locals().keys()) m = __import__('System.Xml', globals(), locals(), ['*']) assert m.__name__ == 'System.Xml' @@ -201,16 +203,14 @@ def test_from_module_import_star(): def test_implicit_assembly_load(): """Test implicit assembly loading via import.""" - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") + with pytest.raises(ImportError): + # MS.Build should not have been added as a reference yet + # (and should exist for mono) - # should trigger a DeprecationWarning as Microsoft.Build hasn't - # been added as a reference yet (and should exist for mono) + # The implicit behavior has been disabled in 3.0 + # therefore this should fail import Microsoft.Build - assert len(w) == 1 - assert isinstance(w[0].message, DeprecationWarning) - with warnings.catch_warnings(record=True) as w: clr.AddReference("System.Windows.Forms") import System.Windows.Forms as Forms