/[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 2744 - (show annotations)
Thu Mar 15 13:48:58 2007 UTC (17 years, 11 months ago) by bramz
File MIME type: text/plain
File size: 13722 byte(s)
shapelibmodule.c, dbflibmodule.c: added some Unicode support for the filenames (no internal encoding for DBFFile yet).  It now should similar Unicode support Python's file() (concerning the filename, that is).
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 if (kwds != NULL && PyDict_Size(kwds) > 0)
42 {
43 PyErr_Format(PyExc_TypeError, "dbflib.DBFFile.__init__ takes no keyword arguments");
44 return -1;
45 }
46
47 if (!PyArg_ParseTuple(args, "et|s:__init__", Py_FileSystemDefaultEncoding, &file, &mode)) return -1;
48
49 self->handle = DBFOpen(file, mode);
50 PyMem_Free(file);
51
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)
146 {
147 PyErr_Format(PyExc_IOError,
148 "Can't read value for row %d column %d",
149 record, field);
150 return NULL;
151 }
152 return PyString_FromString(temp);
153
154 case FTInteger:
155 return PyInt_FromLong((long)DBFReadIntegerAttribute(handle, record, field));
156
157 case FTDouble:
158 return PyFloat_FromDouble(DBFReadDoubleAttribute(handle, record, field));
159
160 default:
161 PyErr_Format(PyExc_TypeError, "Invalid field data type %d", type);
162 return NULL;
163 }
164 }
165 }
166
167
168
169 /* the read_attribute method. Return the value of the given record and
170 * field as a python object of the appropriate type.
171 */
172 static PyObject* dbffile_read_attribute(DBFFileObject* self, PyObject* args)
173 {
174 int record, field;
175
176 if (!PyArg_ParseTuple(args, "ii:read_field", &record, &field)) return NULL;
177
178 if (record < 0 || record >= DBFGetRecordCount(self->handle))
179 {
180 PyErr_Format(PyExc_ValueError,
181 "record index %d out of bounds (record count: %d)",
182 record, DBFGetRecordCount(self->handle));
183 return NULL;
184 }
185
186 if (field < 0 || field >= DBFGetFieldCount(self->handle))
187 {
188 PyErr_Format(PyExc_ValueError,
189 "field index %d out of bounds (field count: %d)",
190 field, DBFGetFieldCount(self->handle));
191 return NULL;
192 }
193
194 return do_read_attribute(self->handle, record, field, NULL);
195 }
196
197
198
199 /* the read_record method. Return the record record as a dictionary with
200 * whose keys are the names of the fields, and their values as the
201 * appropriate Python type.
202 */
203 static PyObject* dbffile_read_record(DBFFileObject* self, PyObject* args)
204 {
205 int record;
206 int num_fields;
207 int i;
208 char name[12];
209 PyObject *dict;
210 PyObject *value = NULL;
211
212 if (!PyArg_ParseTuple(args, "i:read_record", &record)) return NULL;
213
214 if (record < 0 || record >= DBFGetRecordCount(self->handle))
215 {
216 PyErr_Format(PyExc_ValueError,
217 "record index %d out of bounds (record count: %d)",
218 record, DBFGetRecordCount(self->handle));
219 return NULL;
220 }
221
222 dict = PyDict_New();
223 if (!dict) return NULL;
224
225 num_fields = DBFGetFieldCount(self->handle);
226 for (i = 0; i < num_fields; i++)
227 {
228 value = do_read_attribute(self->handle, record, i, name);
229 if (!value || PyDict_SetItemString(dict, name, value) < 0) goto fail;
230 Py_DECREF(value);
231 value = NULL;
232 }
233
234 return dict;
235
236 fail:
237 Py_XDECREF(value);
238 Py_DECREF(dict);
239 return NULL;
240 }
241
242
243
244 /* write a single field of a record. */
245 static int do_write_field(DBFHandle handle, int record, int field, int type, PyObject* value)
246 {
247 char * string_value;
248 int int_value;
249 double double_value;
250
251 if (value == Py_None)
252 {
253 if (!DBFWriteNULLAttribute(handle, record, field))
254 {
255 PyErr_Format(PyExc_IOError,
256 "can't write NULL field %d of record %d",
257 field, record);
258 return 0;
259 }
260 }
261 else
262 {
263 switch (type)
264 {
265 case FTString:
266 string_value = PyString_AsString(value);
267 if (!string_value) return 0;
268 if (!DBFWriteStringAttribute(handle, record, field, string_value))
269 {
270 PyErr_Format(PyExc_IOError,
271 "can't write field %d of record %d",
272 field, record);
273 return 0;
274 }
275 break;
276
277 case FTInteger:
278 int_value = PyInt_AsLong(value);
279 if (int_value == -1 && PyErr_Occurred()) return 0;
280 if (!DBFWriteIntegerAttribute(handle, record, field, int_value))
281 {
282 PyErr_Format(PyExc_IOError,
283 "can't write field %d of record %d",
284 field, record);
285 return 0;
286 }
287 break;
288
289 case FTDouble:
290 double_value = PyFloat_AsDouble(value);
291 if (double_value == -1 && PyErr_Occurred()) return 0;
292 if (!DBFWriteDoubleAttribute(handle, record, field, double_value))
293 {
294 PyErr_Format(PyExc_IOError,
295 "can't write field %d of record %d",
296 field, record);
297 return 0;
298 }
299 break;
300
301 default:
302 PyErr_Format(PyExc_TypeError, "Invalid field data type %d", type);
303 return 0;
304 }
305 }
306
307 return 1;
308 }
309
310
311
312 static PyObject* dbffile_write_field(DBFFileObject* self, PyObject* args)
313 {
314 int record, field;
315 PyObject* value;
316 int type;
317
318 if (!PyArg_ParseTuple(args, "iiO:write_field", &record, &field, &value)) return NULL;
319
320 if (field < 0 || field >= DBFGetFieldCount(self->handle))
321 {
322 PyErr_Format(PyExc_ValueError,
323 "field index %d out of bounds (field count: %d)",
324 field, DBFGetFieldCount(self->handle));
325 return NULL;
326 }
327
328 type = DBFGetFieldInfo(self->handle, field, NULL, NULL, NULL);
329 if (!do_write_field(self->handle, record, field, type, value)) return NULL;
330 Py_RETURN_NONE;
331 }
332
333
334
335 static PyObject* dbffile_write_record(DBFFileObject* self, PyObject* args)
336 {
337 int record;
338 PyObject* record_object;
339 int i, num_fields;
340
341 int type;
342 char name[12];
343 PyObject* value = NULL;
344
345 if (!PyArg_ParseTuple(args, "iO:write_record", &record, &record_object)) return NULL;
346
347 num_fields = DBFGetFieldCount(self->handle);
348
349 /* mimic ShapeFile functionality where id = -1 means appending */
350 if (record == -1)
351 {
352 record = num_fields;
353 }
354
355 if (PySequence_Check(record_object))
356 {
357 /* It's a sequence object. Iterate through all items in the
358 * sequence and write them to the appropriate field.
359 */
360 if (PySequence_Length(record_object) != num_fields)
361 {
362 PyErr_SetString(PyExc_TypeError, "record must have one item for each field");
363 return NULL;
364 }
365 for (i = 0; i < num_fields; ++i)
366 {
367 type = DBFGetFieldInfo(self->handle, i, NULL, NULL, NULL);
368 value = PySequence_GetItem(record_object, i);
369 if (!value) return NULL;
370 if (!do_write_field(self->handle, record, i, type, value))
371 {
372 Py_DECREF(value);
373 return NULL;
374 }
375 Py_DECREF(value);
376 }
377 }
378 else
379 {
380 /* It's a dictionary-like object. Iterate over the names of the
381 * known fields and write the corresponding item
382 */
383 for (i = 0; i < num_fields; ++i)
384 {
385 name[0] = '\0';
386 type = DBFGetFieldInfo(self->handle, i, name, NULL, NULL);
387 value = PyDict_GetItemString(record_object, name);
388 if (value && !do_write_field(self->handle, record, i, type, value)) return NULL;
389 }
390 }
391
392 return PyInt_FromLong((long)record);
393 }
394
395
396
397 static PyObject* dbffile_repr(DBFFileObject* self)
398 {
399 /* TODO: it would be nice to do something like "dbflib.DBFFile(filename, mode)" instead */
400 return PyString_FromFormat("<dbflib.DBFFile object at %p>", self->handle);
401 }
402
403
404
405 /* The commit method implementation
406 *
407 * The method relies on the DBFUpdateHeader method which is not
408 * available in shapelib <= 1.2.10. setup.py defines
409 * HAVE_UPDATE_HEADER's value depending on whether the function is
410 * available in the shapelib version the code is compiled with.
411 */
412 #if HAVE_UPDATE_HEADER
413 static PyObject* dbffile_commit(DBFFileObject* self)
414 {
415 DBFUpdateHeader(self->handle);
416 Py_RETURN_NONE;
417 }
418 #endif
419
420
421
422 static struct PyMethodDef dbffile_methods[] =
423 {
424 {"close", (PyCFunction)dbffile_close, METH_NOARGS,
425 "close()\n"
426 "close DBFFile"},
427 {"field_count", (PyCFunction)dbffile_field_count, METH_NOARGS,
428 "field_count()\n"
429 "returns number of fields currently defined"},
430 {"record_count", (PyCFunction)dbffile_record_count, METH_NOARGS,
431 "record_count()\n"
432 "returns number of records that currently exist"},
433 {"field_info", (PyCFunction)dbffile_field_info, METH_VARARGS,
434 "field_info(field_index)\n"
435 "returns info of a field as a tuple (type, name, width, decimals) with:\n"
436 "-type: the type of the field corresponding to the integer value of one of the constants FTString, FTInteger, ...\n"
437 "-name: the name of the field as a string\n"
438 "-width: the width of the field as a number of characters\n"
439 "-decimals: the number of decimal digits" },
440 {"add_field", (PyCFunction)dbffile_add_field, METH_VARARGS,
441 "add_field(type, name, width, decimals)\n"
442 "adds a new field and returns field index if successful\n"
443 "-type: the type of the field corresponding to the integer value of one of the constants FTString, FTInteger, ...\n"
444 "-name: the name of the field as a string\n"
445 "-width: the width of the field as a number of characters\n"
446 "-decimals: the number of decimal digits" },
447 {"read_attribute", (PyCFunction)dbffile_read_attribute, METH_VARARGS,
448 "read_attribute(record_index, field_index)\n"
449 "return the value of one field of a record"},
450 {"read_record", (PyCFunction)dbffile_read_record, METH_VARARGS,
451 "read_record(record_index)\n"
452 "return an entire record as a dict of field names and values"},
453 {"write_field", (PyCFunction)dbffile_write_field, METH_VARARGS,
454 "write_field(record_index, field_index, new_value)\n"
455 "write a single field of a record"},
456 {"write_record", (PyCFunction)dbffile_write_record, METH_VARARGS,
457 "write_record(record_index, record)\n"
458 "write an entire record as a dict or a sequence\n"
459 "record can either be a dictionary in which case the keys are used as field names, "
460 "or a sequence that must have an item for every field (length = field_count())"},
461 #if HAVE_UPDATE_HEADER
462 {"commit", (PyCFunction)dbffile_read_record, METH_NOARGS,
463 "commit()"},
464 #endif
465 {NULL}
466 };
467
468
469
470 static struct PyGetSetDef dbffile_getsetters[] =
471 {
472 {NULL}
473 };
474
475
476
477 static PyTypeObject DBFFileType = PYSHAPELIB_DEFINE_TYPE(DBFFileObject, dbffile, "shapelib.DBFFile", 0);
478
479
480
481 /* --- dbflib -------------------------------------------------------------------------------------------------------- */
482
483 static PyObject* dbflib_open(PyObject* module, PyObject* args)
484 {
485 return PyObject_CallObject((PyObject*)&DBFFileType, args);
486 }
487
488
489
490 static PyObject* dbflib_create(PyObject* module, PyObject* args)
491 {
492 char* file;
493 DBFFileObject* result;
494
495 if (!PyArg_ParseTuple(args, "et:create", Py_FileSystemDefaultEncoding, &file)) return NULL;
496
497 result = PyObject_New(DBFFileObject, &DBFFileType);
498 if (!result)
499 {
500 return PyErr_NoMemory();
501 }
502
503 result->handle = DBFCreate(file);
504 if (!result->handle)
505 {
506 PyObject_Del((PyObject*)result);
507 PyErr_SetString(PyExc_RuntimeError, "Failed to create DBFFile");
508 return NULL;
509 }
510
511 return (PyObject*) result;
512 }
513
514
515
516 static struct PyMethodDef dbflib_methods[] =
517 {
518 {"open", (PyCFunction)dbflib_open, METH_VARARGS,
519 "open(filename [, mode])\n"
520 "open a DBFFile" },
521 {"create", (PyCFunction)dbflib_create, METH_VARARGS,
522 "create(filename)\n"
523 "create a DBFFile" },
524 {NULL}
525 };
526
527
528
529 PyMODINIT_FUNC initdbflib(void)
530 {
531 PyObject* module = Py_InitModule("dbflib", dbflib_methods);
532 if (!module) return;
533
534 PYSHAPELIB_ADD_TYPE(DBFFileType, "DBFFile");
535
536 PYSHAPELIB_ADD_CONSTANT(FTString);
537 PYSHAPELIB_ADD_CONSTANT(FTInteger);
538 PYSHAPELIB_ADD_CONSTANT(FTDouble);
539 PYSHAPELIB_ADD_CONSTANT(FTInvalid);
540 PyModule_AddIntConstant(module, "_have_commit", HAVE_UPDATE_HEADER);
541 }

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26