21
votes

I have the following:

         ModuleFolder
              |
              |-->. ModuleFile.py .
              |
              '-->. TestsFolder .
                         |
                         '---> UnitTest1.py

I'm trying to import from the parent directory. In this case I am trying to run "UnitTest1.py" from the test folder, and import from the directory immediately above it (the file "ModuleFile.py").

  • I know there are plenty of answers to this already. SO Question1, SO Question2, Every Other SO Question. I just couldn't find "using ../" as a relative import rather than the explicit path.
  • I know that as of Python 2.5 they supported "relative imports" as per the documentation that mentions the use of from .. import * but I am specifically trying to do an import MyModuleName so I can be more explicit in the unittest and avoid mangling/collisions of names.

What I am doing (and it is working for me) is the following:

sys.path.append("../")

And then importing what I need from the parent directory.

  • Yes, there is an __init__.py in the parent directory,
  • No, my parent path is not part of the Python path or environment variable
  • Why don't I just add the parent path to the sys.path? Because it's relative. If I am running from /home/workspace/MyModule/unittests/ and my module is under /home/workspace/MyModule/ I assumed adding /home/workspace/MyModule/ to the path won't necessarily be true if a coworker runs this on his machine under his own directory of /home/documents/MyModule.

My Question:

Is this Python-proper? If not, what's wrong with this. Is there a better way? Or is this truly an RTFM moment where the answer is in one of the 7+ SO questions I've already looked at? (I saw those all recommending the explicit path rather than the relative path approach I took).

Other useful information:

  • Python 2.6
  • Working in Linux but could just as easily jump over to Win.
6
It's better to start using your code as a package and importing everything as such. Relative imports are then irrelevant. - Acumenus

6 Answers

28
votes

Don't run the test from the tests folder. Run it from the root of your project, which is the module folder. You should very rarely need to muck with either sys.path or PYTHONPATH, and when you do, you're either causing bugs for other libraries down the road or making life harder on your users.

python -m TestsFolder.UnitTest1

If you use a test runner like py.test, you can just run py.test from the root of your checkout and it'll find the tests for you. (Assuming you name your tests something more like test_unit1.py. Your current naming scheme is a little unorthodox. ;))

5
votes

It's better to insert your relative path at the begening of sys.pathlike this:

import sys
sys.path.insert(0, '../')
2
votes

My suggestion is:

Don't try to be clever, do what you're supposed to do. I.e. make sure that your modules and packages are somewhere in your Python path.

The simplest way is to set the environment variable PYTHONPATH in the shell that you use to execute your scripts:

$ export PYTHONPATH=/the/directory/where/your/modules/and/packages/are
$ cd /the/directory/where/your/unit/tests/are
$ python test1.py
2
votes

Make your test folder a module (by adding __init__.py).
Then you can use run python -m tests.test_name or use the following Makefile in your project home:

TEST_FILES = $(wildcard tests/test_*.py)
TESTS = $(subst .py,,$(subst /,.,$(TEST_FILES)))
all.PHONY: test
test:
    @- $(foreach TEST,$(TESTS), \
        echo === Running test: $(TEST); \
        python -m $(TEST); \
        )
0
votes

You can also make symlink of the module path which you want to import and then use that symlink to import. Create a symlink in the python dist-packages.

To create a symlink:

ln -s "/path/to/ModuleFolder" "/path/to/python/dist/packages/module_symlink_name"

To import module in the script:

from module_symlink_name import ModuleFile

No need to export python path or modifying sys path.

0
votes

This is always very cofusing. Relative imports are relative to the module structure. To test relative imports, you need to import your module in your main file (from outside the module). Let me try to show a case that works:

$ python use_module.py
Hello World!

Or you can make it an executable module and run it with python -m (always from outside the module)

$ python -m module.child_a
Hello World!

Structure

test_module.py
module
├── __init__.py [can be empty]
├── child_a
   ├── __init__.py
   ├── __main__.py
│── child_b
   ├── foo.py

test_module.py

from module.child_a import message
print(message)

foo.py

phrase = "Hello World"
mark = '!'

child_a/__init__.py

from .__main__ import message

child_a/__main__.py

from .. child_b.foo import phrase, mark

message = phrase + mark

if __name__ == "__main__":
    #this block is run when executing `python -m module.etl`

    print(message)

NOTE: if you add another file in child_a say child_a/myscript.py you can still execute it with python -m module.sub_a.myscript (always from outside the module)