diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..2cd19db --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,66 @@ +name: Documentation + +on: + push: + branches: + - main + pull_request: + branches: + - main + - dev + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Needed for hatch-vcs to determine version + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + python-version: "3.12" + + - name: Install the project + run: uv sync --only-group docs + + - name: Build documentation + run: | + cd docs + uv run make html + + - name: Add .nojekyll file + run: touch docs/build/html/.nojekyll + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: 'docs/build/html' + + deploy: + # Only deploy when a release is published + if: github.event_name == 'release' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/python-publish-ezmsg-websocket.yml b/.github/workflows/python-publish-ezmsg-websocket.yml deleted file mode 100644 index 34c6e1f..0000000 --- a/.github/workflows/python-publish-ezmsg-websocket.yml +++ /dev/null @@ -1,41 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Upload Python Package - ezmsg-websocket - -on: - release: - types: [published] - workflow_dispatch: - -permissions: - contents: read - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.8" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - - name: Build ezmsg-websocket - run: python -m build extensions/ezmsg-websocket - - name: Publish ezmsg-websocket - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 - with: - password: ${{ secrets.PYPI_API_TOKEN_WEBSOCKET }} - packages_dir: extensions/ezmsg-websocket/dist diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..902bc78 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,28 @@ +# This workflow will upload a Python Package using Twine when a release is created + +name: Upload Python Package + +on: + release: + types: [published] + workflow_dispatch: + +jobs: + build: + name: build and upload release to PyPI + runs-on: ubuntu-latest + environment: "release" + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v6 + + - name: Build Package + run: uv build + + - name: Publish package distributions to PyPI + run: uv publish diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 958b1e6..085a43c 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -4,14 +4,16 @@ on: push: branches: [main] pull_request: - branches: [main] + branches: + - main + - dev workflow_dispatch: jobs: build: strategy: matrix: - python-version: [3.8, 3.9, "3.10"] + python-version: ["3.10.15", "3.11", "3.12", "3.13"] os: - "ubuntu-latest" - "windows-latest" @@ -21,31 +23,14 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Upgrade pip and install pipx - run: | - python -m pip install --upgrade pip pipx flake8 - - - name: Install Poetry via pipx - run: | - pipx install poetry - echo "$HOME/.local/bin/" >> $GITHUB_PATH + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v6 - - name: Install dependencies with Poetry - run: | - poetry install --with test + - name: Install the project + run: uv sync --python ${{ matrix.python-version }} - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Lint + run: uv tool run ruff check --output-format=github src - - name: Test ezmsg-sigproc - run: | - poetry run python -m pytest -v tests + - name: Run tests + run: uv run pytest tests diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05e1aa3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,146 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +docs/source/_build +docs/source/generated + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# JetBrains +.idea/ + +uv.lock +src/ezmsg/websocket/__version__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5605562 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,9 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.8.3 + hooks: + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/README.md b/README.md index abb4a64..2ba1c29 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,17 @@ Websocket server and client units for ezmsg ## Dependencies * `websockets` -## Setup (Development) -1. Install `ezmsg` either using `pip install ezmsg` or set up the repo for development as described in the `ezmsg` readme. -2. `cd` to this directory (`ezmsg-websocket`) and run `pip install -e .` -3. Signal processing modules are available under `import ezmsg.websocket` +## Development +We use [`uv`](https://docs.astral.sh/uv/getting-started/installation/) for development. +1. Install [`uv`](https://docs.astral.sh/uv/getting-started/installation/) if not already installed. +2. Fork this repository and clone your fork locally. +3. Open a terminal and `cd` to the cloned folder. +4. Run `uv sync` to create a `.venv` and install dependencies. +5. (Optional) Install pre-commit hooks: `uv run pre-commit install` +6. After making changes, run the test suite: `uv run pytest tests` + +## License + +MIT License - see [LICENSE](LICENSE) for details. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..dc1312a --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst new file mode 100644 index 0000000..51334f1 --- /dev/null +++ b/docs/source/api/index.rst @@ -0,0 +1,8 @@ +API Reference +============= + +.. autosummary:: + :toctree: generated + :recursive: + + ezmsg.websocket diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..601c528 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,122 @@ +# Configuration file for the Sphinx documentation builder. + +import os +import sys + +# Add the source directory to the path +sys.path.insert(0, os.path.abspath("../../src")) + +# -- Project information -------------------------- + +project = "ezmsg.websocket" +copyright = "2025, ezmsg Contributors" +author = "ezmsg Contributors" + +# The version is managed by hatch-vcs and stored in __version__.py +try: + from ezmsg.websocket.__version__ import version as release +except ImportError: + release = "unknown" + +# For display purposes, extract the base version without git commit info +version = release.split("+")[0] if release != "unknown" else release + +# -- General configuration -------------------------- + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + "sphinx.ext.duration", + "sphinx_autodoc_typehints", + "sphinx_copybutton", + "myst_parser", # For markdown files +] + +templates_path = ["_templates"] +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# The toctree master document +master_doc = "index" + +# -- Autodoc configuration ------------------------------ + +# Auto-generate API docs +autosummary_generate = True +autosummary_imported_members = False +autodoc_typehints = "description" +autodoc_member_order = "bysource" +autodoc_typehints_format = "short" +python_use_unqualified_type_names = True +autodoc_default_options = { + "members": True, + "member-order": "bysource", + "special-members": "__init__", + "undoc-members": True, + "show-inheritance": True, +} + +# Don't show the full module path in the docs +add_module_names = False + +# -- Intersphinx configuration -------------------------- + +intersphinx_mapping = { + "python": ("https://docs.python.org/3/", None), + "numpy": ("https://numpy.org/doc/stable/", None), + "ezmsg": ("https://www.ezmsg.org/ezmsg/", None), +} +intersphinx_disabled_domains = ["std"] + +# -- Options for HTML output ----------------------------- + +html_theme = "pydata_sphinx_theme" +html_static_path = ["_static"] + +# Set the base URL for the documentation +html_baseurl = "https://www.ezmsg.org/ezmsg-websocket/" + +html_theme_options = { + "logo": { + "text": f"ezmsg.websocket {version}", + "link": "https://ezmsg.org", + }, + "header_links_before_dropdown": 4, + "navbar_start": ["navbar-logo"], + "navbar_end": ["theme-switcher", "navbar-icon-links"], + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/ezmsg-org/ezmsg-websocket", + "icon": "fa-brands fa-github", + }, + { + "name": "ezmsg.org", + "url": "https://www.ezmsg.org", + "icon": "fa-solid fa-house", + }, + ], +} + +# Timestamp is inserted at every page bottom in this strftime format. +html_last_updated_fmt = "%Y-%m-%d" + +# -- Options for linkcode ----------------------------- + +branch = "main" +code_url = f"https://github.com/ezmsg-org/ezmsg-websocket/blob/{branch}/" + + +def linkcode_resolve(domain, info): + if domain != "py": + return None + if not info["module"]: + return None + filename = info["module"].replace(".", "/") + return f"{code_url}src/{filename}.py" diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..1767a3a --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,60 @@ +ezmsg.websocket +============= + +Short description of your ezmsg package. + +Overview +-------- + +``ezmsg-websocket`` provides ... for the `ezmsg `_ framework. + +Key features: + +* **Feature 1** - Description +* **Feature 2** - Description +* **Feature 3** - Description + +Installation +------------ + +Install from PyPI: + +.. code-block:: bash + + pip install ezmsg-websocket + +Or install the latest development version: + +.. code-block:: bash + + pip install git+https://github.com/ezmsg-org/ezmsg-websocket@main + +Dependencies +^^^^^^^^^^^^ + +Core dependencies: + +* ``ezmsg`` - Core messaging framework +* ``numpy`` - Numerical computing + +Quick Start +----------- + +For general ezmsg tutorials and guides, visit `ezmsg.org `_. + + +Documentation +------------- + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + api/index + + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` diff --git a/examples/ezmsg_websocket.py b/examples/ezmsg_websocket.py index 588766c..8ed1f32 100644 --- a/examples/ezmsg_websocket.py +++ b/examples/ezmsg_websocket.py @@ -1,23 +1,23 @@ +import asyncio import json import math import time -import asyncio +from typing import Any, AsyncGenerator, Dict, Tuple import ezmsg.core as ez -from ezmsg.websocket.units import WebsocketServer, WebsocketClient, WebsocketSettings - -from typing import Any, AsyncGenerator, Dict, Tuple +from ezmsg.websocket.units import WebsocketClient, WebsocketServer, WebsocketSettings # LFO: Low Frequency Oscillator + class LFOSettings(ez.Settings): freq: float = 0.2 # Hz, sinus frequency update_rate: float = 2.0 # Hz, update rate class LFO(ez.Unit): - SETTINGS: LFOSettings + SETTINGS = LFOSettings OUTPUT = ez.OutputStream(float) @@ -64,7 +64,7 @@ class WebsocketSystemSettings(ez.Settings): class WebsocketSystem(ez.Collection): - SETTINGS: WebsocketSystemSettings + SETTINGS = WebsocketSystemSettings OSC = LFO() SERVER = WebsocketServer() @@ -75,13 +75,9 @@ class WebsocketSystem(ez.Collection): def configure(self) -> None: self.OSC.apply_settings(LFOSettings(freq=0.2, update_rate=1.0)) - self.SERVER.apply_settings( - WebsocketSettings(host=self.SETTINGS.host, port=self.SETTINGS.port) - ) + self.SERVER.apply_settings(WebsocketSettings(host=self.SETTINGS.host, port=self.SETTINGS.port)) - self.CLIENT.apply_settings( - WebsocketSettings(host=self.SETTINGS.host, port=self.SETTINGS.port) - ) + self.CLIENT.apply_settings(WebsocketSettings(host=self.SETTINGS.host, port=self.SETTINGS.port)) # Define Connections def network(self) -> ez.NetworkDefinition: @@ -104,4 +100,4 @@ def process_components(self) -> Tuple[ez.Component, ...]: # Run the websocket system system = WebsocketSystem() system.apply_settings(WebsocketSystemSettings(host=host, port=port)) - ez.run(SYSTEM = system) + ez.run(SYSTEM=system) diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index ba9c5ad..0000000 --- a/poetry.lock +++ /dev/null @@ -1,295 +0,0 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.5.3" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "exceptiongroup" -version = "1.2.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "ezmsg" -version = "3.3.3" -description = "A simple DAG-based computation model" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ezmsg-3.3.3-py3-none-any.whl", hash = "sha256:62920470d8a692fcd986e980a80e27d0ec3c0a36677d2068ee75b8cf301a0cde"}, - {file = "ezmsg-3.3.3.tar.gz", hash = "sha256:411dd4e027e37bb322bfbcef75264d47134ea64efa2b428522c257d959ca439f"}, -] - -[package.dependencies] -typing-extensions = "*" - -[package.extras] -all-ext = ["ezmsg-sigproc", "ezmsg-websocket", "ezmsg-zmq"] -test = ["numpy", "pytest", "pytest-asyncio", "pytest-cov"] - -[[package]] -name = "flake8" -version = "5.0.4" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "packaging" -version = "24.1" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pycodestyle" -version = "2.9.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, - {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, -] - -[[package]] -name = "pyflakes" -version = "2.5.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, -] - -[[package]] -name = "pytest" -version = "7.4.4" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "5.0.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, -] - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "typing-extensions" -version = "4.9.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, -] - -[[package]] -name = "websockets" -version = "8.1" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"}, - {file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"}, - {file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"}, - {file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"}, - {file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"}, - {file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"}, - {file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"}, - {file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"}, - {file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"}, - {file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"}, - {file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"}, - {file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"}, - {file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"}, - {file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"}, - {file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"}, - {file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"}, - {file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"}, - {file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"}, - {file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"}, - {file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"}, - {file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"}, - {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.8" -content-hash = "3e73caeb193a2d43e48747a5879ebe5b483d427c1220b1da5ca52b2a2e21a7b2" diff --git a/pyproject.toml b/pyproject.toml index ec2aa9e..ec3322c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,29 +1,74 @@ -[tool.poetry] +[project] name = "ezmsg-websocket" -version = "1.1.2" description = "Websocket server and client units for ezmsg" authors = [ - "Milsap, Griffin ", - "Peranich, Preston ", + { name = "Griffin Milsap", email = "griffin.milsap@gmail.com" }, + { name = "Preston Peranich", email = "pperanich@gmail.com" }, ] license = "MIT" readme = "README.md" -packages = [{ include = "ezmsg", from = "src" }] -classifiers = [ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", +requires-python = ">=3.10" +dynamic = ["version"] +dependencies = [ + "ezmsg>=3.6.0", + "websockets>=8.1", ] -[tool.poetry.dependencies] -python = "^3.8" -ezmsg = "^3.3.0" -websockets = "^8.1" +[dependency-groups] +dev = [ + "pre-commit>=4.0.0", + {include-group = "lint"}, + {include-group = "test"}, +] +lint = [ + "ruff", +] +test = [ + "pytest>=8.0.0", + "pytest-cov", +] +docs = [ + "sphinx>=8.0", + "pydata-sphinx-theme", + "sphinx-autodoc-typehints", + "sphinx-copybutton", + "myst-parser", +] -[tool.poetry.group.test.dependencies] -pytest = "^7.0.0" -pytest-cov = "*" -flake8 = "*" +# Uncomment if your package provides CLI commands +# [project.scripts] +# ezmsg-example = "ezmsg.example.main:main" [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[tool.hatch.version] +source = "vcs" + +[tool.hatch.build.hooks.vcs] +version-file = "src/ezmsg/websocket/__version__.py" + +[tool.hatch.build.targets.wheel] +packages = ["src/ezmsg"] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" + +[tool.ruff] +line-length = 120 +target-version = "py310" +# Exclude auto-generated files +exclude = ["*/__version__.py"] + +[tool.ruff.lint] +select = ["E", "F", "I", "W"] + +[tool.ruff.lint.isort] +known-first-party = ["ezmsg.websocket"] +known-third-party = ["ezmsg", "websockets"] + +[tool.uv.sources] +# Uncomment to use development version of ezmsg from git +#ezmsg = { git = "https://github.com/ezmsg-org/ezmsg.git", branch = "dev" } diff --git a/src/ezmsg/websocket/__init__.py b/src/ezmsg/websocket/__init__.py index 5da1016..e881cad 100644 --- a/src/ezmsg/websocket/__init__.py +++ b/src/ezmsg/websocket/__init__.py @@ -1,4 +1,3 @@ import importlib.metadata - __version__ = importlib.metadata.version("ezmsg-websocket") diff --git a/src/ezmsg/websocket/units.py b/src/ezmsg/websocket/units.py index 6d9e25c..88d0957 100644 --- a/src/ezmsg/websocket/units.py +++ b/src/ezmsg/websocket/units.py @@ -1,15 +1,12 @@ import asyncio import ssl - from dataclasses import field - -import websockets.server -import websockets.exceptions -from websockets.legacy.client import connect, WebSocketClientProtocol +from typing import AsyncGenerator, Optional, Union import ezmsg.core as ez - -from typing import Optional, Union, AsyncGenerator +import websockets.exceptions +import websockets.server +from websockets.legacy.client import WebSocketClientProtocol, connect class WebsocketSettings(ez.Settings): @@ -19,36 +16,27 @@ class WebsocketSettings(ez.Settings): class WebsocketState(ez.State): - incoming_queue: "asyncio.Queue[Union[str,bytes]]" = field( - default_factory=asyncio.Queue - ) - outgoing_queue: "asyncio.Queue[Union[str,bytes]]" = field( - default_factory=asyncio.Queue - ) + incoming_queue: "asyncio.Queue[Union[str,bytes]]" = field(default_factory=asyncio.Queue) + outgoing_queue: "asyncio.Queue[Union[str,bytes]]" = field(default_factory=asyncio.Queue) class WebsocketServer(ez.Unit): - """ Receives arbitrary content from outside world and injects it into system in a DataArray """ - SETTINGS: WebsocketSettings - STATE: WebsocketState + SETTINGS = WebsocketSettings + STATE = WebsocketState INPUT = ez.InputStream(bytes) OUTPUT = ez.OutputStream(bytes) @ez.task async def start_server(self): - ez.logger.info( - f"Starting WS Input Server @ ws://{self.SETTINGS.host}:{self.SETTINGS.port}" - ) + ez.logger.info(f"Starting WS Input Server @ ws://{self.SETTINGS.host}:{self.SETTINGS.port}") - async def connection( - websocket: websockets.server.WebSocketServerProtocol, path - ): + async def connection(websocket: websockets.server.WebSocketServerProtocol, path): async def loop(mode): try: if mode == "rx": @@ -78,9 +66,7 @@ async def loop(mode): else: ssl_context = None - server = await websockets.server.serve( - connection, self.SETTINGS.host, self.SETTINGS.port, ssl=ssl_context - ) + server = await websockets.server.serve(connection, self.SETTINGS.host, self.SETTINGS.port, ssl=ssl_context) await server.wait_closed() @@ -99,8 +85,8 @@ async def transmit_outgoing(self, message: bytes): class WebsocketClient(ez.Unit): - SETTINGS: WebsocketSettings - STATE: WebsocketState + SETTINGS = WebsocketSettings + STATE = WebsocketState INPUT = ez.InputStream(bytes) OUTPUT = ez.OutputStream(bytes) @@ -130,7 +116,7 @@ async def connection(self): try: websocket = await connect(uri) break - except: + except Exception: await asyncio.sleep(0.5) if websocket is None: @@ -138,9 +124,7 @@ async def connection(self): receive_task = asyncio.ensure_future(self.rx_from(websocket)) transmit_task = asyncio.ensure_future(self.tx_to(websocket)) - done, pending = await asyncio.wait( - [receive_task, transmit_task], return_when=asyncio.FIRST_COMPLETED - ) + done, pending = await asyncio.wait([receive_task, transmit_task], return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel()