/* teamtrack-record.cpp -- Python wrapper round TSRecord class Gareth Rees, Ravenbrook Limited, 2000-08-07 $Id: //info.ravenbrook.com/project/p4dti/branch/2000-09-13/demo-debugging/code/python-teamtrack-interface/teamtrack-record.cpp#1 $ The teamtrack_record type is a mapping type. See [Lutz 1996, page 557] for some hints. */ #include "teamtrack-module.h" #include "teamtrack-record.h" #include "TSField.h" /* teamtrack_record_new(r, server, delete_p) returns a Python wrapper for the TSRecord object r, or 0 if there is an error. The server argument is the Python wrapper for the TSServer object to which the record belongs. Iff delete_p is true, then the TSRecord object will be deleted when the Python wrapper is deleted. */ PyObject * teamtrack_record_new(TSRecord *r, teamtrack_server *server, int delete_p) { /* Check that the TSRecord's server reference matches the Python wrapper's server reference. */ if (server->s != r->serverRef) { teamtrack_raise("Attempt to create a TeamTrack record object whose " "server reference doesn't match the supplied Python " "wrapper's server reference.", 0); } teamtrack_record *self = PyObject_NEW(teamtrack_record, &teamtrack_record_type); if (!self) return 0; self->r = r; self->server = server; Py_INCREF(server); /* The new record object has a reference to it. DECREF is in teamtrack_record_dealloc. */ self->delete_p = delete_p; return (PyObject *)self; } /* Utilities */ PyObject * teamtrack_field_to_pyobject(TSField *f) { switch (f->dataType) { case TS_DATATYPE_BOOL: case TS_DATATYPE_INTEGER: return PyInt_FromLong((long)f->intValue); case TS_DATATYPE_DOUBLE: return PyFloat_FromDouble(f->doubleValue); case TS_DATATYPE_STRING: return PyString_FromString(f->charValue); case TS_DATATYPE_RECORDLIST: teamtrack_raise("Recordlist fields are not supported by the " "Python interface to TeamTrack.", 0); case TS_DATATYPE_INTLIST: teamtrack_raise("Intlist fields are not supported by the " "Python interface to TeamTrack.", 0); case TS_DATATYPE_UNKNOWN: default: teamtrack_raise("Unknown field type.", 0); } } /* Instance methods */ static PyObject * teamtrack_record_add(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; if (!PyArg_ParseTuple(args, "")) return 0; /* Pass the TSRecord object twice, so that it gets updated to match the new record in the database. */ teamtrack_try(ttr->server->s->AddRecord(ttr->r, ttr->r), ttr->server->s, 0); Py_INCREF(self); /* It will be on Python's stack when returned. */ return self; } static PyObject * teamtrack_record_add_field(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; if (!PyArg_ParseTuple(args, "")) return 0; /* Check that the record is in the right format for the TS_FIELDS table. */ if (ttr->r->tableId != TS_TBLID_FIELDS) { teamtrack_raise("add_field only works for TS_FIELDS records", 0); } /* Get the field type of the field we are trying to add. */ TSField *field_type_field = ttr->r->fieldList.FindFieldByName("FLDTYPE", 0); if (!field_type_field) { teamtrack_raise("record passed to add_field has no FLDTYPE field", 0); } int field_type = field_type_field->intValue; /* Attempt to add it. */ teamtrack_try(ttr->server->s->AddField(ttr->r, TS_TBLID_FIELDS, field_type), ttr->server->s, 0); Py_INCREF(Py_None); /* It will be on Python's stack when returned. */ return Py_None; } static PyObject * teamtrack_record_has_key(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; PyObject *key; if (!PyArg_ParseTuple(args, "O", &key)) return 0; char *keystring = PyString_AsString(key); if (!keystring) return 0; TSField *f = ttr->r->fieldList.FindFieldByName(keystring, ttr->r->fieldType); return PyInt_FromLong(f != 0); } static PyObject * teamtrack_record_keys(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; if (!PyArg_ParseTuple(args, "")) return 0; int nkeys = ttr->r->fieldList.Length(); PyObject *keys = PyList_New(nkeys); if (!keys) return 0; int i; /* Position in Python list. */ TSPosition *p; /* Position in TSFieldList. */ for (i = 0, p = ttr->r->fieldList.GetFirst(); i < nkeys && p; ++i, p = ttr->r->fieldList.GetNext(p)) { TSField *f = ttr->r->fieldList.GetAt(p); if (!f) { teamtrack_set_error(ttr->r->serverRef); break; } PyObject *key = PyString_FromString(f->fieldName); if (!key) { break; } PyList_SetItem(keys, i, key); /* key is now owned by keys. Don't decref it because PyList_SetItem didn't incref it - see [van Rossum 1999-04-13, 1.10.2]. */ } if (PyErr_Occurred()) { Py_DECREF(keys); /* Delete. */ return 0; } return keys; } static PyObject * teamtrack_record_submit(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; Py_INCREF(Py_None); /* It will be on Python's stack when returned. */ return Py_None; } static PyObject * teamtrack_record_transition(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; Py_INCREF(Py_None); /* It will be on Python's stack when returned. */ return Py_None; } static PyObject * teamtrack_record_table(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; if (!PyArg_ParseTuple(args, "")) return 0; return PyInt_FromLong(ttr->r->tableId); } static PyObject * teamtrack_record_update(PyObject *self, PyObject *args) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; if (!PyArg_ParseTuple(args, "")) return 0; /* Pass the TSRecord object twice, so that it gets updated to match the update record in the database. */ teamtrack_try(ttr->server->s->UpdateRecord(ttr->r, ttr->r), ttr->server->s, 0); Py_INCREF(self); /* It will be on Python's stack when returned. */ return self; } static struct PyMethodDef teamtrack_record_methods[] = { { "add", teamtrack_record_add, 1 }, { "add_field", teamtrack_record_add_field, 1 }, { "has_key", teamtrack_record_has_key, 1 }, { "keys", teamtrack_record_keys, 1 }, { "table", teamtrack_record_table, 1 }, { "update", teamtrack_record_update, 1 }, { NULL, NULL } /* End of methods. */ }; /* Dictionary operators */ /* Length operator returns -1 on error. See Python's abstract.h. */ static int teamtrack_record_length(PyObject *self) { teamtrack_check_type(teamtrack_record, self, -1); teamtrack_record *ttr = (teamtrack_record *)self; return ttr->r->fieldList.Length(); } static PyObject * teamtrack_record_subscript(PyObject *self, PyObject *key) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; char *keystring = PyString_AsString(key); if (!keystring) return 0; TSField *f = ttr->r->fieldList.FindFieldByName(keystring, ttr->r->fieldType); if (!f) { PyErr_SetObject(PyExc_KeyError, key); return 0; } return teamtrack_field_to_pyobject(f); } /* It is not clear from the documentation, but assigning to a subscript must return 0 for success and -1 for failure. See PyDict_SetItem in Python's dictobject.c. */ static int teamtrack_record_ass_sub(PyObject *self, PyObject *key, PyObject *value) { teamtrack_check_type(teamtrack_record, self, -1); teamtrack_record *ttr = (teamtrack_record *)self; char *keystring = PyString_AsString(key); if (!keystring) return -1; TSField *f = ttr->r->fieldList.FindFieldByName(keystring, ttr->r->fieldType); if (!f) { PyErr_SetObject(PyExc_KeyError, key); return -1; } if (!value) { teamtrack_raise("Deletion of fields is not supported " "by TeamTrack records.", -1); } switch (f->dataType) { case TS_DATATYPE_BOOL: case TS_DATATYPE_INTEGER: { long v = PyInt_AsLong(value); if (PyErr_Occurred()) teamtrack_raise("Attempt to assign non-integer to integer field.", -1); if (v < INT_MIN || v > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "Field value outside integer range."); return -1; } teamtrack_try(ttr->r->SetInt(keystring, (int)v), ttr->r->serverRef, -1); return 0; } case TS_DATATYPE_DOUBLE: { double v = PyFloat_AsDouble(value); if (PyErr_Occurred()) teamtrack_raise("Attempt to assign non-float to double field.", -1); teamtrack_try(ttr->r->SetDouble(keystring, v), ttr->r->serverRef, -1); return 0; } case TS_DATATYPE_STRING: { char *v = PyString_AsString(value); if (!v) teamtrack_raise("Attempt to assign non-string to string field.", -1); teamtrack_try(ttr->r->SetString(keystring, v), ttr->r->serverRef, -1); return 0; } case TS_DATATYPE_RECORDLIST: teamtrack_raise("Recordlist fields are not supported by the " "Python interface to TeamTrack.", -1); case TS_DATATYPE_INTLIST: teamtrack_raise("Intlist fields are not supported by the " "Python interface to TeamTrack.", -1); case TS_DATATYPE_UNKNOWN: default: teamtrack_raise("Unknown field type.", -1); } } static PyMappingMethods teamtrack_record_mapping_methods = { teamtrack_record_length, teamtrack_record_subscript, teamtrack_record_ass_sub, }; /* Basic Python operators */ static void teamtrack_record_dealloc(PyObject *self) { if (!teamtrack_record_p(self)) Py_FatalError("teamtrack_record_deallocate called on an object that's " "not a teamtrack_record."); teamtrack_record *ttr = (teamtrack_record *)self; if (ttr->delete_p) delete ttr->r; Py_DECREF(ttr->server); /* INCREF is in teamtrack_record_new. */ PyMem_DEL(self); } static PyObject * teamtrack_record_repr(PyObject *self) { teamtrack_check_type(teamtrack_record, self, 0); teamtrack_record *ttr = (teamtrack_record *)self; TSString s = ttr->r->StringDump(1, " "); return PyString_FromString(s.GetBuffer()); } static PyObject * teamtrack_record_getattr(PyObject *self, char *name) { teamtrack_check_type(teamtrack_record, self, 0); return Py_FindMethod(teamtrack_record_methods, self, name); } /* Type object for the record class */ PyTypeObject teamtrack_record_type = { /* Type header */ PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "record", /* tp_name */ sizeof(teamtrack_record), /* tp_basicsize */ 0, /* tp_itemsize */ /* Basic methods */ teamtrack_record_dealloc, 0, /* print */ teamtrack_record_getattr, 0, /* setattr */ 0, /* compare */ teamtrack_record_repr, /* Type categories */ 0, /* number operators */ 0, /* sequence operators */ &teamtrack_record_mapping_methods, /* mapping operators */ /* Other methods are 0: see Python's object.h for details */ };