pydantic-core
pydantic-core copied to clipboard
Can't generate schema when `typing.Never` is involved
pydantic
fails to generate a schema for typing.Never
; I ran into this in a model generic over a TypeVar
whose bound involved Never
.
Error encountered
pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for typing.Never. Set
arbitrary_types_allowed=True
in the model_config to ignore this error or implement__get_pydantic_core_schema__
on your type to fully support it.
Test case
from pydantic import Field
from pydantic.dataclasses import dataclass
from typing import Generic, Never, TypeVar
T = TypeVar('T', None, Never)
@dataclass(frozen=True, kw_only=True)
class GenericConfig(Generic[T]):
foo: str
bar: str | T = Field(None)
ParsedConfig = GenericConfig[None]
ResolvedConfig = GenericConfig[Never]
Workarounds
In this particular case, removing the bounds on T
works, but is highly undesirable: one could then instantiate things like GenericConfig[int]
without type-checking error.
I attempted to provide the schema manually, but couldn't get that to work:
from pydantic_core import core_schema
from pydantic import GetPydanticSchema
from typing import Annotated, Never as _Never
Never = Annotated[
_Never,
GetPydanticSchema(
lambda tp, handler: core_schema.no_info_after_validator_function(
lambda _: False, handler(tp)
)
),
]
I'm assuming the handler(tp)
call is the problem, but I couldn't find a way to directly construct a CoreSchema
.
I don't think typing.Never
makes sense in the context of a field. It only really makes sense in the context of a function return / control flow.
@adriangb Not sure what you mean there: Never
is the bottom type, and it's generally-useful for tracking (at the type-level) situations that cannot happen (like someObj.bar
being something else than a str
).
I even believe this kind of use was intended given that:
-
Never
is semantically-equivalent toNoReturn
, but the former was added in Py3.11 to have a more-sensible name in usecases other than a function's return type; - mypy type-checks that kind of code without issue.
Note: if the field's type was T
(rather than bar: str | T
here), the type GenericConfig[Never]
would indeed be uninhabited; however, it's common to have inhabited types defined by expressions that happen to contain Never
.
I can see how Never
might appear in generic code. I think it would be straightforward for us to add a never_schema
which can never succeed validation. For example even list[Never]
is sort of reasonable to me as a typing concept; it would mean a list which cannot ever contain a value.
Going to close this as a duplicate of https://github.com/pydantic/pydantic/issues/9731. I think that issue is more likely to get views / potential support from community members.