/[thuban]/branches/WIP-pyshapelib-bramz/libraries/pyshapelib/dbflibmodule.c
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/libraries/pyshapelib/dbflibmodule.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2745 - (show annotations)
Thu Mar 15 22:27:02 2007 UTC (17 years, 11 months ago) by bramz
File MIME type: text/plain
File size: 13946 byte(s)
Added FTLogical support to dbflib.  Accept None as no-data for M values.  Added name and mode keywords to mimic Python's file behaviour where appropriate.  Added unpack_vertex() to lift some load of shpobject_init().
1 #include "pyshapelib_common.h"
2
3 /* --- DBFFile ------------------------------------------------------------------------------------------------------- */
4
5 typedef struct {
6 PyObject_HEAD
7 DBFHandle handle;
8 } DBFFileObject;
9
10
11
12 /* allocator
13 */
14 static PyObject* dbffile_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
15 {
16 DBFFileObject* self;
17 self = (DBFFileObject*) type->tp_alloc(type, 0);
18 self->handle = NULL;
19 return (PyObject*) self;
20 }
21
22
23
24 /* deallocator
25 */
26 static void dbffile_dealloc(DBFFileObject* self)
27 {
28 DBFClose(self->handle);
29 self->handle = NULL;
30 self->ob_type->tp_free((PyObject*)self);
31 }
32
33
34
35 /* constructor
36 */
37 static int dbffile_init(DBFFileObject* self, PyObject* args, PyObject* kwds)
38 {
39 char* file;
40 char* mode = "rb";
41 static char *kwlist[] = {"name", "mode", NULL};
42 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|s:__init__", kwlist,
43 Py_FileSystemDefaultEncoding, &file, &mode)) return -1;
44
45 self->handle = DBFOpen(file, mode);
46 PyMem_Free(file);
47
48 return self->handle ? 0 : -1;
49 }
50
51
52
53 static PyObject* dbffile_close(DBFFileObject* self)
54 {
55 DBFClose(self->handle);
56 self->handle = NULL;
57 Py_RETURN_NONE;
58 }
59
60
61
62 static PyObject* dbffile_field_count(DBFFileObject* self)
63 {
64 return PyInt_FromLong((long)DBFGetFieldCount(self->handle));
65 }
66
67
68
69 static PyObject* dbffile_record_count(DBFFileObject* self)
70 {
71 return PyInt_FromLong((long)DBFGetRecordCount(self->handle));
72 }
73
74
75
76 static PyObject* dbffile_field_info(DBFFileObject* self, PyObject* args)
77 {
78 char field_name[12];
79 int field, width = 0, decimals = 0, field_type;
80
81 if (!PyArg_ParseTuple(args, "i:field_info", &field)) return NULL;
82
83 field_name[0] = '\0';
84 field_type = DBFGetFieldInfo(self->handle, field, field_name, &width, &decimals);
85
86 return Py_BuildValue("isii", field_type, field_name, width, decimals);
87 }
88
89
90
91 static PyObject* dbffile_add_field(DBFFileObject* self, PyObject* args)
92 {
93 char* name;
94 int type, width, decimals;
95 int field;
96
97 if (!PyArg_ParseTuple(args, "siii:add_field", &name, &type, &width, &decimals)) return NULL;
98
99 field = DBFAddField(self->handle, name, (DBFFieldType)type, width, decimals);
100
101 if (field < 0)
102 {
103 PyErr_SetString(PyExc_ValueError, "Failed to add field due to inappropriate field definition");
104 return NULL;
105 }
106 return PyInt_FromLong((long)field);
107 }
108
109
110
111 /* Read one attribute from the dbf handle and return it as a new python object
112 *
113 * If an error occurs, set the appropriate Python exception and return
114 * NULL.
115 *
116 * Assume that the values of the record and field arguments are valid.
117 * The name argument will be passed to DBFGetFieldInfo as is and should
118 * thus be either NULL or a pointer to an array of at least 12 chars
119 */
120 static PyObject* do_read_attribute(DBFHandle handle, int record, int field, char * name)
121 {
122 int type, width;
123 const char* temp;
124 type = DBFGetFieldInfo(handle, field, name, &width, NULL);
125
126 /* For strings NULL and the empty string are indistinguishable
127 * in DBF files. We prefer empty strings instead for backwards
128 * compatibility reasons because older wrapper versions returned
129 * emtpy strings as empty strings.
130 */
131 if (type != FTString && DBFIsAttributeNULL(handle, record, field))
132 {
133 Py_RETURN_NONE;
134 }
135 else
136 {
137 switch (type)
138 {
139 case FTString:
140 temp = DBFReadStringAttribute(handle, record, field);
141 if (temp) return PyString_FromString(temp);
142
143 case FTInteger:
144 return PyInt_FromLong((long)DBFReadIntegerAttribute(handle, record, field));
145
146 case FTDouble:
147 return PyFloat_FromDouble(DBFReadDoubleAttribute(handle, record, field));
148
149 case FTLogical:
150 temp = DBFReadLogicalAttribute(handle, record, field);
151 if (temp)
152 {
153 switch (temp[0])
154 {
155 case 'F':
156 case 'N':
157 Py_RETURN_FALSE;
158 case 'T':
159 case 'Y':
160 Py_RETURN_TRUE;
161 }
162 }
163 break;
164
165 default:
166 PyErr_Format(PyExc_TypeError, "Invalid field data type %d", type);
167 return NULL;
168 }
169 }
170
171 PyErr_Format(PyExc_IOError, "Can't read value for row %d column %d", record, field);
172 return NULL;
173 }
174
175
176
177 /* the read_attribute method. Return the value of the given record and
178 * field as a python object of the appropriate type.
179 */
180 static PyObject* dbffile_read_attribute(DBFFileObject* self, PyObject* args)
181 {
182 int record, field;
183
184 if (!PyArg_ParseTuple(args, "ii:read_field", &record, &field)) return NULL;
185
186 if (record < 0 || record >= DBFGetRecordCount(self->handle))
187 {
188 PyErr_Format(PyExc_ValueError,
189 "record index %d out of bounds (record count: %d)",
190 record, DBFGetRecordCount(self->handle));
191 return NULL;
192 }
193
194 if (field < 0 || field >= DBFGetFieldCount(self->handle))
195 {
196 PyErr_Format(PyExc_ValueError,
197 "field index %d out of bounds (field count: %d)",
198 field, DBFGetFieldCount(self->handle));
199 return NULL;
200 }
201
202 return do_read_attribute(self->handle, record, field, NULL);
203 }
204
205
206
207 /* the read_record method. Return the record record as a dictionary with
208 * whose keys are the names of the fields, and their values as the
209 * appropriate Python type.
210 */
211 static PyObject* dbffile_read_record(DBFFileObject* self, PyObject* args)
212 {
213 int record;
214 int num_fields;
215 int i;
216 char name[12];
217 PyObject *dict;
218 PyObject *value = NULL;
219
220 if (!PyArg_ParseTuple(args, "i:read_record", &record)) return NULL;
221
222 if (record < 0 || record >= DBFGetRecordCount(self->handle))
223 {
224 PyErr_Format(PyExc_ValueError,
225 "record index %d out of bounds (record count: %d)",
226 record, DBFGetRecordCount(self->handle));
227 return NULL;
228 }
229
230 dict = PyDict_New();
231 if (!dict) return NULL;
232
233 num_fields = DBFGetFieldCount(self->handle);
234 for (i = 0; i < num_fields; i++)
235 {
236 value = do_read_attribute(self->handle, record, i, name);
237 if (!value || PyDict_SetItemString(dict, name, value) < 0) goto fail;
238 Py_DECREF(value);
239 value = NULL;
240 }
241
242 return dict;
243
244 fail:
245 Py_XDECREF(value);
246 Py_DECREF(dict);
247 return NULL;
248 }
249
250
251
252 /* write a single field of a record. */
253 static int do_write_field(DBFHandle handle, int record, int field, int type, PyObject* value)
254 {
255 char * string_value;
256 int int_value;
257 double double_value;
258 int logical_value;
259
260 if (value == Py_None)
261 {
262 if (DBFWriteNULLAttribute(handle, record, field)) return 1;
263 }
264 else
265 {
266 switch (type)
267 {
268 case FTString:
269 string_value = PyString_AsString(value);
270 if (!string_value) return 0;
271 if (DBFWriteStringAttribute(handle, record, field, string_value)) return 1;
272 break;
273
274 case FTInteger:
275 int_value = PyInt_AsLong(value);
276 if (int_value == -1 && PyErr_Occurred()) return 0;
277 if (DBFWriteIntegerAttribute(handle, record, field, int_value)) return 1;
278 break;
279
280 case FTDouble:
281 double_value = PyFloat_AsDouble(value);
282 if (double_value == -1 && PyErr_Occurred()) return 0;
283 if (DBFWriteDoubleAttribute(handle, record, field, double_value)) return 1;
284 break;
285
286 case FTLogical:
287 logical_value = PyObject_IsTrue(value);
288 if (logical_value == -1) return 0;
289 if (DBFWriteLogicalAttribute(handle, record, field, logical_value ? 'T' : 'F')) return 1;
290 break;
291
292 default:
293 PyErr_Format(PyExc_TypeError, "Invalid field data type %d", type);
294 return 0;
295 }
296 }
297
298 PyErr_Format(PyExc_IOError, "can't write field %d of record %d", field, record);
299 return 0;
300 }
301
302
303
304 static PyObject* dbffile_write_field(DBFFileObject* self, PyObject* args)
305 {
306 int record, field;
307 PyObject* value;
308 int type;
309
310 if (!PyArg_ParseTuple(args, "iiO:write_field", &record, &field, &value)) return NULL;
311
312 if (field < 0 || field >= DBFGetFieldCount(self->handle))
313 {
314 PyErr_Format(PyExc_ValueError,
315 "field index %d out of bounds (field count: %d)",
316 field, DBFGetFieldCount(self->handle));
317 return NULL;
318 }
319
320 type = DBFGetFieldInfo(self->handle, field, NULL, NULL, NULL);
321 if (!do_write_field(self->handle, record, field, type, value)) return NULL;
322 Py_RETURN_NONE;
323 }
324
325
326
327 static PyObject* dbffile_write_record(DBFFileObject* self, PyObject* args)
328 {
329 int record;
330 PyObject* record_object;
331 int i, num_fields;
332
333 int type;
334 char name[12];
335 PyObject* value = NULL;
336
337 if (!PyArg_ParseTuple(args, "iO:write_record", &record, &record_object)) return NULL;
338
339 num_fields = DBFGetFieldCount(self->handle);
340
341 /* mimic ShapeFile functionality where id = -1 means appending */
342 if (record == -1)
343 {
344 record = num_fields;
345 }
346
347 if (PySequence_Check(record_object))
348 {
349 /* It's a sequence object. Iterate through all items in the
350 * sequence and write them to the appropriate field.
351 */
352 if (PySequence_Length(record_object) != num_fields)
353 {
354 PyErr_SetString(PyExc_TypeError, "record must have one item for each field");
355 return NULL;
356 }
357 for (i = 0; i < num_fields; ++i)
358 {
359 type = DBFGetFieldInfo(self->handle, i, NULL, NULL, NULL);
360 value = PySequence_GetItem(record_object, i);
361 if (!value) return NULL;
362 if (!do_write_field(self->handle, record, i, type, value))
363 {
364 Py_DECREF(value);
365 return NULL;
366 }
367 Py_DECREF(value);
368 }
369 }
370 else
371 {
372 /* It's a dictionary-like object. Iterate over the names of the
373 * known fields and write the corresponding item
374 */
375 for (i = 0; i < num_fields; ++i)
376 {
377 name[0] = '\0';
378 type = DBFGetFieldInfo(self->handle, i, name, NULL, NULL);
379 value = PyDict_GetItemString(record_object, name);
380 if (value && !do_write_field(self->handle, record, i, type, value)) return NULL;
381 }
382 }
383
384 return PyInt_FromLong((long)record);
385 }
386
387
388
389 static PyObject* dbffile_repr(DBFFileObject* self)
390 {
391 /* TODO: it would be nice to do something like "dbflib.DBFFile(filename, mode)" instead */
392 return PyString_FromFormat("<dbflib.DBFFile object at %p>", self->handle);
393 }
394
395
396
397 /* The commit method implementation
398 *
399 * The method relies on the DBFUpdateHeader method which is not
400 * available in shapelib <= 1.2.10. setup.py defines
401 * HAVE_UPDATE_HEADER's value depending on whether the function is
402 * available in the shapelib version the code is compiled with.
403 */
404 #if HAVE_UPDATE_HEADER
405 static PyObject* dbffile_commit(DBFFileObject* self)
406 {
407 DBFUpdateHeader(self->handle);
408 Py_RETURN_NONE;
409 }
410 #endif
411
412
413
414 static struct PyMethodDef dbffile_methods[] =
415 {
416 {"close", (PyCFunction)dbffile_close, METH_NOARGS,
417 "close() -> None\n\n"
418 "closes DBFFile"},
419 {"field_count", (PyCFunction)dbffile_field_count, METH_NOARGS,
420 "field_count() -> integer\n\n"
421 "returns number of fields currently defined"},
422 {"record_count", (PyCFunction)dbffile_record_count, METH_NOARGS,
423 "record_count() -> integer\n\n"
424 "returns number of records that currently exist"},
425 {"field_info", (PyCFunction)dbffile_field_info, METH_VARARGS,
426 "field_info(field_index) -> (type, name, width, decimals)\n\n"
427 "returns info of a field as a tuple with:\n"
428 "- type: the type of the field corresponding to the integer value of one "
429 " of the constants FTString, FTInteger, ...\n"
430 "- name: the name of the field as a string\n"
431 "- width: the width of the field as a number of characters\n"
432 "- decimals: the number of decimal digits" },
433 {"add_field", (PyCFunction)dbffile_add_field, METH_VARARGS,
434 "add_field(type, name, width, decimals) -> field_index\n\n"
435 "adds a new field and returns field index if successful\n"
436 "- type: the type of the field corresponding to the integer value of one "
437 " of the constants FTString, FTInteger, ...\n"
438 "- name: the name of the field as a string\n"
439 "- width: the width of the field as a number of characters\n"
440 "- decimals: the number of decimal digits" },
441 {"read_attribute", (PyCFunction)dbffile_read_attribute, METH_VARARGS,
442 "read_attribute(record_index, field_index) -> value\n\n"
443 "returns the value of one field of a record"},
444 {"read_record", (PyCFunction)dbffile_read_record, METH_VARARGS,
445 "read_record(record_index) -> dict\n\n"
446 "returns an entire record as a dictionary of field names and values"},
447 {"write_field", (PyCFunction)dbffile_write_field, METH_VARARGS,
448 "write_field(record_index, field_index, new_value)\n"
449 "writes a single field of a record"},
450 {"write_record", (PyCFunction)dbffile_write_record, METH_VARARGS,
451 "write_record(record_index, record) -> record_index\n\n"
452 "Writes an entire record as a dict or a sequence, and return index of record\n"
453 "Record can either be a dictionary in which case the keys are used as field names, "
454 "or a sequence that must have an item for every field (length = field_count())"},
455 #if HAVE_UPDATE_HEADER
456 {"commit", (PyCFunction)dbffile_read_record, METH_NOARGS,
457 "commit() -> None"},
458 #endif
459 {NULL}
460 };
461
462
463
464 static struct PyGetSetDef dbffile_getsetters[] =
465 {
466 {NULL}
467 };
468
469
470
471 static PyTypeObject DBFFileType = PYSHAPELIB_DEFINE_TYPE(DBFFileObject, dbffile, "shapelib.DBFFile", 0);
472
473
474
475 /* --- dbflib -------------------------------------------------------------------------------------------------------- */
476
477 static PyObject* dbflib_open(PyObject* module, PyObject* args)
478 {
479 return PyObject_CallObject((PyObject*)&DBFFileType, args);
480 }
481
482
483
484 static PyObject* dbflib_create(PyObject* module, PyObject* args)
485 {
486 char* file;
487 DBFFileObject* result;
488
489 if (!PyArg_ParseTuple(args, "et:create", Py_FileSystemDefaultEncoding, &file)) return NULL;
490
491 result = PyObject_New(DBFFileObject, &DBFFileType);
492 if (!result)
493 {
494 return PyErr_NoMemory();
495 }
496
497 result->handle = DBFCreate(file);
498 if (!result->handle)
499 {
500 PyObject_Del((PyObject*)result);
501 PyErr_SetString(PyExc_RuntimeError, "Failed to create DBFFile");
502 return NULL;
503 }
504
505 return (PyObject*) result;
506 }
507
508
509
510 static struct PyMethodDef dbflib_methods[] =
511 {
512 {"open", (PyCFunction)dbflib_open, METH_VARARGS,
513 "open(name [, mode]) -> DBFFile\n\n"
514 "opens a DBFFile" },
515 {"create", (PyCFunction)dbflib_create, METH_VARARGS,
516 "create(name) -> DBFFile\n\n"
517 "create a DBFFile" },
518 {NULL}
519 };
520
521
522
523 PyMODINIT_FUNC initdbflib(void)
524 {
525 PyObject* module = Py_InitModule("dbflib", dbflib_methods);
526 if (!module) return;
527
528 PYSHAPELIB_ADD_TYPE(DBFFileType, "DBFFile");
529
530 PYSHAPELIB_ADD_CONSTANT(FTString);
531 PYSHAPELIB_ADD_CONSTANT(FTInteger);
532 PYSHAPELIB_ADD_CONSTANT(FTDouble);
533 PYSHAPELIB_ADD_CONSTANT(FTLogical);
534 PYSHAPELIB_ADD_CONSTANT(FTInvalid);
535 PyModule_AddIntConstant(module, "_have_commit", HAVE_UPDATE_HEADER);
536 }

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26