diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index 6d2a61a437..1490d1b03d 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -254,7 +254,7 @@ Some rules for writing good tests: `__ Most major features have both real tests and tests using fakes or - stubs. For example, :class:`~trio.ssl.SSLStream` has some tests that + stubs. For example, :class:`~trio.SSLStream` has some tests that use Trio to make a real socket connection to real SSL server implemented using blocking I/O, because it sure would be embarrassing if that didn't work. And then there are also a bunch of diff --git a/docs/source/history.rst b/docs/source/history.rst index cc5bfa6d3b..356293eebb 100644 --- a/docs/source/history.rst +++ b/docs/source/history.rst @@ -12,9 +12,9 @@ Features ~~~~~~~~ - Initial :ref:`subprocess support `. Add - :class:`trio.subprocess.Process`, an async wrapper around the stdlib + :class:`trio.subprocess.Process `, an async wrapper around the stdlib :class:`subprocess.Popen` class, which permits spawning subprocesses and - communicating with them over standard Trio streams. :mod:`trio.subprocess` + communicating with them over standard Trio streams. ``trio.subprocess`` also reexports all the stdlib :mod:`subprocess` exceptions and constants for convenience. (`#4 `__) - You can now create an unbounded :class:`CapacityLimiter` by initializing with @@ -49,12 +49,13 @@ Deprecations and Removals Miscellaneous internal changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- There are a number of methods on :class:`trio.ssl.SSLStream` that report - information about the negotiated TLS connection, like +- There are a number of methods on :class:`trio.ssl.SSLStream ` + that report information about the negotiated TLS connection, like ``selected_alpn_protocol``, and thus cannot succeed until after the handshake has been performed. Previously, we returned None from these methods, like the stdlib :mod:`ssl` module does, but this is confusing, because that can also - be a valid return value. Now we raise :exc:`trio.ssl.NeedHandshakeError` + be a valid return value. Now we raise :exc:`trio.ssl.NeedHandshakeError + ` instead. (`#735 `__) @@ -384,11 +385,11 @@ Highlights `__. See: :func:`trio.open_ssl_over_tcp_stream`, :func:`trio.serve_ssl_over_tcp`, - :func:`trio.open_ssl_over_tcp_listeners`, and :mod:`trio.ssl`. + :func:`trio.open_ssl_over_tcp_listeners`, and ``trio.ssl``. - Interesting fact: the test suite for :mod:`trio.ssl` has so far + Interesting fact: the test suite for ``trio.ssl`` has so far found bugs in CPython's ssl module, PyPy's ssl module, PyOpenSSL, - and OpenSSL. (:mod:`trio.ssl` doesn't use PyOpenSSL.) Trio's test + and OpenSSL. (``trio.ssl`` doesn't use PyOpenSSL.) Trio's test suite is fairly thorough. * You know thread-local storage? Well, Trio now has an equivalent: @@ -436,7 +437,7 @@ that worked on 0.1.0): * When a socket ``sendall`` call was cancelled, it used to attach some metadata to the exception reporting how much data was actually sent. It no longer does this, because in common configurations like an - :class:`~trio.ssl.SSLStream` wrapped around a + :class:`~trio.SSLStream` wrapped around a :class:`~trio.SocketStream` it becomes ambiguous which "level" the partial metadata applies to, leading to confusion and bugs. There is no longer any way to tell how much data was sent after a ``sendall`` diff --git a/docs/source/reference-io.rst b/docs/source/reference-io.rst index bb0be79fa9..b42d9d04c2 100644 --- a/docs/source/reference-io.rst +++ b/docs/source/reference-io.rst @@ -18,11 +18,11 @@ create complex transport configurations. Here's some examples: * :class:`trio.SocketStream` wraps a raw socket (like a TCP connection over the network), and converts it to the standard stream interface. -* :class:`trio.ssl.SSLStream` is a "stream adapter" that can take any +* :class:`trio.SSLStream` is a "stream adapter" that can take any object that implements the :class:`trio.abc.Stream` interface, and convert it into an encrypted stream. In trio the standard way to speak SSL over the network is to wrap an - :class:`~trio.ssl.SSLStream` around a :class:`~trio.SocketStream`. + :class:`~trio.SSLStream` around a :class:`~trio.SocketStream`. * If you spawn a :ref:`subprocess`, you can get a :class:`~trio.abc.SendStream` that lets you write to its stdin, and @@ -30,9 +30,9 @@ create complex transport configurations. Here's some examples: stdout. If for some reason you wanted to speak SSL to a subprocess, you could use a :class:`StapledStream` to combine its stdin/stdout into a single bidirectional :class:`~trio.abc.Stream`, and then wrap - that in an :class:`~trio.ssl.SSLStream`:: + that in an :class:`~trio.SSLStream`:: - ssl_context = trio.ssl.create_default_context() + ssl_context = ssl.create_default_context() ssl_context.check_hostname = False s = SSLStream(StapledStream(process.stdin, process.stdout), ssl_context) @@ -41,8 +41,8 @@ create complex transport configurations. Here's some examples: HTTPS. So you end up having to do `SSL-on-top-of-SSL `__. In trio this is trivial – just wrap your first - :class:`~trio.ssl.SSLStream` in a second - :class:`~trio.ssl.SSLStream`:: + :class:`~trio.SSLStream` in a second + :class:`~trio.SSLStream`:: # Get a raw SocketStream connection to the proxy: s0 = await open_tcp_stream("proxy", 443) @@ -104,7 +104,7 @@ Abstract base classes - :class:`SendStream`, :class:`ReceiveStream` - - - - :class:`~trio.ssl.SSLStream` + - :class:`~trio.SSLStream` * - :class:`HalfCloseableStream` - :class:`Stream` - :meth:`~HalfCloseableStream.send_eof` @@ -114,7 +114,7 @@ Abstract base classes - :class:`AsyncResource` - :meth:`~Listener.accept` - - - :class:`~trio.SocketListener`, :class:`~trio.ssl.SSLListener` + - :class:`~trio.SocketListener`, :class:`~trio.SSLListener` * - :class:`SendChannel` - :class:`AsyncResource` - :meth:`~SendChannel.send`, :meth:`~SendChannel.send_nowait` @@ -220,17 +220,18 @@ abstraction. SSL / TLS support ~~~~~~~~~~~~~~~~~ -.. module:: trio.ssl +Trio provides SSL/TLS support based on the standard library :mod:`ssl` +module. Trio's :class:`SSLStream` and :class:`SSLListener` take their +configuration from a :class:`ssl.SSLContext`, which you can create +using :func:`ssl.create_default_context` and customize using the +other constants and functions in the :mod:`ssl` module. -The :mod:`trio.ssl` module implements SSL/TLS support for Trio, using -the standard library :mod:`ssl` module. It re-exports most of -:mod:`ssl`\´s API, with the notable exception of -:class:`ssl.SSLContext`, which has unsafe defaults; if you really want -to use :class:`ssl.SSLContext` you can import it from :mod:`ssl`, but -normally you should create your contexts using -:func:`trio.ssl.create_default_context `. +.. warning:: Avoid instantiating :class:`ssl.SSLContext` directly. + A newly constructed :class:`~ssl.SSLContext` has less secure + defaults than one returned by :func:`ssl.create_default_context`, + dramatically so before Python 3.6. -Instead of using :meth:`ssl.SSLContext.wrap_socket`, though, you +Instead of using :meth:`ssl.SSLContext.wrap_socket`, you create a :class:`SSLStream`: .. autoclass:: SSLStream @@ -643,24 +644,16 @@ Asynchronous file objects The underlying synchronous file object. -.. module:: trio.subprocess .. _subprocess: -Spawning subprocesses with :mod:`trio.subprocess` -------------------------------------------------- +Spawning subprocesses +--------------------- -The :mod:`trio.subprocess` module provides support for spawning -other programs, communicating with them via pipes, sending them signals, -and waiting for them to exit. Its interface is based on the -:mod:`subprocess` module in the standard library; differences -are noted below. - -The constants and exceptions from the standard :mod:`subprocess` -module are re-exported by :mod:`trio.subprocess` unchanged. -So, if you like, you can say ``from trio import subprocess`` -and continue referring to ``subprocess.PIPE``, -:exc:`subprocess.CalledProcessError`, and so on, in the same -way you would in synchronous code. +Trio provides support for spawning other programs as subprocesses, +communicating with them via pipes, sending them signals, and waiting +for them to exit. Currently this interface consists of the +:class:`trio.Process` class, which is modelled after :class:`subprocess.Popen` +in the standard library. .. _subprocess-options: @@ -676,13 +669,15 @@ overwhelming, you're not alone; you might prefer to start with just the `frequently used ones `__.) -Trio makes use of the :mod:`subprocess` module's logic for spawning processes, -so almost all of these options can be used with their same semantics when -starting subprocesses under Trio. The exceptions are ``encoding``, ``errors``, +Trio makes use of the :mod:`subprocess` module's logic for spawning +processes, so almost all of these options can be used with their same +semantics when starting subprocesses under Trio. (You may need to +``import subprocess`` in order to access constants such as ``PIPE`` or +``DEVNULL``.) The exceptions are ``encoding``, ``errors``, ``universal_newlines`` (and its 3.7+ alias ``text``), and ``bufsize``; -Trio always uses unbuffered byte streams for communicating with a process, -so these options don't make sense. Text I/O should use a layer -on top of the raw byte streams, just as it does with sockets. +Trio always uses unbuffered byte streams for communicating with a +process, so these options don't make sense. Text I/O should use a +layer on top of the raw byte streams, just as it does with sockets. [This layer does not yet exist, but is in the works.] @@ -692,7 +687,7 @@ Running a process and waiting for it to finish We're `working on `__ figuring out the best API for common higher-level subprocess operations. In the meantime, you can implement something like the standard library -:func:`subprocess.run` in terms of :class:`trio.subprocess.Process` +:func:`subprocess.run` in terms of :class:`trio.Process` as follows:: async def run( @@ -706,7 +701,7 @@ as follows:: stdout_chunks = [] stderr_chunks = [] - async with trio.subprocess.Process(command, **options) as proc: + async with trio.Process(command, **options) as proc: async def feed_input(): async with proc.stdin: @@ -750,27 +745,24 @@ Interacting with a process as it runs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can spawn a subprocess by creating an instance of -:class:`trio.subprocess.Process` and then interact with it using its -:attr:`~trio.subprocess.Process.stdin`, -:attr:`~trio.subprocess.Process.stdout`, and/or -:attr:`~trio.subprocess.Process.stderr` streams. +:class:`trio.Process` and then interact with it using its +:attr:`~trio.Process.stdin`, +:attr:`~trio.Process.stdout`, and/or +:attr:`~trio.Process.stderr` streams. -.. autoclass:: trio.subprocess.Process +.. autoclass:: trio.Process :members: -Differences from the standard library -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Differences from :class:`subprocess.Popen` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * All arguments to the constructor of - :class:`~trio.subprocess.Process`, except the command to run, must be + :class:`~trio.Process`, except the command to run, must be passed using keywords. -* :func:`~subprocess.call`, :func:`~subprocess.check_call`, and - :func:`~subprocess.check_output` are not provided. - * :meth:`~subprocess.Popen.communicate` is not provided as a method on - :class:`~trio.subprocess.Process` objects; use a higher-level + :class:`~trio.Process` objects; use a higher-level function instead, or write the loop yourself if you have unusual needs. :meth:`~subprocess.Popen.communicate` has quite unusual cancellation behavior in the standard library (on some @@ -778,22 +770,22 @@ Differences from the standard library the child process even after the timeout has expired) and we wanted to provide an interface with fewer surprises. -* :meth:`~trio.subprocess.Process.wait` is an async function that does +* :meth:`~trio.Process.wait` is an async function that does not take a ``timeout`` argument; combine it with :func:`~trio.fail_after` if you want a timeout. * Text I/O is not supported: you may not use the - :class:`~trio.subprocess.Process` constructor arguments + :class:`~trio.Process` constructor arguments ``universal_newlines`` (or its 3.7+ alias ``text``), ``encoding``, or ``errors``. -* :attr:`~trio.subprocess.Process.stdin` is a :class:`~trio.abc.SendStream` and - :attr:`~trio.subprocess.Process.stdout` and :attr:`~trio.subprocess.Process.stderr` +* :attr:`~trio.Process.stdin` is a :class:`~trio.abc.SendStream` and + :attr:`~trio.Process.stdout` and :attr:`~trio.Process.stderr` are :class:`~trio.abc.ReceiveStream`\s, rather than file objects. The - :class:`~trio.subprocess.Process` constructor argument ``bufsize`` is + :class:`~trio.Process` constructor argument ``bufsize`` is not supported since there would be no file object to pass it to. -* :meth:`~trio.subprocess.Process.aclose` (and thus also +* :meth:`~trio.Process.aclose` (and thus also ``__aexit__``) behave like the standard :class:`~subprocess.Popen` context manager exit (close pipes to the process, then wait for it to exit), but add additional behavior if cancelled: kill the process diff --git a/newsfragments/852.removal.rst b/newsfragments/852.removal.rst new file mode 100644 index 0000000000..86e96896f7 --- /dev/null +++ b/newsfragments/852.removal.rst @@ -0,0 +1,6 @@ +The submodules ``trio.ssl`` and ``trio.subprocess`` are now deprecated. +Their nontrivial contents (:class:`~trio.Process`, :class:`~trio.SSLStream`, +and :class:`~trio.SSLListener`) have been moved to the main :mod:`trio` +namespace. For the numerous constants, exceptions, and other helpers +that were previously reexported from the standard :mod:`ssl` and +:mod:`subprocess` modules, you should now use those modules directly. diff --git a/trio/__init__.py b/trio/__init__.py index 2cab1e5818..9aac6c1469 100644 --- a/trio/__init__.py +++ b/trio/__init__.py @@ -48,6 +48,10 @@ from ._path import Path +from ._subprocess import Process + +from ._ssl import SSLStream, SSLListener, NeedHandshakeError + from ._highlevel_serve_listeners import serve_listeners from ._highlevel_open_tcp_stream import open_tcp_stream @@ -66,12 +70,13 @@ from . import hazmat from . import socket from . import abc -from . import ssl -from . import subprocess # Not imported by default: testing if False: from . import testing +from . import _deprecated_ssl_reexports +from . import _deprecated_subprocess_reexports + _deprecate.enable_attribute_deprecations(__name__) __deprecated_attributes__ = { "BrokenStreamError": @@ -85,6 +90,27 @@ _deprecate.DeprecatedAttribute( BusyResourceError, "0.8.0", issue=620, instead=BusyResourceError ), + "ssl": + _deprecate.DeprecatedAttribute( + _deprecated_ssl_reexports, + "0.11.0", + issue=852, + instead=( + "trio.SSLStream, trio.SSLListener, trio.NeedHandshakeError, " + "and the standard library 'ssl' module (minus SSLSocket and " + "wrap_socket())" + ), + ), + "subprocess": + _deprecate.DeprecatedAttribute( + _deprecated_subprocess_reexports, + "0.11.0", + issue=852, + instead=( + "trio.Process and the constants in the standard " + "library 'subprocess' module" + ), + ), } # Having the public path in .__module__ attributes is important for: @@ -98,5 +124,8 @@ fixup_module_metadata(hazmat.__name__, hazmat.__dict__) fixup_module_metadata(socket.__name__, socket.__dict__) fixup_module_metadata(abc.__name__, abc.__dict__) -fixup_module_metadata(ssl.__name__, ssl.__dict__) +fixup_module_metadata(__name__ + ".ssl", _deprecated_ssl_reexports.__dict__) +fixup_module_metadata( + __name__ + ".subprocess", _deprecated_subprocess_reexports.__dict__ +) del fixup_module_metadata diff --git a/trio/_abc.py b/trio/_abc.py index 704622cf50..c9957f1c5c 100644 --- a/trio/_abc.py +++ b/trio/_abc.py @@ -241,7 +241,7 @@ async def aclose(self): connection. This requires sending a "goodbye" message; but if the peer has become non-responsive, then our attempt to send this message might block forever, and eventually time out and be cancelled. In this case - the :meth:`aclose` method on :class:`~trio.ssl.SSLStream` will + the :meth:`aclose` method on :class:`~trio.SSLStream` will immediately close the underlying transport stream using :func:`trio.aclose_forcefully` before raising :exc:`~trio.Cancelled`. @@ -463,7 +463,7 @@ async def send_eof(self): * On an SSL/TLS-encrypted connection, the protocol doesn't provide any way to do a unidirectional shutdown without closing the connection - entirely, so :class:`~trio.ssl.SSLStream` implements + entirely, so :class:`~trio.SSLStream` implements :class:`Stream`, not :class:`HalfCloseableStream`. If an EOF has already been sent, then this method should silently diff --git a/trio/ssl.py b/trio/_deprecated_ssl_reexports.py similarity index 100% rename from trio/ssl.py rename to trio/_deprecated_ssl_reexports.py diff --git a/trio/subprocess.py b/trio/_deprecated_subprocess_reexports.py similarity index 100% rename from trio/subprocess.py rename to trio/_deprecated_subprocess_reexports.py diff --git a/trio/_highlevel_generic.py b/trio/_highlevel_generic.py index 3a0aa51814..05a03043fa 100644 --- a/trio/_highlevel_generic.py +++ b/trio/_highlevel_generic.py @@ -19,7 +19,7 @@ async def aclose_forcefully(resource): Most users won't need this, but it may be useful on cleanup paths where you can't afford to block, or if you want to close a resource and don't care about handling it gracefully. For example, if - :class:`~trio.ssl.SSLStream` encounters an error and cannot perform its + :class:`~trio.SSLStream` encounters an error and cannot perform its own graceful close, then there's no point in waiting to gracefully shut down the underlying transport either, so it calls ``await aclose_forcefully(self.transport_stream)``. diff --git a/trio/_highlevel_ssl_helpers.py b/trio/_highlevel_ssl_helpers.py index 1d7b80d708..ce78832087 100644 --- a/trio/_highlevel_ssl_helpers.py +++ b/trio/_highlevel_ssl_helpers.py @@ -1,4 +1,5 @@ import trio +import ssl from ._highlevel_open_tcp_stream import DEFAULT_DELAY @@ -33,10 +34,10 @@ async def open_ssl_over_tcp_stream( """Make a TLS-encrypted Connection to the given host and port over TCP. This is a convenience wrapper that calls :func:`open_tcp_stream` and - wraps the result in an :class:`~trio.ssl.SSLStream`. + wraps the result in an :class:`~trio.SSLStream`. This function does not perform the TLS handshake; you can do it - manually by calling :meth:`~trio.ssl.SSLStream.do_handshake`, or else + manually by calling :meth:`~trio.SSLStream.do_handshake`, or else it will be performed automatically the first time you send or receive data. @@ -45,7 +46,7 @@ async def open_ssl_over_tcp_stream( to have a TLS certificate valid for this hostname. port (int): The port to connect to. https_compatible (bool): Set this to True if you're connecting to a web - server. See :class:`~trio.ssl.SSLStream` for details. Default: + server. See :class:`~trio.SSLStream` for details. Default: False. ssl_context (:class:`~ssl.SSLContext` or None): The SSL context to use. If None (the default), :func:`ssl.create_default_context` @@ -53,7 +54,7 @@ async def open_ssl_over_tcp_stream( happy_eyeballs_delay (float): See :func:`open_tcp_stream`. Returns: - trio.ssl.SSLStream: the encrypted connection to the server. + trio.SSLStream: the encrypted connection to the server. """ tcp_stream = await trio.open_tcp_stream( @@ -62,8 +63,8 @@ async def open_ssl_over_tcp_stream( happy_eyeballs_delay=happy_eyeballs_delay, ) if ssl_context is None: - ssl_context = trio.ssl.create_default_context() - return trio.ssl.SSLStream( + ssl_context = ssl.create_default_context() + return trio.SSLStream( tcp_stream, ssl_context, server_hostname=host, @@ -82,15 +83,15 @@ async def open_ssl_over_tcp_listeners( connections. host (str, bytes, or None): The address to bind to; use ``None`` to bind to the wildcard address. See :func:`open_tcp_listeners`. - https_compatible (bool): See :class:`~trio.ssl.SSLStream` for details. - backlog (int or None): See :class:`~trio.ssl.SSLStream` for details. + https_compatible (bool): See :class:`~trio.SSLStream` for details. + backlog (int or None): See :class:`~trio.SSLStream` for details. """ tcp_listeners = await trio.open_tcp_listeners( port, host=host, backlog=backlog ) ssl_listeners = [ - trio.ssl.SSLListener( + trio.SSLListener( tcp_listener, ssl_context, https_compatible=https_compatible, @@ -144,9 +145,9 @@ async def serve_ssl_over_tcp( :func:`open_tcp_listeners`. https_compatible (bool): Set this to True if you want to use - "HTTPS-style" TLS. See :class:`~trio.ssl.SSLStream` for details. + "HTTPS-style" TLS. See :class:`~trio.SSLStream` for details. - backlog (int or None): See :class:`~trio.ssl.SSLStream` for details. + backlog (int or None): See :class:`~trio.SSLStream` for details. handler_nursery: The nursery to start handlers in, or None to use an internal nursery. Passed to :func:`serve_listeners`. diff --git a/trio/_ssl.py b/trio/_ssl.py index a8cdcb1983..2a3204bd3a 100644 --- a/trio/_ssl.py +++ b/trio/_ssl.py @@ -221,8 +221,7 @@ class SSLStream(Stream): ssl_context (~ssl.SSLContext): The :class:`~ssl.SSLContext` used for this connection. Required. Usually created by calling - :func:`trio.ssl.create_default_context() - `. + :func:`ssl.create_default_context`. server_hostname (str or None): The name of the server being connected to. Used for `SNI @@ -294,7 +293,7 @@ class SSLStream(Stream): :meth:`~ssl.SSLSocket.cipher` or :meth:`~ssl.SSLSocket.selected_alpn_protocol`. If you call them before the handshake, when they can't possibly return useful data, then - :class:`ssl.SSLObject` returns None, but :class:`trio.ssl.SSLStream` + :class:`ssl.SSLObject` returns None, but :class:`trio.SSLStream` raises :exc:`NeedHandshakeError`. This also means that if you register a SNI callback using diff --git a/trio/_subprocess.py b/trio/_subprocess.py index 7259228189..41e93dcfe7 100644 --- a/trio/_subprocess.py +++ b/trio/_subprocess.py @@ -111,7 +111,7 @@ def __init__( ): if options.get(key): raise TypeError( - "trio.subprocess.Process only supports communicating over " + "trio.Process only supports communicating over " "unbuffered byte streams; the '{}' option is not supported" .format(key) ) diff --git a/trio/_sync.py b/trio/_sync.py index ab475b9471..fc6b76e928 100644 --- a/trio/_sync.py +++ b/trio/_sync.py @@ -617,7 +617,7 @@ class StrictFIFOLock(Lock): lock in strict first-come-first-served order. An example of when this is useful is if you're implementing something like - :class:`trio.ssl.SSLStream` or an HTTP/2 server using `h2 + :class:`trio.SSLStream` or an HTTP/2 server using `h2 `__, where you have multiple concurrent tasks that are interacting with a shared state machine, and at unpredictable moments the state machine requests that a chunk of data be diff --git a/trio/tests/test_highlevel_ssl_helpers.py b/trio/tests/test_highlevel_ssl_helpers.py index 41cdb2dc25..31d69ab77d 100644 --- a/trio/tests/test_highlevel_ssl_helpers.py +++ b/trio/tests/test_highlevel_ssl_helpers.py @@ -77,7 +77,7 @@ async def test_open_ssl_over_tcp_stream_and_everything_else(): 80, ssl_context=CLIENT_CTX, ) - assert isinstance(stream, trio.ssl.SSLStream) + assert isinstance(stream, trio.SSLStream) assert stream.server_hostname == "trio-test-1.example.org" await stream.send_all(b"x") assert await stream.receive_some(1) == b"x" @@ -104,7 +104,7 @@ async def test_open_ssl_over_tcp_listeners(): 0, SERVER_CTX, host="127.0.0.1" ) async with listener: - assert isinstance(listener, trio.ssl.SSLListener) + assert isinstance(listener, trio.SSLListener) tl = listener.transport_listener assert isinstance(tl, trio.SocketListener) assert tl.socket.getsockname()[0] == "127.0.0.1" diff --git a/trio/tests/test_ssl.py b/trio/tests/test_ssl.py index e193691510..55ac8fea36 100644 --- a/trio/tests/test_ssl.py +++ b/trio/tests/test_ssl.py @@ -2,7 +2,7 @@ import threading import socket as stdlib_socket -import ssl as stdlib_ssl +import ssl from contextlib import contextmanager from functools import partial @@ -16,8 +16,8 @@ from .._highlevel_generic import aclose_forcefully from .._core import ClosedResourceError, BrokenResourceError from .._highlevel_open_tcp_stream import open_tcp_stream -from .. import ssl as tssl from .. import socket as tsocket +from .._ssl import SSLStream, SSLListener, NeedHandshakeError from .._util import ConflictDetector from .._core.tests.tutil import slow @@ -51,10 +51,10 @@ TRIO_TEST_CA = trustme.CA() TRIO_TEST_1_CERT = TRIO_TEST_CA.issue_server_cert("trio-test-1.example.org") -SERVER_CTX = stdlib_ssl.create_default_context(stdlib_ssl.Purpose.CLIENT_AUTH) +SERVER_CTX = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) TRIO_TEST_1_CERT.configure_cert(SERVER_CTX) -CLIENT_CTX = stdlib_ssl.create_default_context() +CLIENT_CTX = ssl.create_default_context() TRIO_TEST_CA.configure_trust(CLIENT_CTX) @@ -75,7 +75,7 @@ def ssl_echo_serve_sync(sock, *, expect_fail=False): # Under unclear conditions, CPython sometimes raises # SSLWantWriteError here. This is a bug (bpo-32219), but it's # not our bug, so ignore it. - exceptions += (stdlib_ssl.SSLWantWriteError,) + exceptions += (ssl.SSLWantWriteError,) try: wrapped.unwrap() except exceptions: @@ -119,7 +119,7 @@ async def ssl_echo_server_raw(**kwargs): async def ssl_echo_server(**kwargs): async with ssl_echo_server_raw(**kwargs) as sock: await yield_( - tssl.SSLStream( + SSLStream( sock, CLIENT_CTX, server_hostname="trio-test-1.example.org" ) ) @@ -303,7 +303,7 @@ async def test_PyOpenSSLEchoStream_gives_resource_busy_errors(): @contextmanager def virtual_ssl_echo_server(**kwargs): fakesock = PyOpenSSLEchoStream(**kwargs) - yield tssl.SSLStream( + yield SSLStream( fakesock, CLIENT_CTX, server_hostname="trio-test-1.example.org" ) @@ -311,13 +311,13 @@ def virtual_ssl_echo_server(**kwargs): def ssl_wrap_pair( client_transport, server_transport, *, client_kwargs={}, server_kwargs={} ): - client_ssl = tssl.SSLStream( + client_ssl = SSLStream( client_transport, CLIENT_CTX, server_hostname="trio-test-1.example.org", **client_kwargs ) - server_ssl = tssl.SSLStream( + server_ssl = SSLStream( server_transport, SERVER_CTX, server_side=True, **server_kwargs ) return client_ssl, server_ssl @@ -333,18 +333,6 @@ def ssl_lockstep_stream_pair(**kwargs): return ssl_wrap_pair(client_transport, server_transport, **kwargs) -def test_exports(): - # Just a quick check to make sure _reexport isn't totally broken - assert hasattr(tssl, "SSLError") - assert "SSLError" in tssl.__dict__.keys() - - assert hasattr(tssl, "Purpose") - assert "Purpose" in tssl.__dict__.keys() - - # Intentionally omitted - assert not hasattr(tssl, "SSLContext") - - # Simple smoke test for handshake/send/receive/shutdown talking to a # synchronous server, plus make sure that we do the bare minimum of # certificate checking (even though this is really Python's responsibility) @@ -358,31 +346,31 @@ async def test_ssl_client_basics(): # Didn't configure the CA file, should fail async with ssl_echo_server_raw(expect_fail=True) as sock: - client_ctx = stdlib_ssl.create_default_context() - s = tssl.SSLStream( + client_ctx = ssl.create_default_context() + s = SSLStream( sock, client_ctx, server_hostname="trio-test-1.example.org" ) assert not s.server_side with pytest.raises(BrokenResourceError) as excinfo: await s.send_all(b"x") - assert isinstance(excinfo.value.__cause__, tssl.SSLError) + assert isinstance(excinfo.value.__cause__, ssl.SSLError) # Trusted CA, but wrong host name async with ssl_echo_server_raw(expect_fail=True) as sock: - s = tssl.SSLStream( + s = SSLStream( sock, CLIENT_CTX, server_hostname="trio-test-2.example.org" ) assert not s.server_side with pytest.raises(BrokenResourceError) as excinfo: await s.send_all(b"x") - assert isinstance(excinfo.value.__cause__, tssl.CertificateError) + assert isinstance(excinfo.value.__cause__, ssl.CertificateError) async def test_ssl_server_basics(): a, b = stdlib_socket.socketpair() with a, b: server_sock = tsocket.from_stdlib_socket(b) - server_transport = tssl.SSLStream( + server_transport = SSLStream( SocketStream(server_sock), SERVER_CTX, server_side=True ) assert server_transport.server_side @@ -411,8 +399,8 @@ def client(): async def test_attributes(): async with ssl_echo_server_raw(expect_fail=True) as sock: good_ctx = CLIENT_CTX - bad_ctx = stdlib_ssl.create_default_context() - s = tssl.SSLStream( + bad_ctx = ssl.create_default_context() + s = SSLStream( sock, good_ctx, server_hostname="trio-test-1.example.org" ) @@ -444,7 +432,7 @@ async def test_attributes(): assert s.context is bad_ctx with pytest.raises(BrokenResourceError) as excinfo: await s.do_handshake() - assert isinstance(excinfo.value.__cause__, tssl.SSLError) + assert isinstance(excinfo.value.__cause__, ssl.SSLError) # Note: this test fails horribly if we force TLS 1.2 and trigger a @@ -699,8 +687,8 @@ class NotAStream: async def wait_send_all_might_not_block(self): record.append("ok") - ctx = stdlib_ssl.create_default_context() - s = tssl.SSLStream(NotAStream(), ctx, server_hostname="x") + ctx = ssl.create_default_context() + s = SSLStream(NotAStream(), ctx, server_hostname="x") await s.wait_send_all_might_not_block() assert record == ["ok"] @@ -928,15 +916,15 @@ def close_hook(): async def test_ssl_over_ssl(): client_0, server_0 = memory_stream_pair() - client_1 = tssl.SSLStream( + client_1 = SSLStream( client_0, CLIENT_CTX, server_hostname="trio-test-1.example.org" ) - server_1 = tssl.SSLStream(server_0, SERVER_CTX, server_side=True) + server_1 = SSLStream(server_0, SERVER_CTX, server_side=True) - client_2 = tssl.SSLStream( + client_2 = SSLStream( client_1, CLIENT_CTX, server_hostname="trio-test-1.example.org" ) - server_2 = tssl.SSLStream(server_1, SERVER_CTX, server_side=True) + server_2 = SSLStream(server_1, SERVER_CTX, server_side=True) async def client(): await client_2.send_all(b"hi") @@ -994,8 +982,8 @@ async def test_ssl_handshake_failure_during_aclose(): # the underlying transport. async with ssl_echo_server_raw(expect_fail=True) as sock: # Don't configure trust correctly - client_ctx = stdlib_ssl.create_default_context() - s = tssl.SSLStream( + client_ctx = ssl.create_default_context() + s = SSLStream( sock, client_ctx, server_hostname="trio-test-1.example.org" ) # It's a little unclear here whether aclose should swallow the error @@ -1048,7 +1036,7 @@ async def test_ssl_https_compatibility_disagreement(): async def receive_and_expect_error(): with pytest.raises(BrokenResourceError) as excinfo: await server.receive_some(10) - assert isinstance(excinfo.value.__cause__, tssl.SSLEOFError) + assert isinstance(excinfo.value.__cause__, ssl.SSLEOFError) async with _core.open_nursery() as nursery: nursery.start_soon(client.aclose) @@ -1112,10 +1100,10 @@ async def client_side(cancel_scope): async def test_selected_alpn_protocol_before_handshake(): client, server = ssl_memory_stream_pair() - with pytest.raises(tssl.NeedHandshakeError): + with pytest.raises(NeedHandshakeError): client.selected_alpn_protocol() - with pytest.raises(tssl.NeedHandshakeError): + with pytest.raises(NeedHandshakeError): server.selected_alpn_protocol() @@ -1138,10 +1126,10 @@ async def test_selected_alpn_protocol_when_not_set(): async def test_selected_npn_protocol_before_handshake(): client, server = ssl_memory_stream_pair() - with pytest.raises(tssl.NeedHandshakeError): + with pytest.raises(NeedHandshakeError): client.selected_npn_protocol() - with pytest.raises(tssl.NeedHandshakeError): + with pytest.raises(NeedHandshakeError): server.selected_npn_protocol() @@ -1164,10 +1152,10 @@ async def test_selected_npn_protocol_when_not_set(): async def test_get_channel_binding_before_handshake(): client, server = ssl_memory_stream_pair() - with pytest.raises(tssl.NeedHandshakeError): + with pytest.raises(NeedHandshakeError): client.get_channel_binding() - with pytest.raises(tssl.NeedHandshakeError): + with pytest.raises(NeedHandshakeError): server.get_channel_binding() @@ -1207,10 +1195,10 @@ async def setup(**kwargs): await listen_sock.bind(("127.0.0.1", 0)) listen_sock.listen(1) socket_listener = SocketListener(listen_sock) - ssl_listener = tssl.SSLListener(socket_listener, SERVER_CTX, **kwargs) + ssl_listener = SSLListener(socket_listener, SERVER_CTX, **kwargs) transport_client = await open_tcp_stream(*listen_sock.getsockname()) - ssl_client = tssl.SSLStream( + ssl_client = SSLStream( transport_client, CLIENT_CTX, server_hostname="trio-test-1.example.org" diff --git a/trio/tests/test_subprocess.py b/trio/tests/test_subprocess.py index e0bd0857ed..9a5880336c 100644 --- a/trio/tests/test_subprocess.py +++ b/trio/tests/test_subprocess.py @@ -2,11 +2,12 @@ import os import random import signal +import subprocess import sys import pytest from .. import ( - _core, move_on_after, fail_after, sleep, sleep_forever, subprocess + _core, move_on_after, fail_after, sleep, sleep_forever, Process ) from .._core.tests.tutil import slow from ..testing import wait_all_tasks_blocked @@ -39,14 +40,14 @@ def got_signal(proc, sig): async def test_basic(): - async with subprocess.Process(EXIT_TRUE) as proc: + async with Process(EXIT_TRUE) as proc: assert proc.returncode is None assert proc.returncode == 0 async def test_kill_when_context_cancelled(): with move_on_after(0) as scope: - async with subprocess.Process(SLEEP(10)) as proc: + async with Process(SLEEP(10)) as proc: assert proc.poll() is None # Process context entry is synchronous, so this is the # only checkpoint: @@ -63,7 +64,7 @@ async def test_kill_when_context_cancelled(): async def test_pipes(): - async with subprocess.Process( + async with Process( COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR, stdin=subprocess.PIPE, stdout=subprocess.PIPE, @@ -107,7 +108,7 @@ async def test_interactive(): # out: EOF # err: EOF - async with subprocess.Process( + async with Process( python( "idx = 0\n" "while True:\n" @@ -166,7 +167,7 @@ async def drain_one(stream, count, digit): async def test_stderr_stdout(): - async with subprocess.Process( + async with Process( COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR, stdin=subprocess.PIPE, stdout=subprocess.PIPE, @@ -188,7 +189,7 @@ async def test_stderr_stdout(): # this one hits the branch where stderr=STDOUT but stdout # is not redirected - async with subprocess.Process( + async with Process( CAT, stdin=subprocess.PIPE, stderr=subprocess.STDOUT ) as proc: assert proc.stdout is None @@ -200,7 +201,7 @@ async def test_stderr_stdout(): try: r, w = os.pipe() - async with subprocess.Process( + async with Process( COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR, stdin=subprocess.PIPE, stdout=w, @@ -220,7 +221,7 @@ async def test_stderr_stdout(): async def test_errors(): with pytest.raises(TypeError) as excinfo: - subprocess.Process(["ls"], encoding="utf-8") + Process(["ls"], encoding="utf-8") assert "unbuffered byte streams" in str(excinfo.value) assert "the 'encoding' option is not supported" in str(excinfo.value) @@ -228,7 +229,7 @@ async def test_errors(): async def test_signals(): async def test_one_signal(send_it, signum): with move_on_after(1.0) as scope: - async with subprocess.Process(SLEEP(3600)) as proc: + async with Process(SLEEP(3600)) as proc: send_it(proc) assert not scope.cancelled_caught if posix: @@ -236,8 +237,8 @@ async def test_one_signal(send_it, signum): else: assert proc.returncode != 0 - await test_one_signal(subprocess.Process.kill, SIGKILL) - await test_one_signal(subprocess.Process.terminate, SIGTERM) + await test_one_signal(Process.kill, SIGKILL) + await test_one_signal(Process.terminate, SIGTERM) if posix: await test_one_signal(lambda proc: proc.send_signal(SIGINT), SIGINT) @@ -249,7 +250,7 @@ async def test_wait_reapable_fails(): # With SIGCHLD disabled, the wait() syscall will wait for the # process to exit but then fail with ECHILD. Make sure we # support this case as the stdlib subprocess module does. - async with subprocess.Process(SLEEP(3600)) as proc: + async with Process(SLEEP(3600)) as proc: async with _core.open_nursery() as nursery: nursery.start_soon(proc.wait) await wait_all_tasks_blocked() @@ -269,10 +270,9 @@ def test_waitid_eintr(): if not wait_child_exiting.__module__.endswith("waitid"): pytest.skip("waitid only") from .._subprocess_platform.waitid import sync_wait_reapable - import subprocess as stdlib_subprocess got_alarm = False - sleeper = stdlib_subprocess.Popen(["sleep", "3600"]) + sleeper = subprocess.Popen(["sleep", "3600"]) def on_alarm(sig, frame): nonlocal got_alarm @@ -291,13 +291,3 @@ def on_alarm(sig, frame): sleeper.kill() sleeper.wait() signal.signal(signal.SIGALRM, old_sigalrm) - - -def test_all_constants_reexported(): - trio_subprocess_exports = set(dir(subprocess)) - import subprocess as stdlib_subprocess - - for name in dir(stdlib_subprocess): - if name.isupper() and name[0] != "_": - stdlib_constant = name - assert stdlib_constant in trio_subprocess_exports diff --git a/trio/tests/test_util.py b/trio/tests/test_util.py index 06bf86b66b..87475bbaad 100644 --- a/trio/tests/test_util.py +++ b/trio/tests/test_util.py @@ -70,14 +70,13 @@ def test_module_metadata_is_fixed_up(): import trio assert trio.Cancelled.__module__ == "trio" assert trio.open_cancel_scope.__module__ == "trio" - assert trio.ssl.SSLStream.__module__ == "trio.ssl" assert trio.abc.Stream.__module__ == "trio.abc" assert trio.hazmat.wait_task_rescheduled.__module__ == "trio.hazmat" import trio.testing assert trio.testing.trio_test.__module__ == "trio.testing" # Also check methods - assert trio.ssl.SSLStream.__init__.__module__ == "trio.ssl" + assert trio.hazmat.ParkingLot.__init__.__module__ == "trio.hazmat" assert trio.abc.Stream.send_all.__module__ == "trio.abc"