From c22d2f1a26c7610f8d8369585cf42d2fd2c44d66 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sat, 16 Oct 2021 11:01:52 -0700 Subject: [PATCH] fixed recursive dependency in clr module initialization when there's a public class that implements IEnumerable in global namespace https://github.com/pythonnet/pythonnet/issues/1601 the fix is to delay updating clr module dict with contents of .NET namespaces until after our internal modules are loaded --- src/embed_tests/pyimport.cs | 11 +++++++++++ src/runtime/importhook.cs | 25 ++++++++++++++++--------- src/runtime/pythonengine.cs | 7 ++++--- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/embed_tests/pyimport.cs b/src/embed_tests/pyimport.cs index f590ada4c..efb00cdcc 100644 --- a/src/embed_tests/pyimport.cs +++ b/src/embed_tests/pyimport.cs @@ -112,3 +112,14 @@ import clr } } } + +// regression for https://github.com/pythonnet/pythonnet/issues/1601 +// initialize fails if a class derived from IEnumerable is in global namespace +public class PublicEnumerator : System.Collections.IEnumerable +{ + public System.Collections.IEnumerator GetEnumerator() + { + return null; + } +} + diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs index 0feb06b89..6675858fe 100644 --- a/src/runtime/importhook.cs +++ b/src/runtime/importhook.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Diagnostics; namespace Python.Runtime { @@ -10,7 +11,14 @@ internal static class ImportHook { private static CLRModule root; private static IntPtr py_clr_module; - static BorrowedReference ClrModuleReference => new BorrowedReference(py_clr_module); + internal static BorrowedReference ClrModuleReference + { + get + { + Debug.Assert(py_clr_module != IntPtr.Zero); + return new BorrowedReference(py_clr_module); + } + } private const string LoaderCode = @" import importlib.abc @@ -43,7 +51,7 @@ def find_spec(klass, fullname, paths=None, target=None): return importlib.machinery.ModuleSpec(fullname, DotNetLoader(), is_package=True) return None "; - const string availableNsKey = "_available_namespaces"; + const string _available_namespaces = "_available_namespaces"; /// /// Initialization performed on startup of the Python runtime. @@ -154,12 +162,11 @@ static void SetupNamespaceTracking() { throw PythonException.ThrowLastAsClrException(); } - if (Runtime.PyDict_SetItemString(root.DictRef, availableNsKey, newset) != 0) - { - throw PythonException.ThrowLastAsClrException(); - } } - + if (Runtime.PyDict_SetItemString(root.DictRef, _available_namespaces, newset) != 0) + { + throw PythonException.ThrowLastAsClrException(); + } } /// @@ -168,7 +175,7 @@ static void SetupNamespaceTracking() static void TeardownNameSpaceTracking() { // If the C# runtime isn't loaded, then there are no namespaces available - Runtime.PyDict_SetItemString(root.dict, availableNsKey, Runtime.PyNone); + Runtime.PyDict_SetItemString(root.dict, _available_namespaces, Runtime.PyNone); } static readonly ConcurrentQueue addPending = new(); @@ -190,7 +197,7 @@ internal static void AddNamespaceWithGIL(string name) var pyNs = Runtime.PyString_FromString(name); try { - var nsSet = Runtime.PyDict_GetItemString(root.DictRef, availableNsKey); + var nsSet = Runtime.PyDict_GetItemString(root.DictRef, _available_namespaces); if (!(nsSet.IsNull || nsSet.DangerousGetAddress() == Runtime.PyNone)) { if (Runtime.PySet_Add(nsSet, new BorrowedReference(pyNs)) != 0) diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 10808a1cd..91e013e86 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -220,8 +220,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, } // Load the clr.py resource into the clr module - NewReference clr = Python.Runtime.ImportHook.GetCLRModule(); - BorrowedReference clr_dict = Runtime.PyModule_GetDict(clr); + BorrowedReference clr_dict = Runtime.PyModule_GetDict(ImportHook.ClrModuleReference); var locals = new PyDict(); try @@ -236,7 +235,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, LoadSubmodule(module_globals, "clr.interop", "interop.py"); - LoadMixins(module_globals); + LoadMixins(module_globals); // add the imported module to the clr module, and copy the API functions // and decorators into the main clr module. @@ -257,6 +256,8 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, { locals.Dispose(); } + + ImportHook.UpdateCLRModuleDict(); } static BorrowedReference DefineModule(string name)