pax_global_header00006660000000000000000000000064145450117250014516gustar00rootroot0000000000000052 comment=3e6a08ef4d45cbc11dacf454af9343209510887e comm-0.2.1/000077500000000000000000000000001454501172500124515ustar00rootroot00000000000000comm-0.2.1/.github/000077500000000000000000000000001454501172500140115ustar00rootroot00000000000000comm-0.2.1/.github/dependabot.yml000066400000000000000000000005071454501172500166430ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" - package-ecosystem: "pip" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" comm-0.2.1/.github/workflows/000077500000000000000000000000001454501172500160465ustar00rootroot00000000000000comm-0.2.1/.github/workflows/downstream.yml000066400000000000000000000025141454501172500207560ustar00rootroot00000000000000name: Test downstream projects on: push: branches: ["main"] pull_request: concurrency: group: downstream-${{ github.ref }} cancel-in-progress: true jobs: ipykernel: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - uses: jupyterlab/maintainer-tools/.github/actions/downstream-test@v1 with: package_name: ipykernel ipywidgets: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Base Setup uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Run Test uses: jupyterlab/maintainer-tools/.github/actions/downstream-test@v1 with: package_name: ipywidgets test_command: pytest -vv -raXxs -k \"not deprecation_fa_icons and not tooltip_deprecation and not on_submit_deprecation\" -W default --durations 10 --color=yes downstream_check: # This job does nothing and is only used for the branch protection if: always() needs: - ipykernel - ipywidgets runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} comm-0.2.1/.github/workflows/prep-release.yml000066400000000000000000000026571454501172500211670ustar00rootroot00000000000000name: "Step 1: Prep Release" on: workflow_dispatch: inputs: version_spec: description: "New Version Specifier" default: "next" required: false branch: description: "The branch to target" required: false post_version_spec: description: "Post Version Specifier" required: false since: description: "Use PRs with activity since this date or git reference" required: false since_last_stable: description: "Use PRs with activity since the last stable git tag" required: false type: boolean jobs: prep_release: runs-on: ubuntu-latest steps: - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Prep Release id: prep-release uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2 with: token: ${{ secrets.ADMIN_GITHUB_TOKEN }} version_spec: ${{ github.event.inputs.version_spec }} post_version_spec: ${{ github.event.inputs.post_version_spec }} target: ${{ github.event.inputs.target }} branch: ${{ github.event.inputs.branch }} since: ${{ github.event.inputs.since }} since_last_stable: ${{ github.event.inputs.since_last_stable }} - name: "** Next Step **" run: | echo "Optional): Review Draft Release: ${{ steps.prep-release.outputs.release_url }}" comm-0.2.1/.github/workflows/publish-release.yml000066400000000000000000000034761454501172500216670ustar00rootroot00000000000000name: "Step 2: Publish Release" on: workflow_dispatch: inputs: branch: description: "The target branch" required: false release_url: description: "The URL of the draft GitHub release" required: false steps_to_skip: description: "Comma separated list of steps to skip" required: false jobs: publish_release: runs-on: ubuntu-latest steps: - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Populate Release id: populate-release uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2 with: token: ${{ secrets.ADMIN_GITHUB_TOKEN }} target: ${{ github.event.inputs.target }} branch: ${{ github.event.inputs.branch }} release_url: ${{ github.event.inputs.release_url }} steps_to_skip: ${{ github.event.inputs.steps_to_skip }} - name: Finalize Release id: finalize-release env: PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} PYPI_TOKEN_MAP: ${{ secrets.PYPI_TOKEN_MAP }} TWINE_USERNAME: __token__ NPM_TOKEN: ${{ secrets.NPM_TOKEN }} uses: jupyter-server/jupyter-releaser/.github/actions/finalize-release@v2 with: token: ${{ secrets.ADMIN_GITHUB_TOKEN }} target: ${{ github.event.inputs.target }} release_url: ${{ steps.populate-release.outputs.release_url }} - name: "** Next Step **" if: ${{ success() }} run: | echo "Verify the final release" echo ${{ steps.finalize-release.outputs.release_url }} - name: "** Failure Message **" if: ${{ failure() }} run: | echo "Failed to Publish the Draft Release Url:" echo ${{ steps.populate-release.outputs.release_url }} comm-0.2.1/.github/workflows/python-tests.yml000066400000000000000000000043601454501172500212550ustar00rootroot00000000000000name: Comm Tests on: push: branches: ["main"] pull_request: workflow_dispatch: schedule: - cron: "0 8 * * *" jobs: build: runs-on: ${{ matrix.os }} timeout-minutes: 20 strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ["3.8", "3.12"] include: - os: windows-latest python-version: "3.9" - os: ubuntu-latest python-version: "pypy-3.9" - os: ubuntu-latest python-version: "3.10" - os: macos-latest python-version: "3.11" steps: - name: Checkout uses: actions/checkout@v4 - name: Base Setup uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Run test run: hatch run test:test test_lint: name: Test Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Run Linters run: | hatch run typing:test hatch run lint:build pipx run 'validate-pyproject[all]' pyproject.toml check_release: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Base Setup uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - name: Install Dependencies run: | pip install -e . - name: Check Release uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2 with: version_spec: 100.100.100 token: ${{ secrets.GITHUB_TOKEN }} check_links: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1 check: # This job does nothing and is only used for the branch protection if: always() needs: - check_links - check_release - test_lint - build runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} comm-0.2.1/.gitignore000066400000000000000000000000371454501172500144410ustar00rootroot00000000000000comm.egg-info/ **/__pycache__/ comm-0.2.1/.pre-commit-config.yaml000066400000000000000000000041361454501172500167360ustar00rootroot00000000000000ci: autoupdate_schedule: monthly autoupdate_commit_msg: "chore: update pre-commit hooks" repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-case-conflict - id: check-ast - id: check-docstring-first - id: check-executables-have-shebangs - id: check-added-large-files - id: check-case-conflict - id: check-merge-conflict - id: check-json - id: check-toml - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema rev: 0.27.3 hooks: - id: check-github-workflows - repo: https://github.com/executablebooks/mdformat rev: 0.7.17 hooks: - id: mdformat additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-footnote] - repo: https://github.com/pre-commit/mirrors-prettier rev: "v4.0.0-alpha.8" hooks: - id: prettier types_or: [yaml, html, json] - repo: https://github.com/adamchainz/blacken-docs rev: "1.16.0" hooks: - id: blacken-docs additional_dependencies: [black==23.7.0] - repo: https://github.com/codespell-project/codespell rev: "v2.2.6" hooks: - id: codespell args: ["-L", "sur,nd"] - repo: https://github.com/pre-commit/pygrep-hooks rev: "v1.10.0" hooks: - id: rst-backticks - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/pre-commit/mirrors-mypy rev: "v1.8.0" hooks: - id: mypy files: comm stages: [manual] additional_dependencies: ["traitlets>=5.13"] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.9 hooks: - id: ruff types_or: [python, jupyter] args: ["--fix", "--show-fixes"] - id: ruff-format types_or: [python, jupyter] - repo: https://github.com/scientific-python/cookie rev: "2023.12.21" hooks: - id: sp-repo-review additional_dependencies: ["repo-review[cli]"] comm-0.2.1/CHANGELOG.md000066400000000000000000000102171454501172500142630ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. ## 0.2.1 ([Full Changelog](https://github.com/ipython/comm/compare/v0.2.0...46e07dc298d19c1b7ade765d0a435f794e69a020)) ### Maintenance and upkeep improvements - Update ruff config [#23](https://github.com/ipython/comm/pull/23) ([@blink1073](https://github.com/blink1073)) ### Other merged PRs - chore: update pre-commit hooks [#25](https://github.com/ipython/comm/pull/25) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - Prevent exception spam during interpreter teardown [#24](https://github.com/ipython/comm/pull/24) ([@apmorton](https://github.com/apmorton)) - chore: update pre-commit hooks [#22](https://github.com/ipython/comm/pull/22) ([@pre-commit-ci](https://github.com/pre-commit-ci)) - chore: update pre-commit hooks [#21](https://github.com/ipython/comm/pull/21) ([@pre-commit-ci](https://github.com/pre-commit-ci)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/ipython/comm/graphs/contributors?from=2023-11-06&to=2024-01-02&type=c)) [@apmorton](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Aapmorton+updated%3A2023-11-06..2024-01-02&type=Issues) | [@blink1073](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Ablink1073+updated%3A2023-11-06..2024-01-02&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Apre-commit-ci+updated%3A2023-11-06..2024-01-02&type=Issues) ## 0.2.0 ([Full Changelog](https://github.com/ipython/comm/compare/v0.1.4...5e4ad3166b80feba3f74ad074b7b5f98d7a99439)) ### Maintenance and upkeep improvements - Add downstream checks [#20](https://github.com/ipython/comm/pull/20) ([@blink1073](https://github.com/blink1073)) - Adopt sp-repo-review [#19](https://github.com/ipython/comm/pull/19) ([@blink1073](https://github.com/blink1073)) ### Other merged PRs - Bump actions/checkout from 3 to 4 [#17](https://github.com/ipython/comm/pull/17) ([@dependabot](https://github.com/dependabot)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/ipython/comm/graphs/contributors?from=2023-08-02&to=2023-11-06&type=c)) [@blink1073](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Ablink1073+updated%3A2023-08-02..2023-11-06&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Adependabot+updated%3A2023-08-02..2023-11-06&type=Issues) ## 0.1.4 ([Full Changelog](https://github.com/ipython/comm/compare/v0.1.3...136c099e4fb1cc83040661796ad7ea349af04be8)) ### Improvements - Loosen dependency on traitlet so it is available in python 3.6 [#16](https://github.com/ipython/comm/pull/16) ([@vincent-grosbois](https://github.com/vincent-grosbois)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/ipython/comm/graphs/contributors?from=2023-03-22&to=2023-08-02&type=c)) [@martinRenou](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3AmartinRenou+updated%3A2023-03-22..2023-08-02&type=Issues) | [@vincent-grosbois](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Avincent-grosbois+updated%3A2023-03-22..2023-08-02&type=Issues) ## 0.1.3 ([Full Changelog](https://github.com/ipython/comm/compare/0.1.2...309b8295ca950a9ca9bdc0daa796215d72a7cb09)) ### Maintenance and upkeep improvements - Adopt linters and releaser [#12](https://github.com/ipython/comm/pull/12) ([@blink1073](https://github.com/blink1073)) ### Other merged PRs - feat: provide a default implementation [#13](https://github.com/ipython/comm/pull/13) ([@maartenbreddels](https://github.com/maartenbreddels)) ### Contributors to this release ([GitHub contributors page for this release](https://github.com/ipython/comm/graphs/contributors?from=2022-12-08&to=2023-03-22&type=c)) [@blink1073](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Ablink1073+updated%3A2022-12-08..2023-03-22&type=Issues) | [@maartenbreddels](https://github.com/search?q=repo%3Aipython%2Fcomm+involves%3Amaartenbreddels+updated%3A2022-12-08..2023-03-22&type=Issues) ## 0.1.2 Initial release comm-0.2.1/LICENSE000066400000000000000000000027531454501172500134650ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2022, Jupyter All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. comm-0.2.1/README.md000066400000000000000000000024661454501172500137400ustar00rootroot00000000000000# Comm It provides a way to register a Kernel Comm implementation, as per the Jupyter kernel protocol. It also provides a base Comm implementation and a default CommManager that can be used. ## Register a comm implementation in the kernel: ### Case 1: Using the default CommManager and the BaseComm implementations We provide default implementations for usage in IPython: ```python import comm class MyCustomComm(comm.base_comm.BaseComm): def publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys): # TODO implement the logic for sending comm messages through the iopub channel pass comm.create_comm = MyCustomComm ``` This is typically what ipykernel and JupyterLite's pyolite kernel will do. ### Case 2: Providing your own comm manager creation implementation ```python import comm comm.create_comm = custom_create_comm comm.get_comm_manager = custom_comm_manager_getter ``` This is typically what xeus-python does (it has its own manager implementation using xeus's C++ messaging logic). ## Comm users Libraries like ipywidgets can then use the comms implementation that has been registered by the kernel: ```python from comm import create_comm, get_comm_manager # Create a comm comm_manager = get_comm_manager() comm = create_comm() comm_manager.register_comm(comm) ``` comm-0.2.1/comm/000077500000000000000000000000001454501172500134045ustar00rootroot00000000000000comm-0.2.1/comm/__init__.py000066400000000000000000000026411454501172500155200ustar00rootroot00000000000000"""Comm package. Copyright (c) IPython Development Team. Distributed under the terms of the Modified BSD License. This package provides a way to register a Kernel Comm implementation, as per the Jupyter kernel protocol. It also provides a base Comm implementation and a default CommManager for the IPython case. """ from __future__ import annotations from typing import Any from .base_comm import BaseComm, BuffersType, CommManager, MaybeDict __version__ = "0.2.1" __all__ = [ "create_comm", "get_comm_manager", "__version__", ] _comm_manager = None class DummyComm(BaseComm): def publish_msg( self, msg_type: str, data: MaybeDict = None, metadata: MaybeDict = None, buffers: BuffersType = None, **keys: Any, ) -> None: pass def _create_comm(*args: Any, **kwargs: Any) -> BaseComm: """Create a Comm. This method is intended to be replaced, so that it returns your Comm instance. """ return DummyComm(*args, **kwargs) def _get_comm_manager() -> CommManager: """Get the current Comm manager, creates one if there is none. This method is intended to be replaced if needed (if you want to manage multiple CommManagers). """ global _comm_manager # noqa: PLW0603 if _comm_manager is None: _comm_manager = CommManager() return _comm_manager create_comm = _create_comm get_comm_manager = _get_comm_manager comm-0.2.1/comm/base_comm.py000066400000000000000000000236511454501172500157120ustar00rootroot00000000000000"""Default classes for Comm and CommManager, for usage in IPython. """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import annotations import contextlib import logging import typing as t import uuid from traitlets.utils.importstring import import_item import comm if t.TYPE_CHECKING: from zmq.eventloop.zmqstream import ZMQStream logger = logging.getLogger("Comm") MessageType = t.Dict[str, t.Any] MaybeDict = t.Optional[t.Dict[str, t.Any]] BuffersType = t.Optional[t.List[bytes]] CommCallback = t.Callable[[MessageType], None] CommTargetCallback = t.Callable[["BaseComm", MessageType], None] class BaseComm: """Class for communicating between a Frontend and a Kernel Must be subclassed with a publish_msg method implementation which sends comm messages through the iopub channel. """ def __init__( self, target_name: str = "comm", data: MaybeDict = None, metadata: MaybeDict = None, buffers: BuffersType = None, comm_id: str | None = None, primary: bool = True, target_module: str | None = None, topic: bytes | None = None, _open_data: MaybeDict = None, _close_data: MaybeDict = None, **kwargs: t.Any, ) -> None: super().__init__(**kwargs) self.comm_id = comm_id if comm_id else uuid.uuid4().hex self.primary = primary self.target_name = target_name self.target_module = target_module self.topic = topic if topic else ("comm-%s" % self.comm_id).encode("ascii") self._open_data = _open_data if _open_data else {} self._close_data = _close_data if _close_data else {} self._msg_callback: CommCallback | None = None self._close_callback: CommCallback | None = None self._closed = True if self.primary: # I am primary, open my peer. self.open(data=data, metadata=metadata, buffers=buffers) else: self._closed = False def publish_msg( self, msg_type: str, # noqa: ARG002 data: MaybeDict = None, # noqa: ARG002 metadata: MaybeDict = None, # noqa: ARG002 buffers: BuffersType = None, # noqa: ARG002 **keys: t.Any, # noqa: ARG002 ) -> None: msg = "publish_msg Comm method is not implemented" raise NotImplementedError(msg) def __del__(self) -> None: """trigger close on gc""" with contextlib.suppress(Exception): # any number of things can have gone horribly wrong # when called during interpreter teardown self.close(deleting=True) # publishing messages def open( self, data: MaybeDict = None, metadata: MaybeDict = None, buffers: BuffersType = None ) -> None: """Open the frontend-side version of this comm""" if data is None: data = self._open_data comm_manager = comm.get_comm_manager() if comm_manager is None: msg = "Comms cannot be opened without a comm_manager." # type:ignore[unreachable] raise RuntimeError(msg) comm_manager.register_comm(self) try: self.publish_msg( "comm_open", data=data, metadata=metadata, buffers=buffers, target_name=self.target_name, target_module=self.target_module, ) self._closed = False except Exception: comm_manager.unregister_comm(self) raise def close( self, data: MaybeDict = None, metadata: MaybeDict = None, buffers: BuffersType = None, deleting: bool = False, ) -> None: """Close the frontend-side version of this comm""" if self._closed: # only close once return self._closed = True if data is None: data = self._close_data self.publish_msg( "comm_close", data=data, metadata=metadata, buffers=buffers, ) if not deleting: # If deleting, the comm can't be registered comm.get_comm_manager().unregister_comm(self) def send( self, data: MaybeDict = None, metadata: MaybeDict = None, buffers: BuffersType = None ) -> None: """Send a message to the frontend-side version of this comm""" self.publish_msg( "comm_msg", data=data, metadata=metadata, buffers=buffers, ) # registering callbacks def on_close(self, callback: CommCallback | None) -> None: """Register a callback for comm_close Will be called with the `data` of the close message. Call `on_close(None)` to disable an existing callback. """ self._close_callback = callback def on_msg(self, callback: CommCallback | None) -> None: """Register a callback for comm_msg Will be called with the `data` of any comm_msg messages. Call `on_msg(None)` to disable an existing callback. """ self._msg_callback = callback # handling of incoming messages def handle_close(self, msg: MessageType) -> None: """Handle a comm_close message""" logger.debug("handle_close[%s](%s)", self.comm_id, msg) if self._close_callback: self._close_callback(msg) def handle_msg(self, msg: MessageType) -> None: """Handle a comm_msg message""" logger.debug("handle_msg[%s](%s)", self.comm_id, msg) if self._msg_callback: from IPython import get_ipython shell = get_ipython() if shell: shell.events.trigger("pre_execute") self._msg_callback(msg) if shell: shell.events.trigger("post_execute") class CommManager: """Default CommManager singleton implementation for Comms in the Kernel""" # Public APIs def __init__(self) -> None: self.comms: dict[str, BaseComm] = {} self.targets: dict[str, CommTargetCallback] = {} def register_target(self, target_name: str, f: CommTargetCallback | str) -> None: """Register a callable f for a given target name f will be called with two arguments when a comm_open message is received with `target`: - the Comm instance - the `comm_open` message itself. f can be a Python callable or an import string for one. """ if isinstance(f, str): f = import_item(f) self.targets[target_name] = t.cast(CommTargetCallback, f) def unregister_target(self, target_name: str, f: CommTargetCallback) -> CommTargetCallback: # noqa: ARG002 """Unregister a callable registered with register_target""" return self.targets.pop(target_name) def register_comm(self, comm: BaseComm) -> str: """Register a new comm""" comm_id = comm.comm_id self.comms[comm_id] = comm return comm_id def unregister_comm(self, comm: BaseComm) -> None: """Unregister a comm, and close its counterpart""" # unlike get_comm, this should raise a KeyError comm = self.comms.pop(comm.comm_id) def get_comm(self, comm_id: str) -> BaseComm | None: """Get a comm with a particular id Returns the comm if found, otherwise None. This will not raise an error, it will log messages if the comm cannot be found. """ try: return self.comms[comm_id] except KeyError: logger.warning("No such comm: %s", comm_id) if logger.isEnabledFor(logging.DEBUG): # don't create the list of keys if debug messages aren't enabled logger.debug("Current comms: %s", list(self.comms.keys())) return None # Message handlers def comm_open(self, stream: ZMQStream, ident: str, msg: MessageType) -> None: # noqa: ARG002 """Handler for comm_open messages""" from comm import create_comm content = msg["content"] comm_id = content["comm_id"] target_name = content["target_name"] f = self.targets.get(target_name, None) comm = create_comm( comm_id=comm_id, primary=False, target_name=target_name, ) self.register_comm(comm) if f is None: logger.error("No such comm target registered: %s", target_name) else: try: f(comm, msg) return except Exception: logger.error("Exception opening comm with target: %s", target_name, exc_info=True) # Failure. try: comm.close() except Exception: logger.error( """Could not close comm during `comm_open` failure clean-up. The comm may not have been opened yet.""", exc_info=True, ) def comm_msg(self, stream: ZMQStream, ident: str, msg: MessageType) -> None: # noqa: ARG002 """Handler for comm_msg messages""" content = msg["content"] comm_id = content["comm_id"] comm = self.get_comm(comm_id) if comm is None: return try: comm.handle_msg(msg) except Exception: logger.error("Exception in comm_msg for %s", comm_id, exc_info=True) def comm_close(self, stream: ZMQStream, ident: str, msg: MessageType) -> None: # noqa: ARG002 """Handler for comm_close messages""" content = msg["content"] comm_id = content["comm_id"] comm = self.get_comm(comm_id) if comm is None: return self.comms[comm_id]._closed = True del self.comms[comm_id] try: comm.handle_close(msg) except Exception: logger.error("Exception in comm_close for %s", comm_id, exc_info=True) __all__ = ["CommManager", "BaseComm"] comm-0.2.1/comm/py.typed000066400000000000000000000000001454501172500150710ustar00rootroot00000000000000comm-0.2.1/pyproject.toml000066400000000000000000000060221454501172500153650ustar00rootroot00000000000000[build-system] requires = ["hatchling>=1.10"] build-backend = "hatchling.build" [project] name = "comm" dynamic = ["version"] description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." readme = "README.md" license = { file="LICENSE" } requires-python = ">=3.8" authors = [ { name = "Jupyter contributors" }, ] keywords = [ "ipykernel", "jupyter", "xeus-python", ] classifiers = [ "Framework :: Jupyter", "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3", ] dependencies = [ "traitlets>=4", ] [project.optional-dependencies] test = [ "pytest", ] [project.urls] Homepage = "https://github.com/ipython/comm" [tool.hatch.version] path = "comm/__init__.py" [tool.hatch.build.targets.sdist] include = [ "/comm", ] [tool.hatch.envs.test] features = ["test"] [tool.hatch.envs.test.scripts] test = "python -m pytest -vv {args}" nowarn = "test -W default {args}" [tool.hatch.envs.typing] dependencies = ["pre-commit"] detached = true [tool.hatch.envs.typing.scripts] test = "pre-commit run --all-files --hook-stage manual mypy" [tool.hatch.envs.lint] dependencies = ["pre-commit"] detached = true [tool.hatch.envs.lint.scripts] build = [ "pre-commit run --all-files ruff", "pre-commit run --all-files ruff-format" ] [tool.pytest.ini_options] minversion = "6.0" testpaths = ["tests"] xfail_strict = true log_cli_level = "info" addopts = [ "-raXs", "--durations=10", "--color=yes", "--doctest-modules", "--showlocals", "--strict-markers", "--strict-config" ] filterwarnings = ["error"] [tool.mypy] files = "comm" python_version = "3.8" strict = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] warn_unreachable = true [tool.ruff] line-length = 100 [tool.ruff.lint] extend-select = [ "B", # flake8-bugbear "I", # isort "ARG", # flake8-unused-arguments "C4", # flake8-comprehensions "EM", # flake8-errmsg "ICN", # flake8-import-conventions "G", # flake8-logging-format "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # pylint "PTH", # flake8-use-pathlib "PT", # flake8-pytest-style "RET", # flake8-return "RUF", # Ruff-specific "SIM", # flake8-simplify "T20", # flake8-print "UP", # pyupgrade "YTT", # flake8-2020 "EXE", # flake8-executable "PYI", # flake8-pyi "S", # flake8-bandit ] ignore = [ "PLR", # Design related pylint codes "S101", # Use of `assert` detected "G201" # Logging `.exception(...)` should be used instead ] unfixable = [ # Don't touch print statements "T201", # Don't touch noqa lines "RUF100", ] [tool.ruff.lint.per-file-ignores] "tests/*" = [] [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false mark-parentheses = false parametrize-names-type = "csv" [tool.repo-review] ignore = ["PP308", "PY004", "GH102", "MY101", "RTD100"] comm-0.2.1/tests/000077500000000000000000000000001454501172500136135ustar00rootroot00000000000000comm-0.2.1/tests/test_comm.py000066400000000000000000000007271454501172500161650ustar00rootroot00000000000000from typing import Any from comm.base_comm import BaseComm, CommManager class MyComm(BaseComm): def publish_msg( self, msg_type: str, data: Any = None, metadata: Any = None, buffers: Any = None, **keys: Any, ) -> None: pass def test_comm_manager() -> None: test = CommManager() assert test.targets == {} def test_base_comm() -> None: test = MyComm() assert test.target_name == "comm"