Python Context Manager, what is it for, and how it can help you better handle your exceptions together with Flask.
Handling exceptions is one of the most important things to do. Doing it right when working with Python and a web framework like Flask is definitely helpful for you.

One of the reasons for this is how Python works, is an interpreted language, meaning in a simple way that somethings you will only discover a problem when running.
The concept of exception handling is a very important part when handling data that can have many possible outputs, and you do not know them all, one use case for this is handling/working with databases.
What is Python context?
In simple terms a context or context manager allows you to allocate and release resources when you want.
It does give you more control over how this interaction will happen.
Maybe this looks too generic let’s make this more visible with an example.
Let’s have a problem, so we can understand when to use context.
For this scenario, we will use a Flask application to exemplify what we want to achieve, but remember, this is a Python resource and it will work on any Python code.
For a very simple application we can use this code:
import click
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy_serializer import SerializerMixin
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy()
db.init_app(app)
# Custom commands for your terminal
def create_db():
"""Creates database"""
db.create_all()
def drop_db():
"""Cleans database"""
db.drop_all()
def insert_user():
"""Add user to our database"""
db.session.add(TestModel(user="Felipe"))
db.session.commit()
# add multiple commands in a bulk
for command in [create_db, drop_db, insert_user]:
app.cli.add_command(app.cli.command()(command))
# Model to user with our database
class TestModel(db.Model, SerializerMixin):
user = db.Column(db.String(50), primary_key=True)
# Route to test our data
@app.route("/user/<user_name>")
def show_user(user_name:str):
user_db = TestModel.query.filter_by(user=user_name).first()
return jsonify(user_db.to_dict())
Ok, here we have one API that we can query users, for this, we can just insert one user using the terminal, no further explanation here, but I will insert one user using my name using the command that I created, typing this on my terminal:
First, create our database:flask create-db
Second, create our database:flask insert-user
Let’s try to run our application, hitting: flask run
Remember, you can only use flask run if you have set your Flask application name environment variable properly, if you don’t know much about it I recommend read this post: https://itnext.io/start-using-env-for-your-flask-project-and-stop-using-environment-variables-for-development-247dc12468be
And let’s type in our browser this URL/path to request my user: http://127.0.0.1:5000/user/Felipe

Ok, so far all good, but let’s create a problem, let’s try to get a user that doesn’t exist, like, IronMan. On my browser, I will call like this: http://127.0.0.1:5000/user/IronMan

We have a fatal error, and the reason is simple, only when we have a successful response that we have a user object, and from this, we can indeed transform it into a dictionary using our helper “to_dict”.
In this case, we need to handle this error, and most probably the initial thought on this would be, let’s just use “try exception” right?
It would be something like this:
@app.route("/user/<user_name>")
def show_user(user_name:str):
user_db = TestModel.query.filter_by(user=user_name).first()
try:
return jsonify(user_db.to_dict())
except:
abort(404, "can't find a user")
This would be your result now:

It’s also ok, and of course, it depends on your rule how you want to handle your error.
But imagine that you have more than 5, 10 routes that handle user model, you will need to do a try-catch for each one of those, you could argue that you can create a class to abstract it, but, there’s an alternative, and it’s use context manager.
Creating a custom context to handle your error.
Besides what looks like, a custom context handle, or the use of the statement “with” was indeed created to be an alternative to the try/finally statement.
You can read more about it on PEP 343: https://www.python.org/dev/peps/pep-0343/
As we want an alternative way to handle it, let’s create one custom class, that will implement the methods that we need in order to be able to use the statement “with”.
As for the sake of simplicity we are using the same file for everything, I will also add the class in the same file, your file should become this:
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy_serializer import SerializerMixin
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy()
db.init_app(app)
class TestModel(db.Model, SerializerMixin):
user = db.Column(db.String(50), primary_key=True)
class UserResponseBuilder():
user = None
def __init__(self, user_model:TestModel):
print("Init")
if user_model is not None:
self.user = user_model.user
def __enter__(self):
print("Enter")
if self.user is None:
self.user = "No valid user found"
return self
def __exit__(self, type, value, traceback):
print("Exit")
app.logger.info(f"User handled {self.user}")
@app.route("/user/<user_name>")
def show_user(user_name:str):
user_db = TestModel.query.filter_by(user=user_name).first()
with UserResponseBuilder(user_db) as user_builder:
return jsonify(user_name=user_builder.user)
It’s pretty simple, let’s explain each important part.
- How to use a “context”, we use the statement “with” in our router method, as we are creating an object that we pass our fetched user, is expected to be ok and the output of this creation or the keyword “as” be a variable “user_builder”
- Our custom class “UserResponseBuilder”, is a normal class, that has an initializer but also 2 important methods to be able to use the statement “with”.
2.1 —
__enter__
function, this will be executed right after the initializer. 2.2 —__exit__(self, type, value, traceback)
function, with all these parameters, even if you don’t use them, this method will be called after you successfully finished the creation of this class. - The implementation is a bit different, now create a new object with the name of the user, but, inside the initializer, we will evaluate if is not empty, it is, we don’t set the user name.
- Inside the __enter__ method is the one that is executed right after the initializer we will check if we don’t have a user and will provide a default value to that.
- Inside the __exit__ method we can for example check if is not set or any rule and perhaps log it, so we can check if something wrong is happening. For now, we are not keeping it as None, so we just log. Remember, this will be executed when is finished, there’s nothing that you can do with your object/class any more.
Each one of these methods and steps is what you need in order to create a custom context and use the statement “with”.
Running it again this will be our output:

You may ask, why not use try/exception? There’s no reason why not, but the important to notice here is how we can control and make our code easier to handle.
Anywhere that I want to use my user I can just use this class and use the statement “with” to validate it, and if I need to add more rules, change it, I can, and anywhere that I use will be up-to-date.
Final considerations.
What you should get from this is that we can have a cleaner environment to handle exceptions, and in this case, we “transform” our exception into a handled fail situation, that can be reused.
When you use the try/exception resource you not only need to handle the exception itself, but the good practice is to have your custom exception, never raise the base exception.
A context manager is usually explained using open and close files or connections with the database, which indeed is a very good example, but is not only for this, I demonstrate here how you can use it to help your code be cleaner and better handled.
Conclusion
I hope you enjoyed reading this. If you’d like to support me as a writer, consider signing up to become a Medium member. It’s just $5 a month and you get unlimited access to Medium also if you liked, consider sharing with others that also want to learn more about this.
Also, I started a new channel on YouTube, I want to teach in videos what I write about, so please, subscribe if you liked this tutorial.
Youtube channel:
Linkedin:
Instagram: @thedevproject