Backport changes to the repr of `typing.Unpack` that were made in Python 3.12 (#163)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc5abe7..4e24f90 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -73,6 +73,9 @@
 - Backport the implementation of `NewType` from 3.10 (where it is implemented
   as a class rather than a function). This allows user-defined `NewType`s to be
   pickled. Patch by Alex Waygood.
+- Backport changes to the repr of `typing.Unpack` that were made in order to
+  implement [PEP 692](https://peps.python.org/pep-0692/) (backport of
+  https://github.com/python/cpython/pull/104048). Patch by Alex Waygood.
 
 # Release 4.5.0 (February 14, 2023)
 
diff --git a/README.md b/README.md
index 11434d1..0b888e9 100644
--- a/README.md
+++ b/README.md
@@ -185,6 +185,8 @@
 - `NewType` has been in the `typing` module since Python 3.5.2, but
   user-defined `NewType`s are only pickleable on Python 3.10+.
   `typing_extensions.NewType` backports this feature to all Python versions.
+- `Unpack` was added in Python 3.11, but the repr was changed in Python 3.12;
+  `typing_extensions.Unpack` has the newer repr on all versions.
 
 There are a few types whose interface was modified between different
 versions of typing. For example, `typing.Sequence` was modified to
diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py
index d12c5de..48a5e1a 100644
--- a/src/test_typing_extensions.py
+++ b/src/test_typing_extensions.py
@@ -3644,10 +3644,7 @@
 
     def test_repr(self):
         Ts = TypeVarTuple('Ts')
-        if TYPING_3_11_0:
-            self.assertEqual(repr(Unpack[Ts]), '*Ts')
-        else:
-            self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]')
+        self.assertEqual(repr(Unpack[Ts]), f'{Unpack.__module__}.Unpack[Ts]')
 
     def test_cannot_subclass_vars(self):
         with self.assertRaises(TypeError):
@@ -3769,7 +3766,10 @@
         Ts = TypeVarTuple('Ts')
 
         t = Tuple[tuple(Ts)]
-        self.assertEqual(t.__args__, (Unpack[Ts],))
+        if sys.version_info >= (3, 11):
+            self.assertEqual(t.__args__, (typing.Unpack[Ts],))
+        else:
+            self.assertEqual(t.__args__, (Unpack[Ts],))
         self.assertEqual(t.__parameters__, (Ts,))
 
     def test_pickle(self):
@@ -4035,7 +4035,7 @@
             exclude |= {
                 'Protocol', 'runtime_checkable', 'SupportsAbs', 'SupportsBytes',
                 'SupportsComplex', 'SupportsFloat', 'SupportsIndex', 'SupportsInt',
-                'SupportsRound', 'TypedDict', 'is_typeddict', 'NamedTuple',
+                'SupportsRound', 'TypedDict', 'is_typeddict', 'NamedTuple', 'Unpack',
             }
         for item in typing_extensions.__all__:
             if item not in exclude and hasattr(typing, item):
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
index b74bf13..43c6da5 100644
--- a/src/typing_extensions.py
+++ b/src/typing_extensions.py
@@ -1971,7 +1971,49 @@
         """)
 
 
-if hasattr(typing, "Unpack"):  # 3.11+
+_UNPACK_DOC = """\
+Type unpack operator.
+
+The type unpack operator takes the child types from some container type,
+such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For
+example:
+
+  # For some generic class `Foo`:
+  Foo[Unpack[tuple[int, str]]]  # Equivalent to Foo[int, str]
+
+  Ts = TypeVarTuple('Ts')
+  # Specifies that `Bar` is generic in an arbitrary number of types.
+  # (Think of `Ts` as a tuple of an arbitrary number of individual
+  #  `TypeVar`s, which the `Unpack` is 'pulling out' directly into the
+  #  `Generic[]`.)
+  class Bar(Generic[Unpack[Ts]]): ...
+  Bar[int]  # Valid
+  Bar[int, str]  # Also valid
+
+From Python 3.11, this can also be done using the `*` operator:
+
+    Foo[*tuple[int, str]]
+    class Bar(Generic[*Ts]): ...
+
+The operator can also be used along with a `TypedDict` to annotate
+`**kwargs` in a function signature. For instance:
+
+  class Movie(TypedDict):
+    name: str
+    year: int
+
+  # This function expects two keyword arguments - *name* of type `str` and
+  # *year* of type `int`.
+  def foo(**kwargs: Unpack[Movie]): ...
+
+Note that there is only some runtime checking of this operator. Not
+everything the runtime allows may be accepted by static type checkers.
+
+For more information, see PEP 646 and PEP 692.
+"""
+
+
+if sys.version_info >= (3, 12):  # PEP 692 changed the repr of Unpack[]
     Unpack = typing.Unpack
 
     def _is_unpack(obj):
@@ -1979,6 +2021,10 @@
 
 elif sys.version_info[:2] >= (3, 9):
     class _UnpackSpecialForm(typing._SpecialForm, _root=True):
+        def __init__(self, getitem):
+            super().__init__(getitem)
+            self.__doc__ = _UNPACK_DOC
+
         def __repr__(self):
             return 'typing_extensions.' + self._name
 
@@ -1987,16 +2033,6 @@
 
     @_UnpackSpecialForm
     def Unpack(self, parameters):
-        """A special typing construct to unpack a variadic type. For example:
-
-            Shape = TypeVarTuple('Shape')
-            Batch = NewType('Batch', int)
-
-            def add_batch_axis(
-                x: Array[Unpack[Shape]]
-            ) -> Array[Batch, Unpack[Shape]]: ...
-
-        """
         item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
         return _UnpackAlias(self, (item,))
 
@@ -2016,18 +2052,7 @@
                                       f'{self._name} accepts only a single type.')
             return _UnpackAlias(self, (item,))
 
-    Unpack = _UnpackForm(
-        'Unpack',
-        doc="""A special typing construct to unpack a variadic type. For example:
-
-            Shape = TypeVarTuple('Shape')
-            Batch = NewType('Batch', int)
-
-            def add_batch_axis(
-                x: Array[Unpack[Shape]]
-            ) -> Array[Batch, Unpack[Shape]]: ...
-
-        """)
+    Unpack = _UnpackForm('Unpack', doc=_UNPACK_DOC)
 
     def _is_unpack(obj):
         return isinstance(obj, _UnpackAlias)