- Lab
- Core Tech

Guided: Exception Handling and Modular Programming in Python
Embark on a journey through the realm of Python with this lab focused on exception handling and modular programming. This code lab will equip you with essential skills to ensure your Python programs run smoothly and is easily maintainable. In this code lab, you'll delve deep into the art of handling exceptions, learning how to gracefully manage errors and unexpected situations in your code. Gain expertise in crafting robust and reliable programs as you master try-except blocks and error-handling strategies. But that's not all – you'll also unravel the power of modular programming, showing you how to break down your code into reusable, organized modules. Explore the world of functions, modules, and packages, and understand how they can streamline your Python projects and make your code more manageable.

Path Info
Table of Contents
-
Challenge
Introduction
Welcome to the Guided: Exception Handling and Modular Programming in Python lab.
In the world of programming, there's more to building robust and efficient applications than just data manipulation. You need to ensure that your code is foolproof and adaptable. This is where exception handling and modular programming come into play, providing extra layers of support to promote code reusability, maintainability, and functionality .
Resilient Recipes
Imagine your digital cookbook app is now a well-established kitchen companion. Users rely on it to create and store all of their culinary ideas. But what happens when unexpected events occur? What if a user mistakenly inputs an impossible recipe, an invalid value, or the system encounters an error while saving a recipe?
Exception handling in Python is your safety net. It catches and gracefully handles unexpected mishaps, ensuring your application continues to run smoothly. It's your insurance policy for those unforeseen hiccups in your code.
Modular Cooking
As your cookbook grows, you'll find yourself juggling an extensive array of recipes and features. It becomes crucial to keep your code organized, maintainable, and easy to expand. This is where modular programming steps in, offering you a structured approach to breaking down your application into manageable, reusable components.
Hands-On
You're invited to experiment and learn interactively. Make use of the Terminal to the right to execute the cookbook application by clicking the Run button or by entering the command
python cookbook.py
. If you wish to create another file to test out some things, you can also run that in the Terminal withpython filename.py
. -
Challenge
Exception Handling in Python
Understanding Exceptions
In Python, exceptions are unexpected events that can disrupt the normal flow of your program. These events can range from trying to access a file that doesn't exist to performing mathematical operations that are mathematically impossible. Exception handling is the technique of anticipating, identifying, and managing these exceptions to prevent your program from crashing, typically through the use of a
try-except
clause.Consider this example:
try: result = 10 / 0 except ZeroDivisionError: result = "Division by zero is not allowed"
In this code snippet, an attempt is made to divide by 0, which would cause an exception and normally result in the program crashing. By using the
try-except
clause to catch theZeroDivisionError
, the exception is caught, allowing the program to continue without crashing.
Try-Except
The
try-except
is the key to adding exception handling to your code. As seen in the example above, you encase any code that could raise an exception within thetry
clause. If an exception is raised, the code in thetry
clause will immediately stop. If the exception is caught in theexcept
clause, then the code within theexcept
clause will be executed.You can also add an
else
or afinally
clause to execute after thetry-except
block. Theelse
clause will only execute after thetry
clause if no exception was caught. On the other hand,finally
will always execute whether or not an exception was caught. This makesfinally
especially useful for some tasks such as cleanup tasks. Here are some examples:try: x = 10 y = 0 z = x / y except ZeroDivisionError: print("Cannot divide by zero!") # If y = 0, this exception would be caught and "Cannot divide by zero!" is printed. z will not be printed. else: print(z) # If y != 0, z is printed to console
try: x = 10 y = 0 z = x / y except ZeroDivisionError: print("Cannot divide by zero!") finally: print(x) # Always prints x regardless of whether y=0
Raising Exceptions
Aside from using
try-except
blocks, you can also utilize theraise
statement to force exceptions to occur. Let's take a look at another examplex = 15 y = 0 if y == 0: raise ValueError("y cannot be 0!") z = x / y
As seen in the example, if
y
was0
but theif
statement did not exist, the linez = x / y
would raise aZeroDivisionError
exception. However, since theif
statement is there, ify
was0
it would actually result in aValueError
exception that is forcibly raised with theraise
statement instead of theZeroDivisionError
.
Multiple Exceptions and Best Practices
You can also have multiple
except
clauses for a singletry
clause. However, only the firstexcept
clause to match the raised exception will execute. This behavior is the same as aswitch
statement. Furthermore,except
clauses can catch multiple types of exceptions and alias them when caught. Take a look at the following example:try: # Code that could raise an exception here except (ExceptionType1, ExceptionType2) as Ex1: # Caught exception is aliased as Ex1 print(Ex1) except (ExceptionType3, ExceptionType4) as Ex2: # Caught exception is aliased as Ex2 print(Ex2) except Exception as e: # Generic exception is aliased as e print(e)
So let's unpack this. Notice that there are 3
except
clauses for thetry
clause and the first 2except
clauses have multiple exception types. This means that if an exception of typeExceptionType1
orExceptionType2
is raised, only the firstexcept
clause executes. If the exception is of typeExceptionType3
orExceptionType4
, only the secondexcept
clause executes. Lastly, if the exception is none of the aforementioned types, then the 3rdexcept
clause will execute.Notice that the generic
Exception
catch comes last. If it came first, the other twoexcept
clauses would never execute because the generic would match every exception thrown. This is why you should always put the most specificexcept
clauses first before generic or higher order exception types. This is know as an exception hierarchy.One last thing to note is that in a production setting, you want to avoid using catch-alls such as
except Exception
,except BaseException
, or even a blanketexcept
without a specified exception type because these can potentially obfuscate particular details that you need to know about the exceptions being caught. This can result in serious bugs and security risks stemming from underlying issues in your code. Remember that exception handling is meant to address potential or expected errors in code, not the unexpected. -
Challenge
Modular Programming in Python
Modules in Python
A module in Python is a file containing Python definitions and statements. You can think of a module as a library of functions and variables that can be imported and used in your programs. Here's how you can create and use a simple module:
# mymodule.py def greet(name): return f"Hello, {name}!" # main.py import mymodule message = mymodule.greet("Alice") print(message) # Output: "Hello, Alice!"
Modules help you organize your code, making it more readable and allowing you to reuse functions across different parts of your project.
Packages in Python
Packages are a way to organize related modules into directories, creating a hierarchical structure for your project. A package is a directory that contains a special
__init__.py
file to indicate that it's a package. Here's a simple example:mypackage/ __init__.py module1.py module2.py
Now, you can import modules from the package:
# main.py from mypackage import module1 result = module1.add(3, 4) print(result) # Output: 7
Packages provide a powerful way to compartmentalize your codebase, especially in larger projects, by allowing you to group related modules and submodules together for better maintainability and readability.
The init.py file
The
__init__.py
file is a special file within a package directory. It can contain initialization code, package-level variables, and it's executed when the package is imported. While it can be empty, it often contains package-level setup code. Here's an example:# __init__.py print("mypackage has been imported") # main.py from mypackage import module1
When you import
mypackage
, the code in__init__.py
will execute, allowing you to perform package-level setup. The__init__.py
file must exist within a directory for Python to recognize it as a package. -
Challenge
Adding Exception Handling to the Cookbook
Now that you are familiar with exception-handling and modularity in Python, let's take a look at the cookbook application in
cookbook.py
. This application is fully functional as it is now, but theload_recipes()
,save_recipes()
, andadd_recipe()
functions are susceptible to exceptions that could cause the application to crash. You will be adding some simple exception-handling to remedy this.If you're stuck or want to compare your results, check out the files in the solution directory. There is also a copy of the original
recipes.json
in case you need it to reset the contents of yourrecipes.json
.There are multiple "correct" answers here, so don't be alarmed if your solution is slightly different. What matters is that the application should be working as intended!
Handling load_recipes()
Let's take a look at the
load_recipes()
function. This function uses theopen()
method to open a file with the specifiedfilename
parameter and load it into a Python object. The defaultfilename
it loads isrecipes.json
, which is a valid file. However, if a nonexistentfilename
was provided, this function would throw an exception and crash. Let's add exception-handling to fix that.Details
1. Use a `try-except` block here. 2. The exception to be caught is a `FileNotFoundError`. 3. In the `except` clause, return an empty List.
Handling save_recipes()
Next, let's look at
save_recipes()
. Similarly toload_recipes()
, it utilizes theopen()
method to open a file and write to it. If an nonexistentfilename
is given, this method will generate a file with thatfilename
and dumprecipes
into it. You don't want that, so let's make some adjustments.Details
1. Use a `try-except-else` block here. 2. Before the `with open()` statement, add an `if` statement that checks if `filename` exists using `os.path.exists(filename)`. 3. If the `filename` does not exist, use `raise` to throw a `FileNotFoundError`. Pass in a format string such as `f"File with the name {filename} does not exist."` or something similar to the exception. 4. In the `except` clause, catch the `FileNotFoundError` and print its contents. A format string such as `f"Error: {e}. Recipes was not updated."` works here. 5. In the `else` clause, print a success message like `"Recipes saved successfully!"`.
Handling add_recipe()
Lastly, fix the
add_recipe()
function. Theprep_time
andrating
variables attempt to cast the userinput
to an integer, but what happens if the input was a word such astest
? This would throw an exception because a word cannot be cast as an integer! To address this, use atry-except-else-finally
block.Details
1. Encase everything up to and including `recipes.append(recipe)` within the `try` clause. 2. The `except` clause should catch a `ValueError` if input that cannot be parsed as an integer is given. You can `print` something simple such as `f"Error: {e}"`. 3. In the `else` clause, place the `print("Recipe added successfully!")` statement. 4. In the `finally` clause, return `recipes`. A format string such as `f"Error: {e}. Recipes was not updated."` works here.
With this, you've added exception handling to the application. If a nonexistent filename is given to
load_recipes()
, the function will return an empty List instead of crashing the application. If given tosave_recipes()
, the function will no longer extra unwanted files withrecipes
saved into them. Finally, invalid inputs forprep_time
andrating
will instead reprompt the user from the menu instead of crashing the application as well. -
Challenge
Modularizing the Cookbook
You have one last thing to do and that is to refactor the code in
cookbook.py
. Notice how there are five functions related to recipes and then themain
function. If you were to add more features or expand upon the functionality for recipes, the code would quickly become bloated and difficult to read. Instead, let's make it more modular by moving the recipe functions into a package.
Creating a Package
To create a package, first make a new directory in the workspace. Call it
recipe_manager
or any other name of your choosing, though you'll have to adjust some import statements in the following steps accordingly.As mentioned previously, a directory will only be recognized as a package if it has an
__init__.py
file, even if the file is empty. Go ahead and make one and you will have successfully created a package to move your code into.
Refactoring into Modules
Within your newly created package, create a module called
recipe_operations.py
or any other name that you wish. Move the method definitions forload_recipes()
,save_recipes()
,add_recipe()
,view_recipes()
, andsearch_by_ingredient()
into your new module. Now,cookbook.py
should look much cleaner as it should only contain themain()
method.Don't forget that since you have refactored the recipe functions, you need to import them from your package and module. At the top of
cookbook.py
, add an import statement along the lines offrom recipe_manager.recipe_operations import *
, which will import everything in therecipe_operations
module of therecipe_manager
package. Make sure to use the correct names if you used different names for your package and module.
Just like that, you're done! You've added exception handling to your cookbook application to make it more robust and refactored some of its code into modular components. This will set a solid foundation for any future additions or features you'd like to add. Feel free to tinker with the application as you wish, or run it for yourself!
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.