KirbyBase : replacing string exceptions
Brendan
brendandetracey at yahoo.com
Mon Nov 23 10:22:25 EST 2009
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:
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