X Tutup
Skip to content

Commit a7628ac

Browse files
committed
fixup! Add function of passing an arbitrary .NET object as the value of an attribute of PyObject by dynamic type
1 parent 6378384 commit a7628ac

File tree

2 files changed

+69
-151
lines changed

2 files changed

+69
-151
lines changed

src/embed_tests/dynamic.cs

Lines changed: 34 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -8,73 +8,53 @@ namespace Python.EmbeddingTest
88
[TestFixture]
99
public class dynamicTest
1010
{
11-
private IntPtr gs;
12-
private outstream stream;
11+
private Py.GILState gil;
1312

1413
[SetUp]
1514
public void SetUp()
1615
{
17-
PythonEngine.Initialize();
18-
gs = PythonEngine.AcquireLock();
19-
20-
/* redirect sys.stdout to a .NET object */
21-
stream = new outstream();
16+
gil = Py.GIL();
2217
}
2318

2419
[TearDown]
2520
public void TearDown()
2621
{
27-
PythonEngine.ReleaseLock(gs);
28-
PythonEngine.Shutdown();
22+
gil.Dispose();
2923
}
30-
31-
/// <summary>
32-
/// Set the attribute of a pyobject to null.
33-
/// </summary>
34-
[Test]
35-
public void AssignNone()
36-
{
37-
dynamic sys = Py.Import("sys");
38-
sys.stderr = null;
39-
Assert.IsNull(sys.stderr);
40-
}
41-
24+
4225
/// <summary>
4326
/// Set the attribute of a pyobject with a .NET object.
4427
/// </summary>
4528
[Test]
4629
public void AssignObject()
4730
{
31+
StringBuilder stream = new StringBuilder();
4832
dynamic sys = Py.Import("sys");
49-
sys.stdout = stream;
33+
sys.testattr = stream;
5034
// Check whether there are the same object.
51-
Assert.AreEqual(sys.stdout, stream);
35+
var _stream = sys.testattr.ToObject();
36+
Assert.AreEqual(_stream, stream);
5237

53-
stream.clear();
54-
PythonEngine.RunSimpleString("print('Hello!')");
55-
Assert.AreEqual(stream.getvalue(), "Hello!\n");
38+
PythonEngine.RunSimpleString(
39+
"import sys\n" +
40+
"sys.testattr.Append('Hello!')\n");
41+
Assert.AreEqual(stream.ToString(), "Hello!");
5642
}
5743

5844
/// <summary>
59-
/// When the .NET object was created and used in Python side.
45+
/// Set the attribute of a pyobject to null.
6046
/// </summary>
61-
/// <remarks>
62-
/// Needs Pull Request #376 in order for the exception below to show up.
63-
/// </remarks>
6447
[Test]
65-
[Ignore("System.ArgumentException : Cannot pass a GCHandle across AppDomains")]
66-
public void AssignObjectInPython()
48+
public void AssignNone()
6749
{
68-
PythonEngine.RunSimpleString(
69-
"import sys\n" +
70-
"from Python.EmbeddingTest import outstream\n" +
71-
"sys.stdout = outstream()\n"
72-
);
7350
dynamic sys = Py.Import("sys");
74-
dynamic obj = sys.stdout;
75-
Assert.IsTrue(obj is outstream);
76-
}
51+
sys.testattr = new StringBuilder();
52+
Assert.IsNotNull(sys.testattr);
7753

54+
sys.testattr = null;
55+
Assert.IsNull(sys.testattr);
56+
}
57+
7858
/// <summary>
7959
/// Check whether we can get the attr of a python object when the value of attr is a PyObject.
8060
/// </summary>
@@ -83,8 +63,8 @@ public void AssignPyObject()
8363
{
8464
dynamic sys = Py.Import("sys");
8565
dynamic io = Py.Import("io");
86-
sys.stderr = io.StringIO();
87-
dynamic bb = sys.stderr; //Get the PyObject
66+
sys.testattr = io.StringIO();
67+
dynamic bb = sys.testattr; //Get the PyObject
8868
bb.write("Hello!");
8969
Assert.AreEqual(bb.getvalue().ToString(), "Hello!");
9070
}
@@ -95,26 +75,25 @@ public void AssignPyObject()
9575
[Test]
9676
public void PassObjectInPython()
9777
{
78+
StringBuilder stream = new StringBuilder();
9879
dynamic sys = Py.Import("sys");
99-
sys.stdout = stream;
80+
sys.testattr1 = stream;
10081

10182
//Pass the .NET object in Python side
10283
PythonEngine.RunSimpleString(
10384
"import sys\n" +
104-
"from io import StringIO\n" +
105-
"sys.stderr = sys.stdout\n"
85+
"sys.testattr2 = sys.testattr1\n"
10686
);
10787

10888
//Compare in Python
109-
stream.clear();
11089
PythonEngine.RunSimpleString(
11190
"import sys\n" +
112-
"print((sys.stderr is sys.stdout))\n"
91+
"sys.testattr3 = sys.testattr1 is sys.testattr2\n"
11392
);
114-
Assert.AreEqual(stream.getvalue(), "True\n");
93+
Assert.AreEqual(sys.testattr3.ToString(), "True");
11594

11695
//Compare in .NET
117-
Assert.AreEqual(sys.stdout, sys.stderr);
96+
Assert.AreEqual(sys.testattr1, sys.testattr2);
11897
}
11998

12099
/// <summary>
@@ -123,55 +102,20 @@ public void PassObjectInPython()
123102
[Test]
124103
public void PassPyObjectInNet()
125104
{
105+
StringBuilder stream = new StringBuilder();
126106
dynamic sys = Py.Import("sys");
127-
sys.stdout = stream;
128-
sys.stderr = sys.stdout;
107+
sys.testattr1 = stream;
108+
sys.testattr2 = sys.testattr1;
129109

130110
//Compare in Python
131111
PyObject res = PythonEngine.RunString(
132112
"import sys\n" +
133-
"print(sys.stderr is sys.stdout)\n"
113+
"sys.testattr3 = sys.testattr1 is sys.testattr2\n"
134114
);
135-
Assert.AreEqual(sys.stdout.getvalue().ToString(), "False\n");
115+
Assert.AreEqual(sys.testattr3.ToString(), "True");
136116

137117
//Compare in .NET
138-
Assert.AreEqual(sys.stdout, sys.stderr);
139-
}
140-
}
141-
142-
/// <summary>
143-
/// Implement the interface of the sys.stdout redirection
144-
/// </summary>
145-
public class outstream
146-
{
147-
private StringBuilder buffer;
148-
149-
public outstream()
150-
{
151-
buffer = new StringBuilder();
152-
}
153-
154-
public void write(string str)
155-
{
156-
buffer.Append(str);
157-
}
158-
159-
public void flush()
160-
{
161-
}
162-
163-
public void close()
164-
{
165-
}
166-
167-
public void clear()
168-
{
169-
buffer.Clear();
170-
}
171-
172-
public string getvalue()
173-
{
174-
return buffer.ToString();
118+
Assert.AreEqual(sys.testattr1, sys.testattr2);
175119
}
176120
}
177121
}

src/runtime/pyobject.cs

Lines changed: 35 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -181,32 +181,7 @@ public bool HasAttr(PyObject name)
181181
{
182182
return Runtime.PyObject_HasAttr(obj, name.obj) != 0;
183183
}
184-
185-
186-
/// <summary>
187-
/// GetAttr Method For Dynamic Type
188-
/// </summary>
189-
/// <remarks>
190-
/// Returns the named attribute of the Python object, or raises a
191-
/// PythonException if the attribute access fails.
192-
/// </remarks>
193-
public object GetAttrDynamic(string name)
194-
{
195-
IntPtr op = Runtime.PyObject_GetAttrString(obj, name);
196-
if (op == IntPtr.Zero)
197-
{
198-
throw new PythonException();
199-
}
200-
if (ManagedType.IsManagedType(op))
201-
{
202-
ManagedType managedMethod = ManagedType.GetManagedObject(op);
203-
if (managedMethod is CLRObject)
204-
{
205-
return ((CLRObject)managedMethod).inst;
206-
}
207-
}
208-
return CheckNone(new PyObject(op));
209-
}
184+
210185

211186
/// <summary>
212187
/// GetAttr Method
@@ -299,27 +274,7 @@ public void SetAttr(string name, PyObject value)
299274
throw new PythonException();
300275
}
301276
}
302-
303-
public void SetAttrDynamic(string name, object value)
304-
{
305-
if (value == null)
306-
{
307-
int r = Runtime.PyObject_SetAttrString(obj, name, Runtime.PyNone);
308-
if (r < 0)
309-
{
310-
throw new PythonException();
311-
}
312-
}
313-
else if (value is PyObject)
314-
{
315-
this.SetAttr(name, (PyObject)value);
316-
}
317-
else
318-
{
319-
var ptr = Converter.ToPython(value, value.GetType());
320-
this.SetAttr(name, new PyObject(ptr));
321-
}
322-
}
277+
323278

324279
/// <summary>
325280
/// SetAttr Method
@@ -932,30 +887,49 @@ public override int GetHashCode()
932887
return Runtime.PyObject_Hash(obj).ToInt32();
933888
}
934889

935-
public override bool TryGetMember(GetMemberBinder binder, out object result)
890+
/// <summary>
891+
/// ToObject Method
892+
/// </summary>
893+
/// <remarks>
894+
/// If this PyObject is a wrapped .NET Object, then returns the .NET Object, or returns itself.
895+
/// </remarks>
896+
public virtual object ToObject()
936897
{
937-
if (this.HasAttr(binder.Name))
898+
if (ManagedType.IsManagedType(obj))
938899
{
939-
result = this.GetAttrDynamic(binder.Name);
940-
return true;
900+
ManagedType managedMethod = ManagedType.GetManagedObject(obj);
901+
if (managedMethod is CLRObject)
902+
{
903+
return ((CLRObject)managedMethod).inst;
904+
}
941905
}
942-
else
906+
return this;
907+
}
908+
909+
public long Refcount
910+
{
911+
get
943912
{
944-
return base.TryGetMember(binder, out result);
945-
}
913+
return Runtime.Refcount(obj);
914+
}
915+
}
916+
917+
public override bool TryGetMember(GetMemberBinder binder, out object result)
918+
{
919+
result = CheckNone(this.GetAttr(binder.Name));
920+
return true;
946921
}
947922

948923
public override bool TrySetMember(SetMemberBinder binder, object value)
949924
{
950-
if (this.HasAttr(binder.Name))
951-
{
952-
this.SetAttrDynamic(binder.Name, value);
953-
return true;
954-
}
955-
else
925+
IntPtr ptr = Converter.ToPython(value, value?.GetType());
926+
int r = Runtime.PyObject_SetAttrString(obj, binder.Name, ptr);
927+
if (r < 0)
956928
{
957-
return base.TrySetMember(binder, value);
929+
throw new PythonException();
958930
}
931+
Runtime.XDecref(ptr);
932+
return true;
959933
}
960934

961935
private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs)

0 commit comments

Comments
 (0)
X Tutup