1 module python.object_; 2 3 4 struct PythonObject { 5 import python.raw: PyObject; 6 import std.traits: Unqual; 7 8 private PyObject* _obj; 9 10 this(T)(auto ref T value) if(!is(Unqual!T == PyObject*)) { 11 import python.conv.d_to_python: toPython; 12 _obj = value.toPython; 13 } 14 15 // can only be used on Python C API calls that create a new PyObject* 16 // due to reference count issues 17 private this(PyObject* obj) { 18 _obj = obj; 19 } 20 21 // FIXME destructor should dec ref 22 23 PythonObject str() const { 24 return retPyObject!("PyObject_Str"); 25 } 26 27 PythonObject repr() const { 28 return retPyObject!("PyObject_Repr"); 29 } 30 31 PythonObject bytes() const { 32 return retPyObject!("PyObject_Bytes"); 33 } 34 35 PythonObject type() const { 36 return retPyObject!("PyObject_Type"); 37 } 38 39 PythonObject dir() const { 40 return retPyObject!("PyObject_Dir"); 41 } 42 43 auto hash() const { 44 return retDirect!"PyObject_Hash"; 45 } 46 47 auto len() const { 48 return retDirect!"PyObject_Length"; 49 } 50 alias length = len; 51 52 bool not() const { 53 return cast(bool) retDirect!"PyObject_Not"; 54 } 55 56 bool hasattr(in string attr) const { 57 import std..string: toStringz; 58 return cast(bool) retDirect!"PyObject_HasAttrString"(attr.toStringz); 59 } 60 61 bool hasattr(in PythonObject attr) const { 62 return cast(bool) retDirect!"PyObject_HasAttr"(cast(PyObject*) attr._obj); 63 } 64 65 PythonObject getattr(in string attr) const { 66 import std..string: toStringz; 67 return retPyObject!"PyObject_GetAttrString"(attr.toStringz); 68 } 69 70 PythonObject getattr(in PythonObject attr) const { 71 return retPyObject!"PyObject_GetAttr"(cast(PyObject*) attr._obj); 72 } 73 74 void setattr(T)(in string attr, auto ref T val) if(!is(Unqual!T == PythonObject)) { 75 setattr(attr, PythonObject(val)); 76 } 77 78 void setattr(in string attr, in PythonObject val) { 79 import python.raw: PyObject_SetAttrString; 80 import python.exception: PythonException; 81 import std..string: toStringz; 82 83 const res = PyObject_SetAttrString(cast(PyObject*) _obj, attr.toStringz, cast(PyObject*) val._obj); 84 if(res == -1) throw new PythonException("Error setting attribute " ~ attr); 85 } 86 87 void setattr(T)(in PythonObject attr, auto ref T val) if(!is(Unqual!T == PythonObject)) { 88 setattr(attr, PythonObject(val)); 89 } 90 91 void setattr(in PythonObject attr, in PythonObject val) { 92 import python.raw: PyObject_SetAttr; 93 import python.exception: PythonException; 94 95 const res = PyObject_SetAttr(cast(PyObject*) _obj, cast(PyObject*) attr._obj, cast(PyObject*) val._obj); 96 if(res == -1) throw new PythonException("Error setting attribute "); 97 98 } 99 100 void delattr(in string attr) { 101 import python.raw: PyObject_SetAttrString; 102 import python.exception: PythonException; 103 import std..string: toStringz; 104 105 const res = PyObject_SetAttrString(cast(PyObject*) _obj, attr.toStringz, null); 106 if(res == -1) throw new PythonException("Error setting attribute " ~ attr); 107 } 108 109 void delattr(in PythonObject attr) { 110 import python.raw: PyObject_SetAttr; 111 import python.exception: PythonException; 112 113 const res = PyObject_SetAttr(cast(PyObject*) _obj, cast(PyObject*) attr._obj, null); 114 if(res == -1) throw new PythonException("Error setting attribute "); 115 } 116 117 T to(T)() const { 118 import python.conv.python_to_d: to; 119 return (cast(PyObject*) _obj).to!T; 120 } 121 122 string toString() const { 123 import python.raw: PyObject_Str; 124 import python.conv.python_to_d: to; 125 return PyObject_Str(cast(PyObject*) _obj).to!string; 126 } 127 128 bool callable() const { 129 return retDirect!"pyCallableCheck"; 130 } 131 132 void del() { 133 del(0, len); 134 } 135 136 void del(in size_t idx) { 137 retDirect!"PySequence_DelItem"(idx); 138 } 139 140 void del(in size_t i0, in size_t i1) { 141 retDirect!"PySequence_DelSlice"(i0, i1); 142 } 143 144 void del(in string key) { 145 import std..string: toStringz; 146 retDirect!"PyObject_DelItemString"(key.toStringz); 147 } 148 149 void del(in PythonObject key) { 150 retDirect!"PyObject_DelItem"(cast(PyObject*) key._obj); 151 } 152 153 bool isInstance(in PythonObject klass) const { 154 return cast(bool) retDirect!"PyObject_IsInstance"(cast(PyObject*) klass._obj); 155 } 156 157 bool isSubClass(in PythonObject klass) const { 158 return cast(bool) retDirect!"PyObject_IsSubclass"(cast(PyObject*) klass._obj); 159 } 160 161 InputRange range() { 162 return InputRange(iter); 163 } 164 165 PythonObject iter() const { 166 return retPyObject!"PyObject_GetIter"(); 167 } 168 169 PythonObject next() const { 170 import python.raw: PyIter_Next; 171 return PythonObject(PyIter_Next(cast(PyObject*) _obj)); 172 } 173 174 PythonObject keys() const { 175 import python.raw: pyDictCheck, PyMapping_Keys; 176 if(pyDictCheck(cast(PyObject*) _obj)) 177 return retPyObject!"PyDict_Keys"(); 178 else if(auto keys = PyMapping_Keys(cast(PyObject*) _obj)) 179 return PythonObject(keys); 180 else 181 return getattr("keys"); 182 } 183 184 PythonObject values() const { 185 import python.raw: pyDictCheck, PyMapping_Values; 186 if(pyDictCheck(cast(PyObject*) _obj)) 187 return retPyObject!"PyDict_Values"(); 188 else if(auto keys = PyMapping_Values(cast(PyObject*) _obj)) 189 return PythonObject(keys); 190 else 191 return getattr("values"); 192 } 193 194 PythonObject items() const { 195 import python.raw: pyDictCheck, PyMapping_Items; 196 if(pyDictCheck(cast(PyObject*) _obj)) 197 return retPyObject!"PyDict_Items"(); 198 else if(auto keys = PyMapping_Items(cast(PyObject*) _obj)) 199 return PythonObject(keys); 200 else 201 return getattr("items"); 202 } 203 204 PythonObject copy() const { 205 import python.raw: pyDictCheck; 206 if(pyDictCheck(cast(PyObject*) _obj)) 207 return retPyObject!"PyDict_Copy"(); 208 else 209 return getattr("copy"); 210 } 211 212 bool merge(in PythonObject other, bool override_ = true) { 213 import python.raw: pyDictCheck; 214 import python.exception: PythonException; 215 216 if(pyDictCheck(_obj)) 217 return cast(bool) retDirect!"PyDict_Merge"(cast(PyObject*) other._obj, cast(int) override_); 218 else 219 throw new PythonException("Cannot merge a non-dict"); 220 } 221 222 int opCmp(in PythonObject other) const { 223 import python.raw: PyObject_RichCompareBool, Py_LT, Py_EQ, Py_GT; 224 import python.exception: PythonException; 225 226 static int[int] pyOpToRet; 227 if(pyOpToRet == pyOpToRet.init) 228 pyOpToRet = [Py_LT: -1, Py_EQ: 0, Py_GT: 1]; 229 230 foreach(pyOp, ret; pyOpToRet) { 231 const pyRes = PyObject_RichCompareBool( 232 cast(PyObject*) _obj, 233 cast(PyObject*) other._obj, 234 pyOp 235 ); 236 237 if(pyRes == -1) 238 throw new PythonException("Error comparing Python objects"); 239 240 if(pyRes == 1) 241 return ret; 242 } 243 244 assert(0); 245 } 246 247 PythonObject opDispatch(string identifier, A...)(auto ref A args) inout { 248 import python.exception: PythonException; 249 250 auto value = getattr(identifier); 251 252 if(value.callable) { 253 return value(args); 254 } else { 255 if(args.length > 0) 256 throw new PythonException("`" ~ identifier ~ "`" ~ " is not a callable"); 257 return value; 258 } 259 } 260 261 import std.meta: anySatisfy, allSatisfy; 262 private enum isPythonObject(T) = is(Unqual!T == PythonObject); 263 264 PythonObject opCall(A...)(auto ref A args) if(!anySatisfy!(isPythonObject, A)) { 265 import python.raw: PyTuple_New, PyTuple_SetItem, PyObject_CallObject, pyDecRef; 266 import python.conv.d_to_python: toPython; 267 import python.exception: PythonException; 268 269 auto pyArgs = PyTuple_New(args.length); 270 scope(exit) pyDecRef(pyArgs); 271 272 static foreach(i; 0 .. args.length) { 273 PyTuple_SetItem(pyArgs, i, args[i].toPython); 274 } 275 276 auto ret = PyObject_CallObject(_obj, pyArgs); 277 if(ret is null) throw new PythonException("Could not call callable"); 278 279 return PythonObject(ret); 280 } 281 282 PythonObject opCall(PythonObject args) { 283 import python.raw: PyObject_CallObject; 284 import python.exception: PythonException; 285 286 auto ret = PyObject_CallObject(_obj, args._obj); 287 if(ret is null) throw new PythonException("Could not call callable"); 288 289 return PythonObject(ret); 290 } 291 292 PythonObject opCall(PythonObject args, PythonObject kwargs) { 293 import python.raw: PyObject_Call; 294 import python.exception: PythonException; 295 296 auto ret = PyObject_Call(_obj, args._obj, kwargs._obj); 297 if(ret is null) throw new PythonException("Could not call callable"); 298 299 return PythonObject(ret); 300 } 301 302 PythonObject opIndex(in size_t idx) const { 303 return retPyObject!"PySequence_GetItem"(idx); 304 } 305 306 PythonObject opIndex(in string key) const { 307 import python.raw: pyDictCheck; 308 import std..string: toStringz; 309 310 const keyz = key.toStringz; 311 312 if(pyDictCheck(cast(PyObject*) _obj)) 313 return retPyObject!"PyDict_GetItemString"(keyz); 314 else 315 return retPyObject!"PyMapping_GetItemString"(keyz); 316 } 317 318 PythonObject opIndex(in PythonObject key) const { 319 import std..string: toStringz; 320 return retPyObject!"PyObject_GetItem"(cast(PyObject*) key._obj); 321 } 322 323 void opIndexAssign(K, V)(auto ref V _value, auto ref K _key) { 324 325 import std.traits: isIntegral; 326 327 static if(isPythonObject!V) 328 alias value = _value; 329 else 330 auto value = PythonObject(_value); 331 332 static if(isIntegral!K) { 333 retPyObject!"PySequence_SetItem"( 334 _key, 335 cast(PyObject*) value._obj, 336 ); 337 } else { 338 339 static if(isPythonObject!K) 340 alias key = _key; 341 else 342 auto key = PythonObject(_key); 343 344 retPyObject!"PyObject_SetItem"( 345 cast(PyObject*) key._obj, 346 cast(PyObject*) value._obj, 347 ); 348 } 349 } 350 351 PythonObject opSlice(size_t i0, size_t i1) const { 352 return retPyObject!"PySequence_GetSlice"(i0, i1); 353 } 354 355 PythonObject opSlice() const { 356 return this[0 .. len]; 357 } 358 359 void opSliceAssign(in PythonObject val, in size_t i0, in size_t i1) { 360 retDirect!"PySequence_SetSlice"(i0, i1, cast(PyObject*) val._obj); 361 } 362 363 void opSliceAssign(in PythonObject val) { 364 retDirect!"PySequence_SetSlice"(0, len, cast(PyObject*) val._obj); 365 } 366 367 PythonObject opBinary(string op)(PythonObject other) if(op == "+") { 368 return retPyObject!"PyNumber_Add"(other._obj); 369 } 370 371 PythonObject opBinary(string op)(PythonObject other) if(op == "-") { 372 return retPyObject!"PyNumber_Subtract"(other._obj); 373 } 374 375 PythonObject opBinary(string op)(PythonObject other) if(op == "*") { 376 return retPyObject!"PyNumber_Multiply"(other._obj); 377 } 378 379 PythonObject opBinary(string op)(int i) if(op == "*") { 380 return retPyObject!"PySequence_Repeat"(i); 381 } 382 383 PythonObject opBinary(string op)(PythonObject other) if(op == "/") { 384 return retPyObject!"PyNumber_TrueDivide"(other._obj); 385 } 386 387 PythonObject opBinary(string op)(PythonObject other) if(op == "%") { 388 return retPyObject!"PyNumber_Remainder"(other._obj); 389 } 390 391 PythonObject opBinary(string op)(PythonObject other) if(op == "^^") { 392 import python.raw: pyIncRef, pyNone; 393 pyIncRef(pyNone); 394 return retPyObject!"PyNumber_Power"(other._obj, pyNone); 395 } 396 397 PythonObject opBinary(string op)(PythonObject other) if(op == "<<") { 398 return retPyObject!"PyNumber_Lshift"(other._obj); 399 } 400 401 PythonObject opBinary(string op)(PythonObject other) if(op == ">>") { 402 return retPyObject!"PyNumber_Rshift"(other._obj); 403 } 404 405 PythonObject opBinary(string op)(PythonObject other) if(op == "&") { 406 return retPyObject!"PyNumber_And"(other._obj); 407 } 408 409 PythonObject opBinary(string op)(PythonObject other) if(op == "|") { 410 return retPyObject!"PyNumber_Or"(other._obj); 411 } 412 413 PythonObject opBinary(string op)(PythonObject other) if(op == "^") { 414 return retPyObject!"PyNumber_Xor"(other._obj); 415 } 416 417 PythonObject opBinary(string op)(PythonObject other) if(op == "~") { 418 return retPyObject!"PySequence_Concat"(other._obj); 419 } 420 421 PythonObject opUnary(string op)() if(op == "+") { 422 return retPyObject!"PyNumber_Positive"(); 423 } 424 425 PythonObject opUnary(string op)() if(op == "-") { 426 return retPyObject!"PyNumber_Negative"(); 427 } 428 429 PythonObject opUnary(string op)() if(op == "~") { 430 return retPyObject!"PyNumber_Invert"(); 431 } 432 433 bool opBinaryRight(string op)(in PythonObject key) if(op == "in") { 434 return cast(bool) retDirect!"PySequence_Contains"(cast(PyObject*) key._obj); 435 } 436 437 bool opBinaryRight(string op, T)(auto ref T key) if(op == "in" && !is(Unqual!T == PythonObject)) { 438 return cast(bool) retDirect!"PySequence_Contains"(cast(PyObject*) PythonObject(key)._obj); 439 } 440 441 private: 442 443 PythonObject retPyObject(string funcName, A...)(auto ref A args) const { 444 import std.format: format; 445 446 enum code = q{ 447 448 import python.exception: PythonException; 449 import python.raw: %s; 450 import std.traits: isPointer; 451 452 auto obj = %s(cast(PyObject*) _obj, args); 453 static if(isPointer!(typeof(obj))) 454 if(obj is null) throw new PythonException("%s returned null"); 455 456 return PythonObject(obj); 457 458 }.format(funcName, funcName, funcName); 459 460 mixin(code); 461 } 462 463 auto retDirect(string cApiFunc, A...)(auto ref A args) const { 464 465 import std.format: format; 466 467 enum code = q{ 468 469 import python.exception: PythonException; 470 import python.raw: %s; 471 472 const ret = %s(cast(PyObject*) _obj, args); 473 if(ret == -1) 474 throw new PythonException("Could not call %s"); 475 476 return ret; 477 478 }.format(cApiFunc, cApiFunc, cApiFunc); 479 480 mixin(code); 481 } 482 } 483 484 485 struct InputRange { 486 import python.raw: PyObject; 487 488 private PythonObject _iter; 489 private PythonObject _front; 490 491 private this(PythonObject iter) { 492 import python.raw: PyObject_GetIter; 493 import python.exception: PythonException; 494 495 _iter._obj = iter._obj; 496 if (_iter._obj is null) 497 throw new PythonException("No iterable for object"); 498 499 popFront; 500 } 501 502 // FIXME 503 // ~this() { 504 // import python.raw: pyDecRef; 505 // pyDecRef(_iter); 506 // } 507 508 void popFront() { 509 _front = _iter.next; 510 } 511 512 bool empty() const { 513 return _front._obj is null; 514 } 515 516 auto front() inout { 517 return _front; 518 } 519 }