Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: cannot pickle 'module' object in fastapi

I'm trying to use OAuth2 in my fastapi back-end project.

and I

This belows is my code snippet

from fastapi import APIRouter, HTTPException, status, Depends

from fastapi.security.oauth2 import OAuth2PasswordRequestForm

from ..modules import oauth_module
from ..models.user_model import User
from ..schemas import token_schema

router = APIRouter(
    prefix="/auth",
    tags=["auth"],
)

...

@router.get("/my_info")
async def read_user_info(current_user: User = Depends(oauth_module.get_current_active_user)):
    return current_user

and this code return python TypeError like belows

Process SpawnProcess-12:
Traceback (most recent call last):
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/multiprocessing/process.py", line 315, in _bootstrap
    self.run()
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/uvicorn/subprocess.py", line 76, in subprocess_started
    target(sockets=sockets)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/uvicorn/server.py", line 50, in run
    loop.run_until_complete(self.serve(sockets=sockets))
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/uvicorn/server.py", line 57, in serve
    config.load()
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/uvicorn/config.py", line 318, in load
    self.loaded_app = import_from_string(self.app)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/uvicorn/importer.py", line 22, in import_from_string
    module = importlib.import_module(module_str)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 855, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/Users/supergrammer/GitRepository/AI_Stock_Helper/./user_app/main.py", line 6, in <module>
    from .routers import user_router, user_auth_router
  File "/Users/supergrammer/GitRepository/AI_Stock_Helper/./user_app/routers/user_auth_router.py", line 29, in <module>
    async def read_user_info(current_user: User = Depends(oauth_module.get_current_active_user)):
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/routing.py", line 564, in decorator
    self.add_api_route(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/routing.py", line 509, in add_api_route
    route = route_class(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/routing.py", line 393, in __init__
    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 295, in get_dependant
    sub_dependant = get_param_sub_dependant(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 117, in get_param_sub_dependant
    return get_sub_dependant(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 153, in get_sub_dependant
    sub_dependant = get_dependant(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 295, in get_dependant
    sub_dependant = get_param_sub_dependant(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 117, in get_param_sub_dependant
    return get_sub_dependant(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 153, in get_sub_dependant
    sub_dependant = get_dependant(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 302, in get_dependant
    param_field = get_param_field(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 394, in get_param_field
    field = create_response_field(
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/site-packages/fastapi/utils.py", line 65, in create_response_field
    return response_field(field_info=field_info)
  File "pydantic/fields.py", line 342, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 445, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 473, in pydantic.fields.ModelField._set_default_and_type
  File "pydantic/fields.py", line 345, in pydantic.fields.ModelField.get_default
  File "pydantic/utils.py", line 630, in pydantic.utils.smart_deepcopy
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/supergrammer/anaconda3/envs/FastAPI/lib/python3.9/copy.py", line 161, in deepcopy
    rv = reductor(4)
TypeError: cannot pickle 'module' object

I don't know why this code return TypeError and I think it may related with async/await.

And I add my oauth_module code snippet below...

from datetime import datetime, timedelta

from fastapi import HTTPException, status, Depends, Security

from fastapi.security.oauth2 import OAuth2PasswordBearer, OAuth2PasswordRequestForm, SecurityScopes
from passlib.context import CryptContext
from jose import JWTError, jwt

from pydantic import ValidationError
from sqlalchemy.orm import Session

from ..models.user_model import User
from ..schemas import token_schema

from ..config.config import get_configurations
from ..config.database import get_db

c = get_configurations()
SECRET_KEY = c.secret_key
HASH_ALGORITHM = c.hash_algorithm
ACCESS_TOKEN_EXPIRE_MINUTES = c.access_token_expire_minutes

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(
    tokenUrl="token",
    scopes={"userinfo": "gg"}
)

def verify_password(plain, hashed):
    return pwd_context.verify(plain, hashed)

def get_hashed_password(plain):
    return pwd_context.hash(plain)

def is_authenticated_user(form_data: OAuth2PasswordRequestForm, db: Session = get_db()):
    user = db.query(User).filter(User.email == form_data.username).first()

    if not user or \
        not verify_password(form_data.password, user.password.password_history[-1]):
        return False
    return user    

def create_access_token(data: dict, expire_minutes: int = ACCESS_TOKEN_EXPIRE_MINUTES):
    to_encode = data.copy()
    to_encode.update(
        {"exp": datetime.utcnow() + timedelta(minutes=expire_minutes)})

    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=HASH_ALGORITHM)
    return encoded_jwt

async def get_current_user(security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme), db: Session = get_db()):
    authenticate_value = "Bearer" \
            + f' scope="{security_scopes.scope_str}"' if security_scopes.scopes else ''

    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": authenticate_value}
    )

    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[HASH_ALGORITHM])
        email: str = payload.get("email")

        if not email:
            raise credentials_exception

        token_scopes = payload.get("scopes", [])
        token_data = token_schema.TokenData(email=email, scopes=token_scopes)
    except (JWTError, ValidationError):
        raise credentials_exception

    user = db.query(User).filter(User.email == email).first()
    if not user:
        raise credentials_exception

    for scope in security_scopes.scopes:
        if scope not in token_data.scopes:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Not enough permissions",
                headers={"WWW-Authenticate": authenticate_value}
            )

    return user

async def get_current_active_user(current_user: User = Security(get_current_user, scopes=[])):
    if not current_user:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive user")
    return current_user

Please Help me... 도와주세요...

like image 216
Supergrammer Avatar asked May 10 '26 09:05

Supergrammer


1 Answers

On top of the endpoint definition you posted, the important part is indeed in the User model.

# base_model.py
from sqlalchemy import Column, DateTime
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class BaseMixin():
    created_date = Column(DateTime(timezone=True), default=func.now(), nullable=False)
    updated_date = Column(DateTime(timezone=True), default=func.now(), onupdate=func.now(), nullable=False)

# user.py
#... other sqlalchemy imports
from .base_model import Base, BaseMixin

class User(Base, BaseMixin):
    __tablename__ = "user"

    id = Column(UUID(as_uuid=True), primary_key=True, index=True, nullable=False, default=uuid.uuid4)
    email = Column(String(51), unique=True, index=True, nullable=False)
    password = relationship( \
        "Password",
        backref=backref("user", uselist=False),
        primaryjoin='foreign(User.id) == remote(Password.id)')
    # etc sqlalchemy fields

The fundamental problem is that your User model for which you define a Depends in your endpoint as current_user: User = Depends(oauth_module.get_current_active_user)) is a sqlalchemy model. Sure, the oauth_module.get_current_active_user is a callable, and I'm assuming it works on its own. But since you define the type of the Depends as User, fastapi will look into that model to generate the api documentation and validation. And then fail as it does not understand sqlalchemy models. Fastapi works with pydantic models. You can see in your stacktrace fastapi calls onto many get_dependant (for your nested Depends structure), followed by get_param_field and create_response_field, which then brings it to pydantic to try to resolve the model.

Your solution would be to not type your Depends, and/or create a proxy class. You can declare a Depends() in the __init__ of any class, which would be where you define this call to get the user from db, and use that class to act as proxy you can type more safely.

class ProxyUser():
    def __init__(self, user = Depends(oauth_module.get_current_active_user)):
        self.user = user



@router.get("/my_info")
async def read_user_info(current_user = Depends(ProxyUser)):
    return current_user.user

In any case, it would be a good idea to keep the public API representation of your user separate from the DB representation, so two separate models would also help on that front.

like image 182
LotB Avatar answered May 12 '26 09:05

LotB



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!