2

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?

Subhajit Kundu
  • 383
  • 1
  • 2
  • 13
  • How are you running the `./wrapper.py`? Related - https://stackoverflow.com/questions/11536764/how-to-fix-attempted-relative-import-in-non-package-even-with-init-py – shad0w_wa1k3r Apr 16 '17 at 13:51
  • There are many existing answers which address Q1, simply search for "Attempted relative import in non-package". I can not answer Q2, because the question leads in the wrong direction. You *can* run `python -m src.wrapper`, then `import hello` likewise `from hello import hello` works in `src/wrapper.py`. I think it is considered wise to avoid relative imports from locations other than the top-level in general! – Michael Hoff Apr 16 '17 at 14:08
  • @AshishNitinPatil: Running in "./src" as "python wrapper.py". – Subhajit Kundu Apr 16 '17 at 20:25
  • @Michael Hoff: Didn't quite get you with "python -m src.wrapper". What to do and where. What does it do? – Subhajit Kundu Apr 16 '17 at 20:29
  • FYI, From inside `./src` I want to run `./src/wrapper.py` as `python wrapper.py`. From inside `./src`, I tried running it as `python -m ..util wrapper.py`. It didn't work, with following error (Python 2.7.12): `/usr/bin/python: Relative module names not supported` – Subhajit Kundu Apr 16 '17 at 20:45
  • 1
    No, my solution assumes you are executing python in the *root* directory. As the error message indicates, it does apparently not work with relative package names. – Michael Hoff Apr 16 '17 at 22:13

1 Answers1

1

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.

Community
  • 1
  • 1
nitzpo
  • 497
  • 2
  • 6