From 51d229f6c9796a89547b9114c1fd7a547df9c3e5 Mon Sep 17 00:00:00 2001 From: yag Date: Fri, 10 Feb 2017 13:10:10 +0800 Subject: [PATCH] Add function of passing an arbitrary .NET object as the value of an attribute of PyObject by dynamic type --- AUTHORS.md | 1 + CHANGELOG.md | 2 + src/embed_tests/Python.EmbeddingTest.csproj | 2 + src/embed_tests/dynamic.cs | 122 ++++++++++++++++++++ src/runtime/pyobject.cs | 32 ++--- 5 files changed, 144 insertions(+), 15 deletions(-) create mode 100644 src/embed_tests/dynamic.cs diff --git a/AUTHORS.md b/AUTHORS.md index 2a2110f26..64511275b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -29,6 +29,7 @@ - Sean Freitag ([@cowboygneox](https://github.com/cowboygneox)) - Serge Weinstock ([@sweinst](https://github.com/sweinst)) - Virgil Dupras ([@hsoft](https://github.com/hsoft)) +- Wenguang Yang ([@yagweb](https://github.com/yagweb)) - Xavier Dupré ([@sdpython](https://github.com/sdpython)) - Zane Purvis ([@zanedp](https://github.com/zanedp)) - ([@ArvidJB](https://github.com/ArvidJB)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9161b3977..e55ed6245 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Added Embedded tests on Appveyor (#353) - Added PY3 settings to configuration-manager (#346) - Added `Slack` chat (#384)(#383)(#386) +- Added function of passing an arbitrary .NET object as the value + of an attribute of PyObject (#370)(#373) ### Changed diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index a2e92ed19..ef011d044 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -65,6 +65,7 @@ pdbonly + ..\..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll @@ -76,6 +77,7 @@ + diff --git a/src/embed_tests/dynamic.cs b/src/embed_tests/dynamic.cs new file mode 100644 index 000000000..c70fe203c --- /dev/null +++ b/src/embed_tests/dynamic.cs @@ -0,0 +1,122 @@ +using System; +using System.Text; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + [TestFixture] + public class dynamicTest + { + private Py.GILState gil; + + [SetUp] + public void SetUp() + { + gil = Py.GIL(); + } + + [TearDown] + public void TearDown() + { + gil.Dispose(); + } + + /// + /// Set the attribute of a pyobject with a .NET object. + /// + [Test] + public void AssignObject() + { + StringBuilder stream = new StringBuilder(); + dynamic sys = Py.Import("sys"); + sys.testattr = stream; + // Check whether there are the same object. + var _stream = sys.testattr.AsManagedObject(typeof(StringBuilder)); + Assert.AreEqual(_stream, stream); + + PythonEngine.RunSimpleString( + "import sys\n" + + "sys.testattr.Append('Hello!')\n"); + Assert.AreEqual(stream.ToString(), "Hello!"); + } + + /// + /// Set the attribute of a pyobject to null. + /// + [Test] + public void AssignNone() + { + dynamic sys = Py.Import("sys"); + sys.testattr = new StringBuilder(); + Assert.IsNotNull(sys.testattr); + + sys.testattr = null; + Assert.IsNull(sys.testattr); + } + + /// + /// Check whether we can get the attr of a python object when the + /// value of attr is a PyObject. + /// + [Test] + public void AssignPyObject() + { + dynamic sys = Py.Import("sys"); + dynamic io = Py.Import("io"); + sys.testattr = io.StringIO(); + dynamic bb = sys.testattr; //Get the PyObject + bb.write("Hello!"); + Assert.AreEqual(bb.getvalue().ToString(), "Hello!"); + } + + /// + /// Pass the .NET object in Python side. + /// + [Test] + public void PassObjectInPython() + { + StringBuilder stream = new StringBuilder(); + dynamic sys = Py.Import("sys"); + sys.testattr1 = stream; + + //Pass the .NET object in Python side + PythonEngine.RunSimpleString( + "import sys\n" + + "sys.testattr2 = sys.testattr1\n" + ); + + //Compare in Python + PythonEngine.RunSimpleString( + "import sys\n" + + "sys.testattr3 = sys.testattr1 is sys.testattr2\n" + ); + Assert.AreEqual(sys.testattr3.ToString(), "True"); + + //Compare in .NET + Assert.AreEqual(sys.testattr1, sys.testattr2); + } + + /// + /// Pass the PyObject in .NET side + /// + [Test] + public void PassPyObjectInNet() + { + StringBuilder stream = new StringBuilder(); + dynamic sys = Py.Import("sys"); + sys.testattr1 = stream; + sys.testattr2 = sys.testattr1; + + //Compare in Python + PyObject res = PythonEngine.RunString( + "import sys\n" + + "sys.testattr3 = sys.testattr1 is sys.testattr2\n" + ); + Assert.AreEqual(sys.testattr3.ToString(), "True"); + + //Compare in .NET + Assert.AreEqual(sys.testattr1, sys.testattr2); + } + } +} diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 3296d81f8..47f413409 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -887,30 +887,32 @@ public override int GetHashCode() return Runtime.PyObject_Hash(obj).ToInt32(); } - public override bool TryGetMember(GetMemberBinder binder, out object result) + + public long Refcount { - if (this.HasAttr(binder.Name)) - { - result = CheckNone(this.GetAttr(binder.Name)); - return true; - } - else + get { - return base.TryGetMember(binder, out result); + return Runtime.Refcount(obj); } } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = CheckNone(this.GetAttr(binder.Name)); + return true; + } + public override bool TrySetMember(SetMemberBinder binder, object value) { - if (this.HasAttr(binder.Name)) - { - this.SetAttr(binder.Name, (PyObject)value); - return true; - } - else + IntPtr ptr = Converter.ToPython(value, value?.GetType()); + int r = Runtime.PyObject_SetAttrString(obj, binder.Name, ptr); + if (r < 0) { - return base.TrySetMember(binder, value); + throw new PythonException(); } + Runtime.XDecref(ptr); + return true; } private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs)