I've just joined a new company to do an automation project in Python + Behave + Appium. The person before me set up the baseground of the project, amazing job, but I didn't like the way the code was structured. Every important folder was in the root dir of the project (features, steps, pages, utils, etc), so one of the first things that I did was reorganise this in what I thought was a better way.
To start with, I put all the code related to automation into a src folder and then I structured it as follows:
src
├── main
| ├── resources
| ├── devices.json
| └── project.conf
| ├── utils
| ├── device.py
| ├── jira_linker.py
| └── jira_methods.py
├── test
| ├── features
| ├── environment.py
| └── steps
| ├── pages
| └── resources
| ├── strings
└──
With this structure in mind, the next step was to fix the imports that had broken with the restructure. At first I thought I'd fixed them all, as VSCode didn't complain about them and I could actually navigate to the definitions with ctrl+click.
But when I tried to run a test, to see if everything was OK, then I got a ModuleNotFoundError in environment.py in one of my imports, specifically the first one (although if I fix the first one, the next ones also fail, and so on):
from test.pages.pom_android_player import PlayerPage
from main.utils import utils as utl
from main import device as dvc, jira_methods as jira
Apparently the solution is to put "src." before the rest of the path. So, all the imports within src folder where I don't have the absolute path will fail.
In principle, this is not a big deal. It's only 4 characters more in each import of the project, and here's where I think that maybe it's a silly problem and I'm overthinking this and it isn't even a problem at all, but, on the one hand, having the src folder specified in the imports is something that I think is irrelevant. It's like the root of everything I'm gonna use, so to me, it has no real value and bothers me, and taking that into account, some import lengths are big enough to bother me even more to have to add an extra folder to them.
So I started looking for a solution (and failed every time). Probably, one of the easiest solutions would be to use the python.analysis.extraPaths in the VSCode settings. I don't know if this would work because I haven't tried it, because I'm not gonna be alone in this project in the future and some people might not use VSCode, and I might be wrong, but I think this might be a solution IF everyone in the project would use VSCode.
Beyond this, another solution might be the PYTHONPATH variable, but again, that seems like a solution only to me, and I'm looking for something that anyone who joins the team in the future could have without having to do anything, or at least, with the minimum set up. I mean, what I don't want is something that fixes the problem for me, but then, when someone new joins and clones the project, they find that none of the imports work.
Then, I tried with sys.path.insert. I thought, since I have the environment.py and I have the before_all method, which is the first thing Behave calls every time I run a test, maybe I could insert the src path to PYTHONPATH from here and that'd be it. Wrong. Apparently I did not realise that the imports come first.
So, the last thing I tried, just to see if at least the sys.path.insert solution would work, was to put these statements before the failed imports.
import os
import sys
import allure
import base64
from jira import Issue
from behave.model import Status
from appium.webdriver import Remote
# Insert src directory to the path variable
ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) # This is your Project Root
sys.path.insert(0, ROOT_DIR + "\src")
print(sys.path)
from test.pages.pom_android_player import PlayerPage
from main.utils import utils as utl
from main import device as dvc, jira_methods as jira
Obviously, I don't think it's a good practice at all to do something like that, but it was just to test the solution.... And it didn't work anyway. It does add the src path to sys.path, but it still fails anyway. And this is where I got lost.
I have to say that I'm still a newbie to Python. I have enough knowledge of the language and the previous knowledge of other languages, such as Java, to do the automation coding right, but beyond that, I'm still learning. So maybe some of the things I've tried could be a solution, but I haven't implemented them well, or maybe there is an obvious solution that I can't see due the lack of knowledge of the language.
In this matter, I don't know if for example having a __init__.py
in src folder might be a solution for this, but as I said, I don't have enough knowledge of Python yet to know how to do it if it's a solution.
What I'm looking for, and I'm not sure if it's possible, is to find a solution where I can import everything within src folder without actually having to add src to the import path, and this solution has to meet the requirement that it is suitable for everyone working on the project, not just me. FINAL NOTE: The project runs by executing a run.sh which is in the root directory of the project. I also thought that there might be something that could be done inside this .sh file, but I didn't find anything to do that.
Thanks for the help.
EDIT: Apparently I made a mistake when I used the sys.path.insert()
statement. I'm guessing that when I write this in environment.py, the path I get in ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
line is the directory where environment.py is, not the root project directory.
If I write this instead ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname((os.path.abspath(__file__)))))
then I get the root dir + /src. This means, ROOT_DIR is the route to src dir, which is kind of what I wanted.
However, even with the src folder in my paths when I print sys.path
, I get the same ModuleNotFoundError. No module named test.pages
in the import from test.pages.pom_android_player import PlayerPage