Simple trick to work with relative paths in Python
Calculate the file path at runtime with ease
The goal of this article is to calculate a path to a file in a folder in your project. The reason we calculate this path is that you refer to the correct location no matter where the code is installed. This is the case when you share your code with a coworker or deploy your code on a webserver e.g.
You can’t hardcode the (expected) location of the file because C:/users/mike/myproject/config/.env
doesn’t exist on your machine or on the server we’re deploying to. Ideally we’d like to specify the path relative to the root folder of our project.
In this article we’ll check out a simple Python trick that allows us to refer to files relative to our project folder in a very easy way. Let’s code!
TL/DR; Check out ‘The Trick” below!
Use cases
Two quick example on where this trick comes in handy:
- Loading an environment file from e.g.
/config/.env
- We have an API that receives files; the files should always be stored in
data/received_audio_files
for example.
Preparation
In this article we’ll set up our project structure like this:relative_path
- data
-- mydata.json
- processes
-- load_data.py
All of our code is contained in a folder called relative_path
. This is the root of our projects, meaning it holds all of our code. As you can see we have two folder: the data folder that contains our target json file, and our processes folder that contains load_data.py
; the code will load the mydata.json
.
Loading the data
In this part we’ll start with the most obvious way of loading mydata.json and see its flaws. Then we’ll slowly improve, ending with a method that will load it relatively. Then we’ll compile this knowledge in a simple trick that will help us in all of our future projects.
1. Absolute path
The most straight-forward method is just using an absolute path in our code.import json
f = open(r’C:\projects\relative_path\data\mydata.json’)
data = json.load(f)
While this works perfectly on my laptop, if we deploy this code on the server this might fail if we put the code in another folder. Linux doesn’t even have a C drive so there certainly is no C:\project\relative_path folder
on that server.
2. Using __file__
We can take calculate the absolute path to the file we’re in at runtime:print(__file__)# Results in
# C:\projects\relative_path\processes\load_data.py
The code above will print out the location of the file we’re currently executing
In our case this is C:\projects\relative_path\processes\load_data.py
. We can work from there.
3. Calculating the dirname of __file__
Getting the folder path of the file we’re executing inches us a bit closer:import os
print(os.path.dirname(__file__))# Results in
# C:\projects\relative_path\processes
4. Navigating folders
We have to go up one folder to go to our root path, and then go to the data
folder in order to get the location of our json data:
Here we take our directory path and use os.path.join to navigate:
- First we’ll go up one folder using the ‘..’ (this is the same as in a terminal). This will navigate us to the parent folder, which in this case is the root.
- Then we join ‘data’ to navigate to the data directory.
- Finally we join the file name
In addition we call os.path.realpath to ‘calculate’ the ‘..’
-commands so it will result in our absolute path.
Navigation troubles
Nice! We’ve calculated the correct path at runtime. The problem, however, is that we have to navigate quite a lot. Imagine we are working in a file at this location: \some_folder\some_process\datacollection\thing_one.py
. If we want to load the `mydata.json` file from this file we have to run:
From the file we have to move up three times. This gets a bit convoluted. We also can’t move the file since it calculates the path to our data relative from the file we’re executing. Let’s clean it up a bit in the next part.
The Trick
The trick is to define a variable that calculate the absolute path to our root in a file and then import this variable from anywhere in the project. It works like this:
In our project root create a folder called config
that contains a file called definitions.py
. We’ll put the following code inside:
This short piece of code will always calculate the correct path to the root directory of our project. We know the location of the file in the project and just have to go up one. Then, in other files, we can import the ROOT_DIR variable.
Instead of the long, convoluted code from the previous part we can do the following:
This approach is not only more readable, also it doesn’t bind the location of our processing file to the location of our data file: we calculate the location of our data file relative to the root of the project. You can add the config directory with the definition.py file to any project so you have a very nice, generalized solution for handling relative paths.
Conclusion
Using a single file that contains a single variable we simplify calculating paths a bunch! In addition this trick is very simple an can be easily copied into every project. Using the definitions file we can easily load environment variables into our python program. More on these in this article.
If you have suggestions/clarifications please comment so I can improve this article. In the meantime, check out my other articles on all kinds of programming-related topics like these:
- Create a fast auto-documented, maintainable and easy-to-use Python API in 5 lines of code with FastAPI
- Python to SQL — UPSERT Safely, Easily and Fast
- Create and publish your own Python package
- Create Your Custom, private Python Package That You Can PIP Install From Your Git Repository
- Virtual environments for absolute beginners — what is it and how to create one (+ examples)
- Dramatically improve your database insert speed with a simple upgrade
Happy coding!
— Mike
P.s: like what I’m doing? Follow me!