diff --git a/docs/errors/usage_errors.md b/docs/errors/usage_errors.md index 55b9f811bd..6f57990b7a 100644 --- a/docs/errors/usage_errors.md +++ b/docs/errors/usage_errors.md @@ -900,7 +900,7 @@ except PydanticUserError as exc_info: assert exc_info.code == 'invalid_annotated_type' ``` -## `config` is unused with TypeAdapter {#type-adapter-config-unused} +## `config` is unused with `TypeAdapter` {#type-adapter-config-unused} You will get this error if you try to pass `config` to `TypeAdapter` when the type is a type that has its own config that cannot be overridden (currently this is only `BaseModel`, `TypedDict` and `dataclass`): @@ -939,4 +939,23 @@ class MyTypedDict(TypedDict): TypeAdapter(MyTypedDict) # ok ``` +## Cannot specify `model_config['extra']` with `RootModel` {#root-model-extra} + +Because `RootModel` is not capable of storing or even accepting extra fields during initialization, we raise an error +if you try to specify a value for the config setting `'extra'` when creating a subclass of `RootModel`: + +```py +from pydantic import PydanticUserError, RootModel + +try: + + class MyRootModel(RootModel): + model_config = {'extra': 'allow'} + root: int + +except PydanticUserError as exc_info: + assert exc_info.code == 'root-model-extra' +``` + + {% endraw %} diff --git a/pydantic/errors.py b/pydantic/errors.py index 71d07a2b28..ddca0e2a9a 100644 --- a/pydantic/errors.py +++ b/pydantic/errors.py @@ -56,6 +56,7 @@ 'multiple-field-serializers', 'invalid_annotated_type', 'type-adapter-config-unused', + 'root-model-extra', ] diff --git a/pydantic/root_model.py b/pydantic/root_model.py index 8efb4a180a..73143eafc9 100644 --- a/pydantic/root_model.py +++ b/pydantic/root_model.py @@ -7,6 +7,7 @@ from pydantic_core import PydanticUndefined +from . import PydanticUserError from ._internal import _repr from .main import BaseModel, _object_setattr @@ -43,6 +44,14 @@ class RootModel(BaseModel, typing.Generic[RootModelRootType]): root: RootModelRootType + def __init_subclass__(cls, **kwargs): + extra = cls.model_config.get('extra') + if extra is not None: + raise PydanticUserError( + "`RootModel` does not support setting `model_config['extra']`", code='root-model-extra' + ) + super().__init_subclass__(**kwargs) + def __init__(__pydantic_self__, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore __tracebackhide__ = True if data: diff --git a/tests/test_root_model.py b/tests/test_root_model.py index 26f1da233e..048baeddae 100644 --- a/tests/test_root_model.py +++ b/tests/test_root_model.py @@ -344,7 +344,6 @@ class B(BaseModel): assert B(root=42) != R(42) -@pytest.mark.xfail(reason='TODO: raise error for `extra` with `RootModel`') @pytest.mark.parametrize('extra_value', ['ignore', 'allow', 'forbid']) def test_extra_error(extra_value): with pytest.raises(PydanticUserError, match='extra'):