Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.
  • Labs icon Lab
  • A Cloud Guru
Google Cloud Platform icon
Labs

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

Google Cloud Platform icon
Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 45m
Published
Clock icon Mar 20, 2020

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. 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 from Exception with pass 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.

  2. 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 the Employee class, we need to catch these exceptions and instead raise a DatabaseError.

    ~/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.

  3. Challenge

    Raise `MissingEmployeeError` in `get_at_line` and `save` if `IndexError` Occurs

    Within get_at_line and save, we need to handle the potential IndexError 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 the MissingEmployeeError.

    ~/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
    

The Cloud Content team comprises subject matter experts hyper focused on services offered by the leading cloud vendors (AWS, GCP, and Azure), as well as cloud-related technologies such as Linux and DevOps. The team is thrilled to share their knowledge to help you build modern tech solutions from the ground up, secure and optimize your environments, and so much more!

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.

Start learning by doing today

View Plans