Pydantic: TypeError: issubclass() arg 1 must be a class

Created on 6 Mar 2020  ·  8Comments  ·  Source: samuelcolvin/pydantic

Bug

Getting issubcluss error on /docs in Fastapi

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

             pydantic version: 1.4
            pydantic compiled: False
                 install path: /Users/philippk/anaconda3/envs/py37/lib/python3.7/site-packages/pydantic
               python version: 3.7.3 | packaged by conda-forge | (default, Dec  6 2019, 08:36:57)  [Clang 9.0.0 (tags/RELEASE_900/final)]
                     platform: Darwin-19.2.0-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']
(py37) ZG00021:se-sestimate philippk$ docker run -it -p 80:80 -e ENVKEY -e SNOWFLAKE_PASSWORD='dummy' -e SNOWFLAKE_ACCOUNT='zillow' -e SNOWFLAKE_USER='philippk' -e SNOWFLAKE_WAREHOUSE='LOOKER_WH' -e SNOWFLAKE_DATABASE='FEATURESTORE' -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN -e AWS_SECURITY_TOKEN --entrypoint /bin/bash se-sestimate:local-api
root@d50a194c1238:/app# python -c "import pydantic.utils; print(pydantic.utils.version_info())"
             pydantic version: 1.4
            pydantic compiled: True
                 install path: /usr/local/lib/python3.7/site-packages/pydantic
               python version: 3.7.4 (default, Sep 12 2019, 15:40:15)  [GCC 8.3.0]
                     platform: Linux-4.19.76-linuxkit-x86_64-with-debian-10.1
     optional deps. installed: []

Here is the code of request_models:

from typing import List
from pydantic import BaseModel
from enum import Enum
from datetime import date


class ModelName(str, Enum):
    repeated_sale = "repeated_sale"
    main = "main"
    dummy = "dummy"


class Inference(BaseModel):
    price: int
    model: ModelName
    model_version: float
    date: date

@app.get("/infer", response_model=Inference)
def infer(
    params: dict,
    model: ModelName,
    v: Optional[float] = None,
    date: Optional["date"] = None,
) -> Inference:
    """attempts to pull predictions from snowflake"""
    result = app.model_registry.pull(model=model, v=v, date=date).predict(params)
    return {"model": model, "v": v, **result}

this results in:

  File "/Users/philippk/anaconda3/envs/py37/lib/python3.7/site-packages/pydantic/schema.py", line 657, in field_singleton_schema
    if issubclass(field_type, Enum):
TypeError: issubclass() arg 1 must be a class
bug

Most helpful comment

I just experience the same bug. update_forward_refs does not solve it.

All 8 comments

So, I was able to fix this by changing Optional["date"] to Optional[date] in the signature of infer. The problem is that the forward ref inside the Optional isn't getting resolved first.

I'm not sure whether the problem should be considered to lie in fastapi or pydantic, but it appears to be stemming from an inability for pydantic.schema.field_schema to handle a field with type Optional[ForwardRef("date")] (presumably it would struggle to handle fields with any ForwardRefs).

@samuelcolvin Should FastAPI be taking responsibility for ensuring fields don't have forward refs in their types?

Does update_forward_refs fix this?

If so, I think the problem is on the FastAPI side.

Otherwise, we might be able to have a crack at resolving it in pydantic.

Simple example of the issue. Try to access /docs on FastAPI

from typing import Set

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class A(BaseModel):
    b: 'B' # if this line is commented out, /docs works fine


class B(BaseModel):
    a: A


@app.get("/")
def test(a: A):
    return a

I just experience the same bug. update_forward_refs does not solve it.

Simple example of the issue. Try to access /docs on FastAPI

A.update_forward_refs() does work for me:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class A(BaseModel):
    b: 'B'


class B(BaseModel):
    a: A

A.update_forward_refs() # This fixes the issubclass crash
# B.update_forward_refs() # This does not make a difference

@app.get("/")
def hello(a: A):
    return a


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8001)

Hello
I feel like this issue should be closed as the other issues are not really related.
The issubclass issue is now solved since we use lenient_issubclass.
The second issue comes from how __annotations__ works.
When writing

class A:
    date: Optional[date] = None

the __annotations__ is {'date': <class 'NoneType'>}, which leads to errors!
Unfortunately it has nothing to do with _pydantic_ and this is a tricky error.

This doesn't happen if we use Optional['date'] = None or Optional[date] without default value.
Using from __future__ import annotations also works as it does the same trick (use ForwardRef...) so I feel like this error should be solved once the new annotations system is supported and the default one.
And for this we already have some open issues. Don't know if we need to open a new one 🤷‍♂️

@PrettyWood has this fix released? I'm still getting the error without .update_forward_refs()

This will be fixed by #2221 which I'm about to merge and should get released very soon.

@thiras since you're obviously eager for a fix, it would be really helpful if you could try #2221 and let us know if it fixes your problem or not.

Was this page helpful?
1 / 5 - 1 ratings

Related issues

dmontagu picture dmontagu  ·  3Comments

ashpreetbedi picture ashpreetbedi  ·  3Comments

sommd picture sommd  ·  3Comments

mgresko picture mgresko  ·  3Comments

samuelcolvin picture samuelcolvin  ·  3Comments