KirbyBase : replacing string exceptions
Steve Howell
showell30 at yahoo.com
Mon Nov 23 11:21:10 EST 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