diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index 7a0964a8a..a191290ef 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -88,6 +88,7 @@
+
diff --git a/src/embed_tests/References.cs b/src/embed_tests/References.cs
new file mode 100644
index 000000000..4c7124907
--- /dev/null
+++ b/src/embed_tests/References.cs
@@ -0,0 +1,40 @@
+namespace Python.EmbeddingTest
+{
+ using NUnit.Framework;
+ using Python.Runtime;
+
+ public class References
+ {
+ private Py.GILState _gs;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _gs = Py.GIL();
+ }
+
+ [TearDown]
+ public void Dispose()
+ {
+ _gs.Dispose();
+ }
+
+ [Test]
+ public void MoveToPyObject_SetsNull()
+ {
+ var dict = new PyDict();
+ NewReference reference = Runtime.PyDict_Items(dict.Handle);
+ try
+ {
+ Assert.IsFalse(reference.IsNull());
+
+ using (reference.MoveToPyObject())
+ Assert.IsTrue(reference.IsNull());
+ }
+ finally
+ {
+ reference.Dispose();
+ }
+ }
+ }
+}
diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs
index 3b45f821f..bbeb86dc4 100644
--- a/src/runtime/NewReference.cs
+++ b/src/runtime/NewReference.cs
@@ -1,6 +1,8 @@
namespace Python.Runtime
{
using System;
+ using System.Diagnostics.Contracts;
+
///
/// Represents a reference to a Python object, that is tracked by Python's reference counting.
///
@@ -8,11 +10,6 @@ namespace Python.Runtime
ref struct NewReference
{
IntPtr pointer;
- public bool IsNull => this.pointer == IntPtr.Zero;
-
- /// Gets a raw pointer to the Python object
- public IntPtr DangerousGetAddress()
- => this.IsNull ? throw new NullReferenceException() : this.pointer;
///
/// Returns wrapper around this reference, which now owns
@@ -20,7 +17,7 @@ public IntPtr DangerousGetAddress()
///
public PyObject MoveToPyObject()
{
- if (this.IsNull) throw new NullReferenceException();
+ if (this.IsNull()) throw new NullReferenceException();
var result = new PyObject(this.pointer);
this.pointer = IntPtr.Zero;
@@ -31,9 +28,32 @@ public PyObject MoveToPyObject()
///
public void Dispose()
{
- if (!this.IsNull)
+ if (!this.IsNull())
Runtime.XDecref(this.pointer);
this.pointer = IntPtr.Zero;
}
+
+ [Pure]
+ internal static IntPtr DangerousGetAddress(in NewReference reference)
+ => IsNull(reference) ? throw new NullReferenceException() : reference.pointer;
+ [Pure]
+ internal static bool IsNull(in NewReference reference)
+ => reference.pointer == IntPtr.Zero;
+ }
+
+ ///
+ /// These members can not be directly in type,
+ /// because this is always passed by value, which we need to avoid.
+ /// (note this in NewReference
vs the usual this NewReference
)
+ ///
+ static class NewReferenceExtensions
+ {
+ /// Gets a raw pointer to the Python object
+ [Pure]
+ public static IntPtr DangerousGetAddress(this in NewReference reference)
+ => NewReference.DangerousGetAddress(reference);
+ [Pure]
+ public static bool IsNull(this in NewReference reference)
+ => NewReference.IsNull(reference);
}
}
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 1d40c2a38..fd2d35bde 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -141,6 +141,7 @@
+
diff --git a/src/runtime/ReferenceExtensions.cs b/src/runtime/ReferenceExtensions.cs
new file mode 100644
index 000000000..8fa2731b7
--- /dev/null
+++ b/src/runtime/ReferenceExtensions.cs
@@ -0,0 +1,20 @@
+namespace Python.Runtime
+{
+ using System.Diagnostics.Contracts;
+
+ static class ReferenceExtensions
+ {
+ ///
+ /// Checks if the reference points to Python object None.
+ ///
+ [Pure]
+ public static bool IsNone(this in NewReference reference)
+ => reference.DangerousGetAddress() == Runtime.PyNone;
+ ///
+ /// Checks if the reference points to Python object None.
+ ///
+ [Pure]
+ public static bool IsNone(this BorrowedReference reference)
+ => reference.DangerousGetAddress() == Runtime.PyNone;
+ }
+}
diff --git a/src/runtime/pydict.cs b/src/runtime/pydict.cs
index b396f4f3d..7ff7a83c8 100644
--- a/src/runtime/pydict.cs
+++ b/src/runtime/pydict.cs
@@ -142,7 +142,7 @@ public PyObject Items()
var items = Runtime.PyDict_Items(this.obj);
try
{
- if (items.IsNull)
+ if (items.IsNull())
{
throw new PythonException();
}
diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs
index 4008ce29a..8738824f5 100644
--- a/src/runtime/pyscope.cs
+++ b/src/runtime/pyscope.cs
@@ -278,11 +278,19 @@ public PyObject Eval(string code, PyDict locals = null)
Check();
IntPtr _locals = locals == null ? variables : locals.obj;
var flag = (IntPtr)Runtime.Py_eval_input;
- IntPtr ptr = Runtime.PyRun_String(
+
+ NewReference reference = Runtime.PyRun_String(
code, flag, variables, _locals
);
- Runtime.CheckExceptionOccurred();
- return new PyObject(ptr);
+ try
+ {
+ Runtime.CheckExceptionOccurred();
+ return reference.MoveToPyObject();
+ }
+ finally
+ {
+ reference.Dispose();
+ }
}
///
@@ -316,15 +324,22 @@ public void Exec(string code, PyDict locals = null)
private void Exec(string code, IntPtr _globals, IntPtr _locals)
{
var flag = (IntPtr)Runtime.Py_file_input;
- IntPtr ptr = Runtime.PyRun_String(
+ NewReference reference = Runtime.PyRun_String(
code, flag, _globals, _locals
);
- Runtime.CheckExceptionOccurred();
- if (ptr != Runtime.PyNone)
+
+ try
{
- throw new PythonException();
+ Runtime.CheckExceptionOccurred();
+ if (!reference.IsNone())
+ {
+ throw new PythonException();
+ }
+ }
+ finally
+ {
+ reference.Dispose();
}
- Runtime.XDecref(ptr);
}
///
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index d5492ebb9..df2d98641 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -543,12 +543,13 @@ public static PyObject Eval(string code, IntPtr? globals = null, IntPtr? locals
///
public static void Exec(string code, IntPtr? globals = null, IntPtr? locals = null)
{
- PyObject result = RunString(code, globals, locals, RunFlagType.File);
- if (result.obj != Runtime.PyNone)
+ using (PyObject result = RunString(code, globals, locals, RunFlagType.File))
{
- throw new PythonException();
+ if (result.obj != Runtime.PyNone)
+ {
+ throw new PythonException();
+ }
}
- result.Dispose();
}
@@ -594,13 +595,20 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals,
try
{
- IntPtr result = Runtime.PyRun_String(
+ NewReference result = Runtime.PyRun_String(
code, (IntPtr)flag, globals.Value, locals.Value
);
- Runtime.CheckExceptionOccurred();
+ try
+ {
+ Runtime.CheckExceptionOccurred();
- return new PyObject(result);
+ return result.MoveToPyObject();
+ }
+ finally
+ {
+ result.Dispose();
+ }
}
finally
{
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 963c9f475..9c9d674a6 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -802,7 +802,7 @@ public static extern int Py_Main(
internal static extern int PyRun_SimpleString(string code);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals);
+ internal static extern NewReference PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals);