From e34253adb251acbfbe392288f9e94e16a96f6952 Mon Sep 17 00:00:00 2001 From: "Christian T. Jacobs" Date: Mon, 3 Jul 2017 13:27:12 +0100 Subject: [PATCH] Better error handling. --- pyqso/adif.py | 33 ++++------------ pyqso/awards.py | 11 ++++-- pyqso/cabrillo.py | 22 +++-------- pyqso/log.py | 37 +++++++++--------- pyqso/logbook.py | 96 +++++++++++++++++++++++++++++++---------------- 5 files changed, 103 insertions(+), 96 deletions(-) diff --git a/pyqso/adif.py b/pyqso/adif.py index 0edcdbf..d110e5c 100644 --- a/pyqso/adif.py +++ b/pyqso/adif.py @@ -204,22 +204,13 @@ class ADIF: :arg str path: The path to the ADIF file to read. :returns: A list of dictionaries (one dictionary per QSO), with each dictionary containing field-value pairs, e.g. {FREQ:145.500, BAND:2M, MODE:FM}. If the file cannot be read, the method returns None. :rtype: list - :raises IOError: if the ADIF file does not exist or cannot be read (e.g. due to lack of read permissions). + :raises IOError: If the ADIF file does not exist or cannot be read (e.g. due to lack of read permissions). """ logging.debug("Reading in ADIF file with path: %s..." % path) text = "" - try: - f = open(path, mode='r', errors="replace") + with open(path, mode='r', errors="replace") as f: text = f.read() - f.close() # Close the file, otherwise "bad things" might happen! - except IOError as e: - logging.error("I/O error %d: %s" % (e.errno, e.strerror)) - return None - except Exception as e: - logging.error("An error occurred when reading the ADIF file.") - logging.exception(e) - return None records = self.parse_adi(text) @@ -333,16 +324,13 @@ class ADIF: :arg list records: The list of QSO records to write. :arg str path: The desired path of the ADIF file to write to. - :returns: True if the write process was successful, otherwise False. - :rtype: bool - :raises IOError: if the ADIF file cannot be written (e.g. due to lack of write permissions). + :returns: None + :raises IOError: If the ADIF file cannot be written (e.g. due to lack of write permissions). """ logging.debug("Writing records to an ADIF file...") - success = False - try: - f = open(path, mode='w', errors="replace") # Open file for writing + with open(path, mode='w', errors="replace") as f: # Open file for writing # First write a header containing program version, number of records, etc. dt = datetime.now() @@ -368,15 +356,10 @@ class ADIF: logging.debug("Finished writing records to the ADIF file.") f.close() - logging.info("Wrote %d QSOs to %s in ADIF format." % (len(records), path)) - success = True - except IOError as e: - logging.error("I/O error %d: %s" % (e.errno, e.strerror)) - except Exception as e: # All other exceptions. - logging.error("An error occurred when writing the ADIF file.") - logging.exception(e) - return success + logging.info("Wrote %d QSOs to %s in ADIF format." % (len(records), path)) + + return def is_valid(self, field_name, data, data_type): """ Validate the data in a field with respect to the ADIF specification. diff --git a/pyqso/awards.py b/pyqso/awards.py index 10f9f23..c2c72fc 100644 --- a/pyqso/awards.py +++ b/pyqso/awards.py @@ -19,6 +19,7 @@ from gi.repository import Gtk import logging +import sqlite class Awards: @@ -78,16 +79,18 @@ class Awards: """ logging.debug("Counting the band/mode combinations for the awards table...") + # Wipe everything and start again. self.awards.clear() + # For each mode, add a new list for holding the totals, and initialise the values to zero. count = [] for i in range(0, len(self.bands)): count.append([0]*len(self.bands)) for log in logbook.logs: - records = log.records - if(records is not None): + try: + records = log.records for r in records: if(r["BAND"] is not None and r["MODE"] is not None): if(r["BAND"].lower() in self.bands and r["MODE"] != ""): @@ -101,8 +104,10 @@ class Awards: # FIXME: This assumes that all the other modes in the ADIF list are digital modes. Is this the case? count[2][band] += 1 count[3][band] += 1 # Keep the total of each column in the "Mixed" mode. - else: + + except sqlite.Error as e: logging.error("Could not update the awards table for '%s' because of a database error." % log.name) + logging.exception(e) # Insert the rows containing the totals. for i in range(0, len(self.modes)): diff --git a/pyqso/cabrillo.py b/pyqso/cabrillo.py index 1ddad7c..2debe9d 100644 --- a/pyqso/cabrillo.py +++ b/pyqso/cabrillo.py @@ -40,15 +40,12 @@ class Cabrillo: :arg str path: The desired path of the Cabrillo file to write to. :arg str contest: The name of the contest. :arg str mycall: The callsign used during the contest. - :returns: True if the write process was successful, otherwise False. - :rtype: bool - :raises IOError: if the Cabrillo file cannot be written (e.g. due to lack of write permissions).""" + :returns: None + :raises IOError: If the Cabrillo file cannot be written (e.g. due to lack of write permissions).""" logging.debug("Writing records to a Cabrillo file...") - success = False - try: - f = open(path, mode='w', errors="replace") # Open file for writing + with open(path, mode='w', errors="replace") as f: # Open file for writing # Header f.write("""START-OF-LOG: %s\n""" % (CABRILLO_VERSION)) @@ -62,7 +59,7 @@ class Cabrillo: # Frequency. Note that this must be in kHz. The frequency is stored in MHz in the database, so it's converted to kHz here. try: freq = str(float(r["FREQ"])*1e3) - except ValueError as e: + except ValueError: freq = "" # Mode @@ -103,15 +100,6 @@ class Cabrillo: # Footer f.write("END-OF-LOG:") - f.close() - logging.info("Wrote %d QSOs to %s in Cabrillo format." % (len(records), path)) - success = True - except IOError as e: - logging.error("I/O error %d: %s" % (e.errno, e.strerror)) - except Exception as e: # All other exceptions. - logging.error("An error occurred when writing the Cabrillo file.") - logging.exception(e) - - return success + return diff --git a/pyqso/log.py b/pyqso/log.py index a12437b..bdd8b64 100644 --- a/pyqso/log.py +++ b/pyqso/log.py @@ -52,8 +52,10 @@ class Log(Gtk.ListStore): logging.debug("Populating '%s'..." % self.name) self.add_missing_db_columns() self.clear() - records = self.records - if(records is not None): + + try: + records = self.records + for r in records: liststore_entry = [r["id"]] for field_name in AVAILABLE_FIELD_NAMES_ORDERED: @@ -63,8 +65,11 @@ class Log(Gtk.ListStore): liststore_entry.append(r[field_name]) self.append(liststore_entry) logging.debug("Finished populating '%s'." % self.name) - else: + + except sqlite.Error as e: logging.error("Could not populate '%s' because of a database error." % self.name) + logging.exception(e) + return def add_missing_db_columns(self): @@ -315,15 +320,12 @@ class Log(Gtk.ListStore): :returns: A list of all the records in the log. Each record is represented by a dictionary. :rtype: dict + :raises sqlite.Error: If the records could not be retrieved from the database. """ - try: - with self.connection: - c = self.connection.cursor() - c.execute("SELECT * FROM %s" % self.name) - return c.fetchall() - except sqlite.Error as e: - logging.exception(e) - return None + with self.connection: + c = self.connection.cursor() + c.execute("SELECT * FROM %s" % self.name) + return c.fetchall() @property def record_count(self): @@ -331,12 +333,9 @@ class Log(Gtk.ListStore): :returns: The total number of records in the log. :rtype: int + :raises sqlite.Error: If the record count could not be determined due to a database error. """ - try: - with self.connection: - c = self.connection.cursor() - c.execute("SELECT Count(*) FROM %s" % self.name) - return c.fetchone()[0] - except (sqlite.Error, IndexError) as e: - logging.exception(e) - return None + with self.connection: + c = self.connection.cursor() + c.execute("SELECT Count(*) FROM %s" % self.name) + return c.fetchone()[0] diff --git a/pyqso/logbook.py b/pyqso/logbook.py index 026d764..175b8f4 100644 --- a/pyqso/logbook.py +++ b/pyqso/logbook.py @@ -204,9 +204,9 @@ class Logbook: self.connection = sqlite.connect(path) self.connection.row_factory = sqlite.Row except sqlite.Error as e: - # PyQSO can't connect to the database. + # Cannot connect to the database. logging.exception(e) - error(parent=self.application.window, message="PyQSO cannot connect to the database. Check file permissions?") + error(parent=self.application.window, message="Cannot connect to the database. Check file permissions?") return False logging.debug("Database connection created successfully!") @@ -555,16 +555,21 @@ class Logbook: path = None dialog.destroy() - # Read the records. - adif = ADIF() if(path is None): logging.debug("No file path specified.") return - else: + + # Read the records. + adif = ADIF() + try: records = adif.read(path) - if(records is None): - error(parent=self.application.window, message="Could not import the log.") - return + except IOError as e: + error(parent=self.application.window, message="Could not import the log. I/O error %d: %s" % (e.errno, e.strerror)) + return + except Exception as e: + error(parent=self.application.window, message="Could not import the log.") + logging.exception(e) + return # Get the new log's name (or the name of the existing log the user wants to import into). ln = LogNameDialog(self.application, title="Import Log") @@ -662,16 +667,26 @@ class Logbook: if(path is None): logging.debug("No file path specified.") else: - adif = ADIF() - records = log.records - if(records is not None): - success = adif.write(records, path) - if(success): - info(parent=self.application.window, message="Exported %d QSOs to %s in ADIF format." % (len(records), path)) - else: - error(parent=self.application.window, message="Could not export the records.") - else: + + # Retrieve the log's records from the database. + try: + records = log.records + except sqlite.Error as e: error(parent=self.application.window, message="Could not retrieve the records from the SQL database. No records have been exported.") + logging.exception(e) + return + + # Write the records. + adif = ADIF() + try: + adif.write(records, path) + info(parent=self.application.window, message="Exported %d QSOs to %s in ADIF format." % (len(records), path)) + except IOError as e: + error(parent=self.application.window, message="Could not export the records. I/O error %d: %s" % (e.errno, e.strerror)) + except Exception as e: # All other exceptions. + error(parent=self.application.window, message="Could not export the records.") + logging.exception(e) + return def export_log_cabrillo(self, widget=None): @@ -723,33 +738,50 @@ class Logbook: return ced.dialog.destroy() - cabrillo = Cabrillo() - records = log.records - if(records is not None): - success = cabrillo.write(records, path, contest=contest, mycall=mycall) - if(success): - info(parent=self.application.window, message="Exported %d QSOs to %s in Cabrillo format." % (len(records), path)) - else: - error(parent=self.application.window, message="Could not export the records.") - else: + # Retrieve the log's records from the database. + try: + records = log.records + except sqlite.Error as e: error(parent=self.application.window, message="Could not retrieve the records from the SQL database. No records have been exported.") + logging.exception(e) + return + + # Write the records. + cabrillo = Cabrillo() + try: + cabrillo.write(records, path, contest=contest, mycall=mycall) + info(parent=self.application.window, message="Exported %d QSOs to %s in Cabrillo format." % (len(records), path)) + except IOError as e: + error(parent=self.application.window, message="Could not export the records. I/O error %d: %s" % (e.errno, e.strerror)) + except Exception as e: # All other exceptions. + error(parent=self.application.window, message="Could not export the records.") + logging.exception(e) + return def print_log(self, widget=None): """ Print all the records in the log (that is currently selected). Note that only a few important fields are printed because of the restricted width of the page. """ + page_index = self.notebook.get_current_page() # Get the index of the selected tab in the logbook. if(page_index == 0): # If we are on the Summary page... logging.debug("No log currently selected!") return log_index = self.get_log_index() log = self.logs[log_index] - records = log.records - if(records is not None): - printer = Printer(self.application) - printer.print_records(records, title="Log: %s" % log.name) - else: + + # Retrieve the records. + try: + records = log.records + except sqlite.Error as e: error(parent=self.application.window, message="Could not retrieve the records from the SQL database. No records have been printed.") + logging.exception(e) + return + + # Print the records. + printer = Printer(self.application) + printer.print_records(records, title="Log: %s" % log.name) + return def add_record_callback(self, widget): @@ -997,7 +1029,7 @@ class Logbook: else: return False except (sqlite.Error, IndexError) as e: - logging.exception(e) # Database error. PyQSO could not check if the log name exists. + logging.exception(e) # Database error. Could not check if the log name exists. return None def get_log_index(self, name=None):