Backport recent fixes to `Protocol` from 3.12 (#218)

diff --git a/.github/workflows/third_party.yml b/.github/workflows/third_party.yml
index 4cc7422..cde11c1 100644
--- a/.github/workflows/third_party.yml
+++ b/.github/workflows/third_party.yml
@@ -265,9 +265,7 @@
     strategy:
       fail-fast: false
       matrix:
-        # TODO: add 3.7 back to this matrix when tests pass on 3.7 again
-        # (issue #213)
-        python-version: ["3.8", "3.9", "3.10", "3.11"]
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
     runs-on: ubuntu-latest
     timeout-minutes: 60
     steps:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13a9c3f..a424b72 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@
 - Fix tests on Python 3.13, which removes support for creating
   `TypedDict` classes through the keyword-argument syntax. Patch by
   Jelle Zijlstra.
+- Fix a regression introduced in v4.6.3 that meant that 
+  ``issubclass(object, typing_extensions.Protocol)`` would erroneously raise
+  ``TypeError``. Patch by Alex Waygood (backporting the CPython PR
+  https://github.com/python/cpython/pull/105239).
 - Allow `Protocol` classes to inherit from `typing_extensions.Buffer` or
   `collections.abc.Buffer`. Patch by Alex Waygood (backporting
   https://github.com/python/cpython/pull/104827, by Jelle Zijlstra).
diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py
index 5fa9c0c..37d4c5d 100644
--- a/src/test_typing_extensions.py
+++ b/src/test_typing_extensions.py
@@ -1,6 +1,7 @@
 import sys
 import os
 import abc
+import gc
 import io
 import contextlib
 import collections
@@ -1931,6 +1932,80 @@
         with self.assertRaisesRegex(TypeError, only_classes_allowed):
             issubclass(1, BadPG)
 
+    @skip_if_py312b1
+    def test_issubclass_and_isinstance_on_Protocol_itself(self):
+        class C:
+            def x(self): pass
+
+        self.assertNotIsSubclass(object, Protocol)
+        self.assertNotIsInstance(object(), Protocol)
+
+        self.assertNotIsSubclass(str, Protocol)
+        self.assertNotIsInstance('foo', Protocol)
+
+        self.assertNotIsSubclass(C, Protocol)
+        self.assertNotIsInstance(C(), Protocol)
+
+        only_classes_allowed = r"issubclass\(\) arg 1 must be a class"
+
+        with self.assertRaisesRegex(TypeError, only_classes_allowed):
+            issubclass(1, Protocol)
+        with self.assertRaisesRegex(TypeError, only_classes_allowed):
+            issubclass('foo', Protocol)
+        with self.assertRaisesRegex(TypeError, only_classes_allowed):
+            issubclass(C(), Protocol)
+
+        T = TypeVar('T')
+
+        @runtime_checkable
+        class EmptyProtocol(Protocol): pass
+
+        @runtime_checkable
+        class SupportsStartsWith(Protocol):
+            def startswith(self, x: str) -> bool: ...
+
+        @runtime_checkable
+        class SupportsX(Protocol[T]):
+            def x(self): ...
+
+        for proto in EmptyProtocol, SupportsStartsWith, SupportsX:
+            with self.subTest(proto=proto.__name__):
+                self.assertIsSubclass(proto, Protocol)
+
+        # gh-105237 / PR #105239:
+        # check that the presence of Protocol subclasses
+        # where `issubclass(X, <subclass>)` evaluates to True
+        # doesn't influence the result of `issubclass(X, Protocol)`
+
+        self.assertIsSubclass(object, EmptyProtocol)
+        self.assertIsInstance(object(), EmptyProtocol)
+        self.assertNotIsSubclass(object, Protocol)
+        self.assertNotIsInstance(object(), Protocol)
+
+        self.assertIsSubclass(str, SupportsStartsWith)
+        self.assertIsInstance('foo', SupportsStartsWith)
+        self.assertNotIsSubclass(str, Protocol)
+        self.assertNotIsInstance('foo', Protocol)
+
+        self.assertIsSubclass(C, SupportsX)
+        self.assertIsInstance(C(), SupportsX)
+        self.assertNotIsSubclass(C, Protocol)
+        self.assertNotIsInstance(C(), Protocol)
+
+    @skip_if_py312b1
+    def test_isinstance_checks_not_at_whim_of_gc(self):
+        self.addCleanup(gc.enable)
+        gc.disable()
+
+        with self.assertRaisesRegex(
+            TypeError,
+            "Protocols can only inherit from other protocols"
+        ):
+            class Foo(collections.abc.Mapping, Protocol):
+                pass
+
+        self.assertNotIsInstance([], collections.abc.Mapping)
+
     def test_protocols_issubclass_non_callable(self):
         class C:
             x = 1
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
index 5ac6dcf..135eefd 100644
--- a/src/typing_extensions.py
+++ b/src/typing_extensions.py
@@ -562,6 +562,25 @@
     class _ProtocolMeta(abc.ABCMeta):
         # This metaclass is somewhat unfortunate,
         # but is necessary for several reasons...
+        def __new__(mcls, name, bases, namespace, **kwargs):
+            if name == "Protocol" and len(bases) < 2:
+                pass
+            elif Protocol in bases:
+                for base in bases:
+                    if not (
+                        base in {object, typing.Generic}
+                        or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, [])
+                        or (
+                            isinstance(base, _ProtocolMeta)
+                            and getattr(base, "_is_protocol", False)
+                        )
+                    ):
+                        raise TypeError(
+                            f"Protocols can only inherit from other protocols, "
+                            f"got {base!r}"
+                        )
+            return super().__new__(mcls, name, bases, namespace, **kwargs)
+
         def __init__(cls, *args, **kwargs):
             super().__init__(*args, **kwargs)
             if getattr(cls, "_is_protocol", False):
@@ -573,6 +592,8 @@
                 )
 
         def __subclasscheck__(cls, other):
+            if cls is Protocol:
+                return type.__subclasscheck__(cls, other)
             if not isinstance(other, type):
                 # Same error message as for issubclass(1, int).
                 raise TypeError('issubclass() arg 1 must be a class')
@@ -594,6 +615,8 @@
         def __instancecheck__(cls, instance):
             # We need this method for situations where attributes are
             # assigned in __init__.
+            if cls is Protocol:
+                return type.__instancecheck__(cls, instance)
             if not getattr(cls, "_is_protocol", False):
                 # i.e., it's a concrete subclass of a protocol
                 return super().__instancecheck__(instance)
@@ -662,15 +685,6 @@
                 return NotImplemented
         return True
 
-    def _check_proto_bases(cls):
-        for base in cls.__bases__:
-            if not (base in (object, typing.Generic) or
-                    base.__module__ in _PROTO_ALLOWLIST and
-                    base.__name__ in _PROTO_ALLOWLIST[base.__module__] or
-                    isinstance(base, _ProtocolMeta) and base._is_protocol):
-                raise TypeError('Protocols can only inherit from other'
-                                f' protocols, got {repr(base)}')
-
     if sys.version_info >= (3, 8):
         class Protocol(typing.Generic, metaclass=_ProtocolMeta):
             __doc__ = typing.Protocol.__doc__
@@ -693,8 +707,7 @@
                 if not cls._is_protocol:
                     return
 
-                # ... otherwise check consistency of bases, and prohibit instantiation.
-                _check_proto_bases(cls)
+                # ... otherwise prohibit instantiation.
                 if cls.__init__ is Protocol.__init__:
                     cls.__init__ = _no_init
 
@@ -789,8 +802,7 @@
                 if not cls._is_protocol:
                     return
 
-                # Check consistency of bases.
-                _check_proto_bases(cls)
+                # Prohibit instantiation
                 if cls.__init__ is Protocol.__init__:
                     cls.__init__ = _no_init