2

I've referred to several threads and articles including:

but can't get the desired result.

Say I have a directory called "helloworld":

helloworld
|--__init__.py
|--say_hello.py
|--another_hello
   |--__init__.py
   |--import_hello.py

This is say_hello.py:

def hello_world():
    print("Hello World!")
if __name__ == "__main__":
    hello_world()

This is import_hello.py:

from .. import say_hello
say_hello.hello_world()

I am hoping to import say_hello module wherever I call python /path/to/import_hello.py without using sys module.

However, now when I do python /path/to/import_hello.py, it will return ValueError: attempted relative import beyond top-level package, and I have no idea why it isn't working.

Even this doesn't work:

from helloworld import say_hello
say_hello.hello_world()

It will give me ModuleNotFoundError: No module named 'helloworld'.

ytu
  • 1,822
  • 3
  • 19
  • 42
  • The error is pretty clear. The top-level package has no parent. – Code-Apprentice Jun 18 '18 at 03:22
  • @Code-Apprentice I thought I was specifying the parent folder of where import_hello.py locates. In that case the parent should be the folder helloworld, isn't it? – ytu Jun 18 '18 at 03:25
  • Possible duplicate of [Python Submodule Importing Madness](https://stackoverflow.com/questions/49129300/python-submodule-importing-madness) – atline Jun 18 '18 at 03:29

2 Answers2

1

You can't run a script out of the middle of a package like that. When you do that, you're not running helloworld.another_hello.import_hello based out of /path/to/helloworldsparent/, you're running __main__ based out of /path/to/helloworldsparent/helloworld/another_hello. So, it doesn't have a parent package to import as ...


You can run the module with -m:

$ python -m helloworld.another_hello.import_hello

… assuming helloworld's directory is on your sys.path (e.g., because you've installed it into site-packages, or because your current working directory is its parent, or because you've set up a PYTHONPATH).


But a cleaner solution is usually to leave the deep modules alone and write "entry point" scripts at the top level that look like this:

import helloworld.another_hello.import_hello
helloworld.another_hello.import_hello.main()

If you're using setuptools (and you really should be for anything complicated enough to need two levels of packages), you can make it create the entry point scripts automatically at install time (or at --inplace time, during development). See Automatic Script Creation in the docs (but you're probably going to need to read other sections as well to get the whole idea; the docs are pretty large and complicated).

user2357112
  • 260,549
  • 28
  • 431
  • 505
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • Thanks. But I am not quite clear about what "leaving the deep modules alone" is when using `setuptools`. Does that mean I can leave import_hello.py as it is right now, i.e. `from .. import say_hello; say_hello.hello_world()`? – ytu Jun 18 '18 at 06:41
  • @ytu The `from … import` line will work, but you usually want to move your top-level code (that `say_hello.hello_world()`) into a function that can be called by someone after they `import helloworld.another_hello.import_hello`, rather than leaving it there to be run the first time anyone does that `import` in a process. In other words, you want modules, not scripts, inside packages. – abarnert Jun 18 '18 at 06:47
  • I appreciate your help. Yet I keep failing to import modules in `import_hello` and getting `ModuleNotFoundError` with command created with console_scripts in entry_points of setup.py... – ytu Jun 22 '18 at 03:56
0

I feel you can first try to add the parent path to the system path and then try using the import.

from sys import path as pylib
import os
pylib += os.path.abspath('..')
from helloworld import say_hello

Hope it helps !!

Strik3r
  • 1,052
  • 8
  • 15