Skip to content
Open
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
4 changes: 4 additions & 0 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,10 @@ The AF_* and SOCK_* constants are now :class:`AddressFamily` and
defined then this protocol is unsupported. More constants may be available
depending on the system.

.. versionchanged:: next
:const:`AF_UNIX` is now available on Windows. It requires Windows 10
version 1803 or newer.

.. data:: AF_UNSPEC

:const:`AF_UNSPEC` means that
Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ shlex
(Contributed by Jay Berry in :gh:`148846`.)


socket
------

* Unix domain sockets (:const:`~socket.AF_UNIX`) are now supported on Windows,
which requires Windows 10 version 1803 or newer.
(Contributed by An Long in :gh:`77589`.)


tkinter
-------

Expand Down
8 changes: 8 additions & 0 deletions Lib/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ class HTTPServer(socketserver.TCPServer):
allow_reuse_address = True # Seems to make sense in testing environment
allow_reuse_port = False

def __init__(self, *args, **kwargs):
if sys.platform == 'win32' and hasattr(socket, 'AF_UNIX') and\
self.address_family == socket.AF_UNIX:
# reuse address with AF_UNIX is not supported on Windows
self.allow_reuse_address = False

super().__init__(*args, **kwargs)

def server_bind(self):
"""Override server_bind to store the server name."""
socketserver.TCPServer.server_bind(self)
Expand Down
5 changes: 0 additions & 5 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5744,11 +5744,6 @@ def test_invalid_family(self):
with self.assertRaises(ValueError):
multiprocessing.connection.Listener(r'\\.\test')

@unittest.skipUnless(WIN32, "skipped on non-Windows platforms")
def test_invalid_family_win32(self):
with self.assertRaises(ValueError):
multiprocessing.connection.Listener('/var/test.pipe')

#
# Issue 12098: check sys.flags of child matches that for parent
#
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_asyncio/test_base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,8 @@ def test_create_datagram_endpoint_sock(self):
self.assertEqual('CLOSED', protocol.state)

@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets')
@unittest.skipIf(sys.platform == 'win32', 'AF_UNIX support for asyncio is '
'not implemented on Windows for now')
def test_create_datagram_endpoint_sock_unix(self):
fut = self.loop.create_datagram_endpoint(
lambda: MyDatagramProto(create_future=True, loop=self.loop),
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,9 @@ def test_create_server_reuse_port(self):
server.close()

def _make_unix_server(self, factory, **kwargs):
if sys.platform == 'win32':
raise unittest.SkipTest('AF_UNIX support for asyncio is not '
'implemented on Windows for now')
path = test_utils.gen_unix_socket_path()
self.addCleanup(lambda: os.path.exists(path) and os.unlink(path))

Expand Down Expand Up @@ -1072,6 +1075,8 @@ def test_create_unix_server(self):
server.close()

@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets')
@unittest.skipIf(sys.platform == 'win32', 'AF_UNIX support for asyncio is '
'not implemented on Windows for now')
def test_create_unix_server_path_socket_error(self):
proto = MyProto(loop=self.loop)
sock = socket.socket()
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_asyncio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ def gen_unix_socket_path():

@contextlib.contextmanager
def unix_socket_path():
if sys.platform == 'win32':
raise unittest.SkipTest('AF_UNIX support for asyncio is not '
'implemented on Windows for now')
path = gen_unix_socket_path()
try:
yield path
Expand All @@ -255,6 +258,9 @@ def unix_socket_path():

@contextlib.contextmanager
def run_test_unix_server(*, use_ssl=False):
if sys.platform == 'win32':
raise unittest.SkipTest('AF_UNIX support for asyncio is not '
'implemented on Windows for now')
with unix_socket_path() as path:
yield from _run_test_server(address=path, use_ssl=use_ssl,
server_cls=SilentUnixWSGIServer,
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -2105,6 +2105,8 @@ def test_output(self):
self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')

def test_udp_reconnection(self):
if self.server_exception:
self.skipTest(self.server_exception)
logger = logging.getLogger("slh")
self.sl_hdlr.close()
self.handled.clear()
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_pathlib/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -2860,6 +2860,8 @@ def test_is_socket_false(self):
@unittest.skipIf(
is_wasi, "Cannot create socket on WASI."
)
@unittest.skipIf(sys.platform == 'win32',
"detecting if file is socket is not supported by Windows")
def test_is_socket_true(self):
P = self.cls(self.base, 'mysock')
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Expand All @@ -2876,6 +2878,26 @@ def test_is_socket_true(self):
self.assertIs(self.cls(self.base, 'mysock\udfff').is_socket(), False)
self.assertIs(self.cls(self.base, 'mysock\x00').is_socket(), False)

@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
@unittest.skipUnless(sys.platform == 'win32',
"socket file on Windows is a normal file")
def test_is_socket_on_windows(self):
P = self.cls(self.base, 'mysock')
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.addCleanup(sock.close)
try:
sock.bind(str(P))
except OSError as e:
if (isinstance(e, PermissionError) or
"AF_UNIX path too long" in str(e)):
self.skipTest("cannot bind Unix socket: " + str(e))
raise
self.assertFalse(P.is_socket())
self.assertFalse(P.is_fifo())
self.assertTrue(P.is_file())
self.assertIs(self.cls(self.base, 'mysock\udfff').is_socket(), False)
self.assertIs(self.cls(self.base, 'mysock\x00').is_socket(), False)

def test_is_block_device_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_block_device())
Expand Down
24 changes: 23 additions & 1 deletion Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -5275,7 +5275,7 @@ def __init__(self, methodName='runTest'):

def _check_defaults(self, sock):
self.assertIsInstance(sock, socket.socket)
if hasattr(socket, 'AF_UNIX'):
if sys.platform != 'win32' and hasattr(socket, 'AF_UNIX'):
self.assertEqual(sock.family, socket.AF_UNIX)
else:
self.assertEqual(sock.family, socket.AF_INET)
Expand Down Expand Up @@ -6332,10 +6332,19 @@ def bind(self, sock, path):
else:
raise

@unittest.skipIf(sys.platform == 'win32',
'Windows will raise Error if is not bound')
Comment thread
aisk marked this conversation as resolved.
def testUnbound(self):
# Issue #30205 (note getsockname() can return None on OS X)
self.assertIn(self.sock.getsockname(), ('', None))

@unittest.skipUnless(sys.platform == 'win32',
'Windows-specific behavior')
def test_unbound_on_windows(self):
with self.assertRaises(OSError) as cm:
self.sock.getsockname()
self.assertEqual(cm.exception.winerror, errno.WSAEINVAL)

def testStrAddr(self):
# Test binding to and retrieving a normal string pathname.
path = os.path.abspath(os_helper.TESTFN)
Expand All @@ -6350,6 +6359,8 @@ def testBytesAddr(self):
self.addCleanup(os_helper.unlink, path)
self.assertEqual(self.sock.getsockname(), path)

@unittest.skipIf(sys.platform == 'win32',
'surrogateescape file path is not supported on Windows')
def testSurrogateescapeBind(self):
# Test binding to a valid non-ASCII pathname, with the
# non-ASCII bytes supplied using surrogateescape encoding.
Expand All @@ -6359,6 +6370,9 @@ def testSurrogateescapeBind(self):
self.addCleanup(os_helper.unlink, path)
self.assertEqual(self.sock.getsockname(), path)

@unittest.skipIf(sys.platform == 'win32',
'Windows has a bug which can\'t unlink sock file with '
'TESTFN_UNENCODABLE in its name')
def testUnencodableAddr(self):
# Test binding to a pathname that cannot be encoded in the
# file system encoding.
Expand All @@ -6371,10 +6385,18 @@ def testUnencodableAddr(self):

@unittest.skipIf(sys.platform in ('linux', 'android'),
'Linux behavior is tested by TestLinuxAbstractNamespace')
@unittest.skipIf(sys.platform == 'win32',
'Windows allow bind on empty path')
Comment thread
aisk marked this conversation as resolved.
def testEmptyAddress(self):
# Test that binding empty address fails.
self.assertRaises(OSError, self.sock.bind, "")

@unittest.skipUnless(sys.platform == 'win32',
'Windows-specific behavior')
def test_empty_address_on_windows(self):
self.sock.bind('')
self.assertEqual(self.sock.getsockname(), '')


class BufferIOTest(SocketConnectedTest):
"""
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_socketserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import select
import signal
import socket
import sys
import threading
import unittest
import socketserver
Expand Down Expand Up @@ -223,6 +224,8 @@ def test_ForkingUDPServer(self):
self.dgram_examine)

@requires_unix_sockets
@unittest.skipIf(sys.platform == "win32",
"Unix with Datagram is not supported on Windows")
@unittest.skipIf(test.support.is_apple_mobile and test.support.on_github_actions,
"gh-140702: Test fails regularly on iOS simulator on GitHub Actions")
def test_UnixDatagramServer(self):
Expand All @@ -231,6 +234,8 @@ def test_UnixDatagramServer(self):
self.dgram_examine)

@requires_unix_sockets
@unittest.skipIf(sys.platform == "win32",
"Unix with Datagram is not supported on Windows")
@unittest.skipIf(test.support.is_apple_mobile and test.support.on_github_actions,
"gh-140702: Test fails regularly on iOS simulator on GitHub Actions")
def test_ThreadingUnixDatagramServer(self):
Expand All @@ -240,6 +245,8 @@ def test_ThreadingUnixDatagramServer(self):

@warnings_helper.ignore_fork_in_thread_deprecation_warnings()
@requires_unix_sockets
@unittest.skipIf(sys.platform == "win32",
"Unix with Datagram is not supported on Windows")
@requires_forking
def test_ForkingUnixDatagramServer(self):
self.run_server(socketserver.ForkingUnixDatagramServer,
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_stat.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,23 @@ def test_devices(self):
break

@socket_helper.skip_unless_bind_unix_socket
@unittest.skipIf(sys.platform == 'win32', "didn't work on Windows")
def test_socket(self):
with socket.socket(socket.AF_UNIX) as s:
s.bind(TESTFN)
st_mode, modestr = self.get_mode()
self.assertEqual(modestr[0], 's')
self.assertS_IS("SOCK", st_mode)

@socket_helper.skip_unless_bind_unix_socket
@unittest.skipUnless(sys.platform == 'win32', "didn't work on Windows")
def test_socket_on_windows(self):
with socket.socket(socket.AF_UNIX) as s:
s.bind(TESTFN)
st_mode, modestr = self.get_mode()
self.assertNotEqual(modestr[0], 's')
self.assertS_IS("REG", st_mode)

def test_module_attributes(self):
for key, value in self.stat_struct.items():
modvalue = getattr(self.statmod, key)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Unix domain sockets (:const:`socket.AF_UNIX`) are now supported on Windows,
which requires Windows 10 version 1803 or newer. Patch by An Long.
4 changes: 3 additions & 1 deletion Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ typedef struct {

/* IMPORTANT: make sure the list ordered by descending build_number */
static FlagRuntimeInfo win_runtime_flags[] = {
/* available starting with Windows 10 1803 */
{17134, "AF_UNIX"},
/* available starting with Windows 10 1709 */
{16299, "TCP_KEEPIDLE"},
{16299, "TCP_KEEPINTVL"},
Expand Down Expand Up @@ -1910,7 +1912,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
addr->sun_path[path.len] = 0;

/* including the tailing NUL */
*len_ret = path.len + offsetof(struct sockaddr_un, sun_path) + 1;
*len_ret = (int)path.len + offsetof(struct sockaddr_un, sun_path) + 1;
}
addr->sun_family = s->sock_family;
memcpy(addr->sun_path, path.buf, path.len);
Expand Down
2 changes: 2 additions & 0 deletions Modules/socketmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ typedef int socklen_t;

#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#elif HAVE_AFUNIX_H
# include <afunix.h>
#else
# undef AF_UNIX
#endif
Expand Down
7 changes: 7 additions & 0 deletions PC/pyconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,13 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */
/* Define if you have the <sys/un.h> header file. */
/* #define HAVE_SYS_UN_H 1 */

/* Define if you have the <afunix.h> header file. */
#if defined(__has_include) && __has_include(<afunix.h>)
#define HAVE_AFUNIX_H 1
#else
#define HAVE_AFUNIX_H 0
#endif

/* Define if you have the <sys/utime.h> header file. */
/* #define HAVE_SYS_UTIME_H 1 */

Expand Down
Loading