Search code examples
python-importfastapipython-3.10

ImportError: attempted relative import beyond top-level package FastAPI


I recently started working with FastAPI and faced such a problem. My project structure:

My_Project
└───backend
    │   alembic.ini
    │   sqlite.db
    │   requirements.txt
    │
    ├───alembic
    │   │   env.py
    │   │   script.py.mako
    │   │
    │   └───versions
    │           2023-02-22_added_users_tables.py
    │           __init__.py
    │
    └───app
        │   database.py
        │   main.py
        │   __init__.py
        │
        └───users
                models.py
                router.py
                schemas.py
                __init__.py

I am trying to import Base from database.py, being in models.py:

#models.py
from ..database import Base

And when I try to run the server I get this error:

ImportError: attempted relative import beyond top-level package

I have worked with Django before and have never encountered such errors.

I have looked at several similar questions and answers to them, but I have not figured out how to solve this problem.

I tried to use absolute import:

from app.database import Base

or

from backend.app.database import Base

but I got this error:

ModuleNotFoundError: No module named 'app' # And in the second case: 'backend'

Solution

  • You're going to have to launch your server from outside the folder it's in, or edit the cwd. Then, append your path to import the external files.

    You also need and __init__.py in each of the external directories.

    Here's a working solution from one of my projects:

    main.py

    import sys
    
    from fastapi import FastAPI
    from pathlib import Path
    
    external_path = Path.cwd() / 'external_dir'
    sys.path.insert(1, str(external_path))
    
    import external_module
    
    app = FastAPI()
    

    run_server.py (or you can do this in VSCode's launch.json)

    import uvicorn
    
    if __name__ == "__main__":
        uvicorn.run("app:main", host="127.0.0.1", port=8000, reload=True, log_level="debug", reload_dirs=["app", "exernal_dir"])
    

    or with launch.json

    {
      "configurations": [
        {
          "name": "Python: debug",
          "type": "python",
          "request": "launch",
          "module": "uvicorn",
          "args": [
            "--host=0.0.0.0",
            "--port=8000",
            "--reload",
            "--log_level=debug",
            "--reload-dir=app",
            "--reload-dir=external_dir",
            "--factory",
            "--workers=1",
            "app:main"
          ],
          "jinja": false,
          "justMyCode": false
        }
      ]
    }
    

    Then from your root dir, run python my_app/run_server.py


    Project structure would look like this:

    my_app/
        /external_dir
            __init__.py
            external_module/
                __init__.py
                external.py
    
        /app
            __init__.py
            main.py
            
        /run_server.py