An Answer From 2022
Here's a potential approach from 2022. The issue is identified correctly and if you're using an IDE like VS Code, it doesn't automatically extend the python path and discover modules.
One way you can do this using an .env file that will automatically extend the python path. I used this website k0nze.dev repeatedly to find an answer and actually discovered another solution.
Here are the drawbacks of the solution provided in the k0nze.dev solution:
- It only extends the python path via the launch.json file which doesn't effect running python outside of the debugger in this case
- You can only use the ${workspaceFolder} and other variables within an "env" variable in the launch.json, which gets overwritten in precedence by the existence of a .env file.
- The solution works only within VS Code since it has to be written within the launch.json (- overall portability)
The .env File
In your example tests falls under it's own directory and has it's own init.py. In an IDE like VS Code, it's not going to automatically discover this directory and module. You can see this by creating the below script anywhere in your project and running it:
_path.py
from sys import path as pythonpath
print("\n ,".join(pythonpath))
You shouldn't see your ${workspaceFolder}/tests/ or if you do, it's because your _path.py script is sitting in that directory and python automatically adds the script path to pythonpath. To solve this issue across your project, you need to extend the python path using .env file across all files in your project.
To do this, use dot notation to indicate your ${workspaceFolder} in lieu of being able to actually use ${workspaceFolder}. You have to do dot notation because .env files do not do variable assignment like ${workspaceFolder}. Your env file should look like:
Windows
PYTHONPATH = ".\\tests\\;.\\"
Mac / Linux / etc
PYTHONPATH = "./tests/:./"
where:
- ; and : are the path separators for environment variables for windows and Mac respectively
- ./tests/ and .\tests\ extend python path to the files within the module tests for import in the init.py
- ./ and .\ extend the python path to modules tests and presumably solutions? I don't know if solutions is a module but I'm going to run with it.
Test It Out
Now re-run your _path.py script and you should see permanent additions to your path. This works for deeply nested modules as well if your company has a more stringent project structure.
VS Code
If you are using VS Code, you cannot use environment variables provided by VS Code in the .env file. This includes ${workspaceFolder}, which is very handy to extend a file path to your currently open folder. I've beaten myself up trying to figure out why it's not adding these environment variables to the path for a very long time now and it seems
The solution is instead to use dot notation to prepend the path by using relative file path. This allows the user to append a file path relative to the project structure and not their own file structure.
For Other IDE's
The reason the above is written for VS Code is because it automatically reads in the .env file every time you run a python file. This functionality is very handy and unless your IDE does this, you will need the help of the dotenv package.
You can actually see the location that your version of VS Code is looking for by searching for the below setting in your preferences:
VSCode settings env file
Anyways, to install the package you need to import .env files with, run:
pip install python-dotenv
In your python script, you need to run and import the below to get it to load the .env file as your environment variables:
from dotenv import load_dotenv()
# load env variables
load_dotenv()
"""
The rest of your code here
"""
That's It
Congrats on making it to the bottom. This topic nearly drove me insane when I went to tackle it but I think it's helpful to be elaborate and to understand the issue and how to tackle it without doing hacky sys.path appends or absolute file paths. This also gives you a way to test what's on your path and an explanation of why each path is added in your project structure.
ModuleNotFoundError: No module named 'solutions'
error message. – Discordant