diff --git a/src/decorator/__init__.py b/src/decorator/__init__.py index c967f84..9beab83 100644 --- a/src/decorator/__init__.py +++ b/src/decorator/__init__.py @@ -38,6 +38,7 @@ import operator import itertools import functools +import copy from contextlib import _GeneratorContextManager from collections import namedtuple from inspect import Parameter, iscoroutinefunction, isgeneratorfunction @@ -297,6 +298,8 @@ def decorate(func, caller, extras=(), kwsyntax=False): """ sig = inspect_sig(func) if isinstance(func, functools.partial): + func = copy.copy(func) + func.__dict__ = func.__dict__.copy() func = functools.update_wrapper(func, func.func) fun: Callable[..., Any] if iscoroutinefunction(caller): diff --git a/tests/test.py b/tests/test.py index 03941bc..9b59b4c 100644 --- a/tests/test.py +++ b/tests/test.py @@ -573,6 +573,24 @@ def origin_func(x, y): out = partial_func("y") self.assertEqual(out, 'xy') + def test_decorating_partial_does_not_change_its_signature(self): + def origin_func(x, y): + return x + y + original = functools.partial(origin_func, "x") + original.custom = "preserved" + signature = inspect.signature(original) + + first = partial_before_after(original) + self.assertEqual(inspect.signature(original), signature) + self.assertFalse(hasattr(original, '__wrapped__')) + self.assertEqual(first.custom, "preserved") + + second = partial_before_after(original) + self.assertEqual(inspect.signature(first), signature) + self.assertEqual(inspect.signature(second), signature) + self.assertEqual(first("y"), 'xy') + self.assertEqual(second("z"), 'xz') + if PYVER >= (3, 14): # testing forward references in python 3.14+, see