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