home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / hplip / base / kirbybase.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2007-04-29  |  33.0 KB  |  1,015 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. """
  5. KirbyBase v1.8.1 (applied patch introduced in v1.8.2)
  6.  
  7. Contains classes for a plain-text, client-server dbms.
  8.  
  9. NOTE: This copy has been modified from the standard distribution of KirbyBase. 
  10. To get the complete distribution of KirbyBase, please visit: 
  11. http://www.netpromi.com/kirbybase.html
  12.  
  13. Classes:
  14.     KirbyBase - database class
  15.     KBError - exceptions
  16.  
  17. Example:
  18.     from db import *
  19.  
  20.     db = KirbyBase()
  21.     db.create('plane.tbl', ['name:str', 'country:str', 'speed:int',
  22.      'range:int'])
  23.     db.insert('plane.tbl', ['P-51', 'USA', 403, 1201])
  24.     db.insert('plane.tbl', ['P-38', 'USA', 377, 999])
  25.     db.select('plane.tbl', ['country', 'speed'], ['USA', '>400'])
  26.     db.update('plane.tbl', ['country'], ['USA'], ['United States'],
  27.      ['country'])
  28.     db.delete('plane.tbl', ['speed'], ['<400'])
  29.     db.close()
  30.  
  31. Author:
  32.     Jamey Cribbs -- jcribbs@twmi.rr.com
  33.     www.netpromi.com
  34.  
  35. License:
  36.     KirbyBase is licensed under the Python Software Foundation License.
  37.     KirbyBase carries no warranty!  Use at your own risk. 
  38. """
  39. from __future__ import generators
  40. import re
  41. import os.path as os
  42. import datetime
  43. import cStringIO
  44. import operator
  45.  
  46. class KirbyBase:
  47.     """Database Management System.
  48.  
  49.     Public Methods:
  50.         __init__      - Create an instance of database.
  51.         close         - Close database.
  52.         create        - Create a table.
  53.         insert        - Insert a record into a table.
  54.         insertBatch   - Insert a list of records into a table.
  55.         update        - Update a table.
  56.         delete        - Delete record(s) from a table.
  57.         select        - select record(s) from a table.
  58.         pack          - remove deleted records from a table.
  59.         validate      - validate data in table records.
  60.         drop          - Remove a table.
  61.         getFieldNames - Get a list of a table's field names.
  62.         getFieldTypes - Get a list of a table's field types.
  63.         len           - Total number of records in table.
  64.     """
  65.     
  66.     def __init__(self):
  67.         '''Create an instance of the database and return a reference to it.
  68.         '''
  69.         self.connect_type = type
  70.         self.encodeRegExp = re.compile('\\n|\\r|\\032|\\|')
  71.         self.unencodeRegExp = re.compile('&linefeed;|&carriage_return;|&substitute;|&pipe;')
  72.         self.cmpFuncs = {
  73.             '<': operator.lt,
  74.             '<=': operator.le,
  75.             '>=': operator.ge,
  76.             '>': operator.gt,
  77.             '==': operator.eq,
  78.             '!=': operator.ne,
  79.             '<>': operator.ne }
  80.         self.strToTypes = {
  81.             'int': int,
  82.             'Integer': int,
  83.             'float': float,
  84.             'Float': float,
  85.             'datetime.date': datetime.date,
  86.             'Date': datetime.date,
  87.             'datetime.datetime': datetime.datetime,
  88.             'DateTime': datetime.datetime,
  89.             'bool': bool,
  90.             'Boolean': bool,
  91.             'str': str,
  92.             'String': str }
  93.  
  94.     
  95.     def close(self):
  96.         '''Close connection to database server.
  97.         '''
  98.         pass
  99.  
  100.     
  101.     def create(self, name, fields):
  102.         """Create a new table and return True on success.
  103.  
  104.         Arguments:
  105.             name   - physical filename, including path, that will hold
  106.                      table.
  107.             fields - list holding strings made up of multiple fieldname,
  108.                      fieldtype pairs (i.e. ['plane:str','speed:int']).
  109.                      Valid fieldtypes are: str, int, float, datetime.date,
  110.                      datetime.datetime, bool or, for compatibility with 
  111.                      the Ruby version of KirbyBase use String, Integer,
  112.                      Float, Date, DateTime, and Boolean.
  113.  
  114.         Returns True if no exceptions are raised.
  115.         """
  116.         if os.path.exists(name):
  117.             raise KBError(name + ' already exists!')
  118.         
  119.         for y in fields:
  120.             pass
  121.         
  122.         header_rec = list([
  123.             '000000',
  124.             '000000',
  125.             'recno:int'] + fields)
  126.         fptr = self._openTable(name, 'w')
  127.         fptr.write('|'.join(header_rec) + '\n')
  128.         self._closeTable(fptr)
  129.         return True
  130.  
  131.     
  132.     def insert(self, name, values):
  133.         '''Insert a new record into table, return unique record number.
  134.  
  135.         Arguments:
  136.             name   - physical file name, including path, that holds table.
  137.             values - list, dictionary, or object containing field values
  138.                      of new record.
  139.  
  140.         Returns unique record number assigned to new record when it is
  141.         created.
  142.         '''
  143.         fptr = self._openTable(name, 'r+')
  144.         self._updateHeaderVars(fptr)
  145.         record = self._convertInput(values)
  146.         self._validateUpdateCriteria(record, self.field_names[1:])
  147.         
  148.         try:
  149.             rec_no = self._incrRecnoCounter(fptr)
  150.             record.insert(0, rec_no)
  151.             'end'('|'.join, map, self._encodeString([]([], [ str(item) for item in record ])))
  152.         finally:
  153.             self._closeTable(fptr)
  154.  
  155.         return rec_no
  156.  
  157.     
  158.     def insertBatch(self, name, batchRecords):
  159.         '''Insert a batch of records into table, return a list of rec #s.
  160.  
  161.         Arguments:
  162.             name         - physical file name, including path, that holds 
  163.                            table.
  164.             batchRecords - list of records.  Each record can be a list, a 
  165.                            dictionary, or an object containing field values
  166.                            of new record.
  167.  
  168.         Returns list of unique record numbers assigned.
  169.         '''
  170.         fptr = self._openTable(name, 'r')
  171.         self._updateHeaderVars(fptr)
  172.         self._closeTable(fptr)
  173.         records = []
  174.         for values in batchRecords:
  175.             record = self._convertInput(values)
  176.             self._validateUpdateCriteria(record, self.field_names[1:])
  177.             records.append(record)
  178.         
  179.         rec_nos = []
  180.         fptr = self._openTable(name, 'r+')
  181.         
  182.         try:
  183.             for record in records:
  184.                 rec_no = self._incrRecnoCounter(fptr)
  185.                 record.insert(0, rec_no)
  186.                 'end'('|'.join, map, self._encodeString([]([], [ str(item) for item in record ])))
  187.                 rec_nos.append(rec_no)
  188.         finally:
  189.             self._closeTable(fptr)
  190.  
  191.         return rec_nos
  192.  
  193.     
  194.     def update(self, name, fields, searchData, updates, filter = None, useRegExp = False):
  195.         """Update record(s) in table, return number of records updated.
  196.  
  197.         Arguments:
  198.             name       - physical file name, including path, that holds
  199.                          table.
  200.             fields     - list containing names of fields to search on. If 
  201.                          any of the items in this list is 'recno', then the
  202.                          table will be searched by the recno field only and
  203.                          will update, at most, one record, since recno is 
  204.                          the system generated primary key.
  205.             searchData - list containing actual data to search on.  Each 
  206.                          item in list corresponds to item in the 'fields' 
  207.                          list.
  208.             updates    - list, dictionary, or object containing actual data
  209.                          to put into table field.  If it is a list and 
  210.                          'filter' list is empty or equal to None, then 
  211.                          updates list must have a value for each field in
  212.                          table record.
  213.             filter     - only used if 'updates' is a list.  This is a
  214.                          list containing names of fields to update.  Each
  215.                          item in list corresponds to item in the 'updates'
  216.                          list.  If 'filter' list is empty or equal to None,
  217.                          and 'updates' is a list, then 'updates' list must 
  218.                          have an item for each field in table record, 
  219.                          excepting the recno field.
  220.             useRegExp  - if true, match string fields using regular 
  221.                          expressions, else match string fields using
  222.                          strict equality (i.e. '==').  Defaults to true.
  223.  
  224.         Returns integer specifying number of records that were updated.
  225.  
  226.         Example:
  227.             db.update('plane.tbl',['country','speed'],['USA','>400'],
  228.              [1230],['range'])
  229.  
  230.             This will search for any plane from the USA with a speed
  231.             greater than 400mph and update it's range to 1230 miles.
  232.         """
  233.         patterns = list(searchData)
  234.         fptr = self._openTable(name, 'r+')
  235.         self._updateHeaderVars(fptr)
  236.         if filter:
  237.             if isinstance(updates, list):
  238.                 pass
  239.             elif isinstance(updates, dict):
  240.                 raise KBError('Cannot specify filter when updates is a ' + 'dictionary.')
  241.             else:
  242.                 raise KBError('Cannot specify filter when updates is an ' + 'object.')
  243.         elif isinstance(updates, list):
  244.             filter = self.field_names[1:]
  245.         
  246.         if isinstance(updates, list):
  247.             pass
  248.         elif isinstance(updates, dict):
  249.             filter = _[1]
  250.             updates = [ updates[i] for i in filter ]
  251.         else:
  252.             filter = _[3]
  253.             updates = _[4]
  254.         
  255.         try:
  256.             self._validateMatchCriteria(fields, patterns)
  257.             self._validateUpdateCriteria(updates, filter)
  258.         except KBError:
  259.             []
  260.             []
  261.             []
  262.             fptr.close()
  263.             raise 
  264.         except:
  265.             []
  266.  
  267.         match_list = self._getMatches(fptr, fields, patterns, useRegExp)
  268.         filter_updates = []([], [ self._encodeString(str(u)) for u in updates ])
  269.         updated = 0
  270.         for line, fpos in match_list:
  271.             new_record = line.strip().split('|')
  272.             for field, update in filter_updates:
  273.                 new_record[self.field_names.index(field)] = update
  274.             
  275.             new_line = '|'.join(new_record)
  276.             self._deleteRecord(fptr, fpos, line)
  277.             updated += 1
  278.         
  279.         self._closeTable(fptr)
  280.         return updated
  281.  
  282.     
  283.     def delete(self, name, fields, searchData, useRegExp = False):
  284.         """Delete record(s) from table, return number of records deleted.
  285.  
  286.         Arguments:
  287.             name       - physical file name, including path, that holds
  288.                          table.
  289.             fields     - list containing names of fields to search on. if
  290.                          any of the items in this list is 'recno', then the
  291.                          table will be searched by the recno field only and
  292.                          will delete, at most, one record, since recno is 
  293.                          the system generated primary key.
  294.             searchData - list containing actual data to search on.  Each 
  295.                          item in list corresponds to item in the 'fields'
  296.                          list.
  297.             useRegExp  - if true, match string fields using regular 
  298.                          expressions, else match string fields using
  299.                          strict equality (i.e. '==').  Defaults to true.
  300.  
  301.         Returns integer specifying number of records that were deleted.
  302.  
  303.         Example:
  304.             db.delete('plane.tbl',['country','speed'],['USA','>400'])
  305.  
  306.             This will search for any plane from the USA with a speed
  307.             greater than 400mph and delete it.
  308.         """
  309.         patterns = list(searchData)
  310.         fptr = self._openTable(name, 'r+')
  311.         self._updateHeaderVars(fptr)
  312.         
  313.         try:
  314.             self._validateMatchCriteria(fields, patterns)
  315.         except KBError:
  316.             fptr.close()
  317.             raise 
  318.  
  319.         match_list = self._getMatches(fptr, fields, patterns, useRegExp)
  320.         deleted = 0
  321.         for line, fpos in match_list:
  322.             self._deleteRecord(fptr, fpos, line)
  323.             self._incrDeleteCounter(fptr)
  324.             deleted += 1
  325.         
  326.         self._closeTable(fptr)
  327.         return deleted
  328.  
  329.     
  330.     def select(self, name, fields, searchData, filter = None, useRegExp = False, sortFields = [], sortDesc = [], returnType = 'list', rptSettings = [
  331.         0,
  332.         False]):
  333.         """Select record(s) from table, return list of records selected.
  334.  
  335.         Arguments:
  336.             name          - physical file name, including path, that holds
  337.                             table.
  338.             fields        - list containing names of fields to search on. 
  339.                             If any of the items in this list is 'recno', 
  340.                             then the table will be searched by the recno 
  341.                             field only and will select, at most, one record,
  342.                             since recno is the system generated primary key.
  343.             searchData    - list containing actual data to search on.  Each
  344.                             item in list corresponds to item in the 
  345.                             'fields' list.
  346.             filter        - list containing names of fields to include for
  347.                             selected records.  If 'filter' list is empty or
  348.                             equal to None, then all fields will be included
  349.                             in result set.
  350.             useRegExp     - if true, match string fields using regular 
  351.                             expressions, else match string fields using
  352.                             strict equality (i.e. '==').  Defaults to False.
  353.             sortFields    - list of fieldnames to sort on.  Each must be a
  354.                             valid field name, and, if filter list is not
  355.                             empty, the same fieldname must be in the filter
  356.                             list.  Result set will be sorted in the same
  357.                             order as fields appear in sortFields in 
  358.                             ascending order unless the same field name also 
  359.                             appears in sortDesc, then they will be sorted in
  360.                             descending order.
  361.             sortDesc      - list of fieldnames that you want to sort result
  362.                             set by in descending order.  Each field name
  363.                             must also be in sortFields.
  364.             returnType    - a string specifying the type of the items in the
  365.                             returned list.  Can be 'list' (items are lists
  366.                             of values), 'dict' (items are dictionaries with
  367.                             keys = field names and values = matching
  368.                             values), 'object' (items = instances of the
  369.                             generic Record class) or 'report' (result set
  370.                             is formatted as a table with a header, suitable
  371.                             for printing).  Defaults to list.
  372.             rptSettings   - a list with two elements.  This is only used if
  373.                             returnType is 'report'.  The first element
  374.                             specifies the number of records to print on each
  375.                             page.  The default is 0, which means do not do
  376.                             any page breaks.  The second element is a 
  377.                             boolean specifying whether to print a row 
  378.                             separator (a line of dashes) between each 
  379.                             record.  The default is False.
  380.  
  381.         Returns list of records matching selection criteria.
  382.  
  383.         Example:
  384.             db.select('plane.tbl',['country','speed'],['USA','>400'])
  385.  
  386.             This will search for any plane from the USA with a speed
  387.             greater than 400mph and return it.
  388.         """
  389.         if returnType not in ('list', 'dict', 'object', 'report'):
  390.             raise KBError('Invalid return type: %s' % returnType)
  391.         
  392.         if type(rptSettings[0]) != int:
  393.             raise KBError('Invalid report setting: %s' % rptSettings[0])
  394.         
  395.         if type(rptSettings[1]) != bool:
  396.             raise KBError('Invalid report setting: %s' % rptSettings[1])
  397.         
  398.         patterns = list(searchData)
  399.         fptr = self._openTable(name, 'r')
  400.         self._updateHeaderVars(fptr)
  401.         
  402.         try:
  403.             self._validateMatchCriteria(fields, patterns)
  404.             if filter:
  405.                 self._validateFilter(filter)
  406.             else:
  407.                 filter = self.field_names
  408.         except KBError:
  409.             fptr.close()
  410.             raise 
  411.  
  412.         for sf in sortFields:
  413.             if sf not in filter:
  414.                 continue
  415.             _[1][sf]
  416.         
  417.         for sf in sortDesc:
  418.             if sf not in sortFields:
  419.                 continue
  420.             _[2][sf]
  421.         
  422.         match_list = self._getMatches(fptr, fields, patterns, useRegExp)
  423.         result_set = []
  424.         filterIndeces = [ self.field_names.index(x) for x in filter ]
  425.         for record, fpos in match_list:
  426.             result_rec = []
  427.             fields = record.split('|')
  428.             for None in filterIndeces:
  429.                 i = None
  430.             
  431.             result_set.append(result_rec)
  432.         
  433.         self._closeTable(fptr)
  434.         if returnType == 'object':
  435.             return [ Record(filter, rec) for rec in result_set ]
  436.         elif returnType == 'dict':
  437.             return [ dict(zip(filter, rec)) for rec in result_set ]
  438.         elif returnType == 'report':
  439.             numRecsPerPage = rptSettings[0]
  440.             rowSeparator = rptSettings[1]
  441.             delim = ' | '
  442.             columns = apply(zip, [
  443.                 filter] + result_set)
  444.             maxWidths = [ []([ len(str(item)) for item in column ]) for column in columns ]
  445.             rowDashes = '-' * (sum(maxWidths) + len(delim) * (len(maxWidths) - 1))
  446.             justifyDict = {
  447.                 str: str.ljust,
  448.                 int: str.rjust,
  449.                 float: str.rjust,
  450.                 bool: str.ljust,
  451.                 datetime.date: str.ljust,
  452.                 datetime.datetime: str.ljust }
  453.             headerLine = []([ justifyDict[fieldType](item, width) for item, width, fieldType in zip(filter, maxWidths, self.field_types) ])
  454.             output = cStringIO.StringIO()
  455.             recsOnPageCount = 0
  456.             for row in result_set:
  457.                 print >>[], []([ justifyDict[fieldType](str(item), width) for item, width, fieldType in zip(row, maxWidths, self.field_types) ]),
  458.                 print >>delim.join
  459.                 recsOnPageCount += 1
  460.                 if numRecsPerPage > 0 and recsOnPageCount == numRecsPerPage:
  461.                     print >>output, '\x0c',
  462.                     recsOnPageCount = 0
  463.                     continue
  464.                 [] if rowSeparator else [] if recsOnPageCount == 0 else _[6]
  465.             
  466.             return output.getvalue()
  467.         else:
  468.             return result_set
  469.  
  470.     
  471.     def pack(self, name):
  472.         '''Remove blank records from table and return total removed.
  473.  
  474.         Keyword Arguments:
  475.             name - physical file name, including path, that holds table.
  476.  
  477.         Returns number of blank lines removed from table.
  478.         '''
  479.         fptr = self._openTable(name, 'r')
  480.         lines = fptr.readlines()
  481.         self._closeTable(fptr)
  482.         header_rec = lines[0].split('|')
  483.         header_rec[1] = '000000'
  484.         lines[0] = '|'.join(header_rec)
  485.         fptr = self._openTable(name, 'w')
  486.         lines_deleted = 0
  487.         for line in lines:
  488.             line = line.rstrip()
  489.             if line == '':
  490.                 lines_deleted += 1
  491.                 continue
  492.             
  493.             
  494.             try:
  495.                 fptr.write(line + '\n')
  496.             continue
  497.             raise KBError('Could not write record in: ' + name)
  498.             continue
  499.  
  500.         
  501.         self._closeTable(fptr)
  502.         return lines_deleted
  503.  
  504.     
  505.     def validate(self, name):
  506.         '''Validate all records have field values of proper data type.
  507.  
  508.         Keyword Arguments:
  509.             name - physical file name, including path, that holds table.
  510.  
  511.         Returns list of records that have invalid data.
  512.         '''
  513.         fptr = self._openTable(name, 'r')
  514.         self._updateHeaderVars(fptr)
  515.         invalid_list = []
  516.         for line in fptr:
  517.             line = line[:-1].strip()
  518.             if line == '':
  519.                 continue
  520.             
  521.             record = line.split('|')
  522.             if self.last_recno < int(record[0]):
  523.                 invalid_list.append([
  524.                     record[0],
  525.                     'recno',
  526.                     record[0]])
  527.             
  528.             for i, item in enumerate(record):
  529.                 if item == '':
  530.                     continue
  531.                 
  532.                 
  533.                 try:
  534.                     if self.convert_types_functions[i](item):
  535.                         pass
  536.                 continue
  537.                 invalid_list.append([
  538.                     record[0],
  539.                     self.field_names[i],
  540.                     item])
  541.                 continue
  542.  
  543.             
  544.         
  545.         self._closeTable(fptr)
  546.         return invalid_list
  547.  
  548.     
  549.     def drop(self, name):
  550.         '''Delete physical file containing table and return True.
  551.  
  552.         Arguments:
  553.             name - physical filename, including path, that holds table.
  554.  
  555.         Returns True if no exceptions are raised.
  556.         '''
  557.         os.remove(name)
  558.         return True
  559.  
  560.     
  561.     def getFieldNames(self, name):
  562.         '''Return list of field names for specified table name
  563.  
  564.         Arguments:
  565.             name - physical file name, including path, that holds table.
  566.  
  567.         Returns list of field names for table.
  568.         '''
  569.         fptr = self._openTable(name, 'r')
  570.         self._updateHeaderVars(fptr)
  571.         self._closeTable(fptr)
  572.         return self.field_names
  573.  
  574.     
  575.     def getFieldTypes(self, name):
  576.         '''Return list of field types for specified table name
  577.  
  578.         Arguments:
  579.             name - physical file name, including path, that holds table.
  580.  
  581.         Returns list of field types for table.
  582.         '''
  583.         fptr = self._openTable(name, 'r')
  584.         self._updateHeaderVars(fptr)
  585.         self._closeTable(fptr)
  586.         return self.field_types
  587.  
  588.     
  589.     def len(self, name):
  590.         '''Return total number of non-deleted records in specified table
  591.  
  592.         Arguments:
  593.             name - physical file name, including path, that holds table.
  594.  
  595.         Returns total number of records in table.
  596.         '''
  597.         rec_count = 0
  598.         fptr = self._openTable(name, 'r')
  599.         line = fptr.readline()
  600.         line = fptr.readline()
  601.         while line:
  602.             line = line[0:-1]
  603.             if line.strip() != '':
  604.                 rec_count += 1
  605.             
  606.             line = fptr.readline()
  607.         self._closeTable(fptr)
  608.         return rec_count
  609.  
  610.     
  611.     def _strToBool(self, boolString):
  612.         if boolString == 'True':
  613.             return True
  614.         elif boolString == 'False':
  615.             return False
  616.         else:
  617.             raise KBError('Invalid value for boolean: %s' % boolString)
  618.  
  619.     
  620.     def _strToDate(self, dateString):
  621.         return datetime.date(*map(int, dateString.split('-')))
  622.  
  623.     
  624.     def _strToDateTime(self, dateTimeString):
  625.         tempDateTime = dateTimeString.split('.')
  626.         if len(tempDateTime) > 1:
  627.             microsec = int(tempDateTime[1])
  628.         else:
  629.             microsec = 0
  630.         (tempDate, tempTime) = tempDateTime[0].split(' ')
  631.         (y, m, d) = tempDate.split('-')
  632.         (h, min, sec) = tempTime.split(':')
  633.         return datetime.datetime(int(y), int(m), int(d), int(h), int(min), int(sec), microsec)
  634.  
  635.     
  636.     def _encodeString(self, s):
  637.         '''Encode a string.
  638.  
  639.         Translates problem characters like 
  640. , \r, and \x1a to benign
  641.         character strings.
  642.  
  643.         Keyword Arguments:
  644.             s - string to encode.
  645.  
  646.         Returns encoded string.
  647.         '''
  648.         if self.encodeRegExp.search(s):
  649.             s = s.replace('\n', '&linefeed;')
  650.             s = s.replace('\r', '&carriage_return;')
  651.             s = s.replace('\x1a', '&substitute;')
  652.             s = s.replace('|', '&pipe;')
  653.         
  654.         return s
  655.  
  656.     
  657.     def _unencodeString(self, s):
  658.         '''Unencode a string.
  659.  
  660.         Translates encoded character strings back to special characters
  661.         like 
  662. , \r, \x1a.
  663.  
  664.         Keyword Arguments:
  665.             s - string to unencode.
  666.  
  667.         Returns unencoded string.
  668.         '''
  669.         if self.unencodeRegExp.search(s):
  670.             s = s.replace('&linefeed;', '\n')
  671.             s = s.replace('&carriage_return;', '\r')
  672.             s = s.replace('&substitute;', '\x1a')
  673.             s = s.replace('&pipe;', '|')
  674.         
  675.         return s
  676.  
  677.     
  678.     def _updateHeaderVars(self, fptr):
  679.         fptr.seek(0)
  680.         line = fptr.readline()
  681.         line = line[0:-1]
  682.         header_rec = line.split('|')
  683.         self.last_recno = int(header_rec[0])
  684.         self.del_counter = int(header_rec[1])
  685.         header_fields = header_rec[2:]
  686.         self.field_names = [ item.split(':')[0] for item in header_fields ]
  687.         self.field_types = [ self.strToTypes[x.split(':')[1]] for x in header_fields ]
  688.         convTypes = {
  689.             int: int,
  690.             float: float,
  691.             bool: bool,
  692.             str: self._unencodeString,
  693.             datetime.date: self._strToDate,
  694.             datetime.datetime: self._strToDateTime }
  695.         self.convert_types_functions = [ convTypes[f] for f in self.field_types ]
  696.  
  697.     
  698.     def _validateMatchCriteria(self, fields, patterns):
  699.         '''Run various checks against list of fields and patterns to be
  700.         used as search criteria.  This method is called from all public
  701.         methods that search the database.
  702.         '''
  703.         if len(fields) == 0:
  704.             raise KBError('Length of fields list must be greater ' + 'than zero.')
  705.         
  706.         if len(fields) != len(patterns):
  707.             raise KBError('Length of fields list and patterns list ' + 'not the same.')
  708.         
  709.         for field, pattern in zip(fields, patterns):
  710.             if field not in self.field_names:
  711.                 raise KBError('Invalid field name in fields list: %s' % field)
  712.             
  713.             if field == 'recno':
  714.                 if len(fields) > 1:
  715.                     raise KBError('If selecting by recno, no other ' + 'selection criteria is allowed')
  716.                 
  717.                 if pattern != '*':
  718.                     if not isinstance(pattern, (tuple, list)):
  719.                         pattern = [
  720.                             pattern]
  721.                     
  722.                     for x in pattern:
  723.                         if not isinstance(x, int):
  724.                             raise KBError('Recno argument %s has type %s, expected an integer' % (x, type(x)))
  725.                             continue
  726.                     
  727.                 continue
  728.             
  729.             if self.field_types[self.field_names.index(field)] in [
  730.                 int,
  731.                 float,
  732.                 datetime.date,
  733.                 datetime.datetime]:
  734.                 r = re.search('[\\s]*[\\+-]?\\d', pattern)
  735.                 if not self.cmpFuncs.has_key(pattern[:r.start()]):
  736.                     raise KBError('Invalid comparison syntax: %s' % pattern[:r.start()])
  737.                 
  738.             self.cmpFuncs.has_key(pattern[:r.start()])
  739.         
  740.  
  741.     
  742.     def _convertInput(self, values):
  743.         '''If values is a dictionary or an object, we are going to convert 
  744.         it into a list.  That way, we can use the same validation and 
  745.         updating routines regardless of whether the user passed in a 
  746.         dictionary, an object, or a list.
  747.         '''
  748.         if isinstance(values, list):
  749.             record = list(values)
  750.         elif isinstance(values, dict):
  751.             record = [ values.get(k, '') for k in self.field_names[1:] ]
  752.         else:
  753.             record = [ getattr(values, a, '') for a in self.field_names[1:] ]
  754.         new_rec = []
  755.         for r in record:
  756.             if r == None:
  757.                 new_rec.append('')
  758.                 continue
  759.             []
  760.             new_rec.append(r)
  761.         
  762.         return new_rec
  763.  
  764.     
  765.     def _validateUpdateCriteria(self, updates, filter):
  766.         '''Run various checks against list of updates and fields to be
  767.         used as update criteria.  This method is called from all public
  768.         methods that update the database.
  769.         '''
  770.         if len(updates) == 0:
  771.             raise KBError('Length of updates list must be greater ' + 'than zero.')
  772.         
  773.         if len(updates) != len(filter):
  774.             raise KBError('Length of updates list and filter list ' + 'not the same.')
  775.         
  776.         if 'recno' in filter:
  777.             raise KBError("Cannot update value of 'recno' field.")
  778.         
  779.         self._validateFilter(filter)
  780.         for update, field_name in zip(updates, filter):
  781.             if update in ('', None):
  782.                 continue
  783.             if type(update) != self.field_types[self.field_names.index(field_name)]:
  784.                 raise KBError('Invalid update value for %s' % field_name)
  785.                 continue
  786.         
  787.  
  788.     
  789.     def _validateFilter(self, filter):
  790.         for field in filter:
  791.             if field not in self.field_names:
  792.                 raise KBError('Invalid field name: %s' % field)
  793.                 continue
  794.         
  795.  
  796.     
  797.     def _getMatches(self, fptr, fields, patterns, useRegExp):
  798.         match_list = []
  799.         if 'recno' in fields:
  800.             return self._getMatchByRecno(fptr, patterns)
  801.         else:
  802.             new_patterns = []
  803.             fieldNrs = [ self.field_names.index(x) for x in fields ]
  804.             for fieldPos, pattern in zip(fieldNrs, patterns):
  805.                 if self.field_types[fieldPos] == str:
  806.                     if useRegExp:
  807.                         new_patterns.append(re.compile(pattern))
  808.                     else:
  809.                         new_patterns.append(pattern)
  810.                 useRegExp
  811.                 if self.field_types[fieldPos] == bool:
  812.                     new_patterns.append(str(bool(pattern)))
  813.                     continue
  814.                 []
  815.                 r = re.search('[\\s]*[\\+-]?\\d', pattern)
  816.                 if self.field_types[fieldPos] == int:
  817.                     patternValue = int(pattern[r.start():])
  818.                 elif self.field_types[fieldPos] == float:
  819.                     patternValue = float(pattern[r.start():])
  820.                 else:
  821.                     patternValue = pattern[r.start():]
  822.                 new_patterns.append([
  823.                     self.cmpFuncs[pattern[:r.start()]],
  824.                     patternValue])
  825.             
  826.             fieldPos_new_patterns = zip(fieldNrs, new_patterns)
  827.             maxfield = max(fieldNrs) + 1
  828.             fpos = fptr.tell()
  829.             line = fptr.readline()
  830.             while line:
  831.                 line = line[:-1].strip()
  832.                 
  833.                 try:
  834.                     if line == '':
  835.                         raise 'No Match'
  836.                     
  837.                     record = line.split('|', maxfield)
  838.                     for fieldPos, pattern in fieldPos_new_patterns:
  839.                         if self.field_types[fieldPos] == str:
  840.                             
  841.                             try:
  842.                                 if useRegExp:
  843.                                     if not pattern.search(self._unencodeString(record[fieldPos])):
  844.                                         raise 'No Match'
  845.                                     
  846.                                 elif record[fieldPos] != pattern:
  847.                                     raise 'No Match'
  848.                             except Exception:
  849.                                 raise KBError('Invalid match expression for %s' % self.field_names[fieldPos])
  850.                             except:
  851.                                 None<EXCEPTION MATCH>Exception
  852.                             
  853.  
  854.                         None<EXCEPTION MATCH>Exception
  855.                         if self.field_types[fieldPos] == bool:
  856.                             if record[fieldPos] != pattern:
  857.                                 raise 'No Match'
  858.                             
  859.                         record[fieldPos] != pattern
  860.                         if record[fieldPos] == '':
  861.                             tableValue = None
  862.                         elif self.field_types[fieldPos] == int:
  863.                             tableValue = int(record[fieldPos])
  864.                         elif self.field_types[fieldPos] == float:
  865.                             tableValue = float(record[fieldPos])
  866.                         elif self.field_types[fieldPos] in (datetime.date, datetime.datetime):
  867.                             tableValue = record[fieldPos]
  868.                         else:
  869.                             raise KBError('Invalid field type for %s' % self.field_names[fieldPos])
  870.                         if not pattern[0](tableValue, pattern[1]):
  871.                             raise 'No Match'
  872.                             continue
  873.                 except 'No Match':
  874.                     pass
  875.  
  876.                 match_list.append([
  877.                     line,
  878.                     fpos])
  879.                 fpos = fptr.tell()
  880.                 line = fptr.readline()
  881.         return match_list
  882.  
  883.     
  884.     def _getMatchByRecno(self, fptr, recnos):
  885.         """Search by recnos. recnos is a list, containing '*', an integer, or
  886.         a list or tuple of integers"""
  887.         fpos = fptr.tell()
  888.         line = fptr.readline()
  889.         if recnos == [
  890.             '*']:
  891.             while line:
  892.                 line = line[:-1]
  893.                 if line.strip():
  894.                     yield [
  895.                         line,
  896.                         fpos]
  897.                 
  898.                 fpos = fptr.tell()
  899.                 line = fptr.readline()
  900.         elif isinstance(recnos[0], (tuple, list)):
  901.             recnos = list(recnos[0])
  902.         
  903.         while line:
  904.             line = line[0:-1]
  905.             if line.strip():
  906.                 record = line.split('|')
  907.                 if int(record[0]) in recnos:
  908.                     yield [
  909.                         line,
  910.                         fpos]
  911.                     recnos.remove(int(record[0]))
  912.                     if not recnos:
  913.                         break
  914.                     
  915.                 
  916.             
  917.             fpos = fptr.tell()
  918.             line = fptr.readline()
  919.  
  920.     
  921.     def _incrRecnoCounter(self, fptr):
  922.         last_pos = fptr.tell()
  923.         fptr.seek(0)
  924.         line = fptr.readline()
  925.         header_rec = line[0:-1].split('|')
  926.         self.last_recno += 1
  927.         header_rec[0] = '%06d' % self.last_recno
  928.         self._writeRecord(fptr, 0, '|'.join(header_rec))
  929.         fptr.seek(last_pos)
  930.         return self.last_recno
  931.  
  932.     
  933.     def _incrDeleteCounter(self, fptr):
  934.         last_pos = fptr.tell()
  935.         fptr.seek(0)
  936.         line = fptr.readline()
  937.         header_rec = line[0:-1].split('|')
  938.         self.del_counter += 1
  939.         header_rec[1] = '%06d' % self.del_counter
  940.         self._writeRecord(fptr, 0, '|'.join(header_rec))
  941.         fptr.seek(last_pos)
  942.  
  943.     
  944.     def _deleteRecord(self, fptr, pos, record):
  945.         fptr.seek(pos)
  946.         self._writeRecord(fptr, pos, ' ' * len(record))
  947.  
  948.     
  949.     def _writeRecord(self, fptr, pos, record):
  950.         
  951.         try:
  952.             if pos == 'end':
  953.                 fptr.seek(0, 2)
  954.                 fptr.write(record + '\n')
  955.             else:
  956.                 fptr.seek(pos)
  957.                 fptr.write(record)
  958.         except:
  959.             raise KBError('Could not write record to: ' + fptr.name)
  960.  
  961.  
  962.     
  963.     def _openTable(self, name, access):
  964.         
  965.         try:
  966.             fptr = open(name, access)
  967.         except:
  968.             raise KBError('Could not open table: ' + name)
  969.  
  970.         return fptr
  971.  
  972.     
  973.     def _closeTable(self, fptr):
  974.         
  975.         try:
  976.             fptr.close()
  977.         except:
  978.             raise KBError('Could not close table: ' + fptr.name)
  979.  
  980.  
  981.  
  982.  
  983. class Record(object):
  984.     '''Generic class for record objects.
  985.  
  986.     Public Methods:
  987.         __init__ - Create an instance of Record.
  988.     '''
  989.     
  990.     def __init__(self, names, values):
  991.         self.__dict__ = dict(zip(names, values))
  992.  
  993.  
  994.  
  995. class KBError(Exception):
  996.     '''Exception class for Database Management System.
  997.  
  998.     Public Methods:
  999.         __init__ - Create an instance of exception.
  1000.     '''
  1001.     
  1002.     def __init__(self, value):
  1003.         self.value = value
  1004.  
  1005.     
  1006.     def __str__(self):
  1007.         return `self.value`
  1008.  
  1009.     
  1010.     def __repr__(self):
  1011.         format = 'KBError("%s")'
  1012.         return format % self.value
  1013.  
  1014.  
  1015.