From 7b4c6b587374e8c76a0eff658d01710df8712ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 22 Oct 2026 19:33:43 -0400 Subject: [PATCH 1/4] Remove NullImporter --- src/core/IronPython/Hosting/PythonCommandLine.cs | 2 +- src/core/IronPython/Modules/_imp.cs | 11 ----------- src/core/IronPython/Runtime/Importer.cs | 6 +----- tests/suite/test_imp.py | 2 ++ 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/core/IronPython/Hosting/PythonCommandLine.cs b/src/core/IronPython/Hosting/PythonCommandLine.cs index bc9bb0483..ba4ba624f 100644 --- a/src/core/IronPython/Hosting/PythonCommandLine.cs +++ b/src/core/IronPython/Hosting/PythonCommandLine.cs @@ -638,7 +638,7 @@ private int RunFileWorker(string/*!*/ fileName) { if (Importer.TryImportMainFromZip(DefaultContext.Default, fileName, out importer)) { return 0; } - if (importer != null && importer.GetType() != typeof(PythonImport.NullImporter)) { + if (importer != null) { Console.WriteLine(String.Format("can't find '__main__' module in '{0}'", fileName), Style.Error); return 0; } diff --git a/src/core/IronPython/Modules/_imp.cs b/src/core/IronPython/Modules/_imp.cs index 044a26db1..e0db2f9cf 100644 --- a/src/core/IronPython/Modules/_imp.cs +++ b/src/core/IronPython/Modules/_imp.cs @@ -132,16 +132,5 @@ private static long GetLockCount(CodeContext/*!*/ context) { private static void SetLockCount(CodeContext/*!*/ context, long lockCount) { context.LanguageContext.SetModuleState(_lockCountKey, lockCount); } - - [PythonType] - public sealed class NullImporter { - public NullImporter(string path_string) { - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] - public object find_module([NotNone] params object[] args) { - return null; - } - } } } diff --git a/src/core/IronPython/Runtime/Importer.cs b/src/core/IronPython/Runtime/Importer.cs index 79bedb9c2..39ed7da67 100644 --- a/src/core/IronPython/Runtime/Importer.cs +++ b/src/core/IronPython/Runtime/Importer.cs @@ -825,7 +825,7 @@ internal static bool TryImportMainFromZip(CodeContext/*!*/ context, string/*!*/ return false; } importCache[path] = importer = FindImporterForPath(context, path); - if (importer is null || importer is PythonImport.NullImporter) { + if (importer is null) { return false; } // for consistency with cpython, insert zip as a first entry into sys.path @@ -871,10 +871,6 @@ private static object FindImporterForPath(CodeContext/*!*/ context, string dirna } } - if (!context.LanguageContext.DomainManager.Platform.DirectoryExists(dirname)) { - return new PythonImport.NullImporter(dirname); - } - return null; } diff --git a/tests/suite/test_imp.py b/tests/suite/test_imp.py index ab40e4e6b..e70cce8e1 100644 --- a/tests/suite/test_imp.py +++ b/tests/suite/test_imp.py @@ -1045,6 +1045,8 @@ def test_load_package(self): def test_NullImporter(self): self.assertEqual(imp.NullImporter.__module__, 'imp') + import _imp + self.assertFalse(hasattr(_imp, 'NullImporter')) sys.path.append('directory_that_does_not_exist') try: From dd54c3c9ed5eab1c342436661617076f32a07a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 22 Oct 2026 20:34:41 -0400 Subject: [PATCH 2/4] Set ShowCls when clr is loaded by importlib --- src/core/IronPython/Runtime/Importer.cs | 4 ++++ tests/suite/test_imp.py | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/core/IronPython/Runtime/Importer.cs b/src/core/IronPython/Runtime/Importer.cs index 39ed7da67..b36c40385 100644 --- a/src/core/IronPython/Runtime/Importer.cs +++ b/src/core/IronPython/Runtime/Importer.cs @@ -564,6 +564,10 @@ private static object ImportTopAbsolute(CodeContext/*!*/ context, string/*!*/ na } if (TryLoadMetaPathModule(context, name, null, out ret)) { + // importlib's BuiltinImporter loads clr before ImportBuiltin gets a chance to + if (ret == context.LanguageContext.ClrModule) { + context.ShowCls = true; + } return ret; } diff --git a/tests/suite/test_imp.py b/tests/suite/test_imp.py index e70cce8e1..640a73a18 100644 --- a/tests/suite/test_imp.py +++ b/tests/suite/test_imp.py @@ -1058,6 +1058,19 @@ def test_NullImporter(self): finally: sys.path.remove('directory_that_does_not_exist') + @skipUnlessIronPython() + def test_import_clr_show_cls(self): + # the first import of clr goes through importlib's BuiltinImporter + clr = sys.modules.pop('clr') + try: + g = {} + exec("r1 = hasattr('abc', 'Split')\nimport clr\nr2 = hasattr('abc', 'Split')", g) + self.assertFalse(g['r1']) + self.assertTrue(g['r2']) + self.assertIs(g['clr'], clr) + finally: + sys.modules['clr'] = clr + def test_get_frozen_object(self): # frozen objects not supported, this always fails self.assertRaises(ImportError, imp.get_frozen_object, 'foo') From 2dd56cc9586b79fcb391c9dd6ebae39541dd359f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Tue, 20 Oct 2026 22:27:37 -0400 Subject: [PATCH 3/4] zipimporter: stop on directory --- src/core/IronPython.Modules/zipimport.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/IronPython.Modules/zipimport.cs b/src/core/IronPython.Modules/zipimport.cs index a1883dbdd..072d490b6 100644 --- a/src/core/IronPython.Modules/zipimport.cs +++ b/src/core/IronPython.Modules/zipimport.cs @@ -126,6 +126,9 @@ public zipimporter(CodeContext/*!*/ context, object pathObj, [ParamDictionary] I path = buf; break; } + if (pal.DirectoryExists(buf)) { + break; + } buf = Path.GetDirectoryName(buf); } } catch { @@ -230,7 +233,7 @@ public object load_module(CodeContext/*!*/ context, string fullname) { ModuleOptions.None, out script); dict = mod.__dict__; - // we do these here because we don't want CompileModule to initialize the module until we've set + // we do these here because we don't want CompileModule to initialize the module until we've set // up some additional stuff dict.Add("__name__", fullname); dict.Add("__loader__", this); @@ -453,7 +456,7 @@ private byte[] GetCodeFromData(CodeContext/*!*/ context, bool ispackage, bool is if (data != null) { if (isbytecode) { - // would put in code to unmarshal the bytecode here... + // would put in code to unmarshal the bytecode here... } else { code = data; } @@ -464,7 +467,7 @@ private byte[] GetCodeFromData(CodeContext/*!*/ context, bool ispackage, bool is /// /// Given a path to a Zip archive, build a dict, mapping file names /// (local to the archive, using SEP as a separator) to toc entries. - /// + /// /// A toc_entry is a tuple: /// (__file__, # value to use for __file__, available for all files /// compress, # compression kind; 0 for uncompressed @@ -485,7 +488,7 @@ private PythonDictionary ReadDirectory(CodeContext context, string archive) { BinaryReader fp = null; int header_position, header_size, header_offset, count, compress; int time, date, crc, data_size, name_size, file_size, file_offset; - int arc_offset; // offset from beginning of file to start of zip-archive + int arc_offset; // offset from beginning of file to start of zip-archive PythonDictionary files = null; byte[] endof_central_dir = new byte[22]; @@ -577,13 +580,13 @@ private string GetSubName(string fullname) { /// /// Given a (sub)modulename, write the potential file path in the - /// archive (without extension) to the path buffer. + /// archive (without extension) to the path buffer. /// /// /// /// private string MakeFilename(CodeContext context, string prefix, string name) { - // self.prefix + name [+ SEP + "__init__"] + ".py[co]" + // self.prefix + name [+ SEP + "__init__"] + ".py[co]" if ((prefix.Length + name.Length + 13) >= MAXPATHLEN) { throw MakeError(context, "path to long"); } From f5f7f7399f165bdf117805d87bc1eee29ddeb317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Wed, 21 Oct 2026 14:44:41 -0400 Subject: [PATCH 4/4] Bump DLR --- src/dlr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dlr b/src/dlr index 4b380fe1d..ab42e2d1d 160000 --- a/src/dlr +++ b/src/dlr @@ -1 +1 @@ -Subproject commit 4b380fe1dce52f95f64b4725b679fba5e4b0ca06 +Subproject commit ab42e2d1d10523ea00e98bb3dec800538667d609