Repository: python-trio/trio

Issue:
Fix documentation references to `socket.socket`
This is a class in the stdlib, not a function. I didn't really dig
into why/when sphinx decided to get fussy about it.

I tested this by building the docs and clicking the link on one of the
changed sites, and it took me to the right place in the Python stdlib docs


Discussion:

Resolving patch:
diff --git a/trio/_highlevel_ssl_helpers.py b/trio/_highlevel_ssl_helpers.py
--- a/trio/_highlevel_ssl_helpers.py
+++ b/trio/_highlevel_ssl_helpers.py
@@ -53,6 +53,10 @@ async def open_ssl_over_tcp_stream(
     )
     if ssl_context is None:
         ssl_context = ssl.create_default_context()
+
+        if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"):
+            ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF
+
     return trio.SSLStream(
         tcp_stream, ssl_context, server_hostname=host, https_compatible=https_compatible
     )
diff --git a/trio/_socket.py b/trio/_socket.py
--- a/trio/_socket.py
+++ b/trio/_socket.py
@@ -231,7 +231,7 @@ async def getprotobyname(name):
 
 
 def from_stdlib_socket(sock):
-    """Convert a standard library :func:`socket.socket` object into a Trio
+    """Convert a standard library :class:`socket.socket` object into a Trio
     socket object.
 
     """
@@ -271,7 +271,7 @@ def socket(
     proto=0,
     fileno=None,
 ):
-    """Create a new Trio socket, like :func:`socket.socket`.
+    """Create a new Trio socket, like :class:`socket.socket`.
 
     This function's behavior can be customized using
     :func:`set_custom_socket_factory`.
diff --git a/trio/_ssl.py b/trio/_ssl.py
--- a/trio/_ssl.py
+++ b/trio/_ssl.py
@@ -190,6 +190,16 @@
 STARTING_RECEIVE_SIZE = 16384
 
 
+def _is_eof(exc):
+    # There appears to be a bug on Python 3.10, where SSLErrors
+    # aren't properly translated into SSLEOFErrors.
+    # This stringly-typed error check is borrowed from the AnyIO
+    # project.
+    return isinstance(exc, _stdlib_ssl.SSLEOFError) or (
+        hasattr(exc, "strerror") and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror
+    )
+
+
 class NeedHandshakeError(Exception):
     """Some :class:`SSLStream` methods can't return any meaningful data until
     after the handshake. If you call them before the handshake, they raise
@@ -658,9 +668,9 @@ async def receive_some(self, max_bytes=None):
                 # For some reason, EOF before handshake sometimes raises
                 # SSLSyscallError instead of SSLEOFError (e.g. on my linux
                 # laptop, but not on appveyor). Thanks openssl.
-                if self._https_compatible and isinstance(
-                    exc.__cause__,
-                    (_stdlib_ssl.SSLEOFError, _stdlib_ssl.SSLSyscallError),
+                if self._https_compatible and (
+                    isinstance(exc.__cause__, _stdlib_ssl.SSLSyscallError)
+                    or _is_eof(exc.__cause__)
                 ):
                     await trio.lowlevel.checkpoint()
                     return b""
@@ -683,9 +693,8 @@ async def receive_some(self, max_bytes=None):
                 # BROKEN. But that's actually fine, because after getting an
                 # EOF on TLS then the only thing you can do is close the
                 # stream, and closing doesn't care about the state.
-                if self._https_compatible and isinstance(
-                    exc.__cause__, _stdlib_ssl.SSLEOFError
-                ):
+
+                if self._https_compatible and _is_eof(exc.__cause__):
                     await trio.lowlevel.checkpoint()
                     return b""
                 else:


Test patch:
diff --git a/trio/tests/test_ssl.py b/trio/tests/test_ssl.py
--- a/trio/tests/test_ssl.py
+++ b/trio/tests/test_ssl.py
@@ -20,7 +20,7 @@
 from .._core import ClosedResourceError, BrokenResourceError
 from .._highlevel_open_tcp_stream import open_tcp_stream
 from .. import socket as tsocket
-from .._ssl import SSLStream, SSLListener, NeedHandshakeError
+from .._ssl import SSLStream, SSLListener, NeedHandshakeError, _is_eof
 from .._util import ConflictDetector
 
 from .._core.tests.tutil import slow
@@ -55,6 +55,9 @@
 TRIO_TEST_1_CERT = TRIO_TEST_CA.issue_server_cert("trio-test-1.example.org")
 
 SERVER_CTX = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
+if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"):
+    SERVER_CTX.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF
+
 TRIO_TEST_1_CERT.configure_cert(SERVER_CTX)
 
 # TLS 1.3 has a lot of changes from previous versions. So we want to run tests
@@ -76,6 +79,10 @@
 @pytest.fixture(scope="module", params=client_ctx_params)
 def client_ctx(request):
     ctx = ssl.create_default_context()
+
+    if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"):
+        ctx.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF
+
     TRIO_TEST_CA.configure_trust(ctx)
     if request.param in ["default", "tls13"]:
         return ctx
@@ -1105,7 +1112,8 @@ async def test_ssl_https_compatibility_disagreement(client_ctx):
     async def receive_and_expect_error():
         with pytest.raises(BrokenResourceError) as excinfo:
             await server.receive_some(10)
-        assert isinstance(excinfo.value.__cause__, ssl.SSLEOFError)
+
+        assert _is_eof(excinfo.value.__cause__)
 
     async with _core.open_nursery() as nursery:
         nursery.start_soon(client.aclose)