【问题标题】:TypeError: cannot pickle 'module' object in fastapiTypeError:无法在 fastapi 中腌制“模块”对象
【发布时间】:2021-06-20 08:20:59
【问题描述】:

我正在尝试在我的 fastapi 后端项目中使用 OAuth2。

和我

下面是我的代码sn-p

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

这段代码返回python TypeError,如下所示

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

我不知道为什么这段代码返回 TypeError,我认为它可能与 async/await 有关。

我在下面添加我的 oauth_module 代码 sn-p...

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

请帮帮我...도와주세요...

【问题讨论】:

  • 你正在你的用户模型中做一些事情。这可能就是你的问题所在。您显示的所有代码都是无关紧要的。
  • github.com/Supergrammer/AI_Stock_Helper/tree/feature/tmp 这是我的代码。如果我注释掉路由器的my_info,就不会出现这个错误。

标签: python-3.x async-await oauth-2.0 fastapi


【解决方案1】:

在您发布的端点定义之上,重要的部分确实在用户模型中。

# 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

根本问题是,您在端点中为其定义 Depends 为 current_user: User = Depends(oauth_module.get_current_active_user)) 的 User 模型是 sqlalchemy 模型。当然,oauth_module.get_current_active_user 是可调用的,我假设它可以独立运行。但是由于您将 Depends 的类型定义为 User,fastapi 将查看该模型以生成 api 文档和验证。然后失败,因为它不了解 sqlalchemy 模型。 Fastapi 适用于pydantic 模型。 您可以在堆栈跟踪中看到对许多 get_dependant 的 fastapi 调用(用于嵌套的 Depends 结构),然后是 get_param_fieldcreate_response_field,然后将其带到 pydantic 以尝试解析模型。

您的解决方案是不输入您的 Depends,和/或创建代理类。您可以在任何类的__init__ 中声明一个 Depends(),这将是您定义此调用以从 db 获取用户的位置,并使用该类充当代理,您可以更安全地键入。

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

在任何情况下,将用户的公共 API 表示与 DB 表示分开是一个好主意,因此两个单独的模型也会在这方面有所帮助。

【讨论】:

猜你喜欢
  • 2020-04-07
  • 2017-10-23
  • 2019-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多