Repository: python/cpython
Pull request: gh-107409: set `__wrapped__` attribute in `reprlib.recursive_repr`
fixes #107409

<!-- gh-issue-number: gh-107409 -->
* Issue: gh-107409
<!-- /gh-issue-number -->


Problem statement:
`reprlib.recursive_repr` is not setting `.__wrapped__` attribute
# Bug report
There is no way to get original actual `__repr__` function after applying `@recursive_repr` decorator

```py
import reprlib
class X:
    @reprlib.recursive_repr()
    def __repr__(self) -> str:
        return f'X({self.__dict__})'

# how to get initial X.__repr__ back?
# this doesn't work:
X.__repr__.__wrapped__ # AttributeError: 'function' object has no attribute '__wrapped__'
# this is awful:
X.__repr__.__closure__[2].cell_contents
```

# Your environment
- CPython versions tested on: 3.11, 3.13.0a0


<!-- gh-linked-prs -->
### Linked PRs
* gh-107410
<!-- /gh-linked-prs -->

Hints from issue discussion:

Commit message: remove unnecessary empty lines

Patch to review:
diff --git a/Lib/reprlib.py b/Lib/reprlib.py
index a92b3e3dbb613a..840dd0e20132b1 100644
--- a/Lib/reprlib.py
+++ b/Lib/reprlib.py
@@ -29,6 +29,7 @@ def wrapper(self):
         wrapper.__name__ = getattr(user_function, '__name__')
         wrapper.__qualname__ = getattr(user_function, '__qualname__')
         wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
+        wrapper.__wrapped__ = user_function
         return wrapper
 
     return decorating_function
diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py
index e7216d427200c1..502287b620d066 100644
--- a/Lib/test/test_reprlib.py
+++ b/Lib/test/test_reprlib.py
@@ -765,5 +765,14 @@ def test_assigned_attributes(self):
         for name in assigned:
             self.assertIs(getattr(wrapper, name), getattr(wrapped, name))
 
+    def test__wrapped__(self):
+        class X:
+            def __repr__(self):
+                return 'X()'
+            f = __repr__ # save reference to check it later
+            __repr__ = recursive_repr()(__repr__)
+
+        self.assertIs(X.f, X.__repr__.__wrapped__)
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2023-07-29-02-36-50.gh-issue-107409.HG27Nu.rst b/Misc/NEWS.d/next/Library/2023-07-29-02-36-50.gh-issue-107409.HG27Nu.rst
new file mode 100644
index 00000000000000..e4503104edd7dd
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-07-29-02-36-50.gh-issue-107409.HG27Nu.rst
@@ -0,0 +1 @@
+Set `__wrapped__` attribute in `reprlib.recursive_repr`.


Code review comments:
diff_hunk: @@ -0,0 +1 @@
+Set `__wrapped__` attribute in `reprlib.recursive_repr`.
path: Misc/NEWS.d/next/Library/2023-07-29-02-36-50.gh-issue-107409.HG27Nu.rst
text: @user1:
```suggestion
Set :attr:`!__wrapped__` attribute in :func:`reprlib.recursive_repr`.
```