Blender V2.61 - r43446
|
00001 /* 00002 * ***** BEGIN GPL LICENSE BLOCK ***** 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License 00006 * as published by the Free Software Foundation; either version 2 00007 * of the License, or (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software Foundation, 00016 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 * 00018 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. 00019 * All rights reserved. 00020 * 00021 * The Original Code is: all of this file. 00022 * 00023 * Contributor(s): none yet. 00024 * 00025 * ***** END GPL LICENSE BLOCK ***** 00026 */ 00027 00033 /*------------------------------ 00034 * PyObjectPlus cpp 00035 * 00036 * C++ library routines for Crawl 3.2 00037 * 00038 * Derived from work by 00039 * David Redish 00040 * graduate student 00041 * Computer Science Department 00042 * Carnegie Mellon University (CMU) 00043 * Center for the Neural Basis of Cognition (CNBC) 00044 * http://www.python.org/doc/PyCPP.html 00045 * 00046 ------------------------------*/ 00047 #include <stdlib.h> 00048 #include <stddef.h> 00049 00050 #include "PyObjectPlus.h" 00051 #include "STR_String.h" 00052 #include "MT_Vector3.h" 00053 #include "MEM_guardedalloc.h" 00054 00055 PyObjectPlus::~PyObjectPlus() 00056 { 00057 #ifdef WITH_PYTHON 00058 if(m_proxy) { 00059 BGE_PROXY_REF(m_proxy)= NULL; 00060 Py_DECREF(m_proxy); /* Remove own reference, python may still have 1 */ 00061 } 00062 // assert(ob_refcnt==0); 00063 #endif 00064 } 00065 00066 PyObjectPlus::PyObjectPlus() : SG_QList() // constructor 00067 { 00068 #ifdef WITH_PYTHON 00069 m_proxy= NULL; 00070 #endif 00071 }; 00072 00073 void PyObjectPlus::ProcessReplica() 00074 { 00075 #ifdef WITH_PYTHON 00076 /* Clear the proxy, will be created again if needed with GetProxy() 00077 * otherwise the PyObject will point to the wrong reference */ 00078 m_proxy= NULL; 00079 #endif 00080 } 00081 00082 /* Sometimes we might want to manually invalidate a BGE type even if 00083 * it hasnt been released by the BGE, say for example when an object 00084 * is removed from a scene, accessing it may cause problems. 00085 * 00086 * In this case the current proxy is made invalid, disowned, 00087 * and will raise an error on access. However if python can get access 00088 * to this class again it will make a new proxy and work as expected. 00089 */ 00090 void PyObjectPlus::InvalidateProxy() // check typename of each parent 00091 { 00092 #ifdef WITH_PYTHON 00093 if(m_proxy) { 00094 BGE_PROXY_REF(m_proxy)=NULL; 00095 Py_DECREF(m_proxy); 00096 m_proxy= NULL; 00097 } 00098 #endif 00099 } 00100 00101 00102 #ifdef WITH_PYTHON 00103 00104 /*------------------------------ 00105 * PyObjectPlus Type -- Every class, even the abstract one should have a Type 00106 ------------------------------*/ 00107 00108 00109 PyTypeObject PyObjectPlus::Type = { 00110 PyVarObject_HEAD_INIT(NULL, 0) 00111 "PyObjectPlus", /*tp_name*/ 00112 sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/ 00113 0, /*tp_itemsize*/ 00114 /* methods */ 00115 py_base_dealloc, /* tp_dealloc */ 00116 0, /* printfunc tp_print; */ 00117 0, /* getattrfunc tp_getattr; */ 00118 0, /* setattrfunc tp_setattr; */ 00119 0, /* tp_compare */ /* DEPRECATED in python 3.0! */ 00120 py_base_repr, /* tp_repr */ 00121 0,0,0,0,0,0,0,0,0, /* Method suites for standard classes */ 00122 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/* long tp_flags; */ 00123 0,0,0,0, 00124 /* weak reference enabler */ 00125 #ifdef USE_WEAKREFS 00126 offsetof(PyObjectPlus_Proxy, in_weakreflist), /* long tp_weaklistoffset; */ 00127 #else 00128 0, 00129 #endif 00130 0,0, 00131 Methods, 00132 0, 00133 0, 00134 NULL // no subtype 00135 }; 00136 00137 PyObject *PyObjectPlus::py_base_repr(PyObject *self) // This should be the entry in Type. 00138 { 00139 PyObjectPlus *self_plus= BGE_PROXY_REF(self); 00140 if(self_plus==NULL) { 00141 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); 00142 return NULL; 00143 } 00144 return self_plus->py_repr(); 00145 } 00146 00147 00148 PyObject * PyObjectPlus::py_base_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 00149 { 00150 PyTypeObject *base_type; 00151 PyObjectPlus_Proxy *base = NULL; 00152 00153 if (!PyArg_ParseTuple(args, "O:Base PyObjectPlus", &base)) 00154 return NULL; 00155 00156 /* the 'base' PyObject may be subclassed (multiple times even) 00157 * we need to find the first C++ defined class to check 'type' 00158 * is a subclass of the base arguments type. 00159 * 00160 * This way we can share one tp_new function for every PyObjectPlus 00161 * 00162 * eg. 00163 * 00164 * # CustomOb is called 'type' in this C code 00165 * class CustomOb(GameTypes.KX_GameObject): 00166 * pass 00167 * 00168 * # this calls py_base_new(...), the type of 'CustomOb' is checked to be a subclass of the 'cont.owner' type 00169 * ob = CustomOb(cont.owner) 00170 * 00171 * */ 00172 base_type= Py_TYPE(base); 00173 while(base_type && !BGE_PROXY_CHECK_TYPE(base_type)) 00174 base_type= base_type->tp_base; 00175 00176 if(base_type==NULL || !BGE_PROXY_CHECK_TYPE(base_type)) { 00177 PyErr_SetString(PyExc_TypeError, "can't subclass from a blender game type because the argument given is not a game class or subclass"); 00178 return NULL; 00179 } 00180 00181 /* use base_type rather than Py_TYPE(base) because we could already be subtyped */ 00182 if(!PyType_IsSubtype(type, base_type)) { 00183 PyErr_Format(PyExc_TypeError, "can't subclass blender game type <%s> from <%s> because it is not a subclass", base_type->tp_name, type->tp_name); 00184 return NULL; 00185 } 00186 00187 /* invalidate the existing base and return a new subclassed one, 00188 * this is a bit dodgy in that it also attaches its self to the existing object 00189 * which is not really 'correct' python OO but for our use its OK. */ 00190 00191 PyObjectPlus_Proxy *ret = (PyObjectPlus_Proxy *) type->tp_alloc(type, 0); /* starts with 1 ref, used for the return ref' */ 00192 ret->ref= base->ref; 00193 ret->ptr= base->ptr; 00194 ret->py_owns= base->py_owns; 00195 ret->py_ref = base->py_ref; 00196 00197 if (ret->py_ref) { 00198 base->ref= NULL; /* invalidate! disallow further access */ 00199 base->ptr = NULL; 00200 if (ret->ref) 00201 ret->ref->m_proxy= NULL; 00202 /* 'base' may be free'd after this func finished but not necessarily 00203 * there is no reference to the BGE data now so it will throw an error on access */ 00204 Py_DECREF(base); 00205 if (ret->ref) { 00206 ret->ref->m_proxy= (PyObject *)ret; /* no need to add a ref because one is added when creating. */ 00207 Py_INCREF(ret); /* we return a new ref but m_proxy holds a ref so we need to add one */ 00208 } 00209 } else { 00210 // generic structures don't hold a reference to this proxy, so don't increment ref count 00211 if (ret->py_owns) 00212 // but if the proxy owns the structure, there can be only one owner 00213 base->ptr= NULL; 00214 } 00215 00216 return (PyObject *)ret; 00217 } 00218 00222 void PyObjectPlus::py_base_dealloc(PyObject *self) // python wrapper 00223 { 00224 #ifdef USE_WEAKREFS 00225 if (BGE_PROXY_WKREF(self) != NULL) 00226 PyObject_ClearWeakRefs((PyObject *) self); 00227 #endif 00228 00229 if (BGE_PROXY_PYREF(self)) { 00230 PyObjectPlus *self_plus= BGE_PROXY_REF(self); 00231 if(self_plus) { 00232 if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */ 00233 self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */ 00234 delete self_plus; 00235 } 00236 BGE_PROXY_REF(self)= NULL; // not really needed 00237 } 00238 // the generic pointer is not deleted directly, only through self_plus 00239 BGE_PROXY_PTR(self)= NULL; // not really needed 00240 } else { 00241 void *ptr= BGE_PROXY_PTR(self); 00242 if(ptr) { 00243 if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */ 00244 // generic structure owned by python MUST be created though MEM_alloc 00245 MEM_freeN(ptr); 00246 } 00247 BGE_PROXY_PTR(self)= NULL; // not really needed 00248 } 00249 } 00250 #if 0 00251 /* is ok normally but not for subtyping, use tp_free instead. */ 00252 PyObject_DEL( self ); 00253 #else 00254 Py_TYPE(self)->tp_free(self); 00255 #endif 00256 }; 00257 00258 /*------------------------------ 00259 * PyObjectPlus Methods -- Every class, even the abstract one should have a Methods 00260 ------------------------------*/ 00261 PyMethodDef PyObjectPlus::Methods[] = { 00262 {NULL, NULL} /* Sentinel */ 00263 }; 00264 00265 #define attr_invalid (&(PyObjectPlus::Attributes[0])) 00266 PyAttributeDef PyObjectPlus::Attributes[] = { 00267 KX_PYATTRIBUTE_RO_FUNCTION("invalid", PyObjectPlus, pyattr_get_invalid), 00268 {NULL} //Sentinel 00269 }; 00270 00271 00272 00273 PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) 00274 { 00275 return PyBool_FromLong(self_v ? 0:1); 00276 } 00277 00278 /* note, this is called as a python 'getset, where the PyAttributeDef is the closure */ 00279 PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *attrdef) 00280 { 00281 PyObjectPlus *ref= (BGE_PROXY_REF(self_py)); 00282 char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref; 00283 if(ptr == NULL || (BGE_PROXY_PYREF(self_py) && (ref==NULL || !ref->py_is_valid()))) { 00284 if(attrdef == attr_invalid) 00285 Py_RETURN_TRUE; // dont bother running the function 00286 00287 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); 00288 return NULL; 00289 } 00290 00291 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY) 00292 { 00293 // fake attribute, ignore 00294 return NULL; 00295 } 00296 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION) 00297 { 00298 // the attribute has no field correspondance, handover processing to function. 00299 if (attrdef->m_getFunction == NULL) 00300 return NULL; 00301 return (*attrdef->m_getFunction)(ptr, attrdef); 00302 } 00303 ptr += attrdef->m_offset; 00304 if (attrdef->m_length > 1) 00305 { 00306 PyObject* resultlist = PyList_New(attrdef->m_length); 00307 for (unsigned int i=0; i<attrdef->m_length; i++) 00308 { 00309 switch (attrdef->m_type) { 00310 case KX_PYATTRIBUTE_TYPE_BOOL: 00311 { 00312 bool *val = reinterpret_cast<bool*>(ptr); 00313 ptr += sizeof(bool); 00314 PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val)); 00315 break; 00316 } 00317 case KX_PYATTRIBUTE_TYPE_SHORT: 00318 { 00319 short int *val = reinterpret_cast<short int*>(ptr); 00320 ptr += sizeof(short int); 00321 PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val)); 00322 break; 00323 } 00324 case KX_PYATTRIBUTE_TYPE_ENUM: 00325 // enum are like int, just make sure the field size is the same 00326 if (sizeof(int) != attrdef->m_size) 00327 { 00328 Py_DECREF(resultlist); 00329 return NULL; 00330 } 00331 // walkthrough 00332 case KX_PYATTRIBUTE_TYPE_INT: 00333 { 00334 int *val = reinterpret_cast<int*>(ptr); 00335 ptr += sizeof(int); 00336 PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val)); 00337 break; 00338 } 00339 case KX_PYATTRIBUTE_TYPE_FLOAT: 00340 { 00341 float *val = reinterpret_cast<float*>(ptr); 00342 ptr += sizeof(float); 00343 PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(*val)); 00344 break; 00345 } 00346 default: 00347 // no support for array of complex data 00348 Py_DECREF(resultlist); 00349 return NULL; 00350 } 00351 } 00352 return resultlist; 00353 } 00354 else 00355 { 00356 switch (attrdef->m_type) { 00357 case KX_PYATTRIBUTE_TYPE_FLAG: 00358 { 00359 bool bval; 00360 switch (attrdef->m_size) { 00361 case 1: 00362 { 00363 unsigned char *val = reinterpret_cast<unsigned char*>(ptr); 00364 bval = (*val & attrdef->m_imin); 00365 break; 00366 } 00367 case 2: 00368 { 00369 unsigned short *val = reinterpret_cast<unsigned short*>(ptr); 00370 bval = (*val & attrdef->m_imin); 00371 break; 00372 } 00373 case 4: 00374 { 00375 unsigned int *val = reinterpret_cast<unsigned int*>(ptr); 00376 bval = (*val & attrdef->m_imin); 00377 break; 00378 } 00379 default: 00380 return NULL; 00381 } 00382 if (attrdef->m_imax) 00383 bval = !bval; 00384 return PyLong_FromSsize_t(bval); 00385 } 00386 case KX_PYATTRIBUTE_TYPE_BOOL: 00387 { 00388 bool *val = reinterpret_cast<bool*>(ptr); 00389 return PyLong_FromSsize_t(*val); 00390 } 00391 case KX_PYATTRIBUTE_TYPE_SHORT: 00392 { 00393 short int *val = reinterpret_cast<short int*>(ptr); 00394 return PyLong_FromSsize_t(*val); 00395 } 00396 case KX_PYATTRIBUTE_TYPE_ENUM: 00397 // enum are like int, just make sure the field size is the same 00398 if (sizeof(int) != attrdef->m_size) 00399 { 00400 return NULL; 00401 } 00402 // walkthrough 00403 case KX_PYATTRIBUTE_TYPE_INT: 00404 { 00405 int *val = reinterpret_cast<int*>(ptr); 00406 return PyLong_FromSsize_t(*val); 00407 } 00408 case KX_PYATTRIBUTE_TYPE_FLOAT: 00409 { 00410 float *val = reinterpret_cast<float*>(ptr); 00411 if (attrdef->m_imin == 0) { 00412 if (attrdef->m_imax == 0) { 00413 return PyFloat_FromDouble(*val); 00414 } else { 00415 // vector, verify size 00416 if (attrdef->m_size != attrdef->m_imax*sizeof(float)) 00417 { 00418 return NULL; 00419 } 00420 #ifdef USE_MATHUTILS 00421 return Vector_CreatePyObject(val, attrdef->m_imax, Py_NEW, NULL); 00422 #else 00423 PyObject* resultlist = PyList_New(attrdef->m_imax); 00424 for (unsigned int i=0; i<attrdef->m_imax; i++) 00425 { 00426 PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(val[i])); 00427 } 00428 return resultlist; 00429 #endif 00430 } 00431 } else { 00432 // matrix case 00433 if (attrdef->m_size != attrdef->m_imax*attrdef->m_imin*sizeof(float)) 00434 { 00435 return NULL; 00436 } 00437 #ifdef USE_MATHUTILS 00438 return Matrix_CreatePyObject(val, attrdef->m_imin, attrdef->m_imax, Py_WRAP, NULL); 00439 #else 00440 PyObject* collist = PyList_New(attrdef->m_imin); 00441 for (unsigned int i=0; i<attrdef->m_imin; i++) 00442 { 00443 PyObject* col = PyList_New(attrdef->m_imax); 00444 for (unsigned int j=0; j<attrdef->m_imax; j++) 00445 { 00446 PyList_SET_ITEM(col,j,PyFloat_FromDouble(val[j])); 00447 } 00448 PyList_SET_ITEM(collist,i,col); 00449 val += attrdef->m_imax; 00450 } 00451 return collist; 00452 #endif 00453 } 00454 } 00455 case KX_PYATTRIBUTE_TYPE_VECTOR: 00456 { 00457 MT_Vector3 *val = reinterpret_cast<MT_Vector3*>(ptr); 00458 #ifdef USE_MATHUTILS 00459 float fval[3]= {(*val)[0], (*val)[1], (*val)[2]}; 00460 return Vector_CreatePyObject(fval, 3, Py_NEW, NULL); 00461 #else 00462 PyObject* resultlist = PyList_New(3); 00463 for (unsigned int i=0; i<3; i++) 00464 { 00465 PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble((*val)[i])); 00466 } 00467 return resultlist; 00468 #endif 00469 } 00470 case KX_PYATTRIBUTE_TYPE_STRING: 00471 { 00472 STR_String *val = reinterpret_cast<STR_String*>(ptr); 00473 return PyUnicode_From_STR_String(*val); 00474 } 00475 case KX_PYATTRIBUTE_TYPE_CHAR: 00476 { 00477 return PyUnicode_FromString(ptr); 00478 } 00479 default: 00480 return NULL; 00481 } 00482 } 00483 } 00484 00485 00486 static bool py_check_attr_float(float *var, PyObject *value, const PyAttributeDef *attrdef) 00487 { 00488 double val = PyFloat_AsDouble(value); 00489 if (val == -1.0 && PyErr_Occurred()) 00490 { 00491 PyErr_Format(PyExc_TypeError, "expected float value for attribute \"%s\"", attrdef->m_name); 00492 return false; 00493 } 00494 if (attrdef->m_clamp) 00495 { 00496 if (val < attrdef->m_fmin) 00497 val = attrdef->m_fmin; 00498 else if (val > attrdef->m_fmax) 00499 val = attrdef->m_fmax; 00500 } 00501 else if (val < attrdef->m_fmin || val > attrdef->m_fmax) 00502 { 00503 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); 00504 return false; 00505 } 00506 *var = (float)val; 00507 return true; 00508 } 00509 00510 /* note, this is called as a python getset */ 00511 int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAttributeDef *attrdef) 00512 { 00513 PyObjectPlus *ref= (BGE_PROXY_REF(self_py)); 00514 char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref; 00515 if(ref==NULL || !ref->py_is_valid() || ptr==NULL) { 00516 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); 00517 return PY_SET_ATTR_FAIL; 00518 } 00519 00520 void *undoBuffer = NULL; 00521 void *sourceBuffer = NULL; 00522 size_t bufferSize = 0; 00523 PyObject *item = NULL; // to store object that must be dereferenced in case of error 00524 PyObject *list = NULL; // to store object that must be dereferenced in case of error 00525 00526 ptr += attrdef->m_offset; 00527 if (attrdef->m_length > 1) 00528 { 00529 if (!PySequence_Check(value)) 00530 { 00531 PyErr_Format(PyExc_TypeError, "expected a sequence for attribute \"%s\"", attrdef->m_name); 00532 return PY_SET_ATTR_FAIL; 00533 } 00534 if (PySequence_Size(value) != attrdef->m_length) 00535 { 00536 PyErr_Format(PyExc_TypeError, "incorrect number of elements in sequence for attribute \"%s\"", attrdef->m_name); 00537 return PY_SET_ATTR_FAIL; 00538 } 00539 switch (attrdef->m_type) 00540 { 00541 case KX_PYATTRIBUTE_TYPE_FUNCTION: 00542 if (attrdef->m_setFunction == NULL) 00543 { 00544 PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name); 00545 return PY_SET_ATTR_FAIL; 00546 } 00547 return (*attrdef->m_setFunction)(ref, attrdef, value); 00548 case KX_PYATTRIBUTE_TYPE_BOOL: 00549 bufferSize = sizeof(bool); 00550 break; 00551 case KX_PYATTRIBUTE_TYPE_SHORT: 00552 bufferSize = sizeof(short int); 00553 break; 00554 case KX_PYATTRIBUTE_TYPE_ENUM: 00555 case KX_PYATTRIBUTE_TYPE_INT: 00556 bufferSize = sizeof(int); 00557 break; 00558 case KX_PYATTRIBUTE_TYPE_FLOAT: 00559 bufferSize = sizeof(float); 00560 break; 00561 default: 00562 // should not happen 00563 PyErr_Format(PyExc_AttributeError, "Unsupported attribute type for attribute \"%s\", report to blender.org", attrdef->m_name); 00564 return PY_SET_ATTR_FAIL; 00565 } 00566 // let's implement a smart undo method 00567 bufferSize *= attrdef->m_length; 00568 undoBuffer = malloc(bufferSize); 00569 sourceBuffer = ptr; 00570 if (undoBuffer) 00571 { 00572 memcpy(undoBuffer, sourceBuffer, bufferSize); 00573 } 00574 for (int i=0; i<attrdef->m_length; i++) 00575 { 00576 item = PySequence_GetItem(value, i); /* new ref */ 00577 switch (attrdef->m_type) 00578 { 00579 case KX_PYATTRIBUTE_TYPE_BOOL: 00580 { 00581 bool *var = reinterpret_cast<bool*>(ptr); 00582 ptr += sizeof(bool); 00583 if (PyLong_Check(item)) 00584 { 00585 *var = (PyLong_AsSsize_t(item) != 0); 00586 } 00587 else if (PyBool_Check(item)) 00588 { 00589 *var = (item == Py_True); 00590 } 00591 else 00592 { 00593 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name); 00594 goto UNDO_AND_ERROR; 00595 } 00596 break; 00597 } 00598 case KX_PYATTRIBUTE_TYPE_SHORT: 00599 { 00600 short int *var = reinterpret_cast<short int*>(ptr); 00601 ptr += sizeof(short int); 00602 if (PyLong_Check(item)) 00603 { 00604 long val = PyLong_AsSsize_t(item); 00605 if (attrdef->m_clamp) 00606 { 00607 if (val < attrdef->m_imin) 00608 val = attrdef->m_imin; 00609 else if (val > attrdef->m_imax) 00610 val = attrdef->m_imax; 00611 } 00612 else if (val < attrdef->m_imin || val > attrdef->m_imax) 00613 { 00614 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name); 00615 goto UNDO_AND_ERROR; 00616 } 00617 *var = (short int)val; 00618 } 00619 else 00620 { 00621 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); 00622 goto UNDO_AND_ERROR; 00623 } 00624 break; 00625 } 00626 case KX_PYATTRIBUTE_TYPE_ENUM: 00627 // enum are equivalent to int, just make sure that the field size matches: 00628 if (sizeof(int) != attrdef->m_size) 00629 { 00630 PyErr_Format(PyExc_AttributeError, "Size check error for attribute, \"%s\", report to blender.org", attrdef->m_name); 00631 goto UNDO_AND_ERROR; 00632 } 00633 // walkthrough 00634 case KX_PYATTRIBUTE_TYPE_INT: 00635 { 00636 int *var = reinterpret_cast<int*>(ptr); 00637 ptr += sizeof(int); 00638 if (PyLong_Check(item)) 00639 { 00640 long val = PyLong_AsSsize_t(item); 00641 if (attrdef->m_clamp) 00642 { 00643 if (val < attrdef->m_imin) 00644 val = attrdef->m_imin; 00645 else if (val > attrdef->m_imax) 00646 val = attrdef->m_imax; 00647 } 00648 else if (val < attrdef->m_imin || val > attrdef->m_imax) 00649 { 00650 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name); 00651 goto UNDO_AND_ERROR; 00652 } 00653 *var = (int)val; 00654 } 00655 else 00656 { 00657 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); 00658 goto UNDO_AND_ERROR; 00659 } 00660 break; 00661 } 00662 case KX_PYATTRIBUTE_TYPE_FLOAT: 00663 { 00664 float *var = reinterpret_cast<float*>(ptr); 00665 ptr += sizeof(float); 00666 double val = PyFloat_AsDouble(item); 00667 if (val == -1.0 && PyErr_Occurred()) 00668 { 00669 PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name); 00670 goto UNDO_AND_ERROR; 00671 } 00672 else if (attrdef->m_clamp) 00673 { 00674 if (val < attrdef->m_fmin) 00675 val = attrdef->m_fmin; 00676 else if (val > attrdef->m_fmax) 00677 val = attrdef->m_fmax; 00678 } 00679 else if (val < attrdef->m_fmin || val > attrdef->m_fmax) 00680 { 00681 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name); 00682 goto UNDO_AND_ERROR; 00683 } 00684 *var = (float)val; 00685 break; 00686 } 00687 default: 00688 // should not happen 00689 PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name); 00690 goto UNDO_AND_ERROR; 00691 } 00692 // finished using item, release 00693 Py_DECREF(item); 00694 item = NULL; 00695 } 00696 // no error, call check function if any 00697 if (attrdef->m_checkFunction != NULL) 00698 { 00699 if ((*attrdef->m_checkFunction)(ref, attrdef) != 0) 00700 { 00701 // if the checing function didnt set an error then set a generic one here so we dont set an error with no exception 00702 if (PyErr_Occurred()==0) 00703 PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", reasion unknown", attrdef->m_name); 00704 00705 // post check returned an error, restore values 00706 UNDO_AND_ERROR: 00707 if (undoBuffer) 00708 { 00709 memcpy(sourceBuffer, undoBuffer, bufferSize); 00710 free(undoBuffer); 00711 } 00712 if (item) 00713 Py_DECREF(item); 00714 return PY_SET_ATTR_FAIL; 00715 } 00716 } 00717 if (undoBuffer) 00718 free(undoBuffer); 00719 return PY_SET_ATTR_SUCCESS; 00720 } 00721 else // simple attribute value 00722 { 00723 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION) 00724 { 00725 if (attrdef->m_setFunction == NULL) 00726 { 00727 PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name); 00728 return PY_SET_ATTR_FAIL; 00729 } 00730 return (*attrdef->m_setFunction)(ref, attrdef, value); 00731 } 00732 if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR) 00733 { 00734 // post check function is provided, prepare undo buffer 00735 sourceBuffer = ptr; 00736 switch (attrdef->m_type) 00737 { 00738 case KX_PYATTRIBUTE_TYPE_BOOL: 00739 bufferSize = sizeof(bool); 00740 break; 00741 case KX_PYATTRIBUTE_TYPE_SHORT: 00742 bufferSize = sizeof(short); 00743 break; 00744 case KX_PYATTRIBUTE_TYPE_ENUM: 00745 case KX_PYATTRIBUTE_TYPE_FLAG: 00746 case KX_PYATTRIBUTE_TYPE_CHAR: 00747 bufferSize = attrdef->m_size; 00748 break; 00749 case KX_PYATTRIBUTE_TYPE_INT: 00750 bufferSize = sizeof(int); 00751 break; 00752 case KX_PYATTRIBUTE_TYPE_FLOAT: 00753 bufferSize = sizeof(float); 00754 if (attrdef->m_imax) 00755 bufferSize *= attrdef->m_imax; 00756 if (attrdef->m_imin) 00757 bufferSize *= attrdef->m_imin; 00758 break; 00759 case KX_PYATTRIBUTE_TYPE_STRING: 00760 sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr(); 00761 if (sourceBuffer) 00762 bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1; 00763 break; 00764 case KX_PYATTRIBUTE_TYPE_VECTOR: 00765 bufferSize = sizeof(MT_Vector3); 00766 break; 00767 default: 00768 PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name); 00769 return PY_SET_ATTR_FAIL; 00770 } 00771 if (bufferSize) 00772 { 00773 undoBuffer = malloc(bufferSize); 00774 if (undoBuffer) 00775 { 00776 memcpy(undoBuffer, sourceBuffer, bufferSize); 00777 } 00778 } 00779 } 00780 00781 switch (attrdef->m_type) 00782 { 00783 case KX_PYATTRIBUTE_TYPE_BOOL: 00784 { 00785 bool *var = reinterpret_cast<bool*>(ptr); 00786 if (PyLong_Check(value)) 00787 { 00788 *var = (PyLong_AsSsize_t(value) != 0); 00789 } 00790 else if (PyBool_Check(value)) 00791 { 00792 *var = (value == Py_True); 00793 } 00794 else 00795 { 00796 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name); 00797 goto FREE_AND_ERROR; 00798 } 00799 break; 00800 } 00801 case KX_PYATTRIBUTE_TYPE_FLAG: 00802 { 00803 bool bval; 00804 if (PyLong_Check(value)) 00805 { 00806 bval = (PyLong_AsSsize_t(value) != 0); 00807 } 00808 else if (PyBool_Check(value)) 00809 { 00810 bval = (value == Py_True); 00811 } 00812 else 00813 { 00814 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name); 00815 goto FREE_AND_ERROR; 00816 } 00817 if (attrdef->m_imax) 00818 bval = !bval; 00819 switch (attrdef->m_size) { 00820 case 1: 00821 { 00822 unsigned char *val = reinterpret_cast<unsigned char*>(ptr); 00823 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); 00824 break; 00825 } 00826 case 2: 00827 { 00828 unsigned short *val = reinterpret_cast<unsigned short*>(ptr); 00829 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); 00830 break; 00831 } 00832 case 4: 00833 { 00834 unsigned int *val = reinterpret_cast<unsigned int*>(ptr); 00835 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); 00836 break; 00837 } 00838 default: 00839 PyErr_Format(PyExc_TypeError, "internal error: unsupported flag field \"%s\"", attrdef->m_name); 00840 goto FREE_AND_ERROR; 00841 } 00842 break; 00843 } 00844 case KX_PYATTRIBUTE_TYPE_SHORT: 00845 { 00846 short int *var = reinterpret_cast<short int*>(ptr); 00847 if (PyLong_Check(value)) 00848 { 00849 long val = PyLong_AsSsize_t(value); 00850 if (attrdef->m_clamp) 00851 { 00852 if (val < attrdef->m_imin) 00853 val = attrdef->m_imin; 00854 else if (val > attrdef->m_imax) 00855 val = attrdef->m_imax; 00856 } 00857 else if (val < attrdef->m_imin || val > attrdef->m_imax) 00858 { 00859 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); 00860 goto FREE_AND_ERROR; 00861 } 00862 *var = (short int)val; 00863 } 00864 else 00865 { 00866 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); 00867 goto FREE_AND_ERROR; 00868 } 00869 break; 00870 } 00871 case KX_PYATTRIBUTE_TYPE_ENUM: 00872 // enum are equivalent to int, just make sure that the field size matches: 00873 if (sizeof(int) != attrdef->m_size) 00874 { 00875 PyErr_Format(PyExc_AttributeError, "attribute size check error for attribute \"%s\", report to blender.org", attrdef->m_name); 00876 goto FREE_AND_ERROR; 00877 } 00878 // walkthrough 00879 case KX_PYATTRIBUTE_TYPE_INT: 00880 { 00881 int *var = reinterpret_cast<int*>(ptr); 00882 if (PyLong_Check(value)) 00883 { 00884 long val = PyLong_AsSsize_t(value); 00885 if (attrdef->m_clamp) 00886 { 00887 if (val < attrdef->m_imin) 00888 val = attrdef->m_imin; 00889 else if (val > attrdef->m_imax) 00890 val = attrdef->m_imax; 00891 } 00892 else if (val < attrdef->m_imin || val > attrdef->m_imax) 00893 { 00894 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); 00895 goto FREE_AND_ERROR; 00896 } 00897 *var = (int)val; 00898 } 00899 else 00900 { 00901 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); 00902 goto FREE_AND_ERROR; 00903 } 00904 break; 00905 } 00906 case KX_PYATTRIBUTE_TYPE_FLOAT: 00907 { 00908 float *var = reinterpret_cast<float*>(ptr); 00909 if (attrdef->m_imin != 0) 00910 { 00911 if (attrdef->m_size != attrdef->m_imin*attrdef->m_imax*sizeof(float)) 00912 { 00913 PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name); 00914 goto FREE_AND_ERROR; 00915 } 00916 if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imin) 00917 { 00918 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); 00919 goto FREE_AND_ERROR; 00920 } 00921 for (int i=0; i<attrdef->m_imin; i++) 00922 { 00923 PyObject *list = PySequence_GetItem(value, i); /* new ref */ 00924 if (!PySequence_Check(list) || PySequence_Size(list) != attrdef->m_imax) 00925 { 00926 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); 00927 goto RESTORE_AND_ERROR; 00928 } 00929 for (int j=0; j<attrdef->m_imax; j++) 00930 { 00931 item = PySequence_GetItem(list, j); /* new ref */ 00932 if (!py_check_attr_float(var, item, attrdef)) 00933 { 00934 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); 00935 goto RESTORE_AND_ERROR; 00936 } 00937 Py_DECREF(item); 00938 item = NULL; 00939 ++var; 00940 } 00941 Py_DECREF(list); 00942 list = NULL; 00943 } 00944 } 00945 else if (attrdef->m_imax != 0) 00946 { 00947 if (attrdef->m_size != attrdef->m_imax*sizeof(float)) 00948 { 00949 PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name); 00950 goto FREE_AND_ERROR; 00951 } 00952 if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imax) 00953 { 00954 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d] floats for attribute \"%s\"", attrdef->m_imax, attrdef->m_name); 00955 goto FREE_AND_ERROR; 00956 } 00957 for (int i=0; i<attrdef->m_imax; i++) 00958 { 00959 item = PySequence_GetItem(value, i); /* new ref */ 00960 if (!py_check_attr_float(var, item, attrdef)) 00961 { 00962 goto RESTORE_AND_ERROR; 00963 } 00964 Py_DECREF(item); 00965 item = NULL; 00966 ++var; 00967 } 00968 } 00969 else 00970 { 00971 if (!py_check_attr_float(var, value, attrdef)) 00972 goto FREE_AND_ERROR; 00973 } 00974 break; 00975 } 00976 case KX_PYATTRIBUTE_TYPE_VECTOR: 00977 { 00978 if (!PySequence_Check(value) || PySequence_Size(value) != 3) 00979 { 00980 PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name); 00981 goto FREE_AND_ERROR; 00982 } 00983 MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr); 00984 for (int i=0; i<3; i++) 00985 { 00986 item = PySequence_GetItem(value, i); /* new ref */ 00987 double val = PyFloat_AsDouble(item); 00988 Py_DECREF(item); 00989 item = NULL; 00990 if (val == -1.0 && PyErr_Occurred()) 00991 { 00992 PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name); 00993 goto RESTORE_AND_ERROR; 00994 } 00995 else if (attrdef->m_clamp) 00996 { 00997 if (val < attrdef->m_fmin) 00998 val = attrdef->m_fmin; 00999 else if (val > attrdef->m_fmax) 01000 val = attrdef->m_fmax; 01001 } 01002 else if (val < attrdef->m_fmin || val > attrdef->m_fmax) 01003 { 01004 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); 01005 goto RESTORE_AND_ERROR; 01006 } 01007 (*var)[i] = (MT_Scalar)val; 01008 } 01009 break; 01010 } 01011 case KX_PYATTRIBUTE_TYPE_CHAR: 01012 { 01013 if (PyUnicode_Check(value)) 01014 { 01015 Py_ssize_t val_size; 01016 const char *val = _PyUnicode_AsStringAndSize(value, &val_size); 01017 strncpy(ptr, val, attrdef->m_size); 01018 ptr[attrdef->m_size-1] = 0; 01019 } 01020 else 01021 { 01022 PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name); 01023 goto FREE_AND_ERROR; 01024 } 01025 break; 01026 } 01027 case KX_PYATTRIBUTE_TYPE_STRING: 01028 { 01029 STR_String *var = reinterpret_cast<STR_String*>(ptr); 01030 if (PyUnicode_Check(value)) 01031 { 01032 Py_ssize_t val_len; 01033 const char *val = _PyUnicode_AsStringAndSize(value, &val_len); /* XXX, should be 'const' but we do a silly trick to have a shorter string */ 01034 if (attrdef->m_clamp) 01035 { 01036 if (val_len < attrdef->m_imin) 01037 { 01038 // can't increase the length of the string 01039 PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name); 01040 goto FREE_AND_ERROR; 01041 } 01042 else if (val_len > attrdef->m_imax) 01043 { 01044 // trim the string 01045 var->SetLength(attrdef->m_imax); 01046 memcpy(var->Ptr(), val, attrdef->m_imax - 1); 01047 break; 01048 } 01049 } else if (val_len < attrdef->m_imin || val_len > attrdef->m_imax) 01050 { 01051 PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name); 01052 goto FREE_AND_ERROR; 01053 } 01054 *var = val; 01055 } 01056 else 01057 { 01058 PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name); 01059 goto FREE_AND_ERROR; 01060 } 01061 break; 01062 } 01063 default: 01064 // should not happen 01065 PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name); 01066 goto FREE_AND_ERROR; 01067 } 01068 } 01069 // check if post processing is needed 01070 if (attrdef->m_checkFunction != NULL) 01071 { 01072 if ((*attrdef->m_checkFunction)(ref, attrdef) != 0) 01073 { 01074 // restore value 01075 RESTORE_AND_ERROR: 01076 if (undoBuffer) 01077 { 01078 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING) 01079 { 01080 // special case for STR_String: restore the string 01081 STR_String *var = reinterpret_cast<STR_String*>(ptr); 01082 *var = reinterpret_cast<char*>(undoBuffer); 01083 } 01084 else 01085 { 01086 // other field type have direct values 01087 memcpy(ptr, undoBuffer, bufferSize); 01088 } 01089 } 01090 FREE_AND_ERROR: 01091 if (undoBuffer) 01092 free(undoBuffer); 01093 if (list) 01094 Py_DECREF(list); 01095 if (item) 01096 Py_DECREF(item); 01097 return 1; 01098 } 01099 } 01100 if (undoBuffer) 01101 free(undoBuffer); 01102 return 0; 01103 } 01104 01105 01106 01107 /*------------------------------ 01108 * PyObjectPlus repr -- representations 01109 ------------------------------*/ 01110 PyObject *PyObjectPlus::py_repr(void) 01111 { 01112 PyErr_SetString(PyExc_SystemError, "Representation not overridden by object."); 01113 return NULL; 01114 } 01115 01116 PyObject *PyObjectPlus::GetProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr) 01117 { 01118 if (self->m_proxy==NULL) 01119 { 01120 self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp); 01121 BGE_PROXY_PYOWNS(self->m_proxy) = false; 01122 BGE_PROXY_PYREF(self->m_proxy) = true; 01123 #ifdef USE_WEAKREFS 01124 BGE_PROXY_WKREF(self->m_proxy) = NULL; 01125 #endif 01126 } 01127 //PyObject_Print(self->m_proxy, stdout, 0); 01128 //printf("ref %d\n", self->m_proxy->ob_refcnt); 01129 01130 BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */ 01131 BGE_PROXY_PTR(self->m_proxy) = ptr; 01132 Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */ 01133 return self->m_proxy; 01134 } 01135 01136 PyObject *PyObjectPlus::NewProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr, bool py_owns) 01137 { 01138 if (!self) 01139 { 01140 // in case of proxy without reference to game object 01141 PyObject* proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp); 01142 BGE_PROXY_PYREF(proxy) = false; 01143 BGE_PROXY_PYOWNS(proxy) = py_owns; 01144 BGE_PROXY_REF(proxy) = NULL; 01145 BGE_PROXY_PTR(proxy) = ptr; 01146 #ifdef USE_WEAKREFS 01147 BGE_PROXY_WKREF(proxy) = NULL; 01148 #endif 01149 return proxy; 01150 } 01151 if (self->m_proxy) 01152 { 01153 if(py_owns) 01154 { /* Free */ 01155 BGE_PROXY_REF(self->m_proxy) = NULL; 01156 Py_DECREF(self->m_proxy); 01157 self->m_proxy= NULL; 01158 } 01159 else { 01160 Py_INCREF(self->m_proxy); 01161 return self->m_proxy; 01162 } 01163 01164 } 01165 01166 GetProxyPlus_Ext(self, tp, ptr); 01167 if(py_owns) { 01168 BGE_PROXY_PYOWNS(self->m_proxy) = py_owns; 01169 Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */ 01170 } 01171 return self->m_proxy; 01172 } 01173 01174 PyObject *PyUnicode_From_STR_String(const STR_String& str) 01175 { 01176 return PyUnicode_FromStringAndSize(str.ReadPtr(), str.Length()); 01177 } 01178 01181 /* deprecation warning management */ 01182 01183 bool PyObjectPlus::m_ignore_deprecation_warnings(false); 01184 void PyObjectPlus::SetDeprecationWarnings(bool ignoreDeprecationWarnings) 01185 { 01186 m_ignore_deprecation_warnings = ignoreDeprecationWarnings; 01187 } 01188 01189 void PyObjectPlus::ShowDeprecationWarning_func(const char* old_way,const char* new_way) 01190 { 01191 printf("Method %s is deprecated, please use %s instead.\n", old_way, new_way); 01192 PyC_LineSpit(); 01193 } 01194 01195 void PyObjectPlus::ClearDeprecationWarning() 01196 { 01197 WarnLink *wlink_next; 01198 WarnLink *wlink = GetDeprecationWarningLinkFirst(); 01199 01200 while(wlink) 01201 { 01202 wlink->warn_done= false; /* no need to NULL the link, its cleared before adding to the list next time round */ 01203 wlink_next= reinterpret_cast<WarnLink *>(wlink->link); 01204 wlink->link= NULL; 01205 wlink= wlink_next; 01206 } 01207 NullDeprecationWarning(); 01208 } 01209 01210 WarnLink* m_base_wlink_first= NULL; 01211 WarnLink* m_base_wlink_last= NULL; 01212 01213 WarnLink* PyObjectPlus::GetDeprecationWarningLinkFirst(void) {return m_base_wlink_first;} 01214 WarnLink* PyObjectPlus::GetDeprecationWarningLinkLast(void) {return m_base_wlink_last;} 01215 void PyObjectPlus::SetDeprecationWarningFirst(WarnLink* wlink) {m_base_wlink_first= wlink;} 01216 void PyObjectPlus::SetDeprecationWarningLinkLast(WarnLink* wlink) {m_base_wlink_last= wlink;} 01217 void PyObjectPlus::NullDeprecationWarning() {m_base_wlink_first= m_base_wlink_last= NULL;} 01218 01219 #endif // WITH_PYTHON