Problem with relative import and nosetests

Hi there, I can’t seem to figure this out. I am trying to run nosetests on a project. This project imports from another file in the same directory. When I run the file directly, nothing goes wrong. When I run my tests with nosetests, I get an error concerning the import statement (that is in the module, not in the test file). It seems to have something to do with the tests being in another directory than the modules, and nosetests trying to import the file from the tests file path.

My directory structure is:

textgame/
    setup.py
    bin/
    bureaucratica/
        data/
            (a bunch of txt files)
        __init__.py
        game.py
        lexicon.py
    docs/
    tests/
        __init__.py
        bureaucratica_tests.py

In ‘game.py’ I use ‘import lexicon’. This works fine when running game.py.
However, when running nosetests this gives an import error:

File “/home/pi/projects/textgame/bureaucratica/game.py”, line 14, in
import lexicon
ModuleNotFoundError: No module named ‘lexicon’

When I change it to ‘from bureaucratica import lexicon’ nosetests works.
But then, running game.py itself gives an import error.

Is there a way to make both work?

Thanks!

At the risk of sounding flippant, run a search on this forum about nose and you’ll soon see numerous similar issues.

The general solution is to ditch nose in favour of Pytest or the built in unittest. Nose is deprecated (see from nose site):

Note to Users

Nose has been in maintenance mode for the past several years and will likely cease without a new person/team to take over maintainership. New projects should consider using Nose2, py.test, or just plain unittest/unittest2.

Thank you.

I wanted to try and finish the book with nosetests before switchting to unittest. But this annoying error is enough motivation to start with that now. I’ll have another look at pytest too.

I know what you mean @Tim - perhaps use Unittest first as syntax is very similar, whereas pytest, which is much better has a different syntax (just assert).

I’m trying pytest now, in a separate virtualenv. I do like the shorter syntax. The same import error came up again though, I fixed it by adding the module’s directory to pythonpath. I’m reading here and there that this isn’t the right way to go, but since I’m working from a virtualenv I don’t see a big problem there for now.

1 Like

So, if you’re in textgame/ and you write this code:

import lexicon

Then, it will look for a file named textgame/lexicon.py. I believe you have to change this to:

import lexicon from bureaucratica

You might also have to force Python to look right there for files. On OSX you do this:

export PYTHONPATH=.

On Windows it should be:

$env:PYTHONPATH=.

Basically, where your terminal session is sitting determines what file is imported, so if you’re in textgame (and you should always be there forever, never cd to a lower directory) then it should work.

1 Like

Thanks for the suggestion!

I still did not get it fixed completely. I do understand what is going wrong now:

  • game.py tries: “import lexicon” which works because it is in the same directory.

  • from textgame/ I run pytest. It imports game.py which in turn imports lexicon.py but this time the import fails because we are not in the ‘bureaucratica’ directory.

  • Changing the import to “from bureaucratica import lexicon” in game.py makes pytest work, but now running game.py directly gives an error: there is no bureaucratica folder (because game.py is already inside it).

Here is the package file structure again:

textgame/
        setup.py
        bin/
        bureaucratica/
            data/
                (a bunch of txt files)
            __init__.py
            game.py
            lexicon.py
        docs/
        tests/
            __init__.py
            bureaucratica_tests.py

Great. Good news though, I finally found a fix that doesn’t require changing pythonpath from the terminal.

I found this on https://docs.python-guide.org/writing/structure.

Inside the bureaucratica folder I added a file called ‘context.py’ with the following code:

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

from bureaucratica import lexicon
from bureaucratica import parser_tim

Then, I changed the imports in game.py as follows:

from .context import lexicon
from .context import parser_tim

Now both pytest (running from textgame/) and game.py (running from textgame/bureaucratica/) both work!

From what I understand, the context.py file does the actual import and ensures that python searches from the root folder of the package. So the addition to pythonpath seems to be made from within the program itself now. This should make the imports more reliable on different installs as well. (Not that my package is ready to be thrown out there yet…)

Mystery solved?

Wait…

Just using the dots to indicate the right folder works as well!

In game.py:

from . import lexicon
from . import parser_tim

No import problems in pytest or when running game.py!

A free service run by Zed A. Shaw for learncodethehardway.org.