Python/Flask - Cause of module not found in the docker container - python

The src/online_serving/main.py cannot find lib/common/util_string.py although they are in the same /app directory in which Python process is executed.
/app# python3 src/online_serving/main.py
Traceback (most recent call last):
File "/app/src/online_serving/main.py", line 18, in <module>
from lib.common import util_string
ModuleNotFoundError: No module named 'lib'
My directory layout is fairly straightforward. The Docker image is created using the Dockerfile at the BASE directory.
BASE
├── Dockerfile
├── lib
│ ├── __init__.py
│ ├── common
│ │ ├── __init__.py
│ │ └── util_string.py
├── src
│ ├── online_serving
│ │ ├── __init__.py
│ │ ├── main.py
My Dockerfile contains:
FROM python:3.9-slim
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./
ENTRYPOINT gunicorn --bind :$PORT --timeout 120 src.online_serving.main:app
main.py imports the lib.common.util_string which is causing error.
from flask import (
Flask,
request,
Response
)
from lib.common import util_string
# Flask runtime
app = Flask(__name__)
As a workaround, I can set this line in the Dockerfile
ENV PYTHONPATH /app
But why isn't the PYTHONPATH automatically set to the /app directory in the container?

When the Python interpreter is started with python a/b/c.py, the current directory is not added in sys.path but the directory where the script c.py is.
$ python a/b/c.py
$ pwd
/app
$ tree
a
└── b
└── c.py
$ cat a/b/c.py
import sys
for p in sys.path:
print(p)
$ python3 a/b/c.py
/app/a/b # <--- path[0] is the directory of the script used to invoke the interpreter.
/usr/local/lib/python39.zip
/usr/local/lib/python3.9
/usr/local/lib/python3.9/lib-dynload
/usr/local/lib/python3.9/site-packages
sys.path
A list of strings that specifies the search path for modules. Initialized from the environment variable PYTHONPATH, plus an installation-dependent default.
As initialized upon program startup, the first item of this list,path[0], is the directory containing the script that was used to invoke the Python interpreter.
If the script directory is not available, thenpath[0] is the empty string, which directs Python to search modules in the current directory first.(e.g. if the interpreter is invoked interactively or if the script is read from standard input),
Notice that the script directory is insertedbeforethe entries inserted as a result ofPYTHONPATH.
To add the current directory to sys.path so that a module is searched in the current directory tree, need to use PYTHONPATH or start the interpreter with -m option.
-m
If this option is given, the first element of sys.argv will be the full path to the module file (while the module file is being located, the first element will be set to "-m"). As with the -c option, the current directory will be added to the start of sys.path.
$ pwd
/app
$ tree
a
└── b
└── c.py
$ export PYTHONPATH=${PYTHONPATH}:/app
$ python a/b/c.py
/app/a/b # <--- path[0] is the directory of the script used to invoke the interpreter.
/app # <--- then from the environment variable PYTHONPATH, plus an installation-dependent default.
/usr/local/lib/python39.zip
/usr/local/lib/python3.9
/usr/local/lib/python3.9/lib-dynload
/usr/local/lib/python3.9/site-packages
Or
$ pwd
/app
$ tree
a
└── b
└── c.py
$ cat a/b/c.py
import sys
for p in sys.path:
print(p)
$ python3 -m a.b.c
/app # <--- Current directory is path[0] for python -m <module> invocation
/usr/local/lib/python39.zip
/usr/local/lib/python3.9
/usr/local/lib/python3.9/lib-dynload
/usr/local/lib/python3.9/site-packages

Related

Change PYTHONPATH so that mypy can find local module

My project is structured as follows:
$ tree . -I venv
.
├── mydir
│   └── __init__.py
├── myotherdir
│   └── t.py
└── t.py
2 directories, 3 files
Both t.py and myotherdir/t.py have the same content:
$ cat t.py
import mydir
$ cat myotherdir/t.py
import mydir
Now, if I run mypy t.py, then all works fine:
$ mypy t.py
Success: no issues found in 1 source file
However, if I run it from inside myotherdir, then it's unable to find mydir:
$ cd myotherdir/
$ mypy t.py
t.py:1: error: Cannot find implementation or library stub for module named 'mydir'
t.py:1: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)
I was expecting that I would be able to solve this by modifying PYTHONPATH - however, that didn't work:
$ PYTHONPATH=.. mypy t.py
t.py:1: error: Cannot find implementation or library stub for module named 'mydir'
t.py:1: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)
How can I let mypy recognise mydir when I'm running inside myotherdir?

Error: the command ( (from my_script) could not be found within PATH

While working on a Python FastAPI project using Pipenv and Pytest, I was asked to write a Pipenv script to run the tests.
Project has the following structure:
.
├── app
| ├── main.py
│   ├── my_package
│   │   ├── __init__.py
│   │   ├── (project folders)
│   │   └── tests
| | └── tests.py
│   └── __pycache__
└──(deployment scripts and folders, Pipfile, Dokerfile, etc)
I'd like to run Pytest on tests.py from the top structure (./app/.../tests.py). At least for now.
As recommended here, I currently run them from the top with:
( cd app ; python -m pytest my_package/tests/tests.py )
... which works as expected.
However, when I add that my Pipfile-scripts-section:
[scripts]
my_script = "( cd app ; python -m pytest my_package/tests/tests.py )"
... run it with:
pipenv run my_script
I get the error:
Error: the command ( (from my_script) could not be found within PATH.
I've also tried:
[scripts]
my_script = "cd app && python -m pytest my_package/tests/tests.py"
... which returns another similar error:
Error: the command cd (from my_script) could not be found within PATH.
So it's clear I'm wrong to use it as bash aliases.
I've tried searching for more documentation on how the [scripts] section works as I, but I've had no luck (yet).
I'm not familiar with the tools you're using, but the error message suggests that it's looking for an executable. ( is part of shell syntax, and cd is a shell builtin, not an executable. Try this:
my_script = "bash -c 'cd app && python -m pytest my_package/tests/tests.py'"
Here bash is the executable, and -c makes it run your snippet.
BTW, keep in mind that cd can fail, so the script should bail out if it does.

ModuleNotFound while Dockering python app

I have a simple python app, which uses custom class I've created. The following folder structure is the following:
│ mains
| ├── run_it.py
| ├── __init__.py
│   ├── parsers
│ ├── parser.py
│ ├── __init__.py
In the run_it.py, the main program, I'm calling
from mains.parsers.parser import Parser
In local mode I've added to ~/.bashrc the line and it works good:
export PYTHONPATH="${PYTHONPATH}:/home/.../THE_FOLDER_ABOVE_MAINS"
But when I try to dockerize the app, I get the following error:
File "/app/run_it.py", line 11, in <module>
from mains.parsers.parser import Parser
ModuleNotFoundError: No module named 'mains'
My Dockerfile is:
FROM python:3
RUN mkdir /app
WORKDIR /app
ADD . /app/
RUN apt-get update
RUN pip3 install gunicorn
RUN pip3 install -r requirements.txt
EXPOSE 5000
ENV PYTHONIOENCODING=utf-8
ENV GUNICORN_CMD_ARGS="--bind 0.0.0.0:5000 --workers=2"
CMD ["gunicorn","run_it:app"]
Any idea how can I solve it?
Thanks in advance!
I didn't see you set any module path in container, then for your case, the folder app which run the top script run_it.py was automatically added to module path.
As a result, you should use next:
from parsers.parser import Parser
And another way add next to your Dockerfile(suppose it's the same folder of mains) too:
ENV PYTHONPATH=/app
Then you can still use from mains.parsers.parser import Parser
Try this its working..
from parsers.parser import Parser

How to import other file using relative path to executed file not python command path?

My folder trees:
./
├── README.MD
├── basic
│ └── thresh.py
├── images
│ └── figure.jpg
└── utils
├── util.py
└── util.pyc
I want to import util.py in thresh.py:
import sys
sys.path.append('../utils')
import util
When I run command $ python thresh.py in the basic folder, it's allright. But run $ python ./basic/thresh.py in the topmost folder, I will get the error:
ImportError: No module named util
So how to make $ python ./basic/thresh.py and $ python thresh.py both work to import file by given the file's relative path to executed file regardless of python command path?
You can get the absolute path of the script you are executing with (there are other variants also using __file__, but this should work)
import os
wk_dir = os.path.dirname(os.path.realpath('__file__'))
print( wk_dir )
and then get your dir with it, e.g.
import sys
sys.path.append(wk_dir+'/../utils')
PS: You might need to use __file__ instead of '__file__'.

How to make a .pyc file from Python script [duplicate]

This question already has answers here:
How can I manually generate a .pyc file from a .py file
(9 answers)
Closed 6 years ago.
I know that when Python script is imported in other python script, then a .pyc script is created. Is there any other way to create .pyc file by using linux bash terminal?
Use the following command:
python -m compileall <your_script.py>
This will create your_script.pyc file in the same directory.
You can pass directory also as :
python -m compileall <directory>
This will create .pyc files for all .py files in the directory
Other way is to create another script as
import py_compile
py_compile.compile("your_script.py")
It also create the your_script.pyc file. You can take file name as command line argument
You could use the py_compile module. Run it from command line (-m option):
When this module is run as a script, the main() is used to compile all
the files named on the command line.
Example:
$ tree
.
└── script.py
0 directories, 1 file
$ python3 -mpy_compile script.py
$ tree
.
├── __pycache__
│   └── script.cpython-34.pyc
└── script.py
1 directory, 2 files
compileall provides similar functionality, to use it you'd do something like
$ python3 -m compileall ...
Where ... are files to compile or directories that contain source files, traversed recursively.
Another option is to import the module:
$ tree
.
├── module.py
├── __pycache__
│   └── script.cpython-34.pyc
└── script.py
1 directory, 3 files
$ python3 -c 'import module'
$ tree
.
├── module.py
├── __pycache__
│   ├── module.cpython-34.pyc
│   └── script.cpython-34.pyc
└── script.py
1 directory, 4 files
-c 'import module' is different from -m module, because the former won't execute the if __name__ == '__main__': block in module.py.

Categories