/// <summary>
/// The actual import hook that ties Python to the managed world.
/// </summary>
public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw)
{
var args = new BorrowedReference(argsRaw);
// Replacement for the builtin __import__. The original import
// hook is saved as this.py_import. This version handles CLR
// import and defers to the normal builtin for everything else.
var num_args = Runtime.PyTuple_Size(args);
if (num_args < 1)
{
return(Exceptions.RaiseTypeError("__import__() takes at least 1 argument (0 given)"));
}
BorrowedReference py_mod_name = Runtime.PyTuple_GetItem(args, 0);
if (py_mod_name.IsNull ||
!Runtime.IsStringType(py_mod_name))
{
return(Exceptions.RaiseTypeError("string expected"));
}
// Check whether the import is of the form 'from x import y'.
// This determines whether we return the head or tail module.
BorrowedReference fromList = default;
var fromlist = false;
if (num_args >= 4)
{
fromList = Runtime.PyTuple_GetItem(args, 3);
if (fromList != null &&
Runtime.PyObject_IsTrue(fromList) == 1)
{
fromlist = true;
}
}
string mod_name = Runtime.GetManagedString(py_mod_name);
// Check these BEFORE the built-in import runs; may as well
// do the Incref()ed return here, since we've already found
// the module.
if (mod_name == "clr")
{
NewReference clr_module = GetCLRModule(fromList);
if (!clr_module.IsNull())
{
BorrowedReference sys_modules = Runtime.PyImport_GetModuleDict();
if (!sys_modules.IsNull)
{
Runtime.PyDict_SetItemString(sys_modules, "clr", clr_module);
}
}
return(clr_module.DangerousMoveToPointerOrNull());
}
string realname = mod_name;
// 2010-08-15: Always seemed smart to let python try first...
// This shaves off a few tenths of a second on test_module.py
// and works around a quirk where 'sys' is found by the
// LoadImplicit() deprecation logic.
// Turns out that the AssemblyManager.ResolveHandler() checks to see if any
// Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very
// little sense to me.
IntPtr res = Runtime.PyObject_Call(py_import, args.DangerousGetAddress(), kw);
if (res != IntPtr.Zero)
{
// There was no error.
if (fromlist && IsLoadAll(fromList))
{
var mod = ManagedType.GetManagedObject(res) as ModuleObject;
mod?.LoadNames();
}
return(res);
}
// There was an error
if (!Exceptions.ExceptionMatches(Exceptions.ImportError))
{
// and it was NOT an ImportError; bail out here.
return(IntPtr.Zero);
}
if (mod_name == string.Empty)
{
// Most likely a missing relative import.
// For example site-packages\bs4\builder\__init__.py uses it to check if a package exists:
// from . import _html5lib
// We don't support them anyway
return(IntPtr.Zero);
}
// Save the exception
var originalException = new PythonException();
// Otherwise, just clear the it.
Exceptions.Clear();
string[] names = realname.Split('.');
// See if sys.modules for this interpreter already has the
// requested module. If so, just return the existing module.
BorrowedReference modules = Runtime.PyImport_GetModuleDict();
BorrowedReference module = Runtime.PyDict_GetItem(modules, py_mod_name);
if (module != null)
{
if (fromlist)
{
if (IsLoadAll(fromList))
{
var mod = ManagedType.GetManagedObject(module) as ModuleObject;
mod?.LoadNames();
}
return(new NewReference(module).DangerousMoveToPointer());
}
module = Runtime.PyDict_GetItemString(modules, names[0]);
return(new NewReference(module, canBeNull: true).DangerousMoveToPointer());
}
Exceptions.Clear();
// Traverse the qualified module name to get the named module
// and place references in sys.modules as we go. Note that if
// we are running in interactive mode we pre-load the names in
// each module, which is often useful for introspection. If we
// are not interactive, we stick to just-in-time creation of
// objects at lookup time, which is much more efficient.
// NEW: The clr got a new module variable preload. You can
// enable preloading in a non-interactive python processing by
// setting clr.preload = True
ModuleObject head = mod_name == realname ? null : root;
ModuleObject tail = root;
root.InitializePreload();
foreach (string name in names)
{
ManagedType mt = tail.GetAttribute(name, true);
if (!(mt is ModuleObject))
{
originalException.Restore();
return(IntPtr.Zero);
}
if (head == null)
{
head = (ModuleObject)mt;
}
tail = (ModuleObject)mt;
if (CLRModule.preload)
{
tail.LoadNames();
}
// Add the module to sys.modules
Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.ObjectReference);
}
{
var mod = fromlist ? tail : head;
if (fromlist && IsLoadAll(fromList))
{
mod.LoadNames();
}
Runtime.XIncref(mod.pyHandle);
return(mod.pyHandle);
}
}