Search code examples
pythonapacheflaskmod-wsgi

wsgi:error ModuleNotFoundError in my webapp model


Hello IT ladies and IT gentlemen.

Please, help me with non-recognized folder "devices".

I have a program for reading data from robot. And data are stored in "devices" folder.

My file structure:

  • GRP
    • devices
      • _ _ init _ _.py
      • dev1.py
      • dev2.py
      • dev3.py
      • webapp.py
    • tools
      • _ _ init _ _.py
      • tool1.py
      • tool2.py

Content of _ _ init _ _ .py in devices folder:

__all__ = ["dev1", "dev2", "dev3"]
from dev1 import Dev1Cls
from dev2 import Dev2Cls
from dev3 import Dev3Cls

Problematic part of code in webapp.py:

from devices import Dev1Cls

And... Dev1Cls is class in dev1.py

I have flask Webapp (blabla/grp_exp_structure/devices/webapp.py) whose task is to read data and display them. Webapp reads data from dev1, dev2 and dev3 and that webapp is launched from Apache server via mod_wsgi.

If I run app via built-in Werkzeug server, no problem. But if I run app by start Apache, I get exception ModuleNotFoundError in errorlog: ModuleNotFoundError: No module named 'devices'

Code of app.wsgi:

#! C:/Users/pvachal/AppData/Local/Programs/Python/Python310/python.exe

import sys

sys.path.insert(0, "c:/users/pvachal/documents/projekty/grp_exp_structure/devices")

activate_this = "c:/users/pvachal/documents/projekty/grp_exp_structure/venv/scripts/activate_this.py"
with open(activate_this) as file_:
    exec(file_.read(), dict(__file__=activate_this))

from webapp import Webapp
application = Webapp().app

Code of dev1.py (dev2 and dev3 are similar)

class Dev1Cls:
    def __init__(self):
        self.value = "dev_number_one"

What do I have to do to make it work? I want to leave same file system what I have. Yes, I can change the structure of my files but I'm wondering how to solve this problem without changing the structure.

I think that problem is with non-reading of _ _ init _ _ file. When I run webapp.py directly, everything is alright because _ _ init _ _ file will launch. What I need to do to make it run without problems via wsgi? Is it possible to make _ _ init _ _ run via wsgi too?


Solution

  • Messing with sys.path is a terrible, terrible idea – with what you've done, you probably have two ways of importing dev1 etc: from devices import dev1 and import dev1, and a splitting headache.

    If your WSGI environment needs it (e.g. you can't chdir or otherwise set a project root via mod_wsgi), you can prepend the project root, e.g. sys.path.insert(0, "c:/users/pvachal/documents/projekty/grp_exp_structure") in the WSGI entrypoint file but do not under any circumstance add a package (i.e. something that contains an __init__.py file).

    Now that you've fixed that, you'll need to change the absolute imports in devices/__init__.py to be relative, or to mention the package:

    __all__ = ["dev1", "dev2", "dev3"]
    from .dev1 import Dev1Cls  # or `from devices.dev1 ...`
    from .dev2 import Dev2Cls
    from .dev3 import Dev3Cls
    

    Similarly, fix the import for webapp in the WSGI file:

    from devices.webapp import Webapp
    application = Webapp().app
    

    and finally, if devices.webapp contains other imports that would have been absolute if devices was on the path, fix those up like above too.

    In general, even outside of a WSGI context, you can now test that things are sane with a something.py in the grp_exp_structure directory:

    import devices
    from devices.webapp import Webapp
    

    These imports should both work fine.

    (Finally, as an aside, I would recommend to look at something other than Apache + mod_wsgi.)