0

My file structure is as follows:

monitor/
    core/
        database.py
        processor.py
        timekeeper.py
    jobs/
        jobA.py
        jobB.py
    setup.py

From jobA.py I import like this:

from core.database import Database
from core.timekeeper import Timekeeper 
from core.processor import Processor

While at database.py, processor.py and timekeeper.py I import setup.py.


Get the following error when I run jobA.py:

root@test:/var/www/python/monitor# python3 jobs/jobA.py 
Traceback (most recent call last):
  File "jobs/jobA.py", line 2, in <module>
    from core.database import Database
ModuleNotFoundError: No module named 'core'
Nikk
  • 7,384
  • 8
  • 44
  • 90

4 Answers4

1

To allow import core or import core.database (without the relative dots or double-dots) the parent directory of core should either be the current directory, or be included on sys.path. You appear to have a setup.py. Conventionally that means a file that performs installation and packaging tasks via the setuptools or distutils packages. If that is indeed the role it performs, perhaps you need to run it. One way to run it would be to issue (from the command-line outside Python) the command pip install -e /path/to/monitor. Assuming setup.py was written correctly, this will ensure that the core package, in its current location, is lastingly made available for the default Python distribution. Next time you launch Python, /path/to/monitor will be on sys.path and import core will work from (almost) anywhere.

jez
  • 14,867
  • 5
  • 37
  • 64
  • Wouldn't running `pip install -e /path/to/monitor` build the `monitor` package? So Borsn would have to `from monitor.core import X`? I've only built a few packages so I am genuinely asking, I thought that's how it worked from my experience. Also, thinking about how many packages use `core` or `src` or something to name their directories... – Mason Caiby Oct 03 '19 at 20:14
  • 2
    @MasonCaiby That actually depends on how `setup.py` is written. Conventionally no, nothing is assumed by default about the location of `setup.py`: the code in `setup.py` must explicitly define which packages (relative to its own location) are being targeted for packaging. Incidentally, the `-e` flag means that nothing is actually "built", but rather that the parent location of each targeted package is just added to the path (the package is referred to as being "installed as 'editable'") – jez Oct 03 '19 at 20:18
  • 1
    If this file structure is of the OP's own design, is `core` really intended to be a top-level name, importable from anywhere? If not, and you really mean it to be `monitor.core`, then (a) `setup.py` should be a next to `monitor`, not `core`, and should contain instructions for installing/building the `monitor` package; (b) jobA/jobB should then use relative importing, **but** (c) should they really be there? With such generic names I imagine they're specific use-cases of the `monitor` package, not reusable submodules. If so, they should live outside the package and import `monitor.core`. – jez Oct 03 '19 at 20:30
0

From this answer you can use 2 dots to import from a directory above. So you could potentially use:

from .core.database import Database
from .core.timekeeper import Timekeeper 
from .core.processor import Processor

Python 3.3+ you don’t need an __init__.py file so I don’t believe just adding one will help.

Jab
  • 26,853
  • 21
  • 75
  • 114
  • Error: `Traceback (most recent call last): File "jobs/jobA.py", line 2, in from ..core.database import Database ValueError: attempted relative import beyond top-level package` – Nikk Oct 03 '19 at 20:04
  • My apologies use one dot not 2 I edited the answer. – Jab Oct 03 '19 at 20:08
  • It's not 100% clear to me whether OP intends `monitor` to be a top-level package, or just a directory that contains top-level packages. Two dots **is** correct if `monitor` is supposed to be the top-level package, but it will fail in the manner reported here if the `monitor` package is not accessible (e.g. if the current working directory is `monitor` rather than `monitor`'s parent, and `monitor`'s parent is not in `sys.path`). Never work inside a directory that you intend to be a package—it causes a lot of confusion. – jez Oct 03 '19 at 20:59
  • You are assuming here that the OP meant for the jobs directory to be a package. It doesn’t appear to have been the case here. – Martijn Pieters Oct 03 '19 at 22:17
  • @MartijnPieters right, and if the jobs directory isn’t a package, it shouldn’t be assuming fixed locations of packages relative to itself. – jez Oct 03 '19 at 22:19
0

Add

import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

to the top of your jobA.py file. If you are using python 3.3+, you do not need an __init__.py file. It needs to be above your other import statements.

Mason Caiby
  • 1,846
  • 6
  • 17
-2

What module are you trying to use? Maybe your module is not compatible with Python 3.

flpec
  • 35
  • 2
  • This answer makes more sense if expanded a little bit, to explain that *only* Python 3.3+ will attempt to treat a directory without an `__init__.py` file as a package. – jez Oct 03 '19 at 20:34