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=Truein 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:
-
Neveris 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.