Python ModuleNotFoundError when executing script directly - python

I have browsed over a lot of questions on this topic, and I have found a lot of information, but I am still unable to fully understand what is happening and how to solve my problem
This is the question:
I am using python3.9.5, and I have the following layout:
root_folder/
src/
a.py
b.py
Inside a.py I have:
from src.b import do_something_B
def do_something_A():
do_something_B()
if __name__ == '__main__':
do_something_A()
An b.py:
def do_something_B():
print("Hello from B")
If I run Ipython REPL, I can do:
from src.a import do_something_A
do_something_A() # this prints "Hello from B" without errors
But if I try to execute a.py from console I get the following output:
❯ python src/a.py
Traceback (most recent call last):
File "/home/alejo/playground/root_folder/src/a.py", line 1, in <module>
from src.b import do_something_B
ModuleNotFoundError: No module named 'src'
Creating a __init__.py file inside src folder does not make any difference
What am I understanding wrong?
EDIT
If I change a.py to import b without src namespace (changing first line to from b import do_something_B) then I can execute the script from bash, but it fails when I try to use it with iPython REPL

You don't need to specify the directory as the two files are in the same directory already.
Simply do from b import do_something_B and it should work.
Same thing in b.py
Also to clarify, using src.someFunc implies that there is a module named src not that there is a directory named src.
Look into absolute imports if you need to import across directories, which in this case you do not, so don't worry.

I found the answer to my own question browsing the documentation:
Quoting https://docs.python.org/3/tutorial/modules.html#the-module-search-path
When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:
The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.
As I am specifiying an script, the folder of the script is added to sys.path, not the current folder
I would need to add a c.py file directly in root_folder than imports src.a and executes do_something_A() to be able to call it from bash

Related

How to properly create a package in Python

Say I have a folder named foo. Inside that folder is __init__.py, a folder called test, and another Python file called t1.py. Inside folder test is a Python file called bar.py, and in that file I am trying to do something like:
from foo import t1
And it gives me this error:
ModuleNotFoundError: No module named 'gmuwork'
Do I need to add something to environment variables or sys.path?
Absolute import
If you want to use
from foo import t1
Then yes, foo must be contained in sys.path.
From the docs:
When importing the package, Python searches through the directories on
sys.path looking for the package subdirectory.
In that case take a look at questions such as adding a file path to sys.path in python.
Relative import
Alternatively inside of bar.py you should be able to use
from ..foo import t1
as an intra-package reference.
Lastly: either way, you should put another empty __init__.py file inside of test to let Python know that folder is a subpackage.

Python test script works when using -munittest, but fails due to ModuleNotFoundError when ran directly

I've got a test script which imports modules from my application. This works when run with python -munittest, but it fails with ModuleNotFoundError if I simply use python tests/test_app.py.
This also happens with other scripts in this project which aren't unit tests.
$ python tests/test_app.py
Traceback (most recent call last):
File "tests/test_app.py", line 2, in <module>
import myapp
ModuleNotFoundError: No module named 'myapp'
$ python -munittest tests.test_app
....
----------------------------------------------------------------------
Ran 4 tests in 0.119s
OK
As you can see from the trace, it fails at the very beginning of the file, in the imports, where I use import myapp.
Project structure:
/project
/myapp
__init__.py
models.py
/otherapp
/anotherapp
/tests
test_app.py
/scripts
a_script.py
What magic is the unittest module doing to make my script load?
How do I execute my script directly?
It fails because when you run with python tests/test_app.pyyour python will look for myappin the local folder relative to the test_app.py or in PYTHONPATH and won't find it. Whereas unittest will handle importing the package while starting from project root folder.
This python documentation link explain how import statement work
When a module named spam is imported, the interpreter first searches
for a built-in module with that name. If not found, it then searches
for a file named spam.py in a list of directories given by the
variable sys.path. sys.path is initialized from these locations:
the directory containing the input script (or the current directory).
PYTHONPATH (a list of directory names, with the same syntax as the
shell variable PATH).
the installation-dependent default.
After initialization, Python programs can modify sys.path. The directory
containing the script being run is placed at the beginning of the
search path, ahead of the standard library path. This means that
scripts in that directory will be loaded instead of modules of the
same name in the library directory. This is an error unless the
replacement is intended. See section Standard Modules for more
information.
Therefore, to make your script directly executable you need to tell python to find "myapp" using one of the ways described in the documentation. Here's another SO question discussing how to use import (you can find a lot more with a quick search).

How to split python script into several files using relative imports?

I have import.py script. I want to extract some code into a separate file, say, m1.py:
$ ls
import.py m1.py
$ cat import.py
from .m1 import a
a()
$ cat m1.py
def a():
print('it works')
$ python import.py
Traceback (most recent call last):
File "import.py", line 1, in <module>
from .m1 import a
ModuleNotFoundError: No module named '__main__.m1'; '__main__' is not a package
When I switch to absolute import, it works. But I don't want accidentally importing other module. I want to be sure module from script's directory is imported. How do I make it work? Or what am I doing wrong?
If you're not overriding the built in modules. By default, python looks first in your current directory for the file name you want to import. So if there is another script having the same name in another directory, only the one you have in the current directory is the one that will be imported.
Then, you could import using the absolute import.
from m1 import a
a()
You can check this post out, for more infrotmation about importing in python.
To make sure that the one your importing isn't the built in. You can create your own package in the current directory for example,"my_package" and have your module m1 moved in it. Then you can import by:
from my_package import m1
m1.a()
Add __init__.py in the directory where m1.py is.
EDIT : Run it as a package from the previous working directory. cd .. && python -m prev_dir.import

import python file in another directory failed

I met a very strange problem:
My file structure is like: (core and test are directories)
core
----file1.py
----__init__.py
test
----file2.py
in file2, i wrote:
from core import file1
result is:
ImportError: cannot import name file1
Have to create __init__.py file inside the test dir:
Because The __init__.py files are required to make Python treat the directories as containing packages.
parent/
child1/
__init__.py
file1.py
child2/
__init__.py
file2.py
From the error:
If run the child2/file2.py file directly. You are not able to access child1/file1.py from the child2/file2.py
Because only from the parent directory can access the child.
If have a folder structure like:
parent/
child1/
__init__.py
file1.py
child2/
__init__.py
file2.py
file3.py
If we run the file3.py file. Can able to access both child1/file1.py, child2/file2.py in file3.py
Because It is running from the parent directory.
If we need to access child1/file1 from child2/file2.py, We need to set the parent directory:
By running this below command we can achieve it...
PYTHONPATH=. python child2/file2.py
PYTHONPATH=. It refers the parent path. Then runs child2/file2.py file from the shell
It's not a strange problem, imports simply don't work like that.
From the official documentation: https://docs.python.org/3/tutorial/modules.html
When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:
The directory containing the input script (or the current directory when
no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell
variable PATH).
The installation-dependent default.
You could look into relative imports, here's a good source: https://stackoverflow.com/a/16985066/4886716
The relevant info from that post is that there's no good way to do it unless you add core to PYTHONPATH like Shawn. L says.
When I tried your case, I got
Traceback (most recent call last):
File "file2.py", line 3, in <module>
from core import file1
ImportError: No module named core
The reason is that Python does not find core. In this case, you need to add core to the system path, as shown below (add them at the very beginning of file2.py):
import sys,os
sys.path.append(path_to_core.py)
Or, if you would run it using command line, you could simply put the following at the beginning of file2.py
import sys,os
sys.path.append(os.path.join(os.path.dirname(__file__),'../'))
Here, os.path.join(os.path.dirname(__file__),'../') is to state the path to file2.py.

How to import from current path instead of the script path?

For example,there are 3 files in the current directory
A/x.py
A/__init__.py
scripts/b.py
And the first line of b.py looks like this:
from A.x import *
Then I tried to run b.py in the current directory like this:
python scripts/b.py
However, that will leads to an error:
ImportError: No module named A.x
I think the reason is : A.x is in the current directory under which the shell is run, not in the directory scripts where scripts/b.py locates.
Does anyone have ideas about how to solve this problem to enable the python to import from the current Path of shell apart from the path of the script being run?
The module search logic is explained in this section of the docs.
Basically, you need to get the search directory into your sys.path variable. One way of doing this is by setting it in an environment variable PYTHONPATH.

Categories