3

If I have

a.py
b.py

In b.py I can import a

But if I have

c.py
m
  a.py
  b.py

and in c.py do import m.b, suddenly in b.py I get ModuleNotFoundError: No module named 'a'

What's the problem? I don't see how the second case is any different from the first

So... the modules are searched in the directory of the module that was started initially. I just don't understand the reasoning.

I'm not asking how to fix the problem. But rather asking why there's a problem in the first place...

(tested on Python 3.8.8)

Hrisip
  • 900
  • 4
  • 13
  • https://realpython.com/absolute-vs-relative-python-imports/#absolute-imports – warvariuc Jun 14 '21 at 12:58
  • 1
    Does this answer your question? [Import a file from a subdirectory?](https://stackoverflow.com/questions/1260792/import-a-file-from-a-subdirectory) – thshea Jun 14 '21 at 12:59
  • 1
    As an aside, the best way I've found to deal with these kind of issues when developing a multi-module package is installing your project in editable mode - see https://stackoverflow.com/questions/35064426/when-would-the-e-editable-option-be-useful-with-pip-install. Then all import statements will start from the same source root directory. – jfaccioni Jun 14 '21 at 13:02
  • @warvariuc it doesn't answer why it doesn't work without explicitly specifying that it's a relative path. Yet it works when all modules are in the same directory – Hrisip Jun 14 '21 at 13:12
  • @Hrisip. You have an absolute path to the module `a`, so, your script is expecting module `a` in the root of the top level. You can fix it by importing `a` like this: `import m.a` or `from . import a` – Pavlo Naumenko Jun 14 '21 at 13:41

2 Answers2

0

It's based what file you run python.

If you run with python c.py

file c.py use:

from m import a, b

file b.py also use:

from m import a

If you go python b.py you just need import a to use in b.py

so depend what file you run it's will be the parent for relative import.

tanho
  • 11
  • 3
0

Let's start with why your first one works. Assuming you have a file structure such that:

c.py
m - |
    |
    __init__.py
    a.py
    b.py

Let's say your current directory is within the m folder and you run the command.

python b.py

If within your b.py you place something like:

import a
import sys
print(sys.path)

it will work just fine. But let's take a look at the output from print(sys.path). On windows, this is will look something like ['C:\\path\\to\\myprogram\\m', '<path to python installation>', etc.]

The important thing is that the m folder is on your sys.path and that is how import a gets resolved.

However if we go up one level and run:

python c.py

you should immediately notice that m is no longer on your sys.path and instead is replaced by 'C:\\path\\to\\myprogram'.

That is why it fails. Python automatically includes your current working directory in sys.path and changing out of it means it no longer knows where to look for the import.

This is an example of an absolute import. You can manipulate where python looks for these imports by modifying sys.path to include the path of the file you want to import.

sys.path.append('C:\\path\\to\\myprogram\\m')

But there's a better way to do it. If m is a package or sub-package (includes an __init__.py) you can use a relative import.

from . import a

However, there is a small caveat to this. You can only use relative imports when the file using them is being run as a module or package.

So running them directly as a top level script

python b.py

will produce

ImportError: attempted relative import with no known parent package

Python luckily has the ability to run a script as a module built in. if you cd one level up so as to encapsulate your m package, you can run

python -m m.b

and have it run just fine.

Additionally, since b.py is being treated as a module in c.py because of the import, the relative import will work in this case as well.

python c.py
Axe319
  • 4,255
  • 3
  • 15
  • 31