Skip to content

Commit

Permalink
Fix issue not allowing validate_call decorator to be dynamically assi…
Browse files Browse the repository at this point in the history
…gned to a class method. (#8249)

Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com>
  • Loading branch information
jusexton and sydney-runkle committed Nov 29, 2023
1 parent 1146c88 commit 115322d
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 0 deletions.
5 changes: 5 additions & 0 deletions pydantic/_internal/_validate_call.py
Expand Up @@ -118,6 +118,11 @@ def __call__(self, *args: Any, **kwargs: Any) -> Any:
def __get__(self, obj: Any, objtype: type[Any] | None = None) -> ValidateCallWrapper:
"""Bind the raw function and return another ValidateCallWrapper wrapping that."""
if obj is None:
# It's possible this wrapper is dynamically applied to a class attribute not allowing
# name to be populated by __set_name__. In this case, we'll manually acquire the name
# from the function reference.
if self._name is None:
self._name = self.raw_function.__name__
try:
# Handle the case where a method is accessed as a class attribute
return objtype.__getattribute__(objtype, self._name) # type: ignore
Expand Down
12 changes: 12 additions & 0 deletions tests/test_validate_call.py
Expand Up @@ -684,6 +684,18 @@ def test(self, x: int):
assert bar.test('1') == (bar, 1)


def test_dynamic_method_decoration():
class Foo:
def bar(self, value: str) -> str:
return f'bar-{value}'

Foo.bar = validate_call(Foo.bar)
assert Foo.bar

foo = Foo()
assert foo.bar('test') == 'bar-test'


@pytest.mark.parametrize('decorator', [staticmethod, classmethod])
def test_classmethod_order_error(decorator):
name = decorator.__name__
Expand Down

0 comments on commit 115322d

Please sign in to comment.