1 package jep.test;
2 
3 import java.lang.reflect.UndeclaredThrowableException;
4 import java.util.Arrays;
5 import java.util.Deque;
6 
7 import jep.Interpreter;
8 import jep.JepException;
9 import jep.SubInterpreter;
10 import jep.python.PyCallable;
11 import jep.python.PyObject;
12 
13 /**
14  * @author bsteffen
15  * @since 3.8
16  */
17 public class TestGetJPyObject {
18 
testIdentity(Interpreter interp)19     public static void testIdentity(Interpreter interp) throws JepException {
20         interp.eval(
21                 "t = [object(), 1, 1.5, True, None, [], (), {'key':'value'} ]");
22         PyObject[] diverseTypes = interp.getValue("t", PyObject[].class);
23         for (int i = 0; i < diverseTypes.length; i += 1) {
24             interp.set("t2", diverseTypes[i]);
25             Boolean b = interp.getValue("t[" + i + "] is not t2",
26                     Boolean.class);
27             if (b.booleanValue()) {
28                 throw new IllegalStateException(
29                         "JPyObject " + i + " is not preserving identity.");
30             }
31         }
32     }
33 
buildTestClassPython()34     private static String buildTestClassPython() {
35         StringBuilder testclass = new StringBuilder();
36         testclass.append("class testclass(object):\n");
37         testclass.append("  def __init__(self, attr1, attr2, attr3):\n");
38         testclass.append("    self.attr1 = attr1\n");
39         testclass.append("    self.attr2 = attr2\n");
40         testclass.append("    self.attr3 = attr3\n");
41         testclass.append("    self.selfattr = self\n");
42         return testclass.toString();
43     }
44 
testGetAttr(Interpreter interp)45     public static void testGetAttr(Interpreter interp) throws JepException {
46         interp.eval(buildTestClassPython());
47         interp.eval("t = testclass(1, 'A String', None)");
48         PyObject t = interp.getValue("t", PyObject.class);
49         Number attr1 = t.getAttr("attr1", Number.class);
50         if (attr1.intValue() != 1) {
51             throw new IllegalStateException("JPyObject attr1 lookup failed.");
52         }
53         Object attr2 = t.getAttr("attr2");
54         if (!attr2.equals("A String")) {
55             throw new IllegalStateException("JPyObject attr2 lookup failed.");
56         }
57         Object attr3 = t.getAttr("attr3");
58         if (attr3 != null) {
59             throw new IllegalStateException("JPyObject attr3 lookup failed.");
60         }
61         PyObject selfattr = t.getAttr("selfattr", PyObject.class);
62         interp.set("t2", selfattr);
63         Boolean b = interp.getValue("t is not t2", Boolean.class);
64         if (b.booleanValue()) {
65             throw new IllegalStateException(
66                     "JPyObject selfattr lookup failed.");
67         }
68 
69         try {
70             t.getAttr(null, PyObject.class);
71             throw new IllegalStateException("JPyObject null attr_name failed");
72         } catch (JepException e) {
73             /*
74              * We want an exception as Jep should not have allowed null. Python
75              * will crash when we use the CPython API if the attr_name is null.
76              */
77         }
78     }
79 
testSetAttr(Interpreter interp)80     public static void testSetAttr(Interpreter interp) throws JepException {
81         interp.eval(buildTestClassPython());
82         interp.eval("t = testclass(1, 'A String', None)");
83         PyObject t = interp.getValue("t", PyObject.class);
84 
85         t.setAttr("attr1", 5.5);
86         if (interp.getValue("t.attr1", Double.class) != 5.5) {
87             throw new IllegalStateException("JPyObject attr1 setting failed.");
88         }
89 
90         t.setAttr("attr2", "B String");
91         if (!interp.getValue("t.attr2", String.class).equals("B String")) {
92             throw new IllegalStateException("JPyObject attr2 setting failed.");
93         }
94 
95         t.setAttr("attr3", null);
96         if (!interp.getValue("t.attr3 is None", Boolean.class)) {
97             throw new IllegalStateException("JPyObject attr3 setting failed.");
98         }
99 
100         t.setAttr("attr4", "C String");
101         if (!interp.getValue("t.attr4", String.class).equals("C String")) {
102             throw new IllegalStateException("JPyObject attr4 setting failed.");
103         }
104 
105         try {
106             t.setAttr(null, "ABC");
107             throw new IllegalStateException("JPyObject null attr_name failed");
108         } catch (JepException e) {
109             /*
110              * We want an exception as Jep should not have allowed null. Python
111              * will crash when we use the CPython API if the attr_name is null.
112              */
113         }
114     }
115 
testDelAttr(Interpreter interp)116     public static void testDelAttr(Interpreter interp) throws JepException {
117         interp.eval(buildTestClassPython());
118         interp.eval("t = testclass(1, 'A String', None)");
119         PyObject t = interp.getValue("t", PyObject.class);
120 
121         t.delAttr("attr1");
122         if (interp.getValue("'attr1' in t.__dict__", Boolean.class)) {
123             throw new IllegalStateException("JPyObject attr1 deleting failed.");
124         }
125         try {
126             t.delAttr(null);
127             throw new IllegalStateException("JPyObject null attr_name failed");
128         } catch (JepException e) {
129             /*
130              * We want an exception as Jep should not have allowed null. Python
131              * will crash when we use the CPython API if the attr_name is null.
132              */
133         }
134     }
135 
testJPyCallable(Interpreter interp)136     public static void testJPyCallable(Interpreter interp) throws JepException {
137         PyCallable chr = interp.getValue("chr", PyCallable.class);
138         Object result = chr.call(32);
139         if (!" ".equals(result)) {
140             throw new IllegalStateException(
141                     "JPyCallable chr does not work as expected.");
142         }
143 
144         String typedResultStr = chr.callAs(String.class, 32);
145         if (!" ".equals(typedResultStr)) {
146             throw new IllegalStateException(
147                     "JPyCallable chr does not work as expected.");
148         }
149 
150         PyCallable count = interp.getValue("[1,2,1,4,1,4].count",
151                 PyCallable.class);
152         result = count.call(1);
153         if (((Number) result).intValue() != 3) {
154             throw new IllegalStateException(
155                     "JPyCallable list.count does not work as expected.");
156         }
157 
158         Long typedResultLong = count.callAs(Long.class, 4);
159         if (typedResultLong.intValue() != 2) {
160             throw new IllegalStateException(
161                     "JPyCallable list.count does not work as expected.");
162         }
163 
164         // test that the requested return type is actually respected - if not,
165         // this would return String instead of PyObject
166         PyCallable str = interp.getValue("str", PyCallable.class);
167         PyObject typedResultObj = str.callAs(PyObject.class, 12342);
168         count = typedResultObj.getAttr("count", PyCallable.class);
169         typedResultLong = count.callAs(Long.class, "2");
170 
171         if (typedResultLong.intValue() != 2) {
172             throw new IllegalStateException(
173                     "JPyCallable str.count does not work as expected.");
174         }
175 
176         typedResultLong = count.callAs(Long.class, "1");
177 
178         if (typedResultLong.intValue() != 1) {
179             throw new IllegalStateException(
180                     "JPyCallable str.count does not work as expected.");
181         }
182 
183     }
184 
testToString(Interpreter interp)185     public static void testToString(Interpreter interp) throws JepException {
186         PyObject pylist = interp.getValue("[1, 2, 3, 4, 5]", PyObject.class);
187         if (!"[1, 2, 3, 4, 5]".equals(pylist.toString())) {
188             throw new IllegalStateException(
189                     "JPyObject toString() does not work as expected");
190         }
191     }
192 
testEquals(Interpreter interp)193     public static void testEquals(Interpreter interp) throws JepException {
194         interp.eval("s = 'my python string'");
195         PyObject pystr = interp.getValue("s", PyObject.class);
196         if (pystr.equals("my python string")) {
197             throw new IllegalStateException(
198                     "JPyObject equals() does not work as expected");
199         }
200 
201         if (interp.getValue("1", PyObject.class).equals(1)) {
202             throw new IllegalStateException(
203                     "JPyObject equals() does not work as expected");
204         }
205 
206         if ((Integer.valueOf(1)).equals(interp.getValue("1", PyObject.class))) {
207             throw new IllegalStateException(
208                     "Java equals(JPyObject) does not work as expected");
209         }
210     }
211 
testHashCode(Interpreter interp)212     public static void testHashCode(Interpreter interp) throws JepException {
213         PyObject pyObj1 = interp.getValue("'xyz'", PyObject.class);
214         PyObject pyObj2 = interp.getValue("'xyz'", PyObject.class);
215         Integer hash1 = pyObj1.hashCode();
216         Integer hash2 = pyObj2.hashCode();
217         if (!hash1.equals(hash2)) {
218             throw new IllegalStateException(
219                     "JPyObject hashCode() does not work as expected");
220         }
221 
222         PyObject pyObj3 = interp.getValue("45", PyObject.class);
223         PyObject pyObj4 = interp.getValue("44 + 1", PyObject.class);
224         Integer hash3 = pyObj3.hashCode();
225         Integer hash4 = pyObj4.hashCode();
226         if (!hash3.equals(hash4)) {
227             throw new IllegalStateException(
228                     "JPyObject hashCode() does not work as expected");
229         }
230     }
231 
testThreading(Interpreter interp)232     public static void testThreading(Interpreter interp)
233             throws JepException, InterruptedException {
234         final PyObject o = interp.getValue("object()", PyObject.class);
235         final boolean[] exception = { false };
236         Thread t = new Thread() {
237             @Override
238             public void run() {
239                 try {
240                     o.getAttr("__doc__");
241                 } catch (JepException e) {
242                     exception[0] = true;
243                 }
244             }
245         };
246         t.start();
247         t.join();
248         if (!exception[0]) {
249             throw new IllegalStateException(
250                     "JPyObject allowed access on wrong thread");
251 
252         }
253     }
254 
testClosing(Interpreter interp)255     public static void testClosing(Interpreter interp) throws JepException {
256         PyObject leaky = null;
257         try (PyObject o = interp.getValue("object()", PyObject.class)) {
258             leaky = o;
259             /*
260              * The AutoCloseable interface strongly recommends making it
261              * harmless to call close more than once.
262              */
263             o.close();
264         }
265         try {
266             leaky.getAttr("__doc__");
267             throw new IllegalStateException(
268                     "JPyObject allowed access on wrong thread");
269         } catch (JepException e) {
270             /*
271              * We want an exception as Jep should not have allowed access.
272              */
273         }
274     }
275 
testClosingJep()276     public static void testClosingJep() throws JepException {
277         PyObject leaky = null;
278         try (Interpreter interp = new SubInterpreter()) {
279             leaky = interp.getValue("object()", PyObject.class);
280         }
281         try {
282             leaky.getAttr("__doc__");
283             throw new IllegalStateException(
284                     "JPyObject allowed access on wrong thread");
285         } catch (JepException e) {
286             /*
287              * We want an exception as Jep should not have allowed access.
288              */
289         }
290     }
291 
292     private static interface PyInt {
bit_length()293         public Byte bit_length();
294     }
295 
296     private static interface PyInt2 {
bit_length()297         public byte bit_length();
298     }
299 
testProxy(Interpreter interp)300     public static void testProxy(Interpreter interp) throws JepException {
301         interp.eval("l = [7]");
302         PyObject list = interp.getValue("l", PyObject.class);
303         @SuppressWarnings("unchecked")
304         Deque<Number> q = list.proxy(Deque.class);
305         Number n = q.pop();
306         if (n.intValue() != 7) {
307             throw new IllegalStateException("list.pop returned wrong value");
308         }
309         /* Make sure it is empty now */
310         Boolean b = interp.getValue("len(l) == 0", Boolean.class);
311         if (!b.booleanValue()) {
312             throw new IllegalStateException("list.pop proxy failed");
313         }
314         try {
315             q.push(n);
316             throw new IllegalStateException("list.push worked");
317         } catch (UndeclaredThrowableException e) {
318             /* list doesn't have a push so this is correct */
319         }
320 
321         PyInt i = interp.getValue("1", PyObject.class).proxy(PyInt.class);
322         Byte bit_length = i.bit_length();
323         if (bit_length.intValue() != 1) {
324             throw new IllegalStateException(
325                     "bit_length boxed is wrong: " + bit_length);
326         }
327         PyInt2 i2 = interp.getValue("1", PyObject.class).proxy(PyInt2.class);
328         bit_length = i2.bit_length();
329         if (bit_length.intValue() != 1) {
330             throw new IllegalStateException(
331                     "bit_length primitive is wrong: " + bit_length);
332         }
333 
334         interp.set("i", i);
335         bit_length = interp.getValue("int.bit_length(i)", Byte.class);
336         if (bit_length.intValue() != 1) {
337             throw new IllegalStateException(
338                     "bit_length passback is wrong: " + bit_length);
339         }
340     }
341 
testAs(Interpreter interp)342     public static void testAs(Interpreter interp) throws JepException {
343         int[] test = { 1, 2, 3 };
344         interp.set("test", test);
345         PyObject list = interp.getValue("list(test)", PyObject.class);
346         if (!Arrays.equals(test, list.as(int[].class))) {
347             throw new IllegalStateException("PyObject.as did not work.");
348         }
349     }
350 
main(String[] args)351     public static void main(String[] args) throws Exception {
352         try (Interpreter interp = new SubInterpreter()) {
353             testIdentity(interp);
354             testGetAttr(interp);
355             testSetAttr(interp);
356             testDelAttr(interp);
357             testJPyCallable(interp);
358             testToString(interp);
359             testEquals(interp);
360             testHashCode(interp);
361             testThreading(interp);
362             testClosing(interp);
363             testProxy(interp);
364             testAs(interp);
365         }
366         testClosingJep();
367     }
368 
369 }
370