Featured resource
2025 Tech Upskilling Playbook
Tech Upskilling Playbook

Build future-ready tech teams and hit key business milestones with seven proven plays from industry leaders.

Check it out
  • Lab
    • Libraries: If you want this lab, consider one of these libraries.
    • Cloud
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
Lab platform
Lab Info
Level
Intermediate
Last updated
Aug 15, 2025
Duration
45m

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
    
About the author

Pluralsight Skills gives leaders confidence they have the skills needed to execute technology strategy. Technology teams can benchmark expertise across roles, speed up release cycles and build reliable, secure products. By leveraging our expert content, skill assessments and one-of-a-kind analytics, keep up with the pace of change, put the right people on the right projects and boost productivity. It's the most effective path to developing tech skills at scale.

Real skill practice before real-world application

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.

Learn by doing

Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.

Follow your guide

All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.

Turn time into mastery

On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.

Get started with Pluralsight