Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d932176

Browse files
authored
correctly dispose the result of PyRun_String (#1071)
this also changes a few members of NewReference type to make them work in PyRun_String scenario
1 parent 8e108b4 commit d932176

9 files changed

+129
-24
lines changed

src/embed_tests/Python.EmbeddingTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
<Compile Include="pyimport.cs" />
8989
<Compile Include="pyinitialize.cs" />
9090
<Compile Include="pyrunstring.cs" />
91+
<Compile Include="References.cs" />
9192
<Compile Include="TestConverter.cs" />
9293
<Compile Include="TestCustomMarshal.cs" />
9394
<Compile Include="TestDomainReload.cs" />

src/embed_tests/References.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
namespace Python.EmbeddingTest
2+
{
3+
using NUnit.Framework;
4+
using Python.Runtime;
5+
6+
public class References
7+
{
8+
private Py.GILState _gs;
9+
10+
[SetUp]
11+
public void SetUp()
12+
{
13+
_gs = Py.GIL();
14+
}
15+
16+
[TearDown]
17+
public void Dispose()
18+
{
19+
_gs.Dispose();
20+
}
21+
22+
[Test]
23+
public void MoveToPyObject_SetsNull()
24+
{
25+
var dict = new PyDict();
26+
NewReference reference = Runtime.PyDict_Items(dict.Handle);
27+
try
28+
{
29+
Assert.IsFalse(reference.IsNull());
30+
31+
using (reference.MoveToPyObject())
32+
Assert.IsTrue(reference.IsNull());
33+
}
34+
finally
35+
{
36+
reference.Dispose();
37+
}
38+
}
39+
}
40+
}

src/runtime/NewReference.cs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
namespace Python.Runtime
22
{
33
using System;
4+
using System.Diagnostics.Contracts;
5+
46
/// <summary>
57
/// Represents a reference to a Python object, that is tracked by Python's reference counting.
68
/// </summary>
79
[NonCopyable]
810
ref struct NewReference
911
{
1012
IntPtr pointer;
11-
public bool IsNull => this.pointer == IntPtr.Zero;
12-
13-
/// <summary>Gets a raw pointer to the Python object</summary>
14-
public IntPtr DangerousGetAddress()
15-
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
1613

1714
/// <summary>
1815
/// Returns <see cref="PyObject"/> wrapper around this reference, which now owns
1916
/// the pointer. Sets the original reference to <c>null</c>, as it no longer owns it.
2017
/// </summary>
2118
public PyObject MoveToPyObject()
2219
{
23-
if (this.IsNull) throw new NullReferenceException();
20+
if (this.IsNull()) throw new NullReferenceException();
2421

2522
var result = new PyObject(this.pointer);
2623
this.pointer = IntPtr.Zero;
@@ -31,9 +28,32 @@ public PyObject MoveToPyObject()
3128
/// </summary>
3229
public void Dispose()
3330
{
34-
if (!this.IsNull)
31+
if (!this.IsNull())
3532
Runtime.XDecref(this.pointer);
3633
this.pointer = IntPtr.Zero;
3734
}
35+
36+
[Pure]
37+
internal static IntPtr DangerousGetAddress(in NewReference reference)
38+
=> IsNull(reference) ? throw new NullReferenceException() : reference.pointer;
39+
[Pure]
40+
internal static bool IsNull(in NewReference reference)
41+
=> reference.pointer == IntPtr.Zero;
42+
}
43+
44+
/// <summary>
45+
/// These members can not be directly in <see cref="NewReference"/> type,
46+
/// because <c>this</c> is always passed by value, which we need to avoid.
47+
/// (note <code>this in NewReference</code> vs the usual <code>this NewReference</code>)
48+
/// </summary>
49+
static class NewReferenceExtensions
50+
{
51+
/// <summary>Gets a raw pointer to the Python object</summary>
52+
[Pure]
53+
public static IntPtr DangerousGetAddress(this in NewReference reference)
54+
=> NewReference.DangerousGetAddress(reference);
55+
[Pure]
56+
public static bool IsNull(this in NewReference reference)
57+
=> NewReference.IsNull(reference);
3858
}
3959
}

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
<Compile Include="pythonengine.cs" />
142142
<Compile Include="pythonexception.cs" />
143143
<Compile Include="pytuple.cs" />
144+
<Compile Include="ReferenceExtensions.cs" />
144145
<Compile Include="runtime.cs" />
145146
<Compile Include="typemanager.cs" />
146147
<Compile Include="typemethod.cs" />

src/runtime/ReferenceExtensions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Python.Runtime
2+
{
3+
using System.Diagnostics.Contracts;
4+
5+
static class ReferenceExtensions
6+
{
7+
/// <summary>
8+
/// Checks if the reference points to Python object <c>None</c>.
9+
/// </summary>
10+
[Pure]
11+
public static bool IsNone(this in NewReference reference)
12+
=> reference.DangerousGetAddress() == Runtime.PyNone;
13+
/// <summary>
14+
/// Checks if the reference points to Python object <c>None</c>.
15+
/// </summary>
16+
[Pure]
17+
public static bool IsNone(this BorrowedReference reference)
18+
=> reference.DangerousGetAddress() == Runtime.PyNone;
19+
}
20+
}

src/runtime/pydict.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public PyObject Items()
142142
var items = Runtime.PyDict_Items(this.obj);
143143
try
144144
{
145-
if (items.IsNull)
145+
if (items.IsNull())
146146
{
147147
throw new PythonException();
148148
}

src/runtime/pyscope.cs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,19 @@ public PyObject Eval(string code, PyDict locals = null)
278278
Check();
279279
IntPtr _locals = locals == null ? variables : locals.obj;
280280
var flag = (IntPtr)Runtime.Py_eval_input;
281-
IntPtr ptr = Runtime.PyRun_String(
281+
282+
NewReference reference = Runtime.PyRun_String(
282283
code, flag, variables, _locals
283284
);
284-
Runtime.CheckExceptionOccurred();
285-
return new PyObject(ptr);
285+
try
286+
{
287+
Runtime.CheckExceptionOccurred();
288+
return reference.MoveToPyObject();
289+
}
290+
finally
291+
{
292+
reference.Dispose();
293+
}
286294
}
287295

288296
/// <summary>
@@ -316,15 +324,22 @@ public void Exec(string code, PyDict locals = null)
316324
private void Exec(string code, IntPtr _globals, IntPtr _locals)
317325
{
318326
var flag = (IntPtr)Runtime.Py_file_input;
319-
IntPtr ptr = Runtime.PyRun_String(
327+
NewReference reference = Runtime.PyRun_String(
320328
code, flag, _globals, _locals
321329
);
322-
Runtime.CheckExceptionOccurred();
323-
if (ptr != Runtime.PyNone)
330+
331+
try
324332
{
325-
throw new PythonException();
333+
Runtime.CheckExceptionOccurred();
334+
if (!reference.IsNone())
335+
{
336+
throw new PythonException();
337+
}
338+
}
339+
finally
340+
{
341+
reference.Dispose();
326342
}
327-
Runtime.XDecref(ptr);
328343
}
329344

330345
/// <summary>

src/runtime/pythonengine.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -543,12 +543,13 @@ public static PyObject Eval(string code, IntPtr? globals = null, IntPtr? locals
543543
/// </remarks>
544544
public static void Exec(string code, IntPtr? globals = null, IntPtr? locals = null)
545545
{
546-
PyObject result = RunString(code, globals, locals, RunFlagType.File);
547-
if (result.obj != Runtime.PyNone)
546+
using (PyObject result = RunString(code, globals, locals, RunFlagType.File))
548547
{
549-
throw new PythonException();
548+
if (result.obj != Runtime.PyNone)
549+
{
550+
throw new PythonException();
551+
}
550552
}
551-
result.Dispose();
552553
}
553554

554555

@@ -594,13 +595,20 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals,
594595

595596
try
596597
{
597-
IntPtr result = Runtime.PyRun_String(
598+
NewReference result = Runtime.PyRun_String(
598599
code, (IntPtr)flag, globals.Value, locals.Value
599600
);
600601

601-
Runtime.CheckExceptionOccurred();
602+
try
603+
{
604+
Runtime.CheckExceptionOccurred();
602605

603-
return new PyObject(result);
606+
return result.MoveToPyObject();
607+
}
608+
finally
609+
{
610+
result.Dispose();
611+
}
604612
}
605613
finally
606614
{

src/runtime/runtime.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ public static extern int Py_Main(
802802
internal static extern int PyRun_SimpleString(string code);
803803

804804
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
805-
internal static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals);
805+
internal static extern NewReference PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals);
806806

807807
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
808808
internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals);

0 commit comments

Comments
 (0)