Subhajit Kundu
Subhajit Kundu

Reputation: 403

Issues with Import from sibling directory

This question is in context of python 2.7 and relative imports. I have gone through related questions and things are still not working for me. I don't know what am I missing here?

Following is my dir hierarchy

.
|-> wrapper.py
|-> __init__.py
|-> util
|    |-> hello.py
|    |-> __init__.py
|-> src
|    |-> wrapper.py
|    |-> __init__.py

All __init__.py are blank files only to "treat the directories as containing packages"

Following is how ./util/hello.py reads. This has its own main function and can run of its own.

#!/usr/bin/python
# This is hello.py.

import sys

# Define a main() function that prints a little greeting.
def main():
  print "Hello World!!!"

# Standard boilerplate that calls the main() function.
if __name__ == '__main__':
    main()

Following is how ./wrapper.py reads. This also has its own main function and uses ./util/hello.py to achieve its goal.

#!/usr/bin/python
# This is wrapper.py.

import sys
from util import hello

# Define a main() function that prints a little greeting.
def main():
    hello.main()        # This prints "Hello World!!!"

# Standard boilerplate that calls the main() function.
if __name__ == '__main__':
    main()

Following is how ./src/wrapper.py reads.

#!/usr/bin/python
# This is wrapper.py.

import sys
from ..util import hello

# Define a main() function that prints a little greeting.
def main():
    hello.main()        # This prints "Hello World!!!"

# Standard boilerplate that calls the main() function.
if __name__ == '__main__':
    main()

As you see, It's almost exact copy of ./wrapper.py with minimal changes to make it run (changes in import). All __init__.py are present too. However any attempt to run it gives following error.

Traceback (most recent call last):
  File "wrapper.py", line 8, in <module>
    from ..util import hello
ValueError: Attempted relative import in non-package

However, if I import hello.py as following it works:

import os
sys.path.append(os.path.abspath("../util/"))
import hello

Two Questions:

Q1. What am I doing wrong or what is missing my attention?

Q2. How can I code ./src/__init__.py such that just "import hello" works in ./src/wrapper.py?

Upvotes: 2

Views: 183

Answers (1)

nitzpo
nitzpo

Reputation: 517

The simple answer is that you need to have a parent package for all your code. You can't just put __init__.py in the root directory and expect it to behave as a package.

I'll split your options into good and bad, starting with the bad:

Bad - relative imports: What you should do is put all your code (aka .) in a package named appropriately (src is a bad name for a package btw). Say mypackage. Then you'll be able to import src.wrapper only as mypackage.src.wrapper, from places where it is available on the python path, like a python interpreter open in the directory containing mypackage. Installing the package also makes it available on the path.

For more information on relative imports and why they are bad:

Good - non-relative imports: The better approach is import through the package in an absolute manner. You still have to create a parent package mypackage, but this time you get rid of the relative imports and use import mypackage.util.hello from inside wrapper.py. For this to work you have to set the python path to include the directory containing mypackage.

The recommended way to do this is create a setup.py file and install your package. For development you should use python setup.py develop or pip install -e .

More on python packaging: https://packaging.python.org/distributing/

Edit: for your Q2, if you use absolute imports you can use import mypackage.util.hello as hello or from mypackage.util import hello. Both are the same.

In some special cases you may be interested in writing import util.hello as hello inside /mypackage/__init__.py, and then you could use from mypackage import hello etc.

Upvotes: 1

Related Questions