2

The problem

I have a directory structure for my project which follows the standard for Python packages, as it was created with this cookiecutter template: https://github.com/audreyr/cookiecutter-pypackage#quickstart

The directory structure is

project_name
├── project_name
│   ├── __init__.py         
│   └── module1.py
└── tests         
    └── test_module1.py 

The first code line of test_module1.py is:

from project_name import module1

But I get a ModuleNotFoundError: No module named 'project_name'.

To my understanding, this should work since the folder called project_name is a package, which is ensured by presence of the __init__.py file.

I have always had trouble understanding how imports like this work. For my projects I have always just settled with having my tests in the same folder as the modules to test. I know this is bad practice, but the only way I could get the modules to actually import.

What I already tried

I have tried renaming the folder with the __init__.py file to something else and then import, as I thought it could have something to do with the parent folder and the child folder both having the name project_name. This did not work, same error.

I also tried making the test folder into a package by creating an __init.py__ file inside it, even though the Cookiecutter template does not have that. I read in many places that making the test folder into a package is discouraged, but some suggest that structure. That did not work either.

I have searched thoroughly for solutions to this seemingly very standard problem, some of the links are here:

My last try was to start a project with Cookiecutter, so everything would be set up properly form the beginning. However, I still get the ModuleNotFoundError.

What I don't want

I don't want to modify sys.path as many answers seem to suggest. There must be a cleaner way for such a common problem.

What am I doing wrong?

Edit for some additional info (see question from @Nicholas):

The contents of __init__.py is

# -*- coding: utf-8 -*-
"""Top-level package for project_name."""
__author__ = """my_name"""
__email__ = 'my_email'
__version__ = '0.1.0'

Which was generated by the Cookiecutter template.

Inside test_module1, I added the following before the before the ModuleNotFoundError occurs:

import sys
import os

print(sys.path)    
print(os.getcwd()) 

sys.path prints a list, where first element is the tests directory.

['c:\\Users\\...\\project_name\\tests', 
 'C:\\Users\\...\\Miniconda3\\python37.zip', 
 'C:\\Users\\...\\Miniconda3\\DLLs', 'C:\\Users\\...\\Miniconda3\\lib', 
 'C:\\Users\\...\\Miniconda3', 
 'C:\\Users\\...\\Miniconda3\\lib\\site- packages', 
 'C:\\Users\\...\\Miniconda3\\lib\\site-packages\\win32', 
 'C:\\Users\\...\\Miniconda3\\lib\\site-packages\\win32\\lib', 
 'C:\\Users\\...\\Miniconda3\\lib\\site-packages\\Pythonwin']

I don't know if the lowercase 'c' in the first element matters.

os.getcwd() prints the root directory 'c:\Users\....\project_name'. Also with a lowercase 'c'.

Tim Skov Jacobsen
  • 3,583
  • 4
  • 26
  • 23
  • 1
    Are you running the tests from the root `project_name` directory? When you try to run `python -m project_name` from the root folder, it imports the module correctly? – Nicholas Apr 27 '19 at 16:15
  • Yes, I am running ´pytest´ in Anaconda Prompt after having cd'ed into the root ´project_name´ directory. I'm in a virtual conda environment for the project. But the same thing happens if I try to run the python module itself. – Tim Skov Jacobsen Apr 27 '19 at 16:36
  • 1
    What's the content of `__init__.py`? Also, run Python then `import sys;sys.path`. This shows all the paths Python is using to search for imports. In my system it is a lists with an empty string element. That ensures the current folder is used as a search path. You may also want to check what Python see as "current folder" with `import os;os.getcwd()`. – Nicholas Apr 27 '19 at 16:47
  • I edited the question to include these answers at the bottom. Thanks for suggesting it. – Tim Skov Jacobsen Apr 27 '19 at 17:16

1 Answers1

1

You should create a virtual environment and install the project in order for the test modules to correctly resolve import statements.

In the project root, i.e. the directory project_name which contains a subdirectory project_name and a subdirectory tests, create a setup.py (or pyproject.toml) file for the package metadata. See here for details about that part.

From this same project root directory which is now containing the installer (setup.py), create and activate a venv and install your project:

python3 -m venv .venv
source .venv/bin/activatate  # linux/macOS
# .\Scripts\activate.bat  # windows
pip install --editable .
pip install pytest
pytest

If for some reason you don't want to create an installer for your project, you may run pytest like this, from the project directory:

python3 -m pytest

Unlike the bare pytest command, this will add the current working directory to sys.path allowing import statements to be resolved in tests.

wim
  • 338,267
  • 99
  • 616
  • 750
  • `pip install --editable .` did the trick! I'm on windows and had already the `setup.py` from the Cookiecutter template. I can now run `pytest` properly from Anaconda Prompt. I still can't run the actual test modules in the editor though, which might also be unimportant. Thanks a lot. – Tim Skov Jacobsen Apr 27 '19 at 18:07