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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26