SQLAlchemy module not found despite definitely being installed with Pipenv

Question:

I’m learning to use FastAPI, psycopg2 and SQLAlchemy with python, which has been working fine. Now for some reason whenever I run my web app, the SQLAlchemy module cannot be found.

I am running this in a Pipenv, with python 3.11.1 and SQLAlchemy 1.4.45, and running pip freeze shows SQLAlchemy is definitely installed, and my source is definitely my pipenv environment, the same from which I’m running my fastAPI server.

I have tried uninstalling and reinstalling SQLAlchemy with Pipenv, and when I run python in interactive mode, it is the expected python version and I’m able to import SQLAlchemy and check sqalalchemy.version .

Any ideas why it’s saying it can’t import when I run FastAPI?

Code from my models.py module being imported into main.py:

from sqlalchemy import Column, Integer, String, Boolean
from app.database import Base


class Post(Base):
    __tablename__ = "posts"

    id = Column(Integer, primary_key=True, nullable=False)
    title = Column(String, nullable=False)
    content = Column(String, nullable=False)
    published = Column(Boolean, default=True)
    # timestamp = Column(TIMESTAMP, default=now())

main.py:

from fastapi import FastAPI, Response, status, HTTPException, Depends
from pydantic import BaseModel
import psycopg2
from psycopg2.extras import RealDictCursor
import time
from app import models
from sqlalchemy.orm import Session
from app.database import engine, SessionLocal

models.Base.metadata.create_all(bind=engine)

# FastAPI initialisation
app = FastAPI()


# function to initialise SQlAlchemy DB session dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


# psycopg2 DB connection initialisation
while True:
    try:
        conn = psycopg2.connect(host="localhost", dbname="fastapi", user="postgres",
                                password="*********", cursor_factory=RealDictCursor)
        cursor = conn.cursor()
        print('Database connection successful.')
        break
    except Exception as error:
        print("Connecting to database failed.")
        print("Error: ", error)
        print("Reconnecting after 2 seconds")
        time.sleep(2)


# this class defines the expected fields for the posts extending the BaseModel class
# from Pydantic for input validation and exception handling ==> a "schema"
class Post(BaseModel):
    title: str
    content: str
    published: bool = True


# this list holds posts, with 2 hard coded for testing purposes
my_posts = [{"title": "title of post 1", "content": "content of post 1", "id": 1},
            {"title": "title of post 2", "content": "content of post 2", "id": 2}]


# this small function simply finds posts by id by iterating though the my_posts list
def find_post(find_id):
    for post in my_posts:
        if post["id"] == find_id:
            return post


def find_index(find_id):
    try:
        index = my_posts.index(find_post(find_id))
    except:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Post of id: {find_id} not found.")
    return index


# these decorated functions act as routes for FastAPI.
# the decorator is used to define the HTTP request verb (e.g. get, post, delete, patch, put),
# as well as API endpoints within the app (e.g. "/" is root),
# and default HTTP status codes.
@app.get("/")
async def root():
    return {"message": "Hello World"}


# "CRUD" (Create, Read, Update, Delete) says to use same endpoint
# but with different HTTP request verbs for the different request types.
# (e.g. using "/posts" for all four CRUD operations, but using POST, GET, PUT/PATCH, DELETE respectively.)
@app.get("/posts")
def get_data():
    cursor.execute("SELECT * FROM posts")
    posts = cursor.fetchall()
    print(posts)
    return {"data": posts}


@app.post("/posts", status_code=status.HTTP_201_CREATED)
def create_posts(post: Post):
    cursor.execute("INSERT INTO posts (title, content, published) VALUES (%s, %s, %s) RETURNING *",
                   (post.title, post.content, post.published))
    new_post = cursor.fetchone()

    conn.commit()

    return {"created post": new_post}


@app.delete("/posts/{id}")
def delete_post(id: int):
    cursor.execute("DELETE FROM posts * WHERE id = %s RETURNING *", str(id))
    deleted_post = cursor.fetchone()
    conn.commit()

    if deleted_post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with id: {id} not found.")
    else:
        print("deleted post:", deleted_post)
        return Response(status_code=status.HTTP_204_NO_CONTENT)


@app.get("/posts/{id}")
def get_post(id: int):
    cursor.execute("SELECT * FROM posts WHERE id = %s", str(id))
    post = cursor.fetchone()
    if post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with id: {id} was not found.")
    return {"post_detail": post}


@app.put("/posts/{id}")
def update_post(id: int, put: Post):
    cursor.execute("UPDATE posts SET title = %s, content = %s, published= %s WHERE id = %s RETURNING *",
                   (put.title, put.content, put.published, str(id)))
    updated_post = cursor.fetchone()
    if updated_post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with id: {id} was not found.")
    return {"updated_post_detail": updated_post}


@app.get("/sqlalchemy")
def test_posts(db: Session = Depends(get_db)):
    return {"status": "success"}

ERROR LOG:

 louisgreenhalgh@MacBook-Pro  ~/PycharmProjects/FASTAPI  uvicorn app.main:app --reload                                                                                                ✔  FASTAPI-3Pf2tu2f
INFO:     Will watch for changes in these directories: ['/Users/louisgreenhalgh/PycharmProjects/FASTAPI']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [32662] using WatchFiles
Process SpawnProcess-1:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/uvicorn/_subprocess.py", line 76, in subprocess_started
    target(sockets=sockets)
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/uvicorn/server.py", line 60, in run
    return asyncio.run(self.serve(sockets=sockets))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "uvloop/loop.pyx", line 1517, in uvloop.loop.Loop.run_until_complete
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/uvicorn/server.py", line 67, in serve
    config.load()
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/uvicorn/config.py", line 477, in load
    self.loaded_app = import_from_string(self.app)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/uvicorn/importer.py", line 24, in import_from_string
    raise exc from None
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/uvicorn/importer.py", line 21, in import_from_string
    module = importlib.import_module(module_str)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/Users/louisgreenhalgh/PycharmProjects/FASTAPI/app/main.py", line 6, in <module>
    from app import models
  File "/Users/louisgreenhalgh/PycharmProjects/FASTAPI/app/models.py", line 1, in <module>
    from sqlalchemy import Column, Integer, String, Boolean
ModuleNotFoundError: No module named 'sqlalchemy'

Pipenv Graph Output:

fastapi==0.88.0
  - pydantic [required: >=1.6.2,<2.0.0,!=1.8.1,!=1.8,!=1.7.3,!=1.7.2,!=1.7.1,!=1.7, installed: 1.10.4]
    - typing-extensions [required: >=4.2.0, installed: 4.4.0]
  - starlette [required: ==0.22.0, installed: 0.22.0]
    - anyio [required: >=3.4.0,<5, installed: 3.6.2]
      - idna [required: >=2.8, installed: 3.4]
      - sniffio [required: >=1.1, installed: 1.3.0]
greenlet==2.0.1
psycopg2-binary==2.9.5
SQLAlchemy==1.4.45

Asked By: louistheone48

||

Answers:

Most likely issue is that the uvicorn executable is not present in the same python (v)env.
When a python process starts, it looks into the location of the binary (uvicorn in this case), determines the python base location (either the same folder as the binary is in, or one above), and finally adds the appropriate site_packages location based on that base location.

So in your case, try pip(env) install uvicorn into the same virtual environment

Answered By: ilmarinen
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.