Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ Some rules for writing good tests:
<https://codewithoutrules.com/2016/07/31/verified-fakes/>`__

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
Expand Down
19 changes: 10 additions & 9 deletions docs/source/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ Features
~~~~~~~~

- Initial :ref:`subprocess support <subprocess>`. Add
:class:`trio.subprocess.Process`, an async wrapper around the stdlib
:class:`trio.subprocess.Process <trio.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 <https://github.com/python-trio/trio/issues/4>`__)
- You can now create an unbounded :class:`CapacityLimiter` by initializing with
Expand Down Expand Up @@ -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 <trio.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
<trio.NeedHandshakeError>`
instead. (`#735 <https://github.com/python-trio/trio/issues/735>`__)


Expand Down Expand Up @@ -384,11 +385,11 @@ Highlights
<https://daniel.haxx.se/blog/2016/11/26/https-proxy-with-curl/>`__.
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:
Expand Down Expand Up @@ -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``
Expand Down
108 changes: 50 additions & 58 deletions docs/source/reference-io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ 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
a :class:`~trio.abc.ReceiveStream` that lets you read from its
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)

Expand All @@ -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
<https://daniel.haxx.se/blog/2016/11/26/https-proxy-with-curl/>`__. 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)
Expand Down Expand Up @@ -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`
Expand All @@ -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`
Expand Down Expand Up @@ -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 <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
Expand Down Expand Up @@ -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:
Expand All @@ -676,13 +669,15 @@ overwhelming, you're not alone; you might prefer to start with
just the `frequently used ones
<https://docs.python.org/3/library/subprocess.html#frequently-used-arguments>`__.)

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.]


Expand All @@ -692,7 +687,7 @@ Running a process and waiting for it to finish
We're `working on <https://github.com/python-trio/trio/pull/791>`__
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(
Expand All @@ -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:
Expand Down Expand Up @@ -750,50 +745,47 @@ 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
platforms it spawns a background thread which continues to read from
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
Expand Down
6 changes: 6 additions & 0 deletions newsfragments/852.removal.rst
Original file line number Diff line number Diff line change
@@ -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.
35 changes: 32 additions & 3 deletions trio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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":
Expand All @@ -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:
Expand All @@ -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
4 changes: 2 additions & 2 deletions trio/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down Expand Up @@ -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
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion trio/_highlevel_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)``.
Expand Down
Loading