dbf

Package dbf |
[hide private]

# Source Code for Package dbf

      1  """
      2  Copyright
      3  =========
      4      - Copyright: 2008-2009 Ad-Mail, Inc -- All rights reserved.
      5      - Author: Ethan Furman
      6      - Contact: ethan@stoneleaf.us
      7      - Organization: Ad-Mail, Inc.
      8      - Version: 0.88.016 as of 06 Dec 2010
      9
     10  Redistribution and use in source and binary forms, with or without
     11  modification, are permitted provided that the following conditions are met:
     12      - Redistributions of source code must retain the above copyright
     13        notice, this list of conditions and the following disclaimer.
     14      - Redistributions in binary form must reproduce the above copyright
     15        notice, this list of conditions and the following disclaimer in the
     16        documentation and/or other materials provided with the distribution.
     17      - Neither the name of Ad-Mail, Inc nor the
     18        names of its contributors may be used to endorse or promote products
     19        derived from this software without specific prior written permission.
     20
     21  THIS SOFTWARE IS PROVIDED BY Ad-Mail, Inc ''AS IS'' AND ANY
     22  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     23  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     24  DISCLAIMED. IN NO EVENT SHALL Ad-Mail, Inc BE LIABLE FOR ANY
     25  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     26  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     28  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31
     32  B{I{Summary}}
     33
     34  Python package for reading/writing dBase III and VFP 6 tables and memos
     35
     36  The entire table is read into memory, and all operations occur on the in-memory
     37  table, with data changes being written to disk as they occur.
     38
     39  Goals:  programming style with databases
     40      - C{table = dbf.table('table name' [, fielddesc[, fielddesc[, ....]]])}
     41          - fielddesc examples:  C{name C(30); age N(3,0); wisdom M; marriage D}
     42      - C{record = [ table.current() | table[int] | table.append() | table.[next|prev|top|bottom|goto]() ]}
     43      - C{record.field | record['field']} accesses the field
     44
     45  NOTE:  Of the VFP data types, auto-increment and null settings are not implemented.
     46  """
     47  import os
     48  import csv
     49
     50  from dbf.dates import Date, DateTime, Time
     51  from dbf.exceptions import DbfWarning, Bof, Eof, DbfError, DataOverflow, FieldMissing, DoNotIndex
     52  from dbf.tables import DbfTable, Db3Table, VfpTable, FpTable, List, DbfCsv
     53  from dbf.tables import sql, ascii, codepage, encoding, version_map
     54
     55  version = (0, 88, 16)
     56
     57  default_type = 'db3'    # default format if none specified
     58  sql_user_functions = {}      # user-defined sql functions
     59
     60  __docformat__ = 'epytext'
     61
     62 -def Table(filename, field_specs='', memo_size=128, ignore_memos=False, \
     63            read_only=False, keep_memos=False, meta_only=False, dbf_type=None, codepage=None):
     64      "returns an open table of the correct dbf_type, or creates it if field_specs is given"
     65      #- print "dbf.Table(%s)" % ', '.join(['%r' % arg for arg in (filename, field_specs, dbf_type, codepage)])
     66      if field_specs and dbf_type is None:
     67          dbf_type = default_type
     68      if dbf_type is not None:
     69          dbf_type = dbf_type.lower()
     70          if dbf_type == 'db3':
     71              return Db3Table(filename, field_specs, memo_size, ignore_memos, read_only, keep_memos, meta_only, codepage)
     72          elif dbf_type == 'fp':
     73              return FpTable(filename, field_specs, memo_size, ignore_memos, read_only, keep_memos, meta_only, codepage)
     74          elif dbf_type == 'vfp':
     75              return VfpTable(filename, field_specs, memo_size, ignore_memos, read_only, keep_memos, meta_only, codepage)
     76          elif dbf_type == 'dbf':
     77              return DbfTable(filename, field_specs, memo_size, ignore_memos, read_only, keep_memos, meta_only, codepage)
     78          else:
     79              raise DbfError("Unknown table type: %s" % dbf_type)
     80      else:
     81          possibles = guess_table_type(filename)
     82          if len(possibles) == 1:
     83              return possibles[0][2](filename, field_specs, memo_size, ignore_memos, \
     84                                   read_only, keep_memos, meta_only)
     85          else:
     86              for type, desc, cls in possibles:
     87                  if type == default_type:
     88                      return cls(filename, field_specs, memo_size, ignore_memos, \
     89                                   read_only, keep_memos, meta_only)
     90              else:
     91                  types = ', '.join(["%s" % item[1] for item in possibles])
     92                  abbrs = '[' + ' | '.join(["%s" % item[0] for item in possibles]) + ']'
     93                  raise DbfError("Table could be any of %s.  Please specify %s when opening" % (types, abbrs))
     94 -def index(sequence):
     95      "returns integers 0 - len(sequence)"
     96      for i in xrange(len(sequence)):
     97          yield i
     98 -def guess_table_type(filename):
     99      reported = table_type(filename)
    100      possibles = []
    101      version = reported[0]
    102      for tabletype in (Db3Table, FpTable, VfpTable):
    103          if version in tabletype._supported_tables:
    104              possibles.append((tabletype._versionabbv, tabletype._version, tabletype))
    105      if not possibles:
    106          raise DbfError("Tables of type %s not supported" % str(reported))
    107      return possibles
    108 -def table_type(filename):
    109      "returns text representation of a table's dbf version"
    110      base, ext = os.path.splitext(filename)
    111      if ext == '':
    112          filename = base + '.dbf'
    113      if not os.path.exists(filename):
    114          raise DbfError('File %s not found' % filename)
    115      fd = open(filename)
    116      version = fd.read(1)
    117      fd.close()
    118      fd = None
    119      if not version in version_map:
    120          raise DbfError("Unknown dbf type: %s (%x)" % (version, ord(version)))
    121      return version, version_map[version]
    122
    123 -def add_fields(table, field_specs):
    124      "adds fields to an existing table"
    125      table = Table(table)
    126      try:
    127          table.add_fields(field_specs)
    128      finally:
    129          table.close()
    130 -def delete_fields(table, field_names):
    131      "deletes fields from an existing table"
    132      table = Table(table)
    133      try:
    134          table.delete_fields(field_names)
    135      finally:
    136          table.close()
    137 -def export(table, filename='', fields='', format='csv', header=True):
    138      "creates a csv or tab-delimited file from an existing table"
    139      if fields is None:
    140          fields = []
    141      table = Table(table)
    142      try:
    143          table.export(filename=filename, field_specs=fields, format=format, header=header)
    144      finally:
    145          table.close()
    146 -def first_record(table):
    147      "prints the first record of a table"
    148      table = Table(table)
    149      try:
    150          print str(table[0])
    151      finally:
    152          table.close()
    153 -def from_csv(csvfile, to_disk=False, filename=None, field_names=None, extra_fields=None, dbf_type='db3', memo_size=64, min_field_size=1):
    154      """creates a Character table from a csv file
    155      to_disk will create a table with the same name
    156      filename will be used if provided
    157      field_names default to f0, f1, f2, etc, unless specified (list)
    158      extra_fields can be used to add additional fields -- should be normal field specifiers (list)"""
    159      reader = csv.reader(open(csvfile))
    160      if field_names:
    161          field_names = ['%s M' % fn for fn in field_names]
    162      else:
    163          field_names = ['f0 M']
    164      mtable = Table(':memory:', [field_names[0]], dbf_type=dbf_type, memo_size=memo_size)
    165      fields_so_far = 1
    166      for row in reader:
    167          while fields_so_far < len(row):
    168              if fields_so_far == len(field_names):
    169                  field_names.append('f%d M' % fields_so_far)
    170              mtable.add_fields(field_names[fields_so_far])
    171              fields_so_far += 1
    172          mtable.append(tuple(row))
    173      if filename:
    174          to_disk = True
    175      if not to_disk:
    176          if extra_fields:
    177              mtable.add_fields(extra_fields)
    178      else:
    179          if not filename:
    180              filename = os.path.splitext(csvfile)[0]
    181          length = [min_field_size] * len(field_names)
    182          for record in mtable:
    183              for i in index(record.field_names):
    184                  length[i] = max(length[i], len(record[i]))
    185          fields = mtable.field_names
    186          fielddef = []
    187          for i in index(length):
    188              if length[i] < 255:
    189                  fielddef.append('%s C(%d)' % (fields[i], length[i]))
    190              else:
    191                  fielddef.append('%s M' % (fields[i]))
    192          if extra_fields:
    193              fielddef.extend(extra_fields)
    194          csvtable = Table(filename, fielddef, dbf_type=dbf_type)
    195          for record in mtable:
    196              csvtable.append(record.scatter_fields())
    197          return csvtable
    198      return mtable
    199 -def get_fields(table):
    200      "returns the list of field names of a table"
    201      table = Table(table)
    202      return table.field_names
    203 -def info(table):
    204      "prints table info"
    205      table = Table(table)
    206      print str(table)
    207 -def rename_field(table, oldfield, newfield):
    208      "renames a field in a table"
    209      table = Table(table)
    210      try:
    211          table.rename_field(oldfield, newfield)
    212      finally:
    213          table.close()
    214 -def structure(table, field=None):
    215      "returns the definition of a field (or all fields)"
    216      table = Table(table)
    217      return table.structure(field)
    218 -def hex_dump(records):
    219      "just what it says ;)"
    220      for index,dummy in enumerate(records):
    221          chars = dummy._data
    222          print "%2d: " % index,
    223          for char in chars[1:]:
    224              print " %2x " % ord(char),
    225          print
    226