summaryrefslogtreecommitdiff
path: root/tablib/packages/dbfpy3/dbf.py
diff options
context:
space:
mode:
Diffstat (limited to 'tablib/packages/dbfpy3/dbf.py')
-rw-r--r--tablib/packages/dbfpy3/dbf.py293
1 files changed, 293 insertions, 0 deletions
diff --git a/tablib/packages/dbfpy3/dbf.py b/tablib/packages/dbfpy3/dbf.py
new file mode 100644
index 0000000..42de8a4
--- /dev/null
+++ b/tablib/packages/dbfpy3/dbf.py
@@ -0,0 +1,293 @@
+#! /usr/bin/env python
+"""DBF accessing helpers.
+
+FIXME: more documentation needed
+
+Examples:
+
+ Create new table, setup structure, add records:
+
+ dbf = Dbf(filename, new=True)
+ dbf.addField(
+ ("NAME", "C", 15),
+ ("SURNAME", "C", 25),
+ ("INITIALS", "C", 10),
+ ("BIRTHDATE", "D"),
+ )
+ for (n, s, i, b) in (
+ ("John", "Miller", "YC", (1980, 10, 11)),
+ ("Andy", "Larkin", "", (1980, 4, 11)),
+ ):
+ rec = dbf.newRecord()
+ rec["NAME"] = n
+ rec["SURNAME"] = s
+ rec["INITIALS"] = i
+ rec["BIRTHDATE"] = b
+ rec.store()
+ dbf.close()
+
+ Open existed dbf, read some data:
+
+ dbf = Dbf(filename, True)
+ for rec in dbf:
+ for fldName in dbf.fieldNames:
+ print '%s:\t %s (%s)' % (fldName, rec[fldName],
+ type(rec[fldName]))
+ print
+ dbf.close()
+
+"""
+"""History (most recent first):
+11-feb-2007 [als] export INVALID_VALUE;
+ Dbf: added .ignoreErrors, .INVALID_VALUE
+04-jul-2006 [als] added export declaration
+20-dec-2005 [yc] removed fromStream and newDbf methods:
+ use argument of __init__ call must be used instead;
+ added class fields pointing to the header and
+ record classes.
+17-dec-2005 [yc] split to several modules; reimplemented
+13-dec-2005 [yc] adapted to the changes of the `strutil` module.
+13-sep-2002 [als] support FoxPro Timestamp datatype
+15-nov-1999 [jjk] documentation updates, add demo
+24-aug-1998 [jjk] add some encodeValue methods (not tested), other tweaks
+08-jun-1998 [jjk] fix problems, add more features
+20-feb-1998 [jjk] fix problems, add more features
+19-feb-1998 [jjk] add create/write capabilities
+18-feb-1998 [jjk] from dbfload.py
+"""
+
+__version__ = "$Revision: 1.7 $"[11:-2]
+__date__ = "$Date: 2007/02/11 09:23:13 $"[7:-2]
+__author__ = "Jeff Kunce <kuncej@mail.conservation.state.mo.us>"
+
+__all__ = ["Dbf"]
+
+from . import header
+from . import record
+from .utils import INVALID_VALUE
+
+class Dbf(object):
+ """DBF accessor.
+
+ FIXME:
+ docs and examples needed (dont' forget to tell
+ about problems adding new fields on the fly)
+
+ Implementation notes:
+ ``_new`` field is used to indicate whether this is
+ a new data table. `addField` could be used only for
+ the new tables! If at least one record was appended
+ to the table it's structure couldn't be changed.
+
+ """
+
+ __slots__ = ("name", "header", "stream",
+ "_changed", "_new", "_ignore_errors")
+
+ HeaderClass = header.DbfHeader
+ RecordClass = record.DbfRecord
+ INVALID_VALUE = INVALID_VALUE
+
+ ## initialization and creation helpers
+
+ def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
+ """Initialize instance.
+
+ Arguments:
+ f:
+ Filename or file-like object.
+ new:
+ True if new data table must be created. Assume
+ data table exists if this argument is False.
+ readOnly:
+ if ``f`` argument is a string file will
+ be opend in read-only mode; in other cases
+ this argument is ignored. This argument is ignored
+ even if ``new`` argument is True.
+ headerObj:
+ `header.DbfHeader` instance or None. If this argument
+ is None, new empty header will be used with the
+ all fields set by default.
+ ignoreErrors:
+ if set, failing field value conversion will return
+ ``INVALID_VALUE`` instead of raising conversion error.
+
+ """
+ if isinstance(f, str):
+ # a filename
+ self.name = f
+ if new:
+ # new table (table file must be
+ # created or opened and truncated)
+ self.stream = open(f, "w+b")
+ else:
+ # tabe file must exist
+ self.stream = open(f, ("r+b", "rb")[bool(readOnly)])
+ else:
+ # a stream
+ self.name = getattr(f, "name", "")
+ self.stream = f
+ if new:
+ # if this is a new table, header will be empty
+ self.header = self.HeaderClass()
+ else:
+ # or instantiated using stream
+ self.header = self.HeaderClass.fromStream(self.stream)
+ self.ignoreErrors = ignoreErrors
+ self._new = bool(new)
+ self._changed = False
+
+ ## properties
+
+ closed = property(lambda self: self.stream.closed)
+ recordCount = property(lambda self: self.header.recordCount)
+ fieldNames = property(
+ lambda self: [_fld.name for _fld in self.header.fields])
+ fieldDefs = property(lambda self: self.header.fields)
+ changed = property(lambda self: self._changed or self.header.changed)
+
+ def ignoreErrors(self, value):
+ """Update `ignoreErrors` flag on the header object and self"""
+ self.header.ignoreErrors = self._ignore_errors = bool(value)
+ ignoreErrors = property(
+ lambda self: self._ignore_errors,
+ ignoreErrors,
+ doc="""Error processing mode for DBF field value conversion
+
+ if set, failing field value conversion will return
+ ``INVALID_VALUE`` instead of raising conversion error.
+
+ """)
+
+ ## protected methods
+
+ def _fixIndex(self, index):
+ """Return fixed index.
+
+ This method fails if index isn't a numeric object
+ (long or int). Or index isn't in a valid range
+ (less or equal to the number of records in the db).
+
+ If ``index`` is a negative number, it will be
+ treated as a negative indexes for list objects.
+
+ Return:
+ Return value is numeric object maning valid index.
+
+ """
+ if not isinstance(index, int):
+ raise TypeError("Index must be a numeric object")
+ if index < 0:
+ # index from the right side
+ # fix it to the left-side index
+ index += len(self) + 1
+ if index >= len(self):
+ raise IndexError("Record index out of range")
+ return index
+
+ ## iterface methods
+
+ def close(self):
+ self.flush()
+ self.stream.close()
+
+ def flush(self):
+ """Flush data to the associated stream."""
+ if self.changed:
+ self.header.setCurrentDate()
+ self.header.write(self.stream)
+ self.stream.flush()
+ self._changed = False
+
+ def indexOfFieldName(self, name):
+ """Index of field named ``name``."""
+ # FIXME: move this to header class
+ names = [f.name for f in self.header.fields]
+ return names.index(name.upper())
+
+ def newRecord(self):
+ """Return new record, which belong to this table."""
+ return self.RecordClass(self)
+
+ def append(self, record):
+ """Append ``record`` to the database."""
+ record.index = self.header.recordCount
+ record._write()
+ self.header.recordCount += 1
+ self._changed = True
+ self._new = False
+
+ def addField(self, *defs):
+ """Add field definitions.
+
+ For more information see `header.DbfHeader.addField`.
+
+ """
+ if self._new:
+ self.header.addField(*defs)
+ else:
+ raise TypeError("At least one record was added, "
+ "structure can't be changed")
+
+ ## 'magic' methods (representation and sequence interface)
+
+ def __repr__(self):
+ return "Dbf stream '%s'\n" % self.stream + repr(self.header)
+
+ def __len__(self):
+ """Return number of records."""
+ return self.recordCount
+
+ def __getitem__(self, index):
+ """Return `DbfRecord` instance."""
+ return self.RecordClass.fromStream(self, self._fixIndex(index))
+
+ def __setitem__(self, index, record):
+ """Write `DbfRecord` instance to the stream."""
+ record.index = self._fixIndex(index)
+ record._write()
+ self._changed = True
+ self._new = False
+
+ #def __del__(self):
+ # """Flush stream upon deletion of the object."""
+ # self.flush()
+
+
+def demoRead(filename):
+ _dbf = Dbf(filename, True)
+ for _rec in _dbf:
+ print()
+ print(repr(_rec))
+ _dbf.close()
+
+def demoCreate(filename):
+ _dbf = Dbf(filename, new=True)
+ _dbf.addField(
+ ("NAME", "C", 15),
+ ("SURNAME", "C", 25),
+ ("INITIALS", "C", 10),
+ ("BIRTHDATE", "D"),
+ )
+ for (_n, _s, _i, _b) in (
+ ("John", "Miller", "YC", (1981, 1, 2)),
+ ("Andy", "Larkin", "AL", (1982, 3, 4)),
+ ("Bill", "Clinth", "", (1983, 5, 6)),
+ ("Bobb", "McNail", "", (1984, 7, 8)),
+ ):
+ _rec = _dbf.newRecord()
+ _rec["NAME"] = _n
+ _rec["SURNAME"] = _s
+ _rec["INITIALS"] = _i
+ _rec["BIRTHDATE"] = _b
+ _rec.store()
+ print(repr(_dbf))
+ _dbf.close()
+
+if (__name__=='__main__'):
+ import sys
+ _name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
+ demoCreate(_name)
+ demoRead(_name)
+
+# vim: set et sw=4 sts=4 :