Skip to content

Commit

Permalink
Merge pull request #311 from njsmith/checkpoint-renames-and-deprecations
Browse files Browse the repository at this point in the history
Rename things for consistency with the new "checkpoint" terminology
  • Loading branch information
njsmith committed Sep 4, 2017
2 parents 3613ca6 + 68a14ef commit 6db2eb1
Show file tree
Hide file tree
Showing 42 changed files with 558 additions and 362 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Apache 2. See `LICENSE
- @_testing for stuff that needs tighter integration? kinda weird
that wait_all_tasks_blocked is in hazmat right now
and assert_yields stuff might make more sense in core
and assert_checkpoints stuff might make more sense in core

- make @trio_test accept clock_rate=, clock_autojump_threshold=
arguments
Expand Down
129 changes: 129 additions & 0 deletions docs/source/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,139 @@ Release history
Trio 0.2.0 (coming soon)
------------------------

[this has notes up to 3613ca6df]

Highlights
~~~~~~~~~~

Async I/O for files and filesystem operations: :func:`trio.open_file`,
:func:`trio.Path`

Complete support for TLS over arbitrary transports, including
STARTTLS, renegotiation during full-duplex usage, and `TLS-over-TLS
<https://daniel.haxx.se/blog/2016/11/26/https-proxy-with-curl/>`__:
:func:`trio.open_ssl_over_tcp_stream`,
:func:`trio.serve_ssl_over_tcp`,
:func:`trio.open_ssl_over_tcp_listeners`, and :mod:`trio.ssl`

``nursery.start`` (and rename ``spawn`` to ``start_soon``)

High-level networking interface

testing helpers

Happy eyeballs

Task- and run-local storage (gh-2)


ParkingLot is rewritten to be simpler and faster (gh-272, gh-287)

trio.socket no longer overrides socket options

breaking: getprotobyname is now async
getservbyport, getservbyname, getfqdn removed (they're buggy and obsolete)

controlled way to override hostname resolution and socket behavior in
tests (gh-170)

using yapf now

capacity limitation for threads (gh-10, gh-57, gh-156)

Reliably detect when someone tries to use an incompatible flavor of
async library (e.g. asyncio or curio) and give a helpful error message

IDNA 2008 support in trio.socket (gh-11)

rename yield point to checkpoint

fix minor face condition in IOCP thread shutdown (gh-81)

switch to using set_wakeup_fd to detect control-C on Windows (gh-42)

lots of doc improvements

restrict_keyboard_interrupt_to_checkpoints

``tiebreaker=`` argument to
:func:`trio.testing.wait_all_tasks_blocked`

:class:`StrictFIFOLock`

sendall no longer has partial result notification – this could be
misleading with stream wrappers like :class:`~trio.ssl.SSLStream`

:class:`ResourceBusyError`

:meth:`MultiError.catch` now correctly preserves ``__context__``,
despite Python's bets attempts to stop us. (gh-165)

nursery.child_tasks, nursery.parent_task, task.child_nurseries,
task.parent_nursery

Fix ``sock.accept()`` for IPv6 sockets (https://github.com/python-trio/trio/issues/164)


* ``trio.socket.SocketType`` will no longer be exposed publically in
0.3.0. Since it had no public constructor, the only thing you could
do with it was ``isinstance(obj, SocketType)``. Instead, use
:func:`trio.socket.is_trio_socket`. (https://github.com/python-trio/trio/issues/170)

* The following classes and functions have moved from :mod:`trio` to
:mod:`trio.hazmat`:

- :class:`~trio.hazmat.Task`
- :class:`~trio.hazmat.UnboundedQueue`
- :class:`~trio.hazmat.Result`
- :class:`~trio.hazmat.Error`
- :class:`~trio.hazmat.Value`
- :func:`~trio.hazmat.current_task`

deprecate most of the task and nursery APIs

Renames from https://github.com/python-trio/trio/issues/157

Note that pypy needs 5.9+ to support deprecations properly

Breaking changes and deprecations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Trio is a young project; link to issue #1

Breaking changes:

It turns out

Upcoming breaking changes without warnings (i.e., stuff that in 0.2.0
will work, but won't work in 0.3.0):

* In the next release, the ``bind`` method on Trio socket objects will
become async (XX). Unfortunately, there's no good way to warn about
this. If you switch to the new highlevel networking APIs then
then they'll insulate you from this change.

Upcoming breaking changes with warnings (i.e., stuff that in 0.2.0
will work but complain loudly, and won't work in 0.3.0):

*

``sendall``

``run_in_worker_thread`` → ``run_sync_in_worker_thread``
``nursery.spawn`` → ``nursery.start_soon``

deprecated big chunks of nursery and Task API


Features
~~~~~~~~

* New argument to :func:`trio.run`:
``restrict_keyboard_interrupt_to_checkpoints``.



Trio 0.1.0 (2017-03-10)
-----------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ If you do want to be careful, or if you have some CPU-bound code that
doesn't have enough checkpoints in it, then it's useful to know that
``await trio.sleep(0)`` is an idiomatic way to execute a checkpoint
without doing anything else, and that
:func:`trio.testing.assert_yields` can be used to test that an
:func:`trio.testing.assert_checkpoints` can be used to test that an
arbitrary block of code contains a checkpoint.


Expand Down
41 changes: 21 additions & 20 deletions docs/source/reference-hazmat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -336,31 +336,31 @@ Wait queue abstraction
Low-level checkpoint functions
------------------------------

.. autofunction:: yield_briefly
.. autofunction:: checkpoint

The next two functions are used *together* to make up a checkpoint:

.. autofunction:: yield_if_cancelled
.. autofunction:: yield_briefly_no_cancel
.. autofunction:: checkpoint_if_cancelled
.. autofunction:: cancel_shielded_checkpoint

These are commonly used in cases where you have an operation that
might-or-might-not block, and you want to implement trio's standard
checkpoint semantics. Example::

async def operation_that_maybe_blocks():
await yield_if_cancelled()
await checkpoint_if_cancelled()
try:
ret = attempt_operation()
except BlockingIOError:
# need to block and then retry, which we do below
pass
except:
# some other error, finish the checkpoint then let it propagate
await yield_briefly_no_cancel()
await cancel_shielded_checkpoint()
raise
else:
# operation succeeded, finish the checkpoint then return
await yield_briefly_no_cancel()
await cancel_shielded_checkpoint()
return ret
while True:
await wait_for_operation_to_be_ready()
Expand All @@ -376,7 +376,7 @@ This logic is a bit convoluted, but accomplishes all of the following:

* Our :ref:`cancellation semantics <cancellable-primitives>` say that
:exc:`~trio.Cancelled` should only be raised if the operation didn't
happen. Using :func:`yield_briefly_no_cancel` on the early-exit
happen. Using :func:`cancel_shielded_checkpoint` on the early-exit
branches accomplishes this.

* On the path where we do end up blocking, we don't pass through any
Expand All @@ -387,29 +387,30 @@ This logic is a bit convoluted, but accomplishes all of the following:
``wait_for_operation_to_be_ready``, by keeping the ``while True:``
loop outside of the ``except BlockingIOError:`` block.

These functions can also be useful in other situations, e.g. if you're
going to call an uncancellable operation like
:func:`trio.run_sync_in_worker_thread` or (potentially) overlapped I/O
operations on Windows, then you can call :func:`yield_if_cancelled`
first to make sure that the whole thing is a checkpoint.
These functions can also be useful in other situations. For example,
when :func:`trio.run_sync_in_worker_thread` schedules some work to run
in a worker thread, it blocks until the work is finished (so it's a
schedule point), but by default it doesn't allow cancellation. So to
make sure that the call always acts as a checkpoint, it calls
:func:`checkpoint_if_cancelled` before starting the thread.


Low-level blocking
------------------

.. autofunction:: yield_indefinitely
.. autofunction:: wait_task_rescheduled
.. autoclass:: Abort
.. autofunction:: reschedule

Here's an example lock class implemented using
:func:`yield_indefinitely` directly. This implementation has a number
of flaws, including lack of fairness, O(n) cancellation, missing error
checking, failure to insert a checkpoint on the non-blocking path,
etc. If you really want to implement your own lock, then you should
study the implementation of :class:`trio.Lock` and use
:func:`wait_task_rescheduled` directly. This implementation has a
number of flaws, including lack of fairness, O(n) cancellation,
missing error checking, failure to insert a checkpoint on the
non-blocking path, etc. If you really want to implement your own lock,
then you should study the implementation of :class:`trio.Lock` and use
:class:`ParkingLot`, which handles some of these issues for you. But
this does serve to illustrate the basic structure of the
:func:`yield_indefinitely` API::
:func:`wait_task_rescheduled` API::

class NotVeryGoodLock:
def __init__(self):
Expand All @@ -423,7 +424,7 @@ this does serve to illustrate the basic structure of the
def abort_fn(_):
self._blocked_tasks.remove(task)
return trio.hazmat.Abort.SUCCEEDED
await trio.hazmat.yield_indefinitely(abort_fn)
await trio.hazmat.wait_task_rescheduled(abort_fn)
self._held = True

def release(self):
Expand Down
5 changes: 1 addition & 4 deletions docs/source/reference-testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,5 @@ intended for testing.
Testing checkpoints
--------------------

.. autofunction:: assert_yields
:with:

.. autofunction:: assert_no_yields
.. autofunction:: assert_checkpoints
:with:
2 changes: 1 addition & 1 deletion notes-to-self/schedule-timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async def reschedule_loop(depth):
while RUNNING:
LOOPS += 1
await trio.sleep(0)
#await trio.hazmat.yield_briefly_no_cancel()
#await trio.hazmat.cancel_shielded_checkpoint()
else:
await reschedule_loop(depth - 1)

Expand Down
19 changes: 19 additions & 0 deletions trio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,25 @@
),
}

_deprecate.enable_attribute_deprecations(hazmat.__name__)

hazmat.__deprecated_attributes__ = {
"yield_briefly":
_deprecate.DeprecatedAttribute(hazmat.checkpoint, "0.2.0", issue=157),
"yield_briefly_no_cancel":
_deprecate.DeprecatedAttribute(
hazmat.cancel_shielded_checkpoint, "0.2.0", issue=157
),
"yield_if_cancelled":
_deprecate.DeprecatedAttribute(
hazmat.checkpoint_if_cancelled, "0.2.0", issue=157
),
"yield_indefinitely":
_deprecate.DeprecatedAttribute(
hazmat.wait_task_rescheduled, "0.2.0", issue=157
),
}

# Having the public path in .__module__ attributes is important for:
# - exception names in printed tracebacks
# - sphinx :show-inheritance:
Expand Down
4 changes: 2 additions & 2 deletions trio/_core/_io_epoll.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ async def _epoll_wait(self, fd, attr_name):
self._registered[fd] = EpollWaiters()
waiters = self._registered[fd]
if getattr(waiters, attr_name) is not None:
await _core.yield_briefly()
await _core.checkpoint()
raise _core.ResourceBusyError(
"another task is already reading / writing this fd"
)
Expand All @@ -113,7 +113,7 @@ def abort(_):
self._update_registrations(fd, True)
return _core.Abort.SUCCEEDED

await _core.yield_indefinitely(abort)
await _core.wait_task_rescheduled(abort)

@_public
@_hazmat
Expand Down
4 changes: 2 additions & 2 deletions trio/_core/_io_kqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def monitor_kevent(self, ident, filter):
async def wait_kevent(self, ident, filter, abort_func):
key = (ident, filter)
if key in self._registered:
await _core.yield_briefly()
await _core.checkpoint()
raise _core.ResourceBusyError(
"attempt to register multiple listeners for same "
"ident/filter pair"
Expand All @@ -111,7 +111,7 @@ def abort(raise_cancel):
del self._registered[key]
return r

return await _core.yield_indefinitely(abort)
return await _core.wait_task_rescheduled(abort)

async def _wait_common(self, fd, filter):
if not isinstance(fd, int):
Expand Down
12 changes: 6 additions & 6 deletions trio/_core/_io_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def abort(raise_cancel_):
_check(kernel32.CancelIoEx(handle, lpOverlapped))
return _core.Abort.FAILED

await _core.yield_indefinitely(abort)
await _core.wait_task_rescheduled(abort)
if lpOverlapped.Internal != 0:
if lpOverlapped.Internal == ErrorCodes.ERROR_OPERATION_ABORTED:
assert raise_cancel is not None
Expand Down Expand Up @@ -359,10 +359,10 @@ async def _wait_socket(self, which, sock):
# sockets in another thread? And on unix we don't handle this case at
# all), but hey, why not.
if type(sock) is not stdlib_socket.socket:
await _core.yield_briefly()
await _core.checkpoint()
raise TypeError("need a stdlib socket")
if sock in self._socket_waiters[which]:
await _core.yield_briefly()
await _core.checkpoint()
raise _core.ResourceBusyError(
"another task is already waiting to {} this socket"
.format(which)
Expand All @@ -373,7 +373,7 @@ def abort(_):
del self._socket_waiters[which][sock]
return _core.Abort.SUCCEEDED

await _core.yield_indefinitely(abort)
await _core.wait_task_rescheduled(abort)

@_public
@_hazmat
Expand All @@ -393,14 +393,14 @@ async def wait_socket_writable(self, sock):
# async def perform_overlapped(self, handle, submit_fn):
# # submit_fn(lpOverlapped) submits some I/O
# # it may raise an OSError with ERROR_IO_PENDING
# await _core.yield_if_cancelled()
# await _core.checkpoint_if_cancelled()
# self.register_with_iocp(handle)
# lpOverlapped = ffi.new("LPOVERLAPPED")
# try:
# submit_fn(lpOverlapped)
# except OSError as exc:
# if exc.winerror != Error.ERROR_IO_PENDING:
# await _core.yield_briefly_no_cancel()
# await _core.cancel_shielded_checkpoint()
# raise
# await self.wait_overlapped(handle, lpOverlapped)
# return lpOverlapped
2 changes: 1 addition & 1 deletion trio/_core/_parking_lot.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ async def park(self):
"""
task = _core.current_task()
self._parked[task] = None
await _core.yield_indefinitely(self._abort_func_for(task))
await _core.wait_task_rescheduled(self._abort_func_for(task))

def _pop_several(self, count):
for _ in range(min(count, len(self._parked))):
Expand Down
Loading

0 comments on commit 6db2eb1

Please sign in to comment.