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

Created on 25 May 2019  路  19Comments  路  Source: samuelcolvin/pydantic


Bug

Hi. I'm getting a type error when trying to generate a schema, using fastapi:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 368, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.7/site-packages/starlette/applications.py", line 133, in __call__
    await self.error_middleware(scope, receive, send)
  File "/usr/local/lib/python3.7/site-packages/starlette/middleware/errors.py", line 122, in __call__
    raise exc from None
  File "/usr/local/lib/python3.7/site-packages/starlette/middleware/errors.py", line 100, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.7/site-packages/starlette/middleware/cors.py", line 76, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.7/site-packages/starlette/exceptions.py", line 73, in __call__
    raise exc from None
  File "/usr/local/lib/python3.7/site-packages/starlette/exceptions.py", line 62, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.7/site-packages/starlette/routing.py", line 585, in __call__
    await route(scope, receive, send)
  File "/usr/local/lib/python3.7/site-packages/starlette/routing.py", line 207, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.7/site-packages/starlette/routing.py", line 40, in app
    response = await func(request)
  File "/usr/local/lib/python3.7/site-packages/fastapi/applications.py", line 90, in openapi
    return JSONResponse(self.openapi())
  File "/usr/local/lib/python3.7/site-packages/fastapi/applications.py", line 82, in openapi
    openapi_prefix=self.openapi_prefix,
  File "/usr/local/lib/python3.7/site-packages/fastapi/openapi/utils.py", line 248, in get_openapi
    flat_models=flat_models, model_name_map=model_name_map
  File "/usr/local/lib/python3.7/site-packages/fastapi/utils.py", line 42, in get_model_definitions
    model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
  File "/usr/local/lib/python3.7/site-packages/pydantic/schema.py", line 511, in model_process_schema
    model, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix
  File "/usr/local/lib/python3.7/site-packages/pydantic/schema.py", line 537, in model_type_schema
    f, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix
  File "/usr/local/lib/python3.7/site-packages/pydantic/schema.py", line 274, in field_schema
    ref_prefix=ref_prefix,
  File "/usr/local/lib/python3.7/site-packages/pydantic/schema.py", line 486, in field_type_schema
    ref_prefix=ref_prefix,
  File "/usr/local/lib/python3.7/site-packages/pydantic/schema.py", line 687, in field_singleton_schema
    if issubclass(field.type_, Enum):
TypeError: issubclass() arg 1 must be a class

For bugs/questions:

  • OS: uname -a Linux char-lang-dev 4.18.0-20-generic #21~18.04.1-Ubuntu SMP Wed May 8 08:43:37 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
  • Python version import sys; print(sys.version): '3.7.1 (default, Oct 22 2018, 11:21:55) \n[GCC 8.2.0]'
  • Pydantic version import pydantic; print(pydantic.VERSION): '3.7.1 (default, Oct 22 2018, 11:21:55) \n[GCC 8.2.0]'

Where possible please include a self contained code snippet describing your
bug, question, or where applicable feature request:

from __future__ import annotations
from typing import Optional, List

from pydantic import BaseModel, UUID4


class CategoryCreationValidator(BaseModel):
    name: str = ...
    labels: List[str] = None
    description: str = ...
    parent_id: int = None


class CategorySelectionValidator(BaseModel):
    name: str = ...
    organization_id: str = ...
    labels: Optional[str] = None
    description: str = ...
    parent_id: Optional[int] = None


class CategoryUpdateValidator(BaseModel):
    name: Optional[str] = None
    organization_id: Optional[str] = None
    labels: Optional[List[str]] = None
    description: Optional[str] = None
    parent_id: Optional[int] = None


class ClusterValidator(BaseModel):
    name: str = ...
    category_id: Optional[str] = None


class CategoryModel(BaseModel):
    id: int = ...
    name: str = ...
    description: str = ...
    parent_id: int = None
    children: List[CategoryModel]


#CategoryModel.update_forward_refs()


class ClusterModel(BaseModel):
    id: UUID4 = ...
    name: str = ...
    parent_category_id: str = ...
    queries: List[str] = ...
    urls: List[str] = ...


class SuggestionValidator(BaseModel):
    queries: List[str] = ...
    urls: List[str] = ...


class QueryCreationValidator(BaseModel):
    query_text: str = ...
    cluster_id: UUID4 = ...


class QuerySelectionValidator(BaseModel):
    cluster_id: UUID4 = ...


class UrlCreationValidator(BaseModel):
    url: str = ...
    cluster_id: UUID4 = ...


class UrlSelectionValidator(BaseModel):
    cluster_id: UUID4 = ...


class CreateClusterInput(BaseModel):
    name: str = ...
    category_id: int = ...


class ClusterSelectionValidator(BaseModel):
    category_name: str = ...

...
bug help wanted

Most helpful comment

pydantic==1.4
fastapi==0.52.0
python 3.7.4

issue persist

All 19 comments

Thanks for reporting.

@tiangolo, maybe this is as simple as using lenient_issubclass instead of issubclass?

Coincidentally, I just came here to report an issue about the same error. In my case, it happens when you create a subclass of BaseModel which specifies a field of type Dict which does not specify mapping types, i.e.:

from pydantic import BaseModel
from typing import Dict, Any

class SubclassThatWorks(BaseModel):
    asdf: int
    fdsa: Dict[Any, Any]

class SubclassThatFails(BaseModel):
    asdf: int
    fdsa: Dict

with the error message being:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/validators.py in find_validators(type_, arbitrary_types_allowed)
    385         try:
--> 386             if issubclass(type_, val_type):
    387                 return validators

TypeError: issubclass() arg 1 must be a class

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
<ipython-input-5-c7b931439a60> in <module>
----> 1 class Subclass(BaseModel):
      2     asdf: int
      3     fdsa: Dict

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/main.py in __new__(mcs, name, bases, namespace)
    180                     annotation=ann_type,
    181                     class_validators=vg.get_validators(ann_name),
--> 182                     config=config,
    183                 )
    184 

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in infer(cls, name, value, annotation, class_validators, config)
    136             required=required,
    137             model_config=config,
--> 138             schema=schema,
    139         )
    140 

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in __init__(self, name, type_, class_validators, model_config, default, required, alias, schema)
    105         self.parse_json: bool = False
    106         self.shape: Shape = Shape.SINGLETON
--> 107         self.prepare()
    108 
    109     @classmethod

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in prepare(self)
    170             self.allow_none = True
    171 
--> 172         self._populate_sub_fields()
    173         self._populate_validators()
    174 

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in _populate_sub_fields(self)
    221             assert issubclass(origin, Mapping)
    222             self.key_field = self._create_sub_type(
--> 223                 self.type_.__args__[0], 'key_' + self.name, for_keys=True  # type: ignore
    224             )
    225             self.type_ = self.type_.__args__[1]  # type: ignore

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in _create_sub_type(self, type_, name, for_keys)
    235             name=name,
    236             class_validators=None if for_keys else {k: v for k, v in self.class_validators.items() if not v.whole},
--> 237             model_config=self.model_config,
    238         )
    239 

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in __init__(self, name, type_, class_validators, model_config, default, required, alias, schema)
    105         self.parse_json: bool = False
    106         self.shape: Shape = Shape.SINGLETON
--> 107         self.prepare()
    108 
    109     @classmethod

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in prepare(self)
    171 
    172         self._populate_sub_fields()
--> 173         self._populate_validators()
    174 
    175     def _populate_sub_fields(self) -> None:  # noqa: C901 (ignore complexity)

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/fields.py in _populate_validators(self)
    253                     get_validators()
    254                     if get_validators
--> 255                     else find_validators(self.type_, self.model_config.arbitrary_types_allowed)
    256                 ),
    257                 self.schema is not None and self.schema.const and constant_validator,

/scratch/miniconda3/envs/jupyter/lib/python3.7/site-packages/pydantic/validators.py in find_validators(type_, arbitrary_types_allowed)
    387                 return validators
    388         except TypeError as e:
--> 389             raise RuntimeError(f'error checking inheritance of {type_!r} (type: {display_as_type(type_)})') from e
    390 
    391     if arbitrary_types_allowed:

RuntimeError: error checking inheritance of ~KT (type: KT)

Not sure if this is because of a design decision that Dict types should have the mapping types explicitly defined or not, but the error message wasn't very clear as to what the problem was.

sounds like a different problem, could you create a new issue and perhaps even a fix?

In the meantime you should be able to use just dict instead of Dict which will have the same effect but shouldn't fail.

@samuelcolvin Do you know a workaround for my situation? The doc uses List and Optional in its examples, so I'm confused on what is causing this?

Ok, I can confirm the issue is the self-reference(children: List[CategoryModel]). Using just list or List[Any] avoids the error.

Thanks for tagging me @samuelcolvin .

I think this is then a duplicate of https://github.com/samuelcolvin/pydantic/issues/531

I still have to check/fix the JSON Schema generation when using forward refs.

For @zbarry problem, you can use dict or Dict[Any, Any]. But that's has a separate issue now.

Hit this one, I think, with very simple reproduction:

from typing import NewType

from pydantic import BaseModel

Age = NewType("Age", int)

class Person(BaseModel):
    age: Age

Similar issue when generating schema in FastAPI.

So far using class Age(int): pass as a workaround.

Removing from __future__ import annotations fixed this issue for me. However, it would be cool if there would be support for this.

I don't have from __future__ import annotations in my code btw.

@haizaar This is fixed in the current version of pydantic (v0.28), with or without the __future__ import. Or at least, I am able to run the code snippet you provided above (in python 3.7.3) without any errors.

@DrPyser I'm not sure what was the problem, maybe it was fixed recently in Pydantic or in FastAPI.

But here's a slightly modified (without from __future__ import annotations) self-contained working app:

from typing import List, Optional

from pydantic import UUID4, BaseModel

from fastapi import FastAPI


class CategoryCreationValidator(BaseModel):
    name: str = ...
    labels: List[str] = None
    description: str = ...
    parent_id: int = None


class CategorySelectionValidator(BaseModel):
    name: str = ...
    organization_id: str = ...
    labels: Optional[str] = None
    description: str = ...
    parent_id: Optional[int] = None


class CategoryUpdateValidator(BaseModel):
    name: Optional[str] = None
    organization_id: Optional[str] = None
    labels: Optional[List[str]] = None
    description: Optional[str] = None
    parent_id: Optional[int] = None


class ClusterValidator(BaseModel):
    name: str = ...
    category_id: Optional[str] = None


class CategoryModel(BaseModel):
    id: int = ...
    name: str = ...
    description: str = ...
    parent_id: int = None
    children: List["CategoryModel"]


CategoryModel.update_forward_refs()


class ClusterModel(BaseModel):
    id: UUID4 = ...
    name: str = ...
    parent_category_id: str = ...
    queries: List[str] = ...
    urls: List[str] = ...


class SuggestionValidator(BaseModel):
    queries: List[str] = ...
    urls: List[str] = ...


class QueryCreationValidator(BaseModel):
    query_text: str = ...
    cluster_id: UUID4 = ...


class QuerySelectionValidator(BaseModel):
    cluster_id: UUID4 = ...


class UrlCreationValidator(BaseModel):
    url: str = ...
    cluster_id: UUID4 = ...


class UrlSelectionValidator(BaseModel):
    cluster_id: UUID4 = ...


class CreateClusterInput(BaseModel):
    name: str = ...
    category_id: int = ...


class ClusterSelectionValidator(BaseModel):
    category_name: str = ...



app = FastAPI()


@app.post("/")
def main(
    a: CategoryCreationValidator,
    b: CategorySelectionValidator,
    c: CategoryUpdateValidator,
    d: ClusterValidator,
    e: CategoryModel,
    f: ClusterModel,
    g: SuggestionValidator,
    h: QueryCreationValidator,
    i: QuerySelectionValidator,
    j: UrlCreationValidator,
    k: UrlSelectionValidator,
    l: CreateClusterInput,
    m: ClusterSelectionValidator,
):
    return {"message": "Hello World"}

@tiangolo Yup, I think I've tested that everything now works after updating. Thanks everyone!

Great! I'll close this issue now then.

Having the same issue:

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}

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

python 3.7
pydantic==1.4.0
fastapi==0.49.0

@Casyfill I can't reproduce that error with fastapi 0.52.0 and python 3.8.

Can you try updating your fastapi version and seeing if you still have issues? If so, could you share a full self-contained example (including import statements etc.)?

Also, I'd recommend creating a new issue if there is still a bug as it likely differs enough from this issue to be worth tracking separately (and having the issue be open makes it easier to prioritize).

pydantic==1.4
fastapi==0.52.0
python 3.7.4

issue persist

Could you put together a self-contained reproducible example? If you can do that I'm happy to debug/submit a PR to fix it.

Could you put together a self-contained reproducible example? If you can do that I'm happy to debug/submit a PR to fix it.

Here: https://github.com/samuelcolvin/pydantic/issues/1298#issuecomment-607842409

Submit same trouble as @half2me

Was this page helpful?
0 / 5 - 0 ratings