Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into 406
Browse files Browse the repository at this point in the history
  • Loading branch information
pquentin committed Jan 19, 2018
2 parents 6929d9c + 645e5bb commit 1f723ac
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 43 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ asking for help in our `chat room
<https://gitter.im/python-trio/general>`__, `filing a bug
<https://github.com/python-trio/trio/issues/new>`__, or `posting a
question on StackOverflow
<https://stackoverflow.com/questions/ask?tags=python+trio>`__, and
<https://stackoverflow.com/questions/ask?tags=python+python-trio>`__, and
we'll do our best to help you out.

**Trio is awesome and I want to help make it more awesome!** You're
Expand Down
4 changes: 2 additions & 2 deletions docs/source/_templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@

<p class="trio-help-hint">Need help? Try <a
href="https://gitter.im/python-trio/general">chat</a> or <a
href="https://stackoverflow.com/questions/ask?tags=python+trio">StackOverflow</a>.</p>
{% endblock %}
href="https://stackoverflow.com/questions/ask?tags=python+python-trio">StackOverflow</a>.</p>
{% endblock %}
2 changes: 2 additions & 0 deletions docs/source/reference-io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ abstraction.

.. autofunction:: serve_ssl_over_tcp

.. autofunction:: open_unix_socket

.. autoclass:: SocketStream
:members:
:undoc-members:
Expand Down
4 changes: 2 additions & 2 deletions docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ If you get lost or confused...

...then we want to know! We have a friendly `chat channel
<https://gitter.im/python-trio/general>`__, you can ask questions
`using the "trio" tag on StackOverflow
<https://stackoverflow.com/questions/ask?tags=python+trio>`__, or just
`using the "python-trio" tag on StackOverflow
<https://stackoverflow.com/questions/ask?tags=python+python-trio>`__, or just
`file a bug <https://github.com/python-trio/trio/issues/new>`__ (if
our documentation is confusing, that's our fault, and we want to fix
it!).
Expand Down
1 change: 1 addition & 0 deletions newsfragments/401.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add unix client socket support.
3 changes: 3 additions & 0 deletions trio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
from ._highlevel_open_tcp_listeners import *
__all__ += _highlevel_open_tcp_listeners.__all__

from ._highlevel_open_unix_stream import *
__all__ += _highlevel_open_unix_stream.__all__

from ._highlevel_ssl_helpers import *
__all__ += _highlevel_ssl_helpers.__all__

Expand Down
6 changes: 4 additions & 2 deletions trio/_core/_ki.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,10 @@ def wrapper(*args, **kwargs):

@contextmanager
def ki_manager(deliver_cb, restrict_keyboard_interrupt_to_checkpoints):
if (threading.current_thread() != threading.main_thread()
or signal.getsignal(signal.SIGINT) != signal.default_int_handler):
if (
threading.current_thread() != threading.main_thread()
or signal.getsignal(signal.SIGINT) != signal.default_int_handler
):
yield
return

Expand Down
12 changes: 8 additions & 4 deletions trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,10 @@ def _add_exc(self, exc):
self.cancel_scope.cancel()

def _check_nursery_closed(self):
if (not self._nested_child_running and not self._children
and not self._pending_starts):
if (
not self._nested_child_running and not self._children
and not self._pending_starts
):
self._closed = True
if self._parent_waiting_in_aexit:
self._parent_waiting_in_aexit = False
Expand Down Expand Up @@ -1477,8 +1479,10 @@ async def checkpoint_if_cancelled():
"""
task = current_task()
if (task._pending_cancel_scope() is not None or
(task is task._runner.main_task and task._runner.ki_pending)):
if (
task._pending_cancel_scope() is not None or
(task is task._runner.main_task and task._runner.ki_pending)
):
await _core.checkpoint()
assert False # pragma: no cover
task._cancel_points += 1
Expand Down
6 changes: 4 additions & 2 deletions trio/_core/tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1203,8 +1203,10 @@ def cb(x):
for i in range(100):
token.run_sync_soon(cb, i, idempotent=True)
await wait_all_tasks_blocked()
if (sys.version_info < (3, 6)
and platform.python_implementation() == "CPython"):
if (
sys.version_info < (3, 6)
and platform.python_implementation() == "CPython"
):
# no order guarantees
record.sort()
# Otherwise, we guarantee FIFO
Expand Down
42 changes: 42 additions & 0 deletions trio/_highlevel_open_unix_stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import trio
from trio._highlevel_open_tcp_stream import close_on_error
from trio.socket import socket, SOCK_STREAM

try:
from trio.socket import AF_UNIX
has_unix = True
except ImportError:
has_unix = False

__all__ = ["open_unix_socket"]


async def open_unix_socket(filename,):
"""Opens a connection to the specified
`Unix domain socket <https://en.wikipedia.org/wiki/Unix_domain_socket>`__.
You must have read/write permission on the specified file to connect.
Args:
filename (str or bytes): The filename to open the connection to.
Returns:
SocketStream: a :class:`~trio.abc.Stream` connected to the given file.
Raises:
OSError: If the socket file could not be connected to.
RuntimeError: If AF_UNIX sockets are not supported.
"""
if not has_unix:
raise RuntimeError("Unix sockets are not supported on this platform")

if filename is None:
raise ValueError("Filename cannot be None")

# much more simplified logic vs tcp sockets - one socket type and only one
# possible location to connect to
sock = socket(AF_UNIX, SOCK_STREAM)
with close_on_error(sock):
await sock.connect(filename)

return trio.SocketStream(sock)
6 changes: 3 additions & 3 deletions trio/_highlevel_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ class SocketStream(HalfCloseableStream):
socket: The trio socket object to wrap. Must have type ``SOCK_STREAM``,
and be connected.
By default, :class:`SocketStream` enables ``TCP_NODELAY``, and (on
platforms where it's supported) enables ``TCP_NOTSENT_LOWAT`` with a
reasonable buffer size (currently 16 KiB) – see `issue #72
By default for TCP sockets, :class:`SocketStream` enables ``TCP_NODELAY``,
and (on platforms where it's supported) enables ``TCP_NOTSENT_LOWAT`` with
a reasonable buffer size (currently 16 KiB) – see `issue #72
<https://github.com/python-trio/trio/issues/72>`__ for discussion. You can
of course override these defaults by calling :meth:`setsockopt`.
Expand Down
18 changes: 12 additions & 6 deletions trio/_socket.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
from functools import wraps as _wraps, partial as _partial
import os as _os
import socket as _stdlib_socket
import sys as _sys
import os as _os
from contextlib import contextmanager as _contextmanager
import errno as _errno
from functools import wraps as _wraps

import idna as _idna

from . import _core
from ._deprecate import deprecated
from ._threads import run_sync_in_worker_thread
from ._util import fspath

__all__ = []

Expand Down Expand Up @@ -462,8 +461,10 @@ def dup(self):
async def bind(self, address):
await _core.checkpoint()
address = await self._resolve_local_address(address)
if (hasattr(_stdlib_socket, "AF_UNIX") and self.family == AF_UNIX
and address[0]):
if (
hasattr(_stdlib_socket, "AF_UNIX") and self.family == AF_UNIX
and address[0]
):
# Use a thread for the filesystem traversal (unless it's an
# abstract domain socket)
return await run_sync_in_worker_thread(self._sock.bind, address)
Expand Down Expand Up @@ -504,6 +505,11 @@ async def _resolve_address(self, address, flags):
"address should be a (host, port, [flowinfo, [scopeid]]) "
"tuple"
)
elif self._sock.family == AF_UNIX:
await _core.checkpoint()
# unwrap path-likes
return fspath(address)

else:
await _core.checkpoint()
return address
Expand Down
14 changes: 9 additions & 5 deletions trio/_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,9 +630,11 @@ async def receive_some(self, max_bytes):
# 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__,
(SSLEOFError, SSLSyscallError))):
if (
self._https_compatible and
isinstance(exc.__cause__,
(SSLEOFError, SSLSyscallError))
):
return b""
else:
raise
Expand All @@ -647,8 +649,10 @@ async def receive_some(self, max_bytes):
# 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__, SSLEOFError)):
if (
self._https_compatible
and isinstance(exc.__cause__, SSLEOFError)
):
return b""
else:
raise
Expand Down
58 changes: 51 additions & 7 deletions trio/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import sys
from functools import wraps
import typing as t

import async_generator

Expand All @@ -14,11 +15,8 @@
from . import _core

__all__ = [
"signal_raise",
"aiter_compat",
"acontextmanager",
"ConflictDetector",
"fixup_module_metadata",
"signal_raise", "aiter_compat", "acontextmanager", "ConflictDetector",
"fixup_module_metadata", "fspath"
]

# Equivalent to the C function raise(), which Python doesn't wrap
Expand Down Expand Up @@ -59,6 +57,7 @@
# - generating synthetic signals for tests
# and for both of those purposes, 'raise' works fine.
import cffi

_ffi = cffi.FFI()
_ffi.cdef("int raise(int);")
_lib = _ffi.dlopen("api-ms-win-crt-runtime-l1-1-0.dll")
Expand Down Expand Up @@ -133,8 +132,11 @@ async def __aexit__(self, type, value, traceback):
# Likewise, avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError
# (see PEP 479).
if (isinstance(value, (StopIteration, StopAsyncIteration))
and exc.__cause__ is value):
if (
isinstance(value,
(StopIteration, StopAsyncIteration))
and exc.__cause__ is value
):
return False
raise
except:
Expand Down Expand Up @@ -253,3 +255,45 @@ def fix_one(obj):
for objname in namespace["__all__"]:
obj = namespace[objname]
fix_one(obj)


# This is copied from PEP 519 as the implementation of os.fspath for
# Python 3.5. See: https://www.python.org/dev/peps/pep-0519/#os
# The input typehint is removed as there is no os.PathLike on 3.5.


def fspath(path) -> t.Union[str, bytes]:
"""Return the string representation of the path.
If str or bytes is passed in, it is returned unchanged. If __fspath__()
returns something other than str or bytes then TypeError is raised. If
this function is given something that is not str, bytes, or os.PathLike
then TypeError is raised.
"""
if isinstance(path, (str, bytes)):
return path

# Work from the object's type to match method resolution of other magic
# methods.
path_type = type(path)
try:
path = path_type.__fspath__(path)
except AttributeError:
if hasattr(path_type, '__fspath__'):
raise
else:
if isinstance(path, (str, bytes)):
return path
else:
raise TypeError(
"expected __fspath__() to return str or bytes, "
"not " + type(path).__name__
)

raise TypeError(
"expected str, bytes or os.PathLike object, not " + path_type.__name__
)


if hasattr(os, "fspath"):
fspath = os.fspath
16 changes: 12 additions & 4 deletions trio/testing/_checkpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ def _assert_yields_or_not(expected):
try:
yield
finally:
if (expected and (task._cancel_points == orig_cancel
or task._schedule_points == orig_schedule)):
if (
expected and (
task._cancel_points == orig_cancel
or task._schedule_points == orig_schedule
)
):
raise AssertionError("assert_checkpoints block did not yield!")
elif (not expected and (task._cancel_points != orig_cancel
or task._schedule_points != orig_schedule)):
elif (
not expected and (
task._cancel_points != orig_cancel
or task._schedule_points != orig_schedule
)
):
raise AssertionError("assert_no_yields block yielded!")


Expand Down
6 changes: 4 additions & 2 deletions trio/tests/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ def test_core_is_properly_reexported():
for symbol in _core.__all__:
found = 0
for source in sources:
if (symbol in source.__all__
and getattr(source, symbol) is getattr(_core, symbol)):
if (
symbol in source.__all__
and getattr(source, symbol) is getattr(_core, symbol)
):
found += 1
print(symbol, found)
assert found == 1
Loading

0 comments on commit 1f723ac

Please sign in to comment.