dbf._io

Package dbf :: Module _io |
[hide private]

# Source Code for Module dbf._io

      1  """Routines for saving, retrieving, and creating fields"""
      2
      3  import struct
      4  from decimal import Decimal
      5  from dbf.exceptions import DbfError, DataOverflow
      6  from dbf.dates import Date, DateTime, Time
      7  from math import floor
      8
      9
     10  # Constants
     11  VFPTIME = 1721425
     12
     13 -def packShortInt(value, bigendian=False):
     14          "Returns a two-bye integer from the value, or raises DbfError"
     15          # 256 / 65,536
     16          if value > 65535:
     17              raise DateOverflow("Maximum Integer size exceeded.  Possible: 65535.  Attempted: %d" % value)
     18          if bigendian:
     19              return struct.pack('>H', value)
     20          else:
     21              return struct.pack('<H', value)
     22 -def packLongInt(value, bigendian=False):
     23          "Returns a four-bye integer from the value, or raises DbfError"
     24          # 256 / 65,536 / 16,777,216
     25          if value > 4294967295:
     26              raise DateOverflow("Maximum Integer size exceeded.  Possible: 4294967295.  Attempted: %d" % value)
     27          if bigendian:
     28              return struct.pack('>L', value)
     29          else:
     30              return struct.pack('<L', value)
     31 -def packDate(date):
     32          "Returns a group of three bytes, in integer form, of the date"
     33          return "%c%c%c" % (date.year-1900, date.month, date.day)
     34 -def packStr(string):
     35          "Returns an 11 byte, upper-cased, null padded string suitable for field names; raises DbfError if the string is bigger than 10 bytes"
     36          if len(string) > 10:
     37              raise DbfError("Maximum string size is ten characters -- %s has %d characters" % (string, len(string)))
     38          return struct.pack('11s', string.upper())
     39 -def unpackShortInt(bytes, bigendian=False):
     40          "Returns the value in the two-byte integer passed in"
     41          if bigendian:
     42              return struct.unpack('>H', bytes)[0]
     43          else:
     44              return struct.unpack('<H', bytes)[0]
     45 -def unpackLongInt(bytes, bigendian=False):
     46          "Returns the value in the four-byte integer passed in"
     47          if bigendian:
     48              return int(struct.unpack('>L', bytes)[0])
     49          else:
     50              return int(struct.unpack('<L', bytes)[0])
     51 -def unpackDate(bytestr):
     52          "Returns a Date() of the packed three-byte date passed in"
     53          year, month, day = struct.unpack('<BBB', bytestr)
     54          year += 1900
     55          return Date(year, month, day)
     56 -def unpackStr(chars):
     57          "Returns a normal, lower-cased string from a null-padded byte string"
     58          return struct.unpack('%ds' % len(chars), chars)[0].replace('\x00','').lower()
     59 -def convertToBool(value):
     60      """Returns boolean true or false; normal rules apply to non-string values; string values
     61      must be 'y','t', 'yes', or 'true' (case insensitive) to be True"""
     62      if type(value) == str:
     63          return bool(value.lower() in ['t', 'y', 'true', 'yes'])
     64      else:
     65          return bool(value)
     66 -def unsupportedType(something, field, memo=None):
     67      "called if a data type is not supported for that style of table"
     68      raise DbfError('field type is not supported.')
     69 -def retrieveCharacter(bytes, fielddef={}, memo=None):
     70      "Returns the string in bytes with trailing white space removed"
     71      return bytes.tostring().rstrip()
     72 -def updateCharacter(string, fielddef, memo=None):
     73      "returns the string, truncating if string is longer than it's field"
     74      if type(string) != str:
     75          raise DbfError("incompatible type: %s" % type(string))
     76      return string.rstrip()
     77 -def retrieveCurrency(bytes, fielddef={}, memo=None):
     78      value = struct.unpack('<q', bytes)[0]
     79      return Decimal("%de-4" % value)
     80 -def updateCurrency(value, fielddef={}, memo=None):
     81      currency = int(value * 10000)
     82      if not -9223372036854775808 < currency < 9223372036854775808:
     83          raise DataOverflow("value %s is out of bounds" % value)
     84      return struct.pack('<q', currency)
     85 -def retrieveDate(bytes, fielddef={}, memo=None):
     86      "Returns the ascii coded date as a Date object"
     87      return Date.fromymd(bytes.tostring())
     88 -def updateDate(moment, fielddef={}, memo=None):
     89      "returns the Date or datetime.date object ascii-encoded (yyyymmdd)"
     90      if moment:
     91          return "%04d%02d%02d" % moment.timetuple()[:3]
     92      return '        '
     93 -def retrieveDouble(bytes, fielddef={}, memo=None):
     94      return struct.unpack('<d', bytes)[0]
     95 -def updateDouble(value, fielddef={}, memo=None):
     96      if not (type(value) in (int, long, float)):
     97          raise DbfError("incompatible type: %s" % type(value))
     98      return struct.pack('<d', value)
     99 -def retrieveInteger(bytes, fielddef={}, memo=None):
    100      "Returns the binary number stored in bytes in little-endian format"
    101      return struct.unpack('<i', bytes)[0]
    102 -def updateInteger(value, fielddef={}, memo=None):
    103      "returns value in little-endian binary format"
    104      if not (type(value) in (int, long)):
    105          raise DbfError("incompatible type: %s" % type(value))
    106      if not -2147483648 < value < 2147483647:
    107          raise DataOverflow("Integer size exceeded.  Possible: -2,147,483,648..+2,147,483,647.  Attempted: %d" % value)
    108      return struct.pack('<i', value)
    109 -def retrieveLogical(bytes, fielddef={}, memo=None):
    110      "Returns True if bytes is 't', 'T', 'y', or 'Y', None if '?', and False otherwise"
    111      bytes = bytes.tostring()
    112      if bytes == '?':
    113          return None
    114      return bytes in ['t','T','y','Y']
    115 -def updateLogical(logical, fielddef={}, memo=None):
    116      "Returs 'T' if logical is True, 'F' otherwise"
    117      if type(logical) != bool:
    118          logical = convertToBool(logical)
    119      if type(logical) <> bool:
    120          raise DbfError('Value %s is not logical.' % logical)
    121      return logical and 'T' or 'F'
    122 -def retrieveMemo(bytes, fielddef, memo):
    123      "Returns the block of data from a memo file"
    124      stringval = bytes.tostring()
    125      if stringval.strip():
    126          block = int(stringval.strip())
    127      else:
    128          block = 0
    129      return memo.get_memo(block, fielddef)
    130 -def updateMemo(string, fielddef, memo):
    131      "Writes string as a memo, returns the block number it was saved into"
    132      block = memo.put_memo(string)
    133      if block == 0:
    134          block = ''
    135      return "%*s" % (fielddef['length'], block)
    136 -def retrieveNumeric(bytes, fielddef, memo=None):
    137      "Returns the number stored in bytes as integer if field spec for decimals is 0, float otherwise"
    138      string = bytes.tostring()
    139      if string[0:1] == '*':  # value too big to store (Visual FoxPro idiocy)
    140          return None
    141      if not string.strip():
    142          string = '0'
    143      if fielddef['decimals'] == 0:
    144          return int(string)
    145      else:
    146          return float(string)
    147 -def updateNumeric(value, fielddef, memo=None):
    148      "returns value as ascii representation, rounding decimal portion as necessary"
    149      if not (type(value) in (int, long, float)):
    150          raise DbfError("incompatible type: %s" % type(value))
    151      decimalsize = fielddef['decimals']
    152      if decimalsize:
    153          decimalsize += 1
    154      maxintegersize = fielddef['length']-decimalsize
    155      integersize = len("%.0f" % floor(value))
    156      if integersize > maxintegersize:
    157          raise DataOverflow('Integer portion too big')
    158      return "%*.*f" % (fielddef['length'], fielddef['decimals'], value)
    159 -def retrieveVfpDateTime(bytes, fielddef={}, memo=None):
    160      """returns the date/time stored in bytes; dates <= 01/01/1981 00:00:00
    161      may not be accurate;  BC dates are nulled."""
    162      # two four-byte integers store the date and time.
    163      # millesecords are discarded from time
    164      time = retrieveInteger(bytes[4:])
    165      microseconds = (time % 1000) * 1000
    166      time = time // 1000                      # int(round(time, -3)) // 1000 discard milliseconds
    167      hours = time // 3600
    168      mins = time % 3600 // 60
    169      secs = time % 3600 % 60
    170      time = Time(hours, mins, secs, microseconds)
    171      possible = retrieveInteger(bytes[:4])
    172      possible -= VFPTIME
    173      possible = max(0, possible)
    174      date = Date.fromordinal(possible)
    175      return DateTime.combine(date, time)
    176 -def updateVfpDateTime(moment, fielddef={}, memo=None):
    177      """sets the date/time stored in moment
    178      moment must have fields year, month, day, hour, minute, second, microsecond"""
    179      bytes = [0] * 8
    180      hour = moment.hour
    181      minute = moment.minute
    182      second = moment.second
    183      millisecond = moment.microsecond // 1000       # convert from millionths to thousandths
    184      time = ((hour * 3600) + (minute * 60) + second) * 1000 + millisecond
    185      bytes[4:] = updateInteger(time)
    186      bytes[:4] = updateInteger(moment.toordinal() + VFPTIME)
    187      return ''.join(bytes)
    188 -def retrieveVfpMemo(bytes, fielddef, memo):
    189      "Returns the block of data from a memo file"
    190      block = struct.unpack('<i', bytes)[0]
    191      return memo.get_memo(block, fielddef)
    192 -def updateVfpMemo(string, fielddef, memo):
    193      "Writes string as a memo, returns the block number it was saved into"
    194      block = memo.put_memo(string)
    195      return struct.pack('<i', block)
    196 -def addCharacter(format):
    197      if format[1] != '(' or format[-1] != ')':
    198          raise DbfError("Format for Character field creation is C(n), not %s" % format)
    199      length = int(format[2:-1])
    200      if not 0 < length < 255:
    201          raise ValueError
    202      decimals = 0
    203      return length, decimals
    204 -def addDate(format):
    205      length = 8
    206      decimals = 0
    207      return length, decimals
    208 -def addLogical(format):
    209      length = 1
    210      decimals = 0
    211      return length, decimals
    212 -def addMemo(format):
    213      length = 10
    214      decimals = 0
    215      return length, decimals
    216 -def addNumeric(format):
    217      if format[1] != '(' or format[-1] != ')':
    218          raise DbfError("Format for Numeric field creation is N(n,n), not %s" % format)
    219      length, decimals = format[2:-1].split(',')
    220      length = int(length)
    221      decimals = int(decimals)
    222      if not 0 < length < 18:
    223          raise ValueError
    224      if decimals and not 0 < decimals <= length - 2:
    225          raise ValueError
    226      return length, decimals
    227 -def addVfpCurrency(format):
    228      length = 8
    229      decimals = 0
    230      return length, decimals
    231 -def addVfpDateTime(format):
    232      length = 8
    233      decimals = 8
    234      return length, decimals
    235 -def addVfpDouble(format):
    236      length = 8
    237      decimals = 0
    238      return length, decimals
    239 -def addVfpInteger(format):
    240      length = 4
    241      decimals = 0
    242      return length, decimals
    243 -def addVfpMemo(format):
    244      length = 4
    245      decimals = 0
    246      return length, decimals
    247 -def addVfpNumeric(format):
    248      if format[1] != '(' or format[-1] != ')':
    249          raise DbfError("Format for Numeric field creation is N(n,n), not %s" % format)
    250      length, decimals = format[2:-1].split(',')
    251      length = int(length)
    252      decimals = int(decimals)
    253      if not 0 < length < 21:
    254          raise ValueError
    255      if decimals and not 0 < decimals <= length - 2:
    256          raise ValueError
    257      return length, decimals
    258