diff options
-rw-r--r-- | .github/workflows/build.yml | 2 | ||||
-rw-r--r-- | .github/workflows/codecoverage.yml | 2 | ||||
-rw-r--r-- | .github/workflows/doctest.yml | 4 | ||||
-rw-r--r-- | .github/workflows/lint.yml | 2 | ||||
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | README.md | 15 | ||||
-rw-r--r-- | docs/source/conf.py | 2 | ||||
-rw-r--r-- | docs/source/examples.rst | 4 | ||||
-rw-r--r-- | docs/source/getting_started.rst | 3 | ||||
-rw-r--r-- | docs/source/threading.rst | 2 | ||||
-rw-r--r-- | setup.py | 3 | ||||
-rw-r--r-- | websocket/__init__.py | 2 | ||||
-rw-r--r-- | websocket/_app.py | 15 | ||||
-rw-r--r-- | websocket/_core.py | 11 | ||||
-rw-r--r-- | websocket/_handshake.py | 1 | ||||
-rw-r--r-- | websocket/tests/test_app.py | 2 | ||||
-rw-r--r-- | websocket/tests/test_websocket.py | 2 |
17 files changed, 48 insertions, 29 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab0e495..490cac3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - python-version: ['3.7', '3.8', '3.9', '3.10'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] os: [ubuntu-latest] steps: - name: Check out source repository diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 1b7e103..fe357e7 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -51,4 +51,4 @@ jobs: LOCAL_WS_SERVER_PORT: 8765 WEBSOCKET_CLIENT_CA_BUNDLE: "/usr/lib/ssl/certs/Baltimore_CyberTrust_Root.pem" - name: Submit code coverage report to Codecov.io - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 diff --git a/.github/workflows/doctest.yml b/.github/workflows/doctest.yml index 3478705..47f9916 100644 --- a/.github/workflows/doctest.yml +++ b/.github/workflows/doctest.yml @@ -12,9 +12,9 @@ jobs: - name: Set up Python ${{ matrix.python-version }} environment uses: actions/setup-python@v2 with: - python-version: "3.10" + python-version: "3.11" - name: Run pytest with doctest args run: | - pip3 install pytest 'websocket-client[optional]' rel + pip3 install pytest 'websocket-client[docs]' rel pytest ./docs/source -v --doctest-glob="*.rst" --doctest-modules diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 12b6856..e5fdfe7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Python ${{ matrix.python-version }} environment uses: actions/setup-python@v2 with: - python-version: "3.10" + python-version: "3.11" - name: Perform mandatory flake8 linting run: | @@ -1,6 +1,11 @@ ChangeLog ============ +- 1.4.2 + - create_dispatcher is determined by URL ws/wss, NOT by presence of sslopt args, to maintain consistency (#875) + - Remove redundant key generation line (#864) + - Updated docs to fix old links and updated CI to include Python 3.11 + - 1.4.1 - Fix stack growth bug when `run_forever` reconnects (#854) - Add doctest CI for sphinx docs code examples (d150099) @@ -73,10 +73,17 @@ Many more examples are found in the Most real-world WebSockets situations involve longer-lived connections. The WebSocketApp `run_forever` loop will automatically try to reconnect to an open WebSocket connection when a network -connection is lost if it is provided with a dispatcher parameter, -and provides a variety of event-based connection controls. +connection is lost if it is provided with: + +- a `dispatcher` argument (async dispatcher like rel or pyevent) +- a non-zero `reconnect` argument (delay between disconnection and attempted reconnection) + +`run_forever` provides a variety of event-based connection controls +using callbacks like `on_message` and `on_error`. `run_forever` does not automatically reconnect if the server -closes the WebSocket. Customizing behavior when the server closes +closes the WebSocket gracefully (returning +[a standard websocket close code](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1)). +Customizing behavior when the server closes the WebSocket should be handled in the `on_close` callback. This example uses [rel](https://github.com/bubbleboy14/registeredeventlistener) for the dispatcher to provide automatic reconnection. @@ -107,7 +114,7 @@ if __name__ == "__main__": on_error=on_error, on_close=on_close) - ws.run_forever(dispatcher=rel) # Set dispatcher to automatic reconnection + ws.run_forever(dispatcher=rel, reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if connection closed unexpectedly rel.signal(2, rel.abort) # Keyboard Interrupt rel.dispatch() ``` diff --git a/docs/source/conf.py b/docs/source/conf.py index 1d49533..a07281f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -34,7 +34,7 @@ copyright = '2022' author = 'liris' # The full version, including alpha/beta/rc tags -release = '1.4.1' +release = '1.4.2' # -- General configuration --------------------------------------------------- diff --git a/docs/source/examples.rst b/docs/source/examples.rst index d3d5b99..9b3a905 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -700,7 +700,7 @@ You can use an asynchronous dispatcher such as `rel <https://pypi.org/project/re >>> addr = "wss://api.gemini.com/v1/marketdata/%s" >>> for symbol in ["BTCUSD", "ETHUSD", "ETHBTC"]: ... ws = websocket.WebSocketApp(addr % (symbol,), on_message=lambda w, m : print(m)) - ... ws.run_forever(dispatcher=rel) # doctest: +SKIP + ... ws.run_forever(dispatcher=rel, reconnect=3) # doctest: +SKIP >>> rel.signal(2, rel.abort) # Keyboard Interrupt # doctest: +SKIP >>> rel.dispatch() # doctest: +SKIP @@ -739,7 +739,7 @@ The examples are found in the README and are copied here for sphinx doctests to ... on_error=on_error, ... on_close=on_close) - >>> ws.run_forever(dispatcher=rel) # Set dispatcher to automatic reconnection # doctest: +SKIP + >>> ws.run_forever(dispatcher=rel, reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if connection closed unexpectedly # doctest: +SKIP >>> rel.signal(2, rel.abort) # Keyboard Interrupt <Signal Object | Callback:"abort"> >>> rel.dispatch() # doctest: +SKIP diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 5f3e3f6..c69a1e7 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -12,8 +12,7 @@ For an easy example, run the following: The above command will provide you with an interactive terminal to communicate with the echo.websocket.events server. This server will echo back any message you -send it. You can test this WebSocket connection in your browser, without this -library, by visiting the URL https://websocket.org/echo.html. +send it. The wsdump.py script has many additional options too, so it's a great way to try using this library without writing any custom code. The output of diff --git a/docs/source/threading.rst b/docs/source/threading.rst index 51a74af..7870e71 100644 --- a/docs/source/threading.rst +++ b/docs/source/threading.rst @@ -60,7 +60,7 @@ trigger the ``on_close()`` function. The highlighted rows show the lines added exclusively in the second example. This threading approach is identical to the `echoapp_client.py example <https://github.com/websocket-client/websocket-client/blob/master/examples/echoapp_client.py>`_. However, further testing found that some WebSocket servers, such as -ws://echo.websocket.org, do not trigger the ``on_close()`` function. +ws://echo.websocket.events, do not trigger the ``on_close()`` function. **NOT working on_close() example, without threading** @@ -21,7 +21,7 @@ See the License for the specific language governing permissions and limitations under the License. """ -VERSION = "1.4.1" +VERSION = "1.4.2" install_requires = [] tests_require = [] @@ -51,6 +51,7 @@ setup( "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", diff --git a/websocket/__init__.py b/websocket/__init__.py index 720a842..588a8f2 100644 --- a/websocket/__init__.py +++ b/websocket/__init__.py @@ -23,4 +23,4 @@ from ._exceptions import * from ._logging import * from ._socket import * -__version__ = "1.4.1" +__version__ = "1.4.2" diff --git a/websocket/_app.py b/websocket/_app.py index d57781e..c77a3f0 100644 --- a/websocket/_app.py +++ b/websocket/_app.py @@ -5,6 +5,7 @@ import threading import time import traceback from ._abnf import ABNF +from ._url import parse_url from ._core import WebSocket, getdefaulttimeout from ._exceptions import * from . import _logging @@ -287,12 +288,12 @@ class WebSocketApp: HTTP proxy host name. http_proxy_port: int or str HTTP proxy port. If not set, set to 80. + http_no_proxy: list + Whitelisted host names that don't use the proxy. http_proxy_timeout: int or float HTTP proxy timeout, default is 60 sec as per python-socks. http_proxy_auth: tuple HTTP proxy auth information. tuple of username and password. Default is None. - http_no_proxy: list - Whitelisted host names that don't use the proxy. skip_utf8_validation: bool skip utf8 validation. host: str @@ -303,6 +304,10 @@ class WebSocketApp: customize reading data from socket. suppress_origin: bool suppress outputting origin header. + proxy_type: str + type of proxy from: http, socks4, socks4a, socks5, socks5h + reconnect: int + delay interval when reconnecting Returns ------- @@ -374,7 +379,7 @@ class WebSocketApp: self._callback(self.on_open) - _logging.warning("websocket connected") + _logging.info("websocket connected") dispatcher.read(self.sock.sock, read, check) except (WebSocketConnectionClosedException, ConnectionRefusedError, KeyboardInterrupt, SystemExit, Exception) as e: _logging.error("%s - %s" % (e, reconnect and "reconnecting" or "goodbye")) @@ -405,7 +410,7 @@ class WebSocketApp: frame.data, frame.fin) else: data = frame.data - if op_code == ABNF.OPCODE_TEXT: + if op_code == ABNF.OPCODE_TEXT and not skip_utf8_validation: data = data.decode("utf-8") self._callback(self.on_data, data, frame.opcode, True) self._callback(self.on_message, data) @@ -437,7 +442,7 @@ class WebSocketApp: teardown() custom_dispatcher = bool(dispatcher) - dispatcher = self.create_dispatcher(ping_timeout, dispatcher, not not sslopt) + dispatcher = self.create_dispatcher(ping_timeout, dispatcher, parse_url(self.url)[3]) if ping_interval: event = threading.Event() diff --git a/websocket/_core.py b/websocket/_core.py index 73d38b1..1d68829 100644 --- a/websocket/_core.py +++ b/websocket/_core.py @@ -46,8 +46,11 @@ class WebSocket: >>> import websocket >>> ws = websocket.WebSocket() - >>> ws.connect("ws://echo.websocket.org") + >>> ws.connect("ws://echo.websocket.events") + >>> ws.recv() + 'echo.websocket.events sponsored by Lob.com' >>> ws.send("Hello, Server") + 19 >>> ws.recv() 'Hello, Server' >>> ws.close() @@ -203,7 +206,7 @@ class WebSocket: If you set "header" list object, you can set your own custom header. >>> ws = WebSocket() - >>> ws.connect("ws://echo.websocket.org/", + >>> ws.connect("ws://echo.websocket.events", ... header=["User-Agent: MyProgram", ... "x-custom: header"]) @@ -283,7 +286,7 @@ class WebSocket: """ Send the data frame. - >>> ws = create_connection("ws://echo.websocket.org/") + >>> ws = create_connection("ws://echo.websocket.events") >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT) >>> ws.send_frame(frame) >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0) @@ -543,7 +546,7 @@ def create_connection(url, timeout=None, class_=WebSocket, **options): You can customize using 'options'. If you set "header" list object, you can set your own custom header. - >>> conn = create_connection("ws://echo.websocket.org/", + >>> conn = create_connection("ws://echo.websocket.events", ... header=["User-Agent: MyProgram", ... "x-custom: header"]) diff --git a/websocket/_handshake.py b/websocket/_handshake.py index 6a57c95..07a4cfb 100644 --- a/websocket/_handshake.py +++ b/websocket/_handshake.py @@ -101,7 +101,6 @@ def _get_handshake_headers(resource, url, host, port, options): # Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified if not options.get('header') or 'Sec-WebSocket-Key' not in options['header']: - key = _create_sec_websocket_key() headers.append("Sec-WebSocket-Key: %s" % key) else: key = options['header']['Sec-WebSocket-Key'] diff --git a/websocket/tests/test_app.py b/websocket/tests/test_app.py index 8614d08..5526d3e 100644 --- a/websocket/tests/test_app.py +++ b/websocket/tests/test_app.py @@ -191,7 +191,7 @@ class WebSocketAppTest(unittest.TestCase): """ Test WebSocketApp binary opcode """ # The lack of wss:// in the URL below is on purpose - app = ws.WebSocketApp('streaming.vn.teslamotors.com/streaming/') + app = ws.WebSocketApp('wss://streaming.vn.teslamotors.com/streaming/') app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") diff --git a/websocket/tests/test_websocket.py b/websocket/tests/test_websocket.py index ae42ab5..d47d73e 100644 --- a/websocket/tests/test_websocket.py +++ b/websocket/tests/test_websocket.py @@ -432,7 +432,7 @@ class HandshakeTest(unittest.TestCase): self.assertRaises(ws._exceptions.WebSocketBadStatusException, websock3.connect, "wss://api.bitfinex.com/ws/2", cookie="chocolate", origin="testing_websockets.com", - host="echo.websocket.org/websocket-client-test", + host="echo.websocket.events/websocket-client-test", subprotocols=["testproto"], connection="Upgrade", header={"CustomHeader1":"123", |