diff --git a/SOURCES/0001-Add-support-for-Python-3.7.patch b/SOURCES/0001-Add-support-for-Python-3.7.patch new file mode 100644 index 0000000..10bf62c --- /dev/null +++ b/SOURCES/0001-Add-support-for-Python-3.7.patch @@ -0,0 +1,251 @@ +From 2b83e7ccc12af9fec136e9f4897e1585b3b931aa Mon Sep 17 00:00:00 2001 +From: Aymeric Augustin +Date: Thu, 24 May 2018 22:29:12 +0200 +Subject: [PATCH 1/3] Add support for Python 3.7. + +Hopefully for real this time. + +This is annoyingly complicated. + +Fix #405. + +(cherry picked from commit 6f8f1c877744623f0a5df5917a85b97807bfb7e5) +--- + websockets/client.py | 24 +++++++---------- + websockets/py35/_test_client_server.py | 37 ++++++++++++++++++++++++++ + websockets/py35/client.py | 33 +++++++++++++++++++++++ + websockets/py35/server.py | 22 +++++++++++++++ + websockets/server.py | 25 +++++++---------- + websockets/test_client_server.py | 1 + + 6 files changed, 111 insertions(+), 31 deletions(-) + create mode 100644 websockets/py35/client.py + create mode 100644 websockets/py35/server.py + +diff --git a/websockets/client.py b/websockets/client.py +index 92f29e9..a86b90f 100644 +--- a/websockets/client.py ++++ b/websockets/client.py +@@ -385,15 +385,7 @@ class Connect: + self._creating_connection = loop.create_connection( + factory, host, port, **kwds) + +- @asyncio.coroutine +- def __aenter__(self): +- return (yield from self) +- +- @asyncio.coroutine +- def __aexit__(self, exc_type, exc_value, traceback): +- yield from self.ws_client.close() +- +- def __await__(self): ++ def __iter__(self): # pragma: no cover + transport, protocol = yield from self._creating_connection + + try: +@@ -410,17 +402,19 @@ class Connect: + self.ws_client = protocol + return protocol + +- __iter__ = __await__ +- + +-# Disable asynchronous context manager functionality only on Python < 3.5.1 +-# because it doesn't exist on Python < 3.5 and asyncio.ensure_future didn't +-# accept arbitrary awaitables in Python 3.5; that was fixed in Python 3.5.1. ++# We can't define __await__ on Python < 3.5.1 because asyncio.ensure_future ++# didn't accept arbitrary awaitables until Python 3.5.1. We don't define ++# __aenter__ and __aexit__ either on Python < 3.5.1 to keep things simple. + if sys.version_info[:3] <= (3, 5, 0): # pragma: no cover + @asyncio.coroutine + def connect(*args, **kwds): +- return Connect(*args, **kwds).__await__() ++ return Connect(*args, **kwds).__iter__() + connect.__doc__ = Connect.__doc__ + + else: ++ from .py35.client import __aenter__, __aexit__, __await__ ++ Connect.__aenter__ = __aenter__ ++ Connect.__aexit__ = __aexit__ ++ Connect.__await__ = __await__ + connect = Connect +diff --git a/websockets/py35/_test_client_server.py b/websockets/py35/_test_client_server.py +index 4375248..5360d8d 100644 +--- a/websockets/py35/_test_client_server.py ++++ b/websockets/py35/_test_client_server.py +@@ -13,6 +13,43 @@ from ..server import * + from ..test_client_server import get_server_uri, handler + + ++class AsyncAwaitTests(unittest.TestCase): ++ ++ def setUp(self): ++ self.loop = asyncio.new_event_loop() ++ asyncio.set_event_loop(self.loop) ++ ++ def tearDown(self): ++ self.loop.close() ++ ++ def test_client(self): ++ start_server = serve(handler, 'localhost', 0) ++ server = self.loop.run_until_complete(start_server) ++ ++ async def run_client(): ++ # Await connect. ++ client = await connect(get_server_uri(server)) ++ self.assertEqual(client.state, State.OPEN) ++ await client.close() ++ self.assertEqual(client.state, State.CLOSED) ++ ++ self.loop.run_until_complete(run_client()) ++ ++ server.close() ++ self.loop.run_until_complete(server.wait_closed()) ++ ++ def test_server(self): ++ async def run_server(): ++ # Await serve. ++ server = await serve(handler, 'localhost', 0) ++ self.assertTrue(server.sockets) ++ server.close() ++ await server.wait_closed() ++ self.assertFalse(server.sockets) ++ ++ self.loop.run_until_complete(run_server()) ++ ++ + class ContextManagerTests(unittest.TestCase): + + def setUp(self): +diff --git a/websockets/py35/client.py b/websockets/py35/client.py +new file mode 100644 +index 0000000..7673ea3 +--- /dev/null ++++ b/websockets/py35/client.py +@@ -0,0 +1,33 @@ ++async def __aenter__(self): ++ return await self ++ ++ ++async def __aexit__(self, exc_type, exc_value, traceback): ++ await self.ws_client.close() ++ ++ ++async def __await_impl__(self): ++ # Duplicated with __iter__ because Python 3.7 requires an async function ++ # (as explained in __await__ below) which Python 3.4 doesn't support. ++ transport, protocol = await self._creating_connection ++ ++ try: ++ await protocol.handshake( ++ self._wsuri, origin=self._origin, ++ available_extensions=protocol.available_extensions, ++ available_subprotocols=protocol.available_subprotocols, ++ extra_headers=protocol.extra_headers, ++ ) ++ except Exception: ++ await protocol.fail_connection() ++ raise ++ ++ self.ws_client = protocol ++ return protocol ++ ++ ++def __await__(self): ++ # __await__() must return a type that I don't know how to obtain except ++ # by calling __await__() on the return value of an async function. ++ # I'm not finding a better way to take advantage of PEP 492. ++ return __await_impl__(self).__await__() +diff --git a/websockets/py35/server.py b/websockets/py35/server.py +new file mode 100644 +index 0000000..41a3675 +--- /dev/null ++++ b/websockets/py35/server.py +@@ -0,0 +1,22 @@ ++async def __aenter__(self): ++ return await self ++ ++ ++async def __aexit__(self, exc_type, exc_value, traceback): ++ self.ws_server.close() ++ await self.ws_server.wait_closed() ++ ++ ++async def __await_impl__(self): ++ # Duplicated with __iter__ because Python 3.7 requires an async function ++ # (as explained in __await__ below) which Python 3.4 doesn't support. ++ server = await self._creating_server ++ self.ws_server.wrap(server) ++ return self.ws_server ++ ++ ++def __await__(self): ++ # __await__() must return a type that I don't know how to obtain except ++ # by calling __await__() on the return value of an async function. ++ # I'm not finding a better way to take advantage of PEP 492. ++ return __await_impl__(self).__await__() +diff --git a/websockets/server.py b/websockets/server.py +index 8db0482..46c80dc 100644 +--- a/websockets/server.py ++++ b/websockets/server.py +@@ -729,22 +729,11 @@ class Serve: + self._creating_server = creating_server + self.ws_server = ws_server + +- @asyncio.coroutine +- def __aenter__(self): +- return (yield from self) +- +- @asyncio.coroutine +- def __aexit__(self, exc_type, exc_value, traceback): +- self.ws_server.close() +- yield from self.ws_server.wait_closed() +- +- def __await__(self): ++ def __iter__(self): # pragma: no cover + server = yield from self._creating_server + self.ws_server.wrap(server) + return self.ws_server + +- __iter__ = __await__ +- + + def unix_serve(ws_handler, path, **kwargs): + """ +@@ -761,14 +750,18 @@ def unix_serve(ws_handler, path, **kwargs): + return serve(ws_handler, path=path, **kwargs) + + +-# Disable asynchronous context manager functionality only on Python < 3.5.1 +-# because it doesn't exist on Python < 3.5 and asyncio.ensure_future didn't +-# accept arbitrary awaitables in Python 3.5; that was fixed in Python 3.5.1. ++# We can't define __await__ on Python < 3.5.1 because asyncio.ensure_future ++# didn't accept arbitrary awaitables until Python 3.5.1. We don't define ++# __aenter__ and __aexit__ either on Python < 3.5.1 to keep things simple. + if sys.version_info[:3] <= (3, 5, 0): # pragma: no cover + @asyncio.coroutine + def serve(*args, **kwds): +- return Serve(*args, **kwds).__await__() ++ return Serve(*args, **kwds).__iter__() + serve.__doc__ = Serve.__doc__ + + else: ++ from .py35.server import __aenter__, __aexit__, __await__ ++ Serve.__aenter__ = __aenter__ ++ Serve.__aexit__ = __aexit__ ++ Serve.__await__ = __await__ + serve = Serve +diff --git a/websockets/test_client_server.py b/websockets/test_client_server.py +index 8476913..27a2a71 100644 +--- a/websockets/test_client_server.py ++++ b/websockets/test_client_server.py +@@ -1057,6 +1057,7 @@ class ClientServerOriginTests(unittest.TestCase): + + + try: ++ from .py35._test_client_server import AsyncAwaitTests # noqa + from .py35._test_client_server import ContextManagerTests # noqa + except (SyntaxError, ImportError): # pragma: no cover + pass +-- +2.18.0 + diff --git a/SOURCES/0002-Replace-conditional-errors-with-version-checks.patch b/SOURCES/0002-Replace-conditional-errors-with-version-checks.patch new file mode 100644 index 0000000..a2296af --- /dev/null +++ b/SOURCES/0002-Replace-conditional-errors-with-version-checks.patch @@ -0,0 +1,63 @@ +From 5ec12cf82b3cd0101d00dd762eb883ddc9f9c96c Mon Sep 17 00:00:00 2001 +From: Aymeric Augustin +Date: Tue, 5 Jun 2018 22:08:55 +0200 +Subject: [PATCH 2/3] Replace conditional errors with version checks. + +This avoids silently ignoring tests instead of failing them in case of +mistakes. + +Fix #415. + +(cherry picked from commit ada2987ddf2eccbb36a6ead0a5936ba0ed397032) +--- + websockets/protocol.py | 6 ++---- + websockets/test_client_server.py | 8 ++------ + 2 files changed, 4 insertions(+), 10 deletions(-) + +diff --git a/websockets/protocol.py b/websockets/protocol.py +index dbc9951..66939aa 100644 +--- a/websockets/protocol.py ++++ b/websockets/protocol.py +@@ -15,6 +15,7 @@ import enum + import logging + import random + import struct ++import sys + import warnings + + from .compatibility import asyncio_ensure_future +@@ -1020,9 +1021,6 @@ class WebSocketCommonProtocol(asyncio.StreamReaderProtocol): + super().connection_lost(exc) + + +-try: ++if sys.version_info[:2] >= (3, 6): # pragma: no cover + from .py36.protocol import __aiter__ +-except (SyntaxError, ImportError): # pragma: no cover +- pass +-else: + WebSocketCommonProtocol.__aiter__ = __aiter__ +diff --git a/websockets/test_client_server.py b/websockets/test_client_server.py +index 27a2a71..a3e1e92 100644 +--- a/websockets/test_client_server.py ++++ b/websockets/test_client_server.py +@@ -1056,14 +1056,10 @@ class ClientServerOriginTests(unittest.TestCase): + self.loop.run_until_complete(server.wait_closed()) + + +-try: ++if sys.version_info[:2] >= (3, 5): # pragma: no cover + from .py35._test_client_server import AsyncAwaitTests # noqa + from .py35._test_client_server import ContextManagerTests # noqa +-except (SyntaxError, ImportError): # pragma: no cover +- pass + + +-try: ++if sys.version_info[:2] >= (3, 6): # pragma: no cover + from .py36._test_client_server import AsyncIteratorTests # noqa +-except (SyntaxError, ImportError): # pragma: no cover +- pass +-- +2.18.0 + diff --git a/SOURCES/0003-Support-yield-from-connect-serve-on-Python-3.7.patch b/SOURCES/0003-Support-yield-from-connect-serve-on-Python-3.7.patch new file mode 100644 index 0000000..52989f7 --- /dev/null +++ b/SOURCES/0003-Support-yield-from-connect-serve-on-Python-3.7.patch @@ -0,0 +1,129 @@ +From 9b8f36d08a5bdffa83019f679a9c9d2ef5ca4302 Mon Sep 17 00:00:00 2001 +From: Aymeric Augustin +Date: Sun, 15 Jul 2018 11:07:47 +0200 +Subject: [PATCH 3/3] Support yield from connect/serve on Python 3.7. + +Fix #435. + +(cherry picked from commit 91a376685b1ab7103d3d861ff8b02a1c00f142b1) +--- + websockets/client.py | 1 + + websockets/py35/_test_client_server.py | 3 ++ + websockets/server.py | 1 + + websockets/test_client_server.py | 41 ++++++++++++++++++++++++++ + 4 files changed, 46 insertions(+) + +diff --git a/websockets/client.py b/websockets/client.py +index a86b90f..bb3009b 100644 +--- a/websockets/client.py ++++ b/websockets/client.py +@@ -385,6 +385,7 @@ class Connect: + self._creating_connection = loop.create_connection( + factory, host, port, **kwds) + ++ @asyncio.coroutine + def __iter__(self): # pragma: no cover + transport, protocol = yield from self._creating_connection + +diff --git a/websockets/py35/_test_client_server.py b/websockets/py35/_test_client_server.py +index 5360d8d..c656dd3 100644 +--- a/websockets/py35/_test_client_server.py ++++ b/websockets/py35/_test_client_server.py +@@ -39,6 +39,7 @@ class AsyncAwaitTests(unittest.TestCase): + self.loop.run_until_complete(server.wait_closed()) + + def test_server(self): ++ + async def run_server(): + # Await serve. + server = await serve(handler, 'localhost', 0) +@@ -83,6 +84,7 @@ class ContextManagerTests(unittest.TestCase): + @unittest.skipIf( + sys.version_info[:3] <= (3, 5, 0), 'this test requires Python 3.5.1+') + def test_server(self): ++ + async def run_server(): + # Use serve as an asynchronous context manager. + async with serve(handler, 'localhost', 0) as server: +@@ -99,6 +101,7 @@ class ContextManagerTests(unittest.TestCase): + @unittest.skipUnless( + hasattr(socket, 'AF_UNIX'), 'this test requires Unix sockets') + def test_unix_server(self): ++ + async def run_server(path): + async with unix_serve(handler, path) as server: + self.assertTrue(server.sockets) +diff --git a/websockets/server.py b/websockets/server.py +index 46c80dc..86fa700 100644 +--- a/websockets/server.py ++++ b/websockets/server.py +@@ -729,6 +729,7 @@ class Serve: + self._creating_server = creating_server + self.ws_server = ws_server + ++ @asyncio.coroutine + def __iter__(self): # pragma: no cover + server = yield from self._creating_server + self.ws_server.wrap(server) +diff --git a/websockets/test_client_server.py b/websockets/test_client_server.py +index a3e1e92..6c25784 100644 +--- a/websockets/test_client_server.py ++++ b/websockets/test_client_server.py +@@ -24,6 +24,7 @@ from .extensions.permessage_deflate import ( + ) + from .handshake import build_response + from .http import USER_AGENT, read_response ++from .protocol import State + from .server import * + from .test_protocol import MS + +@@ -1056,6 +1057,46 @@ class ClientServerOriginTests(unittest.TestCase): + self.loop.run_until_complete(server.wait_closed()) + + ++class YieldFromTests(unittest.TestCase): ++ ++ def setUp(self): ++ self.loop = asyncio.new_event_loop() ++ asyncio.set_event_loop(self.loop) ++ ++ def tearDown(self): ++ self.loop.close() ++ ++ def test_client(self): ++ start_server = serve(handler, 'localhost', 0) ++ server = self.loop.run_until_complete(start_server) ++ ++ @asyncio.coroutine ++ def run_client(): ++ # Yield from connect. ++ client = yield from connect(get_server_uri(server)) ++ self.assertEqual(client.state, State.OPEN) ++ yield from client.close() ++ self.assertEqual(client.state, State.CLOSED) ++ ++ self.loop.run_until_complete(run_client()) ++ ++ server.close() ++ self.loop.run_until_complete(server.wait_closed()) ++ ++ def test_server(self): ++ ++ @asyncio.coroutine ++ def run_server(): ++ # Yield from serve. ++ server = yield from serve(handler, 'localhost', 0) ++ self.assertTrue(server.sockets) ++ server.close() ++ yield from server.wait_closed() ++ self.assertFalse(server.sockets) ++ ++ self.loop.run_until_complete(run_server()) ++ ++ + if sys.version_info[:2] >= (3, 5): # pragma: no cover + from .py35._test_client_server import AsyncAwaitTests # noqa + from .py35._test_client_server import ContextManagerTests # noqa +-- +2.18.0 + diff --git a/SOURCES/remove-tests-with-timeouts.patch b/SOURCES/remove-tests-with-timeouts.patch new file mode 100644 index 0000000..1eff026 --- /dev/null +++ b/SOURCES/remove-tests-with-timeouts.patch @@ -0,0 +1,52 @@ +--- a/websockets/test_protocol.py 2017-08-21 19:42:07.788929215 +0200 ++++ b/websockets/test_protocol.py 2017-08-21 19:42:01.260805760 +0200 +@@ -741,49 +741,3 @@ + + # There is no test_local_close_during_send because this cannot really + # happen, considering that writes are serialized. +- +- +-class ServerTests(CommonTests, unittest.TestCase): +- +- def test_close_handshake_timeout(self): +- # Timeout is expected in 10ms. +- self.protocol.timeout = 10 * MS +- # Check the timing within -1/+9ms for robustness. +- with self.assertCompletesWithin(9 * MS, 19 * MS): +- # Unlike previous tests, no close frame will be received in +- # response. The server will stop waiting for the close frame and +- # timeout. +- self.loop.run_until_complete(self.protocol.close(reason='close')) +- self.assertConnectionClosed(1006, '') +- +- +-class ClientTests(CommonTests, unittest.TestCase): +- +- def setUp(self): +- super().setUp() +- self.protocol.is_client = True +- +- def test_close_handshake_timeout(self): +- # Timeout is expected in 2 * 10 = 20ms. +- self.protocol.timeout = 10 * MS +- # Check the timing within -1/+9ms for robustness. +- with self.assertCompletesWithin(19 * MS, 29 * MS): +- # Unlike previous tests, no close frame will be received in +- # response and the connection will not be closed. The client will +- # stop waiting for the close frame and timeout, then stop waiting +- # for the connection close and timeout again. +- self.loop.run_until_complete(self.protocol.close(reason='close')) +- self.assertConnectionClosed(1006, '') +- +- def test_eof_received_timeout(self): +- # Timeout is expected in 10ms. +- self.protocol.timeout = 10 * MS +- # Check the timing within -1/+9ms for robustness. +- with self.assertCompletesWithin(9 * MS, 19 * MS): +- # Unlike previous tests, the close frame will be received in +- # response but the connection will not be closed. The client will +- # stop waiting for the connection close and timeout. +- self.receive_frame(self.close_frame) +- self.loop.run_until_complete(self.protocol.close(reason='close')) +- +- self.assertConnectionClosed(1000, 'close')