diff --git a/docs/concepts/validators.md b/docs/concepts/validators.md index e81fe5621b..ec92c101b9 100644 --- a/docs/concepts/validators.md +++ b/docs/concepts/validators.md @@ -726,10 +726,10 @@ def init_context(value: Dict[str, Any]) -> Iterator[None]: class Model(BaseModel): my_number: int - def __init__(__pydantic_self__, **data: Any) -> None: - __pydantic_self__.__pydantic_validator__.validate_python( + def __init__(self, /, **data: Any) -> None: + self.__pydantic_validator__.validate_python( data, - self_instance=__pydantic_self__, + self_instance=self, context=_init_context_var.get(), ) diff --git a/pydantic/_internal/_generate_schema.py b/pydantic/_internal/_generate_schema.py index bb134a9233..84f8a637db 100644 --- a/pydantic/_internal/_generate_schema.py +++ b/pydantic/_internal/_generate_schema.py @@ -2187,7 +2187,7 @@ def generate_pydantic_signature( # Make sure the parameter for extra kwargs # does not have the same name as a field default_model_signature = [ - ('__pydantic_self__', Parameter.POSITIONAL_OR_KEYWORD), + ('self', Parameter.POSITIONAL_ONLY), ('data', Parameter.VAR_KEYWORD), ] if [(p.name, p.kind) for p in present_params] == default_model_signature: diff --git a/pydantic/main.py b/pydantic/main.py index 4042f59f5e..71bcd578c8 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -151,18 +151,17 @@ class BaseModel(metaclass=_model_construction.ModelMetaclass): __pydantic_complete__ = False __pydantic_root_model__ = False - def __init__(__pydantic_self__, **data: Any) -> None: # type: ignore + def __init__(self, /, **data: Any) -> None: # type: ignore """Create a new model by parsing and validating input data from keyword arguments. Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model. - `__init__` uses `__pydantic_self__` instead of the more common `self` for the first arg to - allow `self` as a field name. + `self` is explicitly positional-only to allow `self` as a field name. """ # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks __tracebackhide__ = True - __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__) + self.__pydantic_validator__.validate_python(data, self_instance=self) # The following line sets a flag that we use to determine when `__init__` gets overridden by the user __init__.__pydantic_base_init__ = True diff --git a/pydantic/mypy.py b/pydantic/mypy.py index 581a748602..eb2127e253 100644 --- a/pydantic/mypy.py +++ b/pydantic/mypy.py @@ -1187,6 +1187,16 @@ def add_method( first = [Argument(Var('_cls'), self_type, None, ARG_POS, True)] else: self_type = self_type or fill_typevars(info) + # `self` is positional *ONLY* here, but this can't be expressed + # fully in the mypy internal API. ARG_POS is the closest we can get. + # Using ARG_POS will, however, give mypy errors if a `self` field + # is present on a model: + # + # Name "self" already defined (possibly by an import) [no-redef] + # + # As a workaround, we give this argument a name that will + # never conflict. By its positional nature, this name will not + # be used or exposed to users. first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)] args = first + args diff --git a/pydantic/root_model.py b/pydantic/root_model.py index 2c58e0dcc0..9738123341 100644 --- a/pydantic/root_model.py +++ b/pydantic/root_model.py @@ -52,7 +52,7 @@ def __init_subclass__(cls, **kwargs): ) super().__init_subclass__(**kwargs) - def __init__(__pydantic_self__, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore + def __init__(self, /, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore __tracebackhide__ = True if data: if root is not PydanticUndefined: @@ -60,7 +60,7 @@ def __init__(__pydantic_self__, root: RootModelRootType = PydanticUndefined, **d '"RootModel.__init__" accepts either a single positional argument or arbitrary keyword arguments' ) root = data # type: ignore - __pydantic_self__.__pydantic_validator__.validate_python(root, self_instance=__pydantic_self__) + self.__pydantic_validator__.validate_python(root, self_instance=self) __init__.__pydantic_base_init__ = True diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index 85b0b2f4c5..500c911080 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -1297,6 +1297,14 @@ class Model(BaseModel): } +def test_no_name_conflict_in_constructor(): + class Model(BaseModel): + self: int + + m = Model(**{'__pydantic_self__': 4, 'self': 2}) + assert m.self == 2 + + def test_self_recursive(): class SubModel(BaseModel): self: int