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