From 3adf071e74789a2fd7e9ef8d208638b3a2d446d1 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 7 May 2026 19:54:01 +0200 Subject: [PATCH] Name missing from __all__ on re-import (#2717) * Adjust test_import to always trigger error-case * Ensure that names are added to __all__ exactly once (cherry picked from commit dc69411dac31f388c0335ba2381c2f730e98d972) --- src/runtime/Types/ModuleObject.cs | 26 ++++++++++++++------------ tests/test_import.py | 5 +++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/runtime/Types/ModuleObject.cs b/src/runtime/Types/ModuleObject.cs index 85438d094..b2ff72b91 100644 --- a/src/runtime/Types/ModuleObject.cs +++ b/src/runtime/Types/ModuleObject.cs @@ -20,6 +20,7 @@ internal class ModuleObject : ExtensionType internal PyDict dict; protected string _namespace; private readonly PyList __all__ = new (); + private readonly HashSet allNames = new(); // Attributes to be set on the module according to PEP302 and 451 // by the import machinery. @@ -179,22 +180,23 @@ public void LoadNames() { foreach (string name in AssemblyManager.GetNames(_namespace)) { - cache.TryGetValue(name, out var m); - if (m != null) + bool hasValidAttribute = cache.TryGetValue(name, out var m); + if (!hasValidAttribute) { - continue; - } - BorrowedReference attr = Runtime.PyDict_GetItemString(dict, name); - // If __dict__ has already set a custom property, skip it. - if (!attr.IsNull) - { - continue; + BorrowedReference attr = Runtime.PyDict_GetItemString(dict, name); + // If __dict__ has already set a custom property, skip it. + if (!attr.IsNull) + { + continue; + } + + using var attrVal = GetAttribute(name, true); + hasValidAttribute = !attrVal.IsNull(); } - using var attrVal = GetAttribute(name, true); - if (!attrVal.IsNull()) + if (hasValidAttribute && allNames.Add(name)) { - // if it's a valid attribute, add it to __all__ + // if it's a valid attribute, add it to __all__ once. using var pyname = Runtime.PyString_FromString(name); if (Runtime.PyList_Append(__all__, pyname.Borrow()) != 0) { diff --git a/tests/test_import.py b/tests/test_import.py index 25877be15..f4e4773d8 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -5,6 +5,11 @@ import pytest import sys +# Unused import to preload the class +# +# This resulted in the FileStream name missing from the wildcard import later +from System.IO import FileStream # noqa: F401 + def test_relative_missing_import(): """Test that a relative missing import doesn't crash. Some modules use this to check if a package is installed.