KirbyBase : replacing string exceptions

B

Brendan

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)
 
S

Steve Howell

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)
 
B

Brendan

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 (
                           - Hide quoted text -

- Show quoted text -...

read more »

Okay. Thanks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top