- Lab
- A Cloud Guru
Creating Custom Python Exception Types
When building a larger system with custom classes, we will likely have different situations come up that wouldn't be encompassed by existing exceptions. In this hands-on lab, we'll create a few custom exception types that will fit into our employee management class hierarchy. To feel comfortable completing this lab, you'll want to know how to create custom exception types (watch the "Creating Custom Exception Types" video from the Certified Associate in Python Programming Certification course).
Path Info
Table of Contents
-
Challenge
Create `MissingEmployeeError` and `DatabaseError` in `employee.py`
Our custom errors don't need to have any implementation, so we're going to create them at the top of our
employee.py
file to inherit fromException
withpass
as the class body.~/employee.py
class MissingEmployeeError(Exception): pass class DatabaseError(Exception): pass # Rest of file unchanged and omitted
Now we'll need to use these exception types in the proper areas of the
Employee
class. -
Challenge
Raise `DatabaseError` Anywhere We Fail to Open the Database File
Our
DatabaseError
occurs whenever we can't open and read the database. This can happen in two situations:- A
FileNotFoundError
because the file doesn't exist yet - A
PermissionError
because we're not allowed to access the file
Everywhere we use
open
in theEmployee
class, we need to catch these exceptions and instead raise aDatabaseError
.~/employee.py
# custom errors omitted class Employee: default_db_file = "employee_file.txt" @classmethod def get_all(cls, file_name=None): results = [] if not file_name: file_name = cls.default_db_file try: with open(file_name, "r") as f: lines = [ line.strip("\n").split(",") + [index + 1] for index, line in enumerate(f.readlines()) ] except (FileNotFoundError, PermissionError) as err: raise DatabaseError(str(err)) for line in lines: results.append(cls(*line)) return results @classmethod def get_at_line(cls, line_number, file_name=None): if not file_name: file_name = cls.default_db_file try: with open(file_name, "r") as f: line = [ line.strip("\n").split(",") + [index + 1] for index, line in enumerate(f.readlines()) ][line_number - 1] return cls(*line) except (FileNotFoundError, PermissionError) as err: raise DatabaseError(str(err)) def __init__(self, name, email_address, title, phone_number=None, identifier=None): self.name = name self.email_address = email_address self.title = title self.phone_number = phone_number self.identifier = identifier def email_signature(self, include_phone=False): signature = f"{self.name} - {self.title}\n{self.email_address}" if include_phone and self.phone_number: signature += f" ({self.phone_number})" return signature def save(self, file_name=None): if not file_name: file_name = self.default_db_file try: with open(file_name, "r+") as f: lines = f.readlines() if self.identifier: lines[self.identifier - 1] = self._database_line() else: lines.append(self._database_line()) f.seek(0) f.writelines(lines) except (FileNotFoundError, PermissionError) as err: raise DatabaseError(str(err)) def _database_line(self): return ( ",".join( [self.name, self.email_address, self.title, self.phone_number or ""] ) + "\n" )
Now all the areas where our class interacts with the database file will raise the proper error if accessing the file doesn't go according to plan.
- A
-
Challenge
Raise `MissingEmployeeError` in `get_at_line` and `save` if `IndexError` Occurs
Within
get_at_line
andsave
, we need to handle the potentialIndexError
that will be raised if we try to access a line that doesn't exist. This indicates the employee we were searching for wasn't found, so we want to raise theMissingEmployeeError
.~/employee.py
# custom errors omitted class Employee: default_db_file = "employee_file.txt" @classmethod def get_all(cls, file_name=None): results = [] if not file_name: file_name = cls.default_db_file try: with open(file_name, "r") as f: lines = [ line.strip("\n").split(",") + [index + 1] for index, line in enumerate(f.readlines()) ] except (FileNotFoundError, PermissionError) as err: raise DatabaseError(str(err)) for line in lines: results.append(cls(*line)) return results @classmethod def get_at_line(cls, line_number, file_name=None): if not file_name: file_name = cls.default_db_file try: with open(file_name, "r") as f: line = [ line.strip("\n").split(",") + [index + 1] for index, line in enumerate(f.readlines()) ][line_number - 1] return cls(*line) except (FileNotFoundError, PermissionError) as err: raise DatabaseError(str(err)) except IndexError: raise MissingEmployeeError(f"no employee at line {line_number}") def __init__(self, name, email_address, title, phone_number=None, identifier=None): self.name = name self.email_address = email_address self.title = title self.phone_number = phone_number self.identifier = identifier def email_signature(self, include_phone=False): signature = f"{self.name} - {self.title}\n{self.email_address}" if include_phone and self.phone_number: signature += f" ({self.phone_number})" return signature def save(self, file_name=None): if not file_name: file_name = self.default_db_file try: with open(file_name, "r+") as f: lines = f.readlines() if self.identifier: lines[self.identifier - 1] = self._database_line() else: lines.append(self._database_line()) f.seek(0) f.writelines(lines) except (FileNotFoundError, PermissionError) as err: raise DatabaseError(str(err)) except IndexError: raise MissingEmployeeError(f"no employee at line {self.identifier}") def _database_line(self): return ( ",".join( [self.name, self.email_address, self.title, self.phone_number or ""] ) + "\n" )
Now if we run the
test_custom_exceptions.py
script, we shouldn't see any issues:python3.7 test_custom_exceptions.py
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.