diff --git a/pydantic/_internal/_validate_call.py b/pydantic/_internal/_validate_call.py index 46bccb510a..bd5f80a1b9 100644 --- a/pydantic/_internal/_validate_call.py +++ b/pydantic/_internal/_validate_call.py @@ -108,6 +108,11 @@ def __get__(self, obj: Any, objtype: type[Any] | None = None) -> ValidateCallWra bound_function = self.raw_function.__get__(obj, objtype) result = self.__class__(bound_function, self._config, self._validate_return) + + # skip binding to instance when obj or objtype has __slots__ attribute + if hasattr(obj, '__slots__') or hasattr(objtype, '__slots__'): + return result + if self._name is not None: if obj is not None: object.__setattr__(obj, self._name, result) @@ -120,3 +125,6 @@ def __set_name__(self, owner: Any, name: str) -> None: def __repr__(self) -> str: return f'ValidateCallWrapper({self.raw_function})' + + def __eq__(self, other): + return self.raw_function == other.raw_function diff --git a/tests/test_validate_call.py b/tests/test_validate_call.py index 3f0209b87f..df6e80ceb9 100644 --- a/tests/test_validate_call.py +++ b/tests/test_validate_call.py @@ -725,3 +725,32 @@ async def foo(a: Any) -> int: 'input': 'x', } ] + + +def test_validate_call_with_slots() -> None: + class ClassWithSlots: + __slots__ = {} + + @validate_call(validate_return=True) + def some_instance_method(self, x: str) -> str: + return x + + @classmethod + @validate_call(validate_return=True) + def some_class_method(cls, x: str) -> str: + return x + + @staticmethod + @validate_call(validate_return=True) + def some_static_method(x: str) -> str: + return x + + c = ClassWithSlots() + assert c.some_instance_method(x='potato') == 'potato' + assert c.some_class_method(x='pepper') == 'pepper' + assert c.some_static_method(x='onion') == 'onion' + + # verify that equality still holds for instance methods + assert c.some_instance_method == c.some_instance_method + assert c.some_class_method == c.some_class_method + assert c.some_static_method == c.some_static_method