add LiteralString (PEP 675) (#1053)

Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
diff --git a/typing_extensions/CHANGELOG b/typing_extensions/CHANGELOG
index 092e04a..b874ddc 100644
--- a/typing_extensions/CHANGELOG
+++ b/typing_extensions/CHANGELOG
@@ -1,5 +1,6 @@
 # Release 4.x.x
 
+- Runtime support for PEP 675 and `typing_extensions.LiteralString`.
 - Add `Never` and `assert_never`. Backport from bpo-46475.
 - `ParamSpec` args and kwargs are now equal to themselves. Backport from
   bpo-46676. Patch by Gregory Beauregard (@GBeauregard).
diff --git a/typing_extensions/README.rst b/typing_extensions/README.rst
index a83ed3c..4430d66 100644
--- a/typing_extensions/README.rst
+++ b/typing_extensions/README.rst
@@ -37,6 +37,7 @@
 
 - Experimental features
 
+  - ``LiteralString`` (see PEP 675)
   - ``@dataclass_transform()`` (see PEP 681)
   - ``NotRequired`` (see PEP 655)
   - ``Required`` (see PEP 655)
diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py
index 9111688..c4db70e 100644
--- a/typing_extensions/src/test_typing_extensions.py
+++ b/typing_extensions/src/test_typing_extensions.py
@@ -22,7 +22,7 @@
 from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard
 from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired
 from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload, final, is_typeddict
-from typing_extensions import dataclass_transform, reveal_type, Never, assert_never
+from typing_extensions import dataclass_transform, reveal_type, Never, assert_never, LiteralString
 try:
     from typing_extensions import get_type_hints
 except ImportError:
@@ -111,6 +111,11 @@
         with self.assertRaises(TypeError):
             type(self.bottom_type)()
 
+    def test_pickle(self):
+        for proto in range(pickle.HIGHEST_PROTOCOL):
+            pickled = pickle.dumps(self.bottom_type, protocol=proto)
+            self.assertIs(self.bottom_type, pickle.loads(pickled))
+
 
 class NoReturnTests(BottomTypeTestsMixin, BaseTestCase):
     bottom_type = NoReturn
@@ -1896,7 +1901,8 @@
     def test_pickle(self):
         samples = [typing.Any, typing.Union[int, str],
                    typing.Optional[str], Tuple[int, ...],
-                   typing.Callable[[str], bytes]]
+                   typing.Callable[[str], bytes],
+                   Self, LiteralString, Never]
 
         for t in samples:
             x = Annotated[t, "a"]
@@ -2290,6 +2296,67 @@
             issubclass(int, TypeGuard)
 
 
+class LiteralStringTests(BaseTestCase):
+    def test_basics(self):
+        class Foo:
+            def bar(self) -> LiteralString: ...
+            def baz(self) -> "LiteralString": ...
+
+        self.assertEqual(gth(Foo.bar), {'return': LiteralString})
+        self.assertEqual(gth(Foo.baz), {'return': LiteralString})
+
+    @skipUnless(PEP_560, "Python 3.7+ required")
+    def test_get_origin(self):
+        from typing_extensions import get_origin
+        self.assertIsNone(get_origin(LiteralString))
+
+    def test_repr(self):
+        if hasattr(typing, 'LiteralString'):
+            mod_name = 'typing'
+        else:
+            mod_name = 'typing_extensions'
+        self.assertEqual(repr(LiteralString), '{}.LiteralString'.format(mod_name))
+
+    def test_cannot_subscript(self):
+        with self.assertRaises(TypeError):
+            LiteralString[int]
+
+    def test_cannot_subclass(self):
+        with self.assertRaises(TypeError):
+            class C(type(LiteralString)):
+                pass
+        with self.assertRaises(TypeError):
+            class C(LiteralString):
+                pass
+
+    def test_cannot_init(self):
+        with self.assertRaises(TypeError):
+            LiteralString()
+        with self.assertRaises(TypeError):
+            type(LiteralString)()
+
+    def test_no_isinstance(self):
+        with self.assertRaises(TypeError):
+            isinstance(1, LiteralString)
+        with self.assertRaises(TypeError):
+            issubclass(int, LiteralString)
+
+    def test_alias(self):
+        StringTuple = Tuple[LiteralString, LiteralString]
+        class Alias:
+            def return_tuple(self) -> StringTuple:
+                return ("foo", "pep" + "675")
+
+    def test_typevar(self):
+        StrT = TypeVar("StrT", bound=LiteralString)
+        self.assertIs(StrT.__bound__, LiteralString)
+
+    def test_pickle(self):
+        for proto in range(pickle.HIGHEST_PROTOCOL):
+            pickled = pickle.dumps(LiteralString, protocol=proto)
+            self.assertIs(LiteralString, pickle.loads(pickled))
+
+
 class SelfTests(BaseTestCase):
     def test_basics(self):
         class Foo:
@@ -2331,6 +2398,11 @@
             def return_tuple(self) -> TupleSelf:
                 return (self, self)
 
+    def test_pickle(self):
+        for proto in range(pickle.HIGHEST_PROTOCOL):
+            pickled = pickle.dumps(Self, protocol=proto)
+            self.assertIs(Self, pickle.loads(pickled))
+
 
 class FinalDecoratorTests(BaseTestCase):
     def test_final_unmodified(self):
diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py
index b67efd0..d0bcc32 100644
--- a/typing_extensions/src/typing_extensions.py
+++ b/typing_extensions/src/typing_extensions.py
@@ -44,6 +44,7 @@
     'ClassVar',
     'Concatenate',
     'Final',
+    'LiteralString',
     'ParamSpec',
     'Self',
     'Type',
@@ -2155,6 +2156,56 @@
             return self._getitem(self, parameters)
 
 
+if hasattr(typing, "LiteralString"):
+    LiteralString = typing.LiteralString
+elif sys.version_info[:2] >= (3, 7):
+    @_SpecialForm
+    def LiteralString(self, params):
+        """Represents an arbitrary literal string.
+
+        Example::
+
+          from typing_extensions import LiteralString
+
+          def query(sql: LiteralString) -> ...:
+              ...
+
+          query("SELECT * FROM table")  # ok
+          query(f"SELECT * FROM {input()}")  # not ok
+
+        See PEP 675 for details.
+
+        """
+        raise TypeError(f"{self} is not subscriptable")
+else:
+    class _LiteralString(typing._FinalTypingBase, _root=True):
+        """Represents an arbitrary literal string.
+
+        Example::
+
+          from typing_extensions import LiteralString
+
+          def query(sql: LiteralString) -> ...:
+              ...
+
+          query("SELECT * FROM table")  # ok
+          query(f"SELECT * FROM {input()}")  # not ok
+
+        See PEP 675 for details.
+
+        """
+
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError(f"{self} cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError(f"{self} cannot be used with issubclass().")
+
+    LiteralString = _LiteralString(_root=True)
+
+
 if hasattr(typing, "Self"):
     Self = typing.Self
 elif sys.version_info[:2] >= (3, 7):