8000 PyIter: do not force dispose previous object upon moving to the next … · pythonnet/pythonnet@7149d5e · GitHub
[go: up one dir, main page]

Skip to content

Commit 7149d5e

Browse files
authored
PyIter: do not force dispose previous object upon moving to the next one (#1331)
also added exception handling for PyIter_Next PyObject now implements `IEnumerable<PyObject>`
1 parent 7a03b61 commit 7149d5e

File tree

5 files changed

+58
-22
lines changed

5 files changed

+58
-22
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ details about the cause of the failure
2626
- BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition
2727
to the regular method return value (unless they are passed with `ref` or `out` keyword).
2828
- BREAKING: Drop support for the long-deprecated CLR.* prefix.
29+
- `PyObject` now implements `IEnumerable<PyObject>` in addition to `IEnumerable`
2930

3031
### Fixed
3132

@@ -40,6 +41,7 @@ details about the cause of the failure
4041
- Fixed a bug where indexers could not be used if they were inherited
4142
- Made it possible to use `__len__` also on `ICollection<>` interface objects
4243
- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects
44+
- Fixed objects returned by enumerating `PyObject` being disposed too soon
4345

4446
### Removed
4547

src/embed_tests/TestPyIter.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Linq;
2+
using System.Text;
3+
4+
using NUnit.Framework;
5+
6+
using Python.Runtime;
7+
8+
namespace Python.EmbeddingTest
9+
{
10+
class TestPyIter
11+
{
12+
[OneTimeSetUp]
13+
public void SetUp()
14+
{
15+
PythonEngine.Initialize();
16+
}
17+
18+
[OneTimeTearDown]
19+
public void Dispose()
20+
{
21+
PythonEngine.Shutdown();
22+
}
23+
24+
[Test]
25+
public void KeepOldObjects()
26+
{
27+
using (Py.GIL())
28+
using (var testString = new PyString("hello world! !$%&/()=?"))
29+
{
30+
PyObject[] chars = testString.ToArray();
31+
Assert.IsTrue(chars.Length > 1);
32+
string reconstructed = string.Concat(chars.Select(c => c.As<string>()));
33+
Assert.AreEqual(testString.As<string>(), reconstructed);
34+
}
35+
}
36+
}
37+
}

src/runtime/pyiter.cs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Python.Runtime
99
/// PY3: https://docs.python.org/3/c-api/iterator.html
1010
/// for details.
1111
/// </summary>
12-
public class PyIter : PyObject, IEnumerator<object>
12+
public class PyIter : PyObject, IEnumerator<PyObject>
1313
{
1414
private PyObject _current;
1515

@@ -46,41 +46,35 @@ public static PyIter GetIter(PyObject iterable)
4646

4747
protected override void Dispose(bool disposing)
4848
{
49-
if (null != _current)
50-
{
51-
_current.Dispose();
52-
_current = null;
53-
}
49+
_current = null;
5450
base.Dispose(disposing);
5551
}
5652

5753
public bool MoveNext()
5854
{
59-
// dispose of the previous object, if there was one
60-
if (null != _current)
55+
NewReference next = Runtime.PyIter_Next(Reference);
56+
if (next.IsNull())
6157
{
62-
_current.Dispose();
63-
_current = null;
64-
}
58+
if (Exceptions.ErrorOccurred())
59+
{
60+
throw new PythonException();
61+
}
6562

66-
IntPtr next = Runtime.PyIter_Next(obj);
67-
if (next == IntPtr.Zero)
68-
{
63+
// stop holding the previous object, if there was one
64+
_current = null;
6965
return false;
7066
}
7167

72-
_current = new PyObject(next);
68+
_current = next.MoveToPyObject();
7369
return true;
7470
}
7571

7672
public void Reset()
7773
{
78-
//Not supported in python.
74+
throw new NotSupportedException();
7975
}
8076

81-
public object Current
82-
{
83-
get { return _current; }
84-
}
77+
public PyObject Current => _current;
78+
object System.Collections.IEnumerator.Current => _current;
8579
}
8680
}

src/runtime/pyobject.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Python.Runtime
1616
/// for details.
1717
/// </summary>
1818
[Serializable]
19-
public partial class PyObject : DynamicObject, IEnumerable, IDisposable
19+
public partial class PyObject : DynamicObject, IEnumerable<PyObject>, IDisposable
2020
{
2121
#if TRACE_ALLOC
2222
/// <summary>
@@ -705,10 +705,11 @@ public PyObject GetIterator()
705705
/// python object to be iterated over in C#. A PythonException will be
706706
/// raised if the object is not iterable.
707707
/// </remarks>
708-
public IEnumerator GetEnumerator()
708+
public IEnumerator<PyObject> GetEnumerator()
709709
{
710710
return PyIter.GetIter(this);
711711
}
712+
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
712713

713714

714715
/// <summary>

src/runtime/runtime.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,8 @@ internal static bool PyIter_Check(IntPtr pointer)
18541854

18551855
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
18561856
internal static extern IntPtr PyIter_Next(IntPtr pointer);
1857+
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1858+
internal static extern NewReference PyIter_Next(BorrowedReference pointer);
18571859

18581860

18591861
//====================================================================

0 commit comments

Comments
 (0)
0