diff --git a/AUTHORS.md b/AUTHORS.md index 7ea639059..ade78d714 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -23,6 +23,7 @@ - Avinash Maddikonda ([@SFM61319](https://github.com/SFM61319)) - BenoƮt Hudson ([@benoithudson](https://github.com/benoithudson)) - Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich)) +- Brandon Carpenter ([@brandon-avantus](https://github.com/brandon-avantus)) - Callum Noble ([@callumnoble](https://github.com/callumnoble)) - Christabella Irwanto([@christabella](https://github.com/christabella)) - Christian Heimes ([@tiran](https://github.com/tiran)) diff --git a/CHANGELOG.md b/CHANGELOG.md index df68fbb39..3cbe9e230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Fixed crash when trying to `del clrObj[...]` for non-arrays - ci: properly exclude job (#2542) +- Fixed conversion of elements when iterating non-generic containers (#2679) ## [3.0.5](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.5) - 2024-12-13 diff --git a/src/runtime/Types/Iterator.cs b/src/runtime/Types/Iterator.cs index 49145d2c3..2170f0684 100644 --- a/src/runtime/Types/Iterator.cs +++ b/src/runtime/Types/Iterator.cs @@ -43,7 +43,12 @@ public static NewReference tp_iternext(BorrowedReference ob) return default; } object item = self.iter.Current; - return Converter.ToPython(item, self.elemType); + // Non-generic enumerator elements are typed as System.Object, + // in which case the actual object type should be converted. + Type elemType = item is null || self.elemType != typeof(object) + ? self.elemType + : item.GetType(); + return Converter.ToPython(item, elemType); } public static NewReference tp_iter(BorrowedReference ob) => new (ob); diff --git a/tests/test_conversion.py b/tests/test_conversion.py index dd70f900a..3b48d9dbe 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -5,7 +5,7 @@ import System from Python.Test import ConversionTest, MethodResolutionInt, UnicodeString, CodecResetter -from Python.Runtime import PyObjectConversions +from Python.Runtime import IPyObjectEncoder, PyObjectConversions from Python.Runtime.Codecs import RawProxyEncoder @@ -755,3 +755,35 @@ def test_explicit_conversion(): assert int(t(123.4)) == 123 with pytest.raises(TypeError): index(t(123.4)) + +@pytest.mark.parametrize('container_type', ['generic', 'non-generic']) +def test_iterator_element_conversion(container_type): + """Test iterator element conversion from Python.""" + from Python.Test import Spam + + try: + from Python.Test import IteratorElementEncoder + except ImportError: + class IteratorElementEncoder(IPyObjectEncoder): + __namespace__ = "Python.Test" + def CanEncode(self, clr_type): + return clr_type.Name == "Spam" and clr_type.Namespace == "Python.Test" + def TryEncode(self, clr_object): + return clr_object.GetValue() + + spam_encoder = IteratorElementEncoder() + PyObjectConversions.RegisterEncoder(spam_encoder) + + values = ["first", "second", "third"] + if container_type == 'generic': + container = System.Collections.Generic.List[Spam]() + for value in values: + container.Add(Spam(value)) + else: + container = System.Array[Spam](Spam(v) for v in values) + + assert type(container[0]) is str + assert next(iter(container.GetEnumerator())) == container[0] + assert list(container.GetEnumerator()) == values + + CodecResetter.Reset()