static PyObject *bpy_escape_identifier(PyObject *UNUSED(self), PyObject *value) { const char *value_str; Py_ssize_t value_str_len; char *value_escape_str; Py_ssize_t value_escape_str_len; PyObject *value_escape; size_t size; value_str = _PyUnicode_AsStringAndSize(value, &value_str_len); if (value_str == NULL) { PyErr_SetString(PyExc_TypeError, "expected a string"); return NULL; } size = (value_str_len * 2) + 1; value_escape_str = PyMem_MALLOC(size); value_escape_str_len = BLI_strescape(value_escape_str, value_str, size); if (value_escape_str_len == value_str_len) { Py_INCREF(value); value_escape = value; } else { value_escape = PyUnicode_FromStringAndSize(value_escape_str, value_escape_str_len); } PyMem_FREE(value_escape_str); return value_escape; }
static char * fp_readl(char *s, int size, struct tok_state *tok) { PyObject* bufobj; const char *buf; Py_ssize_t buflen; /* Ask for one less byte so we can terminate it */ assert(size > 0); size--; if (tok->decoding_buffer) { bufobj = tok->decoding_buffer; Py_INCREF(bufobj); } else { bufobj = PyObject_CallObject(tok->decoding_readline, NULL); if (bufobj == NULL) goto error; } if (PyUnicode_CheckExact(bufobj)) { buf = _PyUnicode_AsStringAndSize(bufobj, &buflen); if (buf == NULL) { goto error; } } else { buf = PyByteArray_AsString(bufobj); if (buf == NULL) { goto error; } buflen = PyByteArray_GET_SIZE(bufobj); } Py_XDECREF(tok->decoding_buffer); if (buflen > size) { /* Too many chars, the rest goes into tok->decoding_buffer */ tok->decoding_buffer = PyByteArray_FromStringAndSize(buf+size, buflen-size); if (tok->decoding_buffer == NULL) goto error; buflen = size; } else tok->decoding_buffer = NULL; memcpy(s, buf, buflen); s[buflen] = '\0'; if (buflen == 0) /* EOF */ s = NULL; Py_DECREF(bufobj); return s; error: Py_XDECREF(bufobj); return error_ret(tok); }
int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* connection, PyObject* sql) { const char* tail; int rc; const char* sql_cstr; Py_ssize_t sql_cstr_len; self->st = NULL; self->in_use = 0; sql_cstr = _PyUnicode_AsStringAndSize(sql, &sql_cstr_len); if (sql_cstr == NULL) { rc = PYSQLITE_SQL_WRONG_TYPE; return rc; } if (strlen(sql_cstr) != (size_t)sql_cstr_len) { PyErr_SetString(PyExc_ValueError, "the query contains a null character"); return PYSQLITE_SQL_WRONG_TYPE; } self->in_weakreflist = NULL; Py_INCREF(sql); self->sql = sql; Py_BEGIN_ALLOW_THREADS rc = sqlite3_prepare(connection->db, sql_cstr, -1, &self->st, &tail); Py_END_ALLOW_THREADS self->db = connection->db; if (rc == SQLITE_OK && pysqlite_check_remaining_sql(tail)) { (void)sqlite3_finalize(self->st); self->st = NULL; rc = PYSQLITE_TOO_MUCH_SQL; } return rc; }
static int BPy_IDGroup_SetName(BPy_IDProperty *self, PyObject *value, void *UNUSED(closure)) { const char *name; Py_ssize_t name_size; if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, "expected a string!"); return -1; } name = _PyUnicode_AsStringAndSize(value, &name_size); if (name_size > MAX_IDPROP_NAME) { PyErr_SetString(PyExc_TypeError, "string length cannot exceed 63 characters!"); return -1; } memcpy(self->prop->name, name, name_size); return 0; }
static const char *idp_try_read_name(PyObject *name_obj) { const char *name = NULL; if (name_obj) { Py_ssize_t name_size; name = _PyUnicode_AsStringAndSize(name_obj, &name_size); if (name == NULL) { PyErr_Format(PyExc_KeyError, "invalid id-property key, expected a string, not a %.200s", Py_TYPE(name_obj)->tp_name); return NULL; } if (name_size > MAX_IDPROP_NAME) { PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters"); return NULL; } } else { name = ""; } return name; }
/** * \note group can be a pointer array or a group. * assume we already checked key is a string. * * \return success. */ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob) { IDProperty *prop = NULL; IDPropertyTemplate val = {0}; const char *name; if (name_obj) { Py_ssize_t name_size; name = _PyUnicode_AsStringAndSize(name_obj, &name_size); if (name == NULL) { PyErr_Format(PyExc_KeyError, "invalid id-property key, expected a string, not a %.200s", Py_TYPE(name_obj)->tp_name); return false; } if (name_size > MAX_IDPROP_NAME) { PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters"); return false; } } else { name = ""; } if (PyFloat_Check(ob)) { val.d = PyFloat_AsDouble(ob); prop = IDP_New(IDP_DOUBLE, &val, name); } else if (PyLong_Check(ob)) { val.i = _PyLong_AsInt(ob); if (val.i == -1 && PyErr_Occurred()) { return false; } prop = IDP_New(IDP_INT, &val, name); } else if (PyUnicode_Check(ob)) { #ifdef USE_STRING_COERCE PyObject *value_coerce = NULL; val.string.str = (char *)PyC_UnicodeAsByte(ob, &value_coerce); val.string.subtype = IDP_STRING_SUB_UTF8; prop = IDP_New(IDP_STRING, &val, name); Py_XDECREF(value_coerce); #else val.str = _PyUnicode_AsString(ob); prop = IDP_New(IDP_STRING, val, name); #endif } else if (PyBytes_Check(ob)) { val.string.str = PyBytes_AS_STRING(ob); val.string.len = PyBytes_GET_SIZE(ob); val.string.subtype = IDP_STRING_SUB_BYTE; prop = IDP_New(IDP_STRING, &val, name); //prop = IDP_NewString(PyBytes_AS_STRING(ob), name, PyBytes_GET_SIZE(ob)); //prop->subtype = IDP_STRING_SUB_BYTE; } else if (PySequence_Check(ob)) { PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop"); PyObject *item; int i; if (ob_seq_fast == NULL) { return false; } if ((val.array.type = idp_sequence_type(ob_seq_fast)) == -1) { Py_DECREF(ob_seq_fast); PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays"); return false; } /* validate sequence and derive type. * we assume IDP_INT unless we hit a float * number; then we assume it's */ val.array.len = PySequence_Fast_GET_SIZE(ob_seq_fast); switch (val.array.type) { case IDP_DOUBLE: { double *prop_data; prop = IDP_New(IDP_ARRAY, &val, name); prop_data = IDP_Array(prop); for (i = 0; i < val.array.len; i++) { item = PySequence_Fast_GET_ITEM(ob_seq_fast, i); if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { Py_DECREF(ob_seq_fast); return false; } } break; } case IDP_INT: { int *prop_data; prop = IDP_New(IDP_ARRAY, &val, name); prop_data = IDP_Array(prop); for (i = 0; i < val.array.len; i++) { item = PySequence_Fast_GET_ITEM(ob_seq_fast, i); if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) { Py_DECREF(ob_seq_fast); return false; } } break; } case IDP_IDPARRAY: { prop = IDP_NewIDPArray(name); for (i = 0; i < val.array.len; i++) { item = PySequence_Fast_GET_ITEM(ob_seq_fast, i); if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { Py_DECREF(ob_seq_fast); return false; } } break; } default: /* should never happen */ Py_DECREF(ob_seq_fast); PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type"); return false; } Py_DECREF(ob_seq_fast); } else if (PyMapping_Check(ob)) { PyObject *keys, *vals, *key, *pval; int i, len; /*yay! we get into recursive stuff now!*/ keys = PyMapping_Keys(ob); vals = PyMapping_Values(ob); /* we allocate the group first; if we hit any invalid data, * we can delete it easily enough.*/ prop = IDP_New(IDP_GROUP, &val, name); len = PyMapping_Length(ob); for (i = 0; i < len; i++) { key = PySequence_GetItem(keys, i); pval = PySequence_GetItem(vals, i); if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) { IDP_FreeProperty(prop); MEM_freeN(prop); Py_XDECREF(keys); Py_XDECREF(vals); Py_XDECREF(key); Py_XDECREF(pval); /* error is already set */ return false; } Py_XDECREF(key); Py_XDECREF(pval); } Py_XDECREF(keys); Py_XDECREF(vals); } else { PyErr_Format(PyExc_TypeError, "invalid id-property type %.200s not supported", Py_TYPE(ob)->tp_name); return false; } if (group->type == IDP_IDPARRAY) { IDP_AppendArray(group, prop); // IDP_FreeProperty(item); /* IDP_AppendArray does a shallow copy (memcpy), only free memory */ MEM_freeN(prop); } else { IDP_ReplaceInGroup(group, prop); } return true; }
int PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) { PyObject *oldv; addr += l->offset; if ((l->flags & READONLY)) { PyErr_SetString(PyExc_AttributeError, "readonly attribute"); return -1; } if (v == NULL) { if (l->type == T_OBJECT_EX) { /* Check if the attribute is set. */ if (*(PyObject **)addr == NULL) { PyErr_SetString(PyExc_AttributeError, l->name); return -1; } } else if (l->type != T_OBJECT) { PyErr_SetString(PyExc_TypeError, "can't delete numeric/char attribute"); return -1; } } switch (l->type) { case T_BOOL:{ if (!PyBool_Check(v)) { PyErr_SetString(PyExc_TypeError, "attribute value type must be bool"); return -1; } if (v == Py_True) *(char*)addr = (char) 1; else *(char*)addr = (char) 0; break; } case T_BYTE:{ long long_val = PyLong_AsLong(v); if ((long_val == -1) && PyErr_Occurred()) return -1; *(char*)addr = (char)long_val; /* XXX: For compatibility, only warn about truncations for now. */ if ((long_val > CHAR_MAX) || (long_val < CHAR_MIN)) WARN("Truncation of value to char"); break; } case T_UBYTE:{ long long_val = PyLong_AsLong(v); if ((long_val == -1) && PyErr_Occurred()) return -1; *(unsigned char*)addr = (unsigned char)long_val; if ((long_val > UCHAR_MAX) || (long_val < 0)) WARN("Truncation of value to unsigned char"); break; } case T_SHORT:{ long long_val = PyLong_AsLong(v); if ((long_val == -1) && PyErr_Occurred()) return -1; *(short*)addr = (short)long_val; if ((long_val > SHRT_MAX) || (long_val < SHRT_MIN)) WARN("Truncation of value to short"); break; } case T_USHORT:{ long long_val = PyLong_AsLong(v); if ((long_val == -1) && PyErr_Occurred()) return -1; *(unsigned short*)addr = (unsigned short)long_val; if ((long_val > USHRT_MAX) || (long_val < 0)) WARN("Truncation of value to unsigned short"); break; } case T_INT:{ long long_val = PyLong_AsLong(v); if ((long_val == -1) && PyErr_Occurred()) return -1; *(int *)addr = (int)long_val; if ((long_val > INT_MAX) || (long_val < INT_MIN)) WARN("Truncation of value to int"); break; } case T_UINT:{ unsigned long ulong_val = PyLong_AsUnsignedLong(v); if ((ulong_val == (unsigned long)-1) && PyErr_Occurred()) { /* XXX: For compatibility, accept negative int values as well. */ PyErr_Clear(); ulong_val = PyLong_AsLong(v); if ((ulong_val == (unsigned long)-1) && PyErr_Occurred()) return -1; *(unsigned int *)addr = (unsigned int)ulong_val; WARN("Writing negative value into unsigned field"); } else *(unsigned int *)addr = (unsigned int)ulong_val; if (ulong_val > UINT_MAX) WARN("Truncation of value to unsigned int"); break; } case T_LONG:{ *(long*)addr = PyLong_AsLong(v); if ((*(long*)addr == -1) && PyErr_Occurred()) return -1; break; } case T_ULONG:{ *(unsigned long*)addr = PyLong_AsUnsignedLong(v); if ((*(unsigned long*)addr == (unsigned long)-1) && PyErr_Occurred()) { /* XXX: For compatibility, accept negative int values as well. */ PyErr_Clear(); *(unsigned long*)addr = PyLong_AsLong(v); if ((*(unsigned long*)addr == (unsigned long)-1) && PyErr_Occurred()) return -1; WARN("Writing negative value into unsigned field"); } break; } case T_PYSSIZET:{ *(Py_ssize_t*)addr = PyLong_AsSsize_t(v); if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1) && PyErr_Occurred()) return -1; break; } case T_FLOAT:{ double double_val = PyFloat_AsDouble(v); if ((double_val == -1) && PyErr_Occurred()) return -1; *(float*)addr = (float)double_val; break; } case T_DOUBLE: *(double*)addr = PyFloat_AsDouble(v); if ((*(double*)addr == -1) && PyErr_Occurred()) return -1; break; case T_OBJECT: case T_OBJECT_EX: Py_XINCREF(v); oldv = *(PyObject **)addr; *(PyObject **)addr = v; Py_XDECREF(oldv); break; case T_CHAR: { char *string; Py_ssize_t len; string = _PyUnicode_AsStringAndSize(v, &len); if (string == NULL || len != 1) { PyErr_BadArgument(); return -1; } *(char*)addr = string[0]; break; } case T_STRING: case T_STRING_INPLACE: PyErr_SetString(PyExc_TypeError, "readonly attribute"); return -1; #ifdef HAVE_LONG_LONG case T_LONGLONG:{ PY_LONG_LONG value; *(PY_LONG_LONG*)addr = value = PyLong_AsLongLong(v); if ((value == -1) && PyErr_Occurred()) return -1; break; } case T_ULONGLONG:{ unsigned PY_LONG_LONG value; /* ??? PyLong_AsLongLong accepts an int, but PyLong_AsUnsignedLongLong doesn't ??? */ if (PyLong_Check(v)) *(unsigned PY_LONG_LONG*)addr = value = PyLong_AsUnsignedLongLong(v); else *(unsigned PY_LONG_LONG*)addr = value = PyLong_AsLong(v); if ((value == (unsigned PY_LONG_LONG)-1) && PyErr_Occurred()) return -1; break; } #endif /* HAVE_LONG_LONG */ default: PyErr_Format(PyExc_SystemError, "bad memberdescr type for %s", l->name); return -1; } return 0; }
static EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, PyObject *def, int *defvalue, const short is_enum_flag) { EnumPropertyItem *items; PyObject *item; const Py_ssize_t seq_len= PySequence_Fast_GET_SIZE(seq_fast); Py_ssize_t totbuf= 0; int i; short def_used= 0; const char *def_cmp= NULL; if(is_enum_flag) { if(seq_len > RNA_ENUM_BITFLAG_SIZE) { PyErr_SetString(PyExc_TypeError, "EnumProperty(...): maximum " STRINGIFY(RNA_ENUM_BITFLAG_SIZE) " members for a ENUM_FLAG type property"); return NULL; } if(def && !PySet_Check(def)) { PyErr_Format(PyExc_TypeError, "EnumProperty(...): default option must be a 'set' " "type when ENUM_FLAG is enabled, not a '%.200s'", Py_TYPE(def)->tp_name); return NULL; } } else { if(def) { def_cmp= _PyUnicode_AsString(def); if(def_cmp==NULL) { PyErr_Format(PyExc_TypeError, "EnumProperty(...): default option must be a 'str' " "type when ENUM_FLAG is disabled, not a '%.200s'", Py_TYPE(def)->tp_name); return NULL; } } } /* blank value */ *defvalue= 0; items= MEM_callocN(sizeof(EnumPropertyItem) * (seq_len + 1), "enum_items_from_py1"); for(i=0; i<seq_len; i++) { EnumPropertyItem tmp= {0, "", 0, "", ""}; Py_ssize_t id_str_size; Py_ssize_t name_str_size; Py_ssize_t desc_str_size; item= PySequence_Fast_GET_ITEM(seq_fast, i); if( (PyTuple_CheckExact(item)) && (PyTuple_GET_SIZE(item) == 3) && (tmp.identifier= _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 0), &id_str_size)) && (tmp.name= _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 1), &name_str_size)) && (tmp.description= _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 2), &desc_str_size)) ) { if(is_enum_flag) { tmp.value= 1<<i; if(def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) { *defvalue |= tmp.value; def_used++; } } else { tmp.value= i; if(def && def_used == 0 && strcmp(def_cmp, tmp.identifier)==0) { *defvalue= tmp.value; def_used++; /* only ever 1 */ } } items[i]= tmp; /* calculate combine string length */ totbuf += id_str_size + name_str_size + desc_str_size + 3; /* 3 is for '\0's */ } else { MEM_freeN(items); PyErr_SetString(PyExc_TypeError, "EnumProperty(...): expected an tuple containing (identifier, name description)"); return NULL; } } if(is_enum_flag) { /* strict check that all set members were used */ if(def && def_used != PySet_GET_SIZE(def)) { MEM_freeN(items); PyErr_Format(PyExc_TypeError, "EnumProperty(..., default={...}): set has %d unused member(s)", PySet_GET_SIZE(def) - def_used); return NULL; } } else { if(def && def_used == 0) { MEM_freeN(items); PyErr_Format(PyExc_TypeError, "EnumProperty(..., default=\'%s\'): not found in enum members", def); return NULL; } } /* disabled duplicating strings because the array can still be freed and * the strings from it referenced, for now we can't support dynamically * created strings from python. */ #if 0 /* this would all work perfectly _but_ the python strings may be freed * immediately after use, so we need to duplicate them, ugh. * annoying because it works most of the time without this. */ { EnumPropertyItem *items_dup= MEM_mallocN((sizeof(EnumPropertyItem) * (seq_len + 1)) + (sizeof(char) * totbuf), "enum_items_from_py2"); EnumPropertyItem *items_ptr= items_dup; char *buf= ((char *)items_dup) + (sizeof(EnumPropertyItem) * (seq_len + 1)); memcpy(items_dup, items, sizeof(EnumPropertyItem) * (seq_len + 1)); for(i=0; i<seq_len; i++, items_ptr++) { buf += strswapbufcpy(buf, &items_ptr->identifier); buf += strswapbufcpy(buf, &items_ptr->name); buf += strswapbufcpy(buf, &items_ptr->description); } MEM_freeN(items); items=items_dup; } /* end string duplication */ #endif return items; }
/* note, this is called as a python getset */ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAttributeDef *attrdef) { PyObjectPlus *ref= (BGE_PROXY_REF(self_py)); char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref; if (ref==NULL || !ref->py_is_valid() || ptr==NULL) { PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG); return PY_SET_ATTR_FAIL; } void *undoBuffer = NULL; void *sourceBuffer = NULL; size_t bufferSize = 0; PyObject *item = NULL; // to store object that must be dereferenced in case of error PyObject *list = NULL; // to store object that must be dereferenced in case of error ptr += attrdef->m_offset; if (attrdef->m_length > 1) { if (!PySequence_Check(value)) { PyErr_Format(PyExc_TypeError, "expected a sequence for attribute \"%s\"", attrdef->m_name); return PY_SET_ATTR_FAIL; } if (PySequence_Size(value) != attrdef->m_length) { PyErr_Format(PyExc_TypeError, "incorrect number of elements in sequence for attribute \"%s\"", attrdef->m_name); return PY_SET_ATTR_FAIL; } switch (attrdef->m_type) { case KX_PYATTRIBUTE_TYPE_FUNCTION: if (attrdef->m_setFunction == NULL) { PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name); return PY_SET_ATTR_FAIL; } return (*attrdef->m_setFunction)(ref, attrdef, value); case KX_PYATTRIBUTE_TYPE_BOOL: bufferSize = sizeof(bool); break; case KX_PYATTRIBUTE_TYPE_SHORT: bufferSize = sizeof(short int); break; case KX_PYATTRIBUTE_TYPE_ENUM: case KX_PYATTRIBUTE_TYPE_INT: bufferSize = sizeof(int); break; case KX_PYATTRIBUTE_TYPE_FLOAT: bufferSize = sizeof(float); break; default: // should not happen PyErr_Format(PyExc_AttributeError, "Unsupported attribute type for attribute \"%s\", report to blender.org", attrdef->m_name); return PY_SET_ATTR_FAIL; } // let's implement a smart undo method bufferSize *= attrdef->m_length; undoBuffer = malloc(bufferSize); sourceBuffer = ptr; if (undoBuffer) { memcpy(undoBuffer, sourceBuffer, bufferSize); } for (int i=0; i<attrdef->m_length; i++) { item = PySequence_GetItem(value, i); /* new ref */ switch (attrdef->m_type) { case KX_PYATTRIBUTE_TYPE_BOOL: { bool *var = reinterpret_cast<bool*>(ptr); ptr += sizeof(bool); if (PyLong_Check(item)) { *var = (PyLong_AsLong(item) != 0); } else if (PyBool_Check(item)) { *var = (item == Py_True); } else { PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name); goto UNDO_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_SHORT: { short int *var = reinterpret_cast<short int*>(ptr); ptr += sizeof(short int); if (PyLong_Check(item)) { int val = PyLong_AsLong(item); if (attrdef->m_clamp) { if (val < attrdef->m_imin) val = attrdef->m_imin; else if (val > attrdef->m_imax) val = attrdef->m_imax; } else if (val < attrdef->m_imin || val > attrdef->m_imax) { PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name); goto UNDO_AND_ERROR; } *var = (short int)val; } else { PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); goto UNDO_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_ENUM: // enum are equivalent to int, just make sure that the field size matches: if (sizeof(int) != attrdef->m_size) { PyErr_Format(PyExc_AttributeError, "Size check error for attribute, \"%s\", report to blender.org", attrdef->m_name); goto UNDO_AND_ERROR; } // walkthrough case KX_PYATTRIBUTE_TYPE_INT: { int *var = reinterpret_cast<int*>(ptr); ptr += sizeof(int); if (PyLong_Check(item)) { int val = PyLong_AsLong(item); if (attrdef->m_clamp) { if (val < attrdef->m_imin) val = attrdef->m_imin; else if (val > attrdef->m_imax) val = attrdef->m_imax; } else if (val < attrdef->m_imin || val > attrdef->m_imax) { PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name); goto UNDO_AND_ERROR; } *var = (int)val; } else { PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); goto UNDO_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_FLOAT: { float *var = reinterpret_cast<float*>(ptr); ptr += sizeof(float); float val = PyFloat_AsDouble(item); if (val == -1.0f && PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name); goto UNDO_AND_ERROR; } else if (attrdef->m_clamp) { if (val < attrdef->m_fmin) val = attrdef->m_fmin; else if (val > attrdef->m_fmax) val = attrdef->m_fmax; } else if (val < attrdef->m_fmin || val > attrdef->m_fmax) { PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name); goto UNDO_AND_ERROR; } *var = (float)val; break; } default: // should not happen PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name); goto UNDO_AND_ERROR; } // finished using item, release Py_DECREF(item); item = NULL; } // no error, call check function if any if (attrdef->m_checkFunction != NULL) { if ((*attrdef->m_checkFunction)(ref, attrdef) != 0) { // if the checing function didnt set an error then set a generic one here so we don't set an error with no exception if (PyErr_Occurred()==0) PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", reasion unknown", attrdef->m_name); // post check returned an error, restore values UNDO_AND_ERROR: if (undoBuffer) { memcpy(sourceBuffer, undoBuffer, bufferSize); free(undoBuffer); } if (item) Py_DECREF(item); return PY_SET_ATTR_FAIL; } } if (undoBuffer) free(undoBuffer); return PY_SET_ATTR_SUCCESS; } else // simple attribute value { if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION) { if (attrdef->m_setFunction == NULL) { PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name); return PY_SET_ATTR_FAIL; } return (*attrdef->m_setFunction)(ref, attrdef, value); } if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR) { // post check function is provided, prepare undo buffer sourceBuffer = ptr; switch (attrdef->m_type) { case KX_PYATTRIBUTE_TYPE_BOOL: bufferSize = sizeof(bool); break; case KX_PYATTRIBUTE_TYPE_SHORT: bufferSize = sizeof(short); break; case KX_PYATTRIBUTE_TYPE_ENUM: case KX_PYATTRIBUTE_TYPE_FLAG: case KX_PYATTRIBUTE_TYPE_CHAR: bufferSize = attrdef->m_size; break; case KX_PYATTRIBUTE_TYPE_INT: bufferSize = sizeof(int); break; case KX_PYATTRIBUTE_TYPE_FLOAT: bufferSize = sizeof(float); if (attrdef->m_imax) bufferSize *= attrdef->m_imax; if (attrdef->m_imin) bufferSize *= attrdef->m_imin; break; case KX_PYATTRIBUTE_TYPE_STRING: sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr(); if (sourceBuffer) bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1; break; case KX_PYATTRIBUTE_TYPE_VECTOR: bufferSize = sizeof(MT_Vector3); break; default: PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name); return PY_SET_ATTR_FAIL; } if (bufferSize) { undoBuffer = malloc(bufferSize); if (undoBuffer) { memcpy(undoBuffer, sourceBuffer, bufferSize); } } } switch (attrdef->m_type) { case KX_PYATTRIBUTE_TYPE_BOOL: { bool *var = reinterpret_cast<bool*>(ptr); if (PyLong_Check(value)) { *var = (PyLong_AsLong(value) != 0); } else if (PyBool_Check(value)) { *var = (value == Py_True); } else { PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_FLAG: { bool bval; if (PyLong_Check(value)) { bval = (PyLong_AsLong(value) != 0); } else if (PyBool_Check(value)) { bval = (value == Py_True); } else { PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } if (attrdef->m_imax) bval = !bval; switch (attrdef->m_size) { case 1: { unsigned char *val = reinterpret_cast<unsigned char*>(ptr); *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); break; } case 2: { unsigned short *val = reinterpret_cast<unsigned short*>(ptr); *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); break; } case 4: { unsigned int *val = reinterpret_cast<unsigned int*>(ptr); *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0); break; } default: PyErr_Format(PyExc_TypeError, "internal error: unsupported flag field \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_SHORT: { short int *var = reinterpret_cast<short int*>(ptr); if (PyLong_Check(value)) { int val = PyLong_AsLong(value); if (attrdef->m_clamp) { if (val < attrdef->m_imin) val = attrdef->m_imin; else if (val > attrdef->m_imax) val = attrdef->m_imax; } else if (val < attrdef->m_imin || val > attrdef->m_imax) { PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } *var = (short int)val; } else { PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_ENUM: // enum are equivalent to int, just make sure that the field size matches: if (sizeof(int) != attrdef->m_size) { PyErr_Format(PyExc_AttributeError, "attribute size check error for attribute \"%s\", report to blender.org", attrdef->m_name); goto FREE_AND_ERROR; } // walkthrough case KX_PYATTRIBUTE_TYPE_INT: { int *var = reinterpret_cast<int*>(ptr); if (PyLong_Check(value)) { int val = PyLong_AsLong(value); if (attrdef->m_clamp) { if (val < attrdef->m_imin) val = attrdef->m_imin; else if (val > attrdef->m_imax) val = attrdef->m_imax; } else if (val < attrdef->m_imin || val > attrdef->m_imax) { PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } *var = (int)val; } else { PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_FLOAT: { float *var = reinterpret_cast<float*>(ptr); if (attrdef->m_imin != 0) { if (attrdef->m_size != attrdef->m_imin*attrdef->m_imax*sizeof(float)) { PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imin) { PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); goto FREE_AND_ERROR; } for (int i=0; i<attrdef->m_imin; i++) { PyObject *list = PySequence_GetItem(value, i); /* new ref */ if (!PySequence_Check(list) || PySequence_Size(list) != attrdef->m_imax) { PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); goto RESTORE_AND_ERROR; } for (int j=0; j<attrdef->m_imax; j++) { item = PySequence_GetItem(list, j); /* new ref */ if (!py_check_attr_float(var, item, attrdef)) { PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name); goto RESTORE_AND_ERROR; } Py_DECREF(item); item = NULL; ++var; } Py_DECREF(list); list = NULL; } } else if (attrdef->m_imax != 0) { if (attrdef->m_size != attrdef->m_imax*sizeof(float)) { PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imax) { PyErr_Format(PyExc_TypeError, "expected a sequence of [%d] floats for attribute \"%s\"", attrdef->m_imax, attrdef->m_name); goto FREE_AND_ERROR; } for (int i=0; i<attrdef->m_imax; i++) { item = PySequence_GetItem(value, i); /* new ref */ if (!py_check_attr_float(var, item, attrdef)) { goto RESTORE_AND_ERROR; } Py_DECREF(item); item = NULL; ++var; } } else { if (!py_check_attr_float(var, value, attrdef)) goto FREE_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_VECTOR: { if (!PySequence_Check(value) || PySequence_Size(value) != 3) { PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr); for (int i=0; i<3; i++) { item = PySequence_GetItem(value, i); /* new ref */ float val = PyFloat_AsDouble(item); Py_DECREF(item); item = NULL; if (val == -1.0f && PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name); goto RESTORE_AND_ERROR; } else if (attrdef->m_clamp) { if (val < attrdef->m_fmin) val = attrdef->m_fmin; else if (val > attrdef->m_fmax) val = attrdef->m_fmax; } else if (val < attrdef->m_fmin || val > attrdef->m_fmax) { PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name); goto RESTORE_AND_ERROR; } (*var)[i] = (MT_Scalar)val; } break; } case KX_PYATTRIBUTE_TYPE_CHAR: { if (PyUnicode_Check(value)) { Py_ssize_t val_size; const char *val = _PyUnicode_AsStringAndSize(value, &val_size); strncpy(ptr, val, attrdef->m_size); ptr[attrdef->m_size-1] = 0; } else { PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } break; } case KX_PYATTRIBUTE_TYPE_STRING: { STR_String *var = reinterpret_cast<STR_String*>(ptr); if (PyUnicode_Check(value)) { Py_ssize_t val_len; const char *val = _PyUnicode_AsStringAndSize(value, &val_len); /* XXX, should be 'const' but we do a silly trick to have a shorter string */ if (attrdef->m_clamp) { if (val_len < attrdef->m_imin) { // can't increase the length of the string PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } else if (val_len > attrdef->m_imax) { // trim the string var->SetLength(attrdef->m_imax); memcpy(var->Ptr(), val, attrdef->m_imax - 1); break; } } else if (val_len < attrdef->m_imin || val_len > attrdef->m_imax) { PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } *var = val; } else { PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name); goto FREE_AND_ERROR; } break; } default: // should not happen PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name); goto FREE_AND_ERROR; } } // check if post processing is needed if (attrdef->m_checkFunction != NULL) { if ((*attrdef->m_checkFunction)(ref, attrdef) != 0) { // restore value RESTORE_AND_ERROR: if (undoBuffer) { if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING) { // special case for STR_String: restore the string STR_String *var = reinterpret_cast<STR_String*>(ptr); *var = reinterpret_cast<char*>(undoBuffer); } else { // other field type have direct values memcpy(ptr, undoBuffer, bufferSize); } } FREE_AND_ERROR: if (undoBuffer) free(undoBuffer); if (list) Py_DECREF(list); if (item) Py_DECREF(item); return 1; } } if (undoBuffer) free(undoBuffer); return 0; }