KirbyBase : replacing string exceptions

Steve Howell showell30 at yahoo.com
Mon Nov 23 17:21:10 CET 2009


On Nov 23, 7:22 am, Brendan <brendandetra... at yahoo.com> wrote:
> In KirbyBase there is a method that uses string exceptions for
> control, even though it has a defined exception. Is there any reason
> the string exceptions below could not be replaced?
> i.e. in code below replace:
> raise "No Match"
> with:
> raise KBError()
> and
> except 'No Match':
> with:
> except KBError:
>

It looks like in some cases KBError() should fall through and only 'No
Match' was intended to be silently caught.

I would consider either leaving it alone if it works, or doing more
serious surgery on the code to simplify the control flow.  The method
is awfully long and nested.

> I have pasted the relevant method and the class definition of KBError
> below
>
> #----------------------------------------------------------------------
>     # _getMatches
>
> #----------------------------------------------------------------------
>     def _getMatches(self, fptr, fields, patterns, useRegExp):
>         # Initialize a list to hold all records that match the search
>         # criteria.
>         match_list = []
>
>         # If one of the fields to search on is 'recno', which is the
>         # table's primary key, then search just on that field and
> return
>         # at most one record.
>         if 'recno' in fields:
>             return self._getMatchByRecno(fptr,patterns)
>         # Otherwise, search table, using all search fields and
> patterns
>         # specified in arguments lists.
>         else:
>             new_patterns = []
>             fieldNrs = [self.field_names.index(x) for x in fields]
>             for fieldPos, pattern in zip(fieldNrs, patterns):
>                 if self.field_types[fieldPos] == str:
>                     # If useRegExp is True, compile the pattern to a
>                     # regular expression object and add it to the
>                     # new_patterns list.  Otherwise,  just add it to
>                     # the new_patterns list.  This will be used below
>                     # when matching table records against the
> patterns.
>                     if useRegExp:
>                         new_patterns.append(re.compile(pattern))
>                         # the pattern can be a tuple with re flags
> like re.I
>                     else:
>                         new_patterns.append(pattern)
>                 elif self.field_types[fieldPos] == bool:
>                     # If type is boolean, I am going to coerce it to
> be
>                     # either True or False by applying bool to it.
> This
>                     # is because it could be '' or [].  Next, I am
> going
>                     # to convert it to the string representation:
> either
>                     # 'True' or 'False'.  The reason I do this is
> because
>                     # that is how it is stored in each record of the
> table
>                     # and it is a lot faster to change this one value
> from
>                     # boolean to string than to change possibly
> thousands
>                     # of table values from string to boolean.  And, if
> they
>                     # both are either 'True' or 'False' I can still
>                     # compare them using the equality test and get the
> same
>                     # result as if they were both booleans.
>                     new_patterns.append(str(bool(pattern)))
>                 else:
>                     # If type is int, float, date, or datetime, this
> next
>                     # bit of code will split the the comparison string
>                     # into the string representing the comparison
>                     # operator (i.e. ">=" and the actual value we are
> going
>                     # to compare the table records against from the
> input
>                     # pattern, (i.e. "5").  So, for example, ">5"
> would be
>                     # split into ">" and "5".
>                     r = re.search('[\s]*[\+-]?\d', pattern)
>                     if self.field_types[fieldPos] == int:
>                         patternValue = int(pattern[r.start():])
>                     elif self.field_types[fieldPos] == float:
>                         patternValue = float(pattern[r.start():])
>                     else:
>                         patternValue = pattern[r.start():]
>                     new_patterns.append(
>                      [self.cmpFuncs[pattern[:r.start()]],
> patternValue]
>                     )
>
>             fieldPos_new_patterns = zip(fieldNrs, new_patterns)
>             maxfield = max(fieldNrs)+1
>
>             # Record current position in table. Then read first detail
>             # record.
>             fpos = fptr.tell()
>             line = fptr.readline()
>
>             # Loop through entire table.
>             while line:
>                 # Strip off newline character and any trailing spaces.
>                 line = line[:-1].strip()
>                 try:
>                     # If blank line, skip this record.
>                     if line == "": raise 'No Match'
>                     # Split the line up into fields.
>                     record = line.split("|", maxfield)
>
>                     # Foreach correspond field and pattern, check to
> see
>                     # if the table record's field matches
> successfully.
>                     for fieldPos, pattern in fieldPos_new_patterns:
>                         # If the field type is string, it
>                         # must be an exact match or a regular
> expression,
>                         # so we will compare the table record's field
> to it
>                         # using either '==' or the regular expression
>                         # engine.  Since it is a string field, we will
> need
>                         # to run it through the unencodeString
> function to
>                         # change any special characters back to their
>                         # original values.
>                         if self.field_types[fieldPos] == str:
>                             try:
>                                 if useRegExp:
>                                     if not pattern.search(
>                                      self._unencodeString(record
> [fieldPos])
>                                      ):
>                                         raise 'No Match'
>                                 else:
>                                     if record[fieldPos] != pattern:
>                                         raise 'No Match'
>                             except Exception:
>                                 raise KBError(
>                                  'Invalid match expression for %s'
>                                  % self.field_names[fieldPos])
>                         # If the field type is boolean, then I will
> simply
>                         # do an equality comparison.  See comments
> above
>                         # about why I am actually doing a string
> compare
>                         # here rather than a boolean compare.
>                         elif self.field_types[fieldPos] == bool:
>                             if record[fieldPos] != pattern:
>                                 raise 'No Match'
>                         # If it is not a string or a boolean, then it
> must
>                         # be a number or a date.
>                         else:
>                             # Convert the table's field value, which
> is a
>                             # string, back into it's native type so
> that
>                             # we can do the comparison.
>                             if record[fieldPos] == '':
>                                 tableValue = None
>                             elif self.field_types[fieldPos] == int:
>                                 tableValue = int(record[fieldPos])
>                             elif self.field_types[fieldPos] == float:
>                                 tableValue = float(record[fieldPos])
>                             # I don't convert datetime values from
> strings
>                             # back into their native types because it
> is
>                             # faster to just leave them as strings
> and
>                             # convert the comparison value that the
> user
>                             # supplied into a string.  Comparing the
> two
>                             # strings works out the same as comparing
> two
>                             # datetime values anyway.
>                             elif self.field_types[fieldPos] in (
>                              datetime.date, datetime.datetime):
>                                 tableValue = record[fieldPos]
>                             else:
>                                 # If it falls through to here, then,
>                                 # somehow, a bad field type got put
> into
>                                 # the table and we show an error.
>                                 raise KBError('Invalid field type for
> %s'
>                                  % self.field_names[fieldPos])
>                             # Now we do the actual comparison.  I used
> to
>                             # just do an eval against the pattern
> string
>                             # here, but I found that eval's are VERY
> slow.
>                             # So, now I determine what type of
> comparison
>                             # they are trying to do and I do it
> directly.
>                             # This sped up queries by 40%.
>                             if not pattern[0](tableValue, pattern[1]):
>                                 raise 'No Match'
>                 # If a 'No Match' exception was raised, then go to the
>                 # next record, otherwise, add it to the list of
> matches.
>                 except 'No Match':
>                     pass
>                 else:
>                     match_list.append([line, fpos])
>                 # Save the file position BEFORE we read the next
> record,
>                 # because after a read it is pointing at the END of
> the
>                 # current record, which, of course, is also the
> BEGINNING
>                 # of the next record.  That's why we have to save the
>                 # position BEFORE we read the next record.
>                 fpos = fptr.tell()
>                 line = fptr.readline()
>
>         # After searching, return the list of matched records.
>         return match_list
>
> #----------------------------------------------------------------------
>
> #--------------------------------------------------------------------------
> # KBError Class
> #--------------------------------------------------------------------------
> class KBError(Exception):
>     """Exception class for Database Management System.
>
>     Public Methods:
>         __init__ - Create an instance of exception.
>     """
>
> #----------------------------------------------------------------------
>     # init
>
> #----------------------------------------------------------------------
>     def __init__(self, value):
>         self.value = value
>
>     def __str__(self):
>         return `self.value`
>
>     # I overrode repr so I could pass error objects from the server to
> the
>     # client across the network.
>     def __repr__(self):
>         format = """KBError("%s")"""
>         return format % (self.value)




More information about the Python-list mailing list