Skip to content

Commit

Permalink
Merge pull request #296 from njsmith/deprecations
Browse files Browse the repository at this point in the history
Add deprecation framework, and do some deprecations
  • Loading branch information
njsmith authored Aug 19, 2017
2 parents 19cd626 + f71aba7 commit 1cdb8ea
Show file tree
Hide file tree
Showing 17 changed files with 347 additions and 102 deletions.
7 changes: 7 additions & 0 deletions docs/source/_static/hackrtd.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ pre {
line-height: normal !important;
}

/* Make .. deprecation:: blocks visible
* (by default they're entirely unstyled)
*/
.deprecated {
background-color: #ffe13b;
}

/* Add a snakey triskelion ornament to <hr>
* https://stackoverflow.com/questions/8862344/css-hr-with-ornament/18541258#18541258
* but only do it to <hr>s in the content box, b/c the RTD popup control panel
Expand Down
21 changes: 12 additions & 9 deletions docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,8 @@ In acknowledgment of this reality, Trio provides two useful utilities
for working with real, operating-system level,
:mod:`threading`\-module-style threads. First, if you're in Trio but
need to push some blocking I/O into a thread, there's
:func:`run_in_worker_thread`. And if you're in a thread and need to
communicate back with trio, there's the closely related
:func:`run_sync_in_worker_thread`. And if you're in a thread and need
to communicate back with trio, there's the closely related
:func:`current_run_in_trio_thread` and
:func:`current_await_in_trio_thread`.

Expand All @@ -1409,7 +1409,7 @@ are spawned and the system gets overloaded and crashes. Instead, the N
threads start executing the first N jobs, while the other
(100,000 - N) jobs sit in a queue and wait their turn. Which is
generally what you want, and this is how
:func:`trio.run_in_worker_thread` works by default.
:func:`trio.run_sync_in_worker_thread` works by default.

The downside of this kind of thread pool is that sometimes, you need
more sophisticated logic for controlling how many threads are run at
Expand Down Expand Up @@ -1456,7 +1456,7 @@ re-using threads, but has no admission control policy: if you give it
responsible for providing the policy to make sure that this doesn't
happen – but since it *only* has to worry about policy, it can be much
simpler. In fact, all there is to it is the ``limiter=`` argument
passed to :func:`run_in_worker_thread`. This defaults to a global
passed to :func:`run_sync_in_worker_thread`. This defaults to a global
:class:`CapacityLimiter` object, which gives us the classic fixed-size
thread pool behavior. (See
:func:`current_default_worker_thread_limiter`.) But if you want to use
Expand Down Expand Up @@ -1510,15 +1510,15 @@ time::


async def run_in_worker_thread_for_user(user_id, async_fn, *args, **kwargs):
# *args belong to async_fn; **kwargs belong to run_in_worker_thread
# *args belong to async_fn; **kwargs belong to run_sync_in_worker_thread
kwargs["limiter"] = get_user_limiter(user_id)
return await trio.run_in_worker_thread(asycn_fn, *args, **kwargs)
return await trio.run_sync_in_worker_thread(asycn_fn, *args, **kwargs)


Putting blocking I/O into worker threads
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. autofunction:: run_in_worker_thread
.. autofunction:: run_sync_in_worker_thread

.. autofunction:: current_default_worker_thread_limiter

Expand Down Expand Up @@ -1665,8 +1665,8 @@ The tutorial has a :ref:`fully-worked example
trio's internal scheduling decisions.


Exceptions
----------
Exceptions and warnings
-----------------------

.. autoexception:: Cancelled

Expand All @@ -1679,3 +1679,6 @@ Exceptions
.. autoexception:: RunFinishedError

.. autoexception:: TrioInternalError

.. autoexception:: TrioDeprecationWarning
:show-inheritance:
2 changes: 1 addition & 1 deletion docs/source/reference-hazmat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ This logic is a bit convoluted, but accomplishes all of the following:

These functions can also be useful in other situations, e.g. if you're
going to call an uncancellable operation like
:func:`trio.run_in_worker_thread` or (potentially) overlapped I/O
: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.

Expand Down
5 changes: 4 additions & 1 deletion docs/source/reference-io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,9 @@ Socket objects
.. method:: sendall(data, flags=0)
:async:

.. deprecated:: 0.2.0
Use :class:`trio.SocketStream` and its ``send_all`` method instead.

Send the data to the socket, blocking until all of it has been
accepted by the operating system.

Expand Down Expand Up @@ -492,7 +495,7 @@ To understand why, you need to know two things.
First, right now no mainstream operating system offers a generic,
reliable, native API for async file for filesystem operations, so we
have to fake it by using threads (specifically,
:func:`run_in_worker_thread`). This is cheap but isn't free: on a
:func:`run_sync_in_worker_thread`). This is cheap but isn't free: on a
typical PC, dispatching to a worker thread adds something like ~100 µs
of overhead to each operation. ("µs" is pronounced "microseconds", and
there are 1,000,000 µs in a second. Note that all the numbers here are
Expand Down
3 changes: 3 additions & 0 deletions trio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
from ._highlevel_ssl_helpers import *
__all__ += _highlevel_ssl_helpers.__all__

from ._deprecate import *
__all__ += _deprecate.__all__

# Imported by default
from . import socket
from . import abc
Expand Down
2 changes: 1 addition & 1 deletion trio/_core/_traps.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def abort_func(raise_cancel):
At that point there are again two possibilities. You can simply ignore
the cancellation altogether: wait for the operation to complete and
then reschedule and continue as normal. (For example, this is what
:func:`trio.run_in_worker_thread` does if cancellation is disabled.)
:func:`trio.run_sync_in_worker_thread` does if cancellation is disabled.)
The other possibility is that the ``abort_func`` does succeed in
cancelling the operation, but for some reason isn't able to report that
right away. (Example: on Windows, it's possible to request that an
Expand Down
73 changes: 73 additions & 0 deletions trio/_deprecate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from functools import wraps, partial
import warnings

__all__ = ["TrioDeprecationWarning"]


# We want our warnings to be visible by default (at least for now), but we
# also want it to be possible to override that using the -W switch. AFAICT
# this means we cannot inherit from DeprecationWarning, because the only way
# to make it visible by default then would be to add our own filter at import
# time, but that would override -W switches...
class TrioDeprecationWarning(FutureWarning):
"""Warning emitted if you use deprecated Trio functionality.
As a young project, Trio is currently quite aggressive about deprecating
and/or removing functionality that we realize was a bad idea. If you use
Trio, you should subscribe to `issue #1
<https://github.com/python-trio/trio/issues/1>`__ to get information about
upcoming deprecations and other backwards compatibility breaking changes.
Despite the name, this class currently inherits from
:class:`FutureWarning`, not :class:`DeprecationWarning`, because while
we're in young-and-aggressive mode we want these warnings to be visible by
default. You can hide them by installing a filter or with the ``-W``
switch: see the :mod:`warnings` documentation for details.
"""


def _stringify(thing):
if hasattr(thing, "__module__") and hasattr(thing, "__qualname__"):
return "{}.{}".format(thing.__module__, thing.__qualname__)
return str(thing)


def warn_deprecated(thing, *, version, alternative, stacklevel=2):
stacklevel += 1
msg = "{} is deprecated since Trio {}".format(_stringify(thing), version)
if alternative is not None:
msg = "{}; use {} instead".format(msg, _stringify(alternative))
warnings.warn(TrioDeprecationWarning(msg), stacklevel=stacklevel)


# deprecated(version=..., alternative=...)
def deprecated(*, thing=None, version, alternative):
def do_wrap(fn):
nonlocal thing

@wraps(fn)
def wrapper(*args, **kwargs):
warn_deprecated(thing, version=version, alternative=alternative)
return fn(*args, **kwargs)

# If our __module__ or __qualname__ get modified, we want to pick up
# on that, so we read them off the wrapper object instead of the (now
# hidden) fn object
if thing is None:
thing = wrapper

return wrapper

return do_wrap


def deprecated_alias(old_qualname, new_fn, *, version):
@deprecated(version=version, alternative=new_fn)
@wraps(new_fn)
def wrapper(*args, **kwargs):
return new_fn(*args, **kwargs)

wrapper.__qualname__ = old_qualname
wrapper.__name__ = old_qualname.rpartition(".")[-1]
return wrapper
10 changes: 5 additions & 5 deletions trio/_file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
class AsyncIOWrapper(AsyncResource):
"""A generic :class:`~io.IOBase` wrapper that implements the :term:`asynchronous
file object` interface. Wrapped methods that could block are executed in
:meth:`trio.run_in_worker_thread`.
:meth:`trio.run_sync_in_worker_thread`.
All properties and methods defined in in :mod:`~io` are exposed by this
wrapper, if they exist in the wrapped file object.
Expand All @@ -80,7 +80,7 @@ def __getattr__(self, name):
@async_wraps(self.__class__, self._wrapped.__class__, name)
async def wrapper(*args, **kwargs):
func = partial(meth, *args, **kwargs)
return await trio.run_in_worker_thread(func)
return await trio.run_sync_in_worker_thread(func)

# cache the generated method
setattr(self, name, wrapper)
Expand Down Expand Up @@ -115,7 +115,7 @@ async def detach(self):
"""

raw = await trio.run_in_worker_thread(self._wrapped.detach)
raw = await trio.run_sync_in_worker_thread(self._wrapped.detach)
return wrap_file(raw)

async def aclose(self):
Expand All @@ -128,7 +128,7 @@ async def aclose(self):

# ensure the underling file is closed during cancellation
with _core.open_cancel_scope(shield=True):
await trio.run_in_worker_thread(self._wrapped.close)
await trio.run_sync_in_worker_thread(self._wrapped.close)

await _core.yield_if_cancelled()

Expand Down Expand Up @@ -165,7 +165,7 @@ async def open_file(
file = file.__fspath__()

_file = wrap_file(
await trio.run_in_worker_thread(
await trio.run_sync_in_worker_thread(
io.open, file, mode, buffering, encoding, errors, newline, closefd,
opener
)
Expand Down
2 changes: 1 addition & 1 deletion trio/_highlevel_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async def send_all(self, data):
raise ClosedStreamError("can't send data after sending EOF")
with self._send_conflict_detector.sync:
with _translate_socket_errors_to_stream_errors():
await self.socket.sendall(data)
await self.socket._sendall(data)

async def wait_send_all_might_not_block(self):
async with self._send_conflict_detector:
Expand Down
6 changes: 3 additions & 3 deletions trio/_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async def wrapper(self, *args, **kwargs):
args = unwrap_paths(args)
meth = getattr(self._wrapped, meth_name)
func = partial(meth, *args, **kwargs)
value = await trio.run_in_worker_thread(func)
value = await trio.run_sync_in_worker_thread(func)
return rewrap_path(value)

return wrapper
Expand Down Expand Up @@ -112,7 +112,7 @@ def generate_magic(cls, attrs):

class Path(metaclass=AsyncAutoWrapperType):
"""A :class:`pathlib.Path` wrapper that executes blocking methods in
:meth:`trio.run_in_worker_thread`.
:meth:`trio.run_sync_in_worker_thread`.
"""

Expand Down Expand Up @@ -155,7 +155,7 @@ async def open(self, *args, **kwargs):
"""

func = partial(self._wrapped.open, *args, **kwargs)
value = await trio.run_in_worker_thread(func)
value = await trio.run_sync_in_worker_thread(func)
return trio.wrap_file(value)


Expand Down
19 changes: 14 additions & 5 deletions trio/_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import idna as _idna

from . import _core
from ._threads import run_in_worker_thread as _run_in_worker_thread
from ._deprecate import deprecated
from ._threads import run_sync_in_worker_thread

__all__ = []

Expand Down Expand Up @@ -241,7 +242,7 @@ def numeric_only_failure(exc):
if hr is not None:
return await hr.getaddrinfo(host, port, family, type, proto, flags)
else:
return await _run_in_worker_thread(
return await run_sync_in_worker_thread(
_stdlib_socket.getaddrinfo,
host,
port,
Expand All @@ -268,7 +269,7 @@ async def getnameinfo(sockaddr, flags):
if hr is not None:
return await hr.getnameinfo(sockaddr, flags)
else:
return await _run_in_worker_thread(
return await run_sync_in_worker_thread(
_stdlib_socket.getnameinfo, sockaddr, flags, cancellable=True
)

Expand All @@ -280,7 +281,7 @@ async def getprotobyname(name):
Like :func:`socket.getprotobyname`, but async.
"""
return await _run_in_worker_thread(
return await run_sync_in_worker_thread(
_stdlib_socket.getprotobyname, name, cancellable=True
)

Expand Down Expand Up @@ -830,7 +831,9 @@ async def sendmsg(self, *args):
# sendall
################################################################

async def sendall(self, data, flags=0):
# XX: When we remove sendall(), we should move this code (and its test)
# into SocketStream.send_all().
async def _sendall(self, data, flags=0):
with memoryview(data) as data:
if not data:
await _core.yield_briefly()
Expand All @@ -841,6 +844,12 @@ async def sendall(self, data, flags=0):
sent = await self.send(remaining, flags)
total_sent += sent

@deprecated(
version="0.2.0", alternative="the high-level SocketStream interface"
)
async def sendall(self, data, flags=0):
return await self._sendall(data, flags)

################################################################
# sendfile
################################################################
Expand Down
12 changes: 6 additions & 6 deletions trio/_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ class CapacityLimiter:
fixed number of seats, and if they're all taken then you have to wait for
someone to get up before you can sit down.
By default, :func:`run_in_worker_thread` uses a :class:`CapacityLimiter` to
limit the number of threads running at once; see
:func:`current_default_worker_thread_limiter` for details.
By default, :func:`run_sync_in_worker_thread` uses a
:class:`CapacityLimiter` to limit the number of threads running at once;
see :func:`current_default_worker_thread_limiter` for details.
If you're familiar with semaphores, then you can think of this as a
restricted semaphore that's specialized for one common use case, with
Expand Down Expand Up @@ -234,9 +234,9 @@ def acquire_on_behalf_of_nowait(self, borrower):
Args:
borrower: A :class:`Task` or arbitrary opaque object used to record
who is borrowing this token. This is used by
:func:`run_in_worker_thread` to allow threads to "hold tokens",
with the intention in the future of using it to `allow deadlock
detection and other useful things
:func:`run_sync_in_worker_thread` to allow threads to "hold
tokens", with the intention in the future of using it to `allow
deadlock detection and other useful things
<https://github.com/python-trio/trio/issues/182>`__
Raises:
Expand Down
Loading

0 comments on commit 1cdb8ea

Please sign in to comment.