{"id":21974263,"url":"https://github.com/frequenz-floss/frequenz-channels-python","last_synced_at":"2026-03-15T18:08:58.367Z","repository":{"id":59983898,"uuid":"528020497","full_name":"frequenz-floss/frequenz-channels-python","owner":"frequenz-floss","description":"Channel implementations for Python","archived":false,"fork":false,"pushed_at":"2025-04-17T06:55:23.000Z","size":7778,"stargazers_count":8,"open_issues_count":13,"forks_count":8,"subscribers_count":3,"default_branch":"v1.x.x","last_synced_at":"2025-04-17T21:22:24.611Z","etag":null,"topics":["channel","channels","frequenz","library","python","python3"],"latest_commit_sha":null,"homepage":"https://frequenz-floss.github.io/frequenz-channels-python/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/frequenz-floss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-08-23T14:10:34.000Z","updated_at":"2025-04-17T06:52:32.000Z","dependencies_parsed_at":"2023-02-15T10:16:37.647Z","dependency_job_id":"c31bca74-8939-4cd6-8dd3-bae6f427bf66","html_url":"https://github.com/frequenz-floss/frequenz-channels-python","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frequenz-floss%2Ffrequenz-channels-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frequenz-floss%2Ffrequenz-channels-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frequenz-floss%2Ffrequenz-channels-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frequenz-floss%2Ffrequenz-channels-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frequenz-floss","download_url":"https://codeload.github.com/frequenz-floss/frequenz-channels-python/tar.gz/refs/heads/v1.x.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251332315,"owners_count":21572595,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["channel","channels","frequenz","library","python","python3"],"created_at":"2024-11-29T15:40:26.660Z","updated_at":"2026-03-15T18:08:53.338Z","avatar_url":"https://github.com/frequenz-floss.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Frequenz channels\n\n[![Build Status](https://github.com/frequenz-floss/frequenz-channels-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-channels-python/actions/workflows/ci.yaml)\n[![PyPI Package](https://img.shields.io/pypi/v/frequenz-channels)](https://pypi.org/project/frequenz-channels/)\n[![Docs](https://img.shields.io/badge/docs-latest-informational)](https://frequenz-floss.github.io/frequenz-channels-python/)\n\n## Introduction\n\n\u003c!-- introduction --\u003e\n\nFrequenz Channels is a *channels* implementation for Python.\n\nAccording to [Wikipedia](https://en.wikipedia.org/wiki/Channel_(programming)):\n\n\u003e A channel is a model for interprocess communication and synchronization via\n\u003e message passing. A message may be sent over a channel, and another process or\n\u003e thread is able to receive messages sent over a channel it has a reference to,\n\u003e as a stream. Different implementations of channels may be buffered or not,\n\u003e and either synchronous or asynchronous.\n\nFrequenz Channels are mostly designed after [Go\nchannels](https://tour.golang.org/concurrency/2) but it also borrows ideas from\n[Rust channels](https://doc.rust-lang.org/book/ch16-02-message-passing.html).\n\n\u003c!-- /introduction --\u003e\n\n## Supported Platforms\n\n\u003c!-- supported-platforms --\u003e\n\nThe following platforms are officially supported (tested):\n\n- **Python:** 3.11\n- **Operating System:** Ubuntu Linux 20.04\n- **Architectures:** amd64, arm64\n\n\u003e [!NOTE]\n\u003e Newer Python versions and other operating systems and architectures might\n\u003e work too, but they are not automatically tested, so we cannot guarantee it.\n\n\u003c!-- /supported-platforms --\u003e\n\n## Quick Start\n\n### Installing\n\n\u003c!-- quick-start-installing --\u003e\n\nAssuming a [supported](#supported-platforms) working Python environment:\n\n```sh\npython3 -m pip install frequenz-channels\n```\n\n\u003e [!TIP]\n\u003e For more details please read the [Installation\n\u003e Guide](docs/user-guide/installation.md).\n\n\u003c!-- /quick-start-installing --\u003e\n\n### Examples\n\n#### Hello World\n\n\u003c!-- quick-start-hello-world --\u003e\n\n```python\nimport asyncio\n\nfrom frequenz.channels import Anycast\n\n\nasync def main() -\u003e None:\n    hello_channel = Anycast[str](name=\"hello-world-channel\")\n    sender = hello_channel.new_sender()\n    receiver = hello_channel.new_receiver()\n\n    await sender.send(\"Hello World!\")\n    message = await receiver.receive()\n    print(message)\n\n\nasyncio.run(main())\n```\n\n\u003c!-- /quick-start-hello-world --\u003e\n\n#### Showcase\n\n\u003c!-- quick-start-showcase --\u003e\n\nThis is a comprehensive example that shows most of the main features of the\nlibrary:\n\n```python\nimport asyncio\nfrom dataclasses import dataclass\nfrom datetime import timedelta\nfrom enum import Enum, auto\nfrom typing import assert_never\n\nfrom frequenz.channels import (\n    Anycast,\n    Broadcast,\n    Receiver,\n    Sender,\n    merge,\n    select,\n    selected_from,\n)\nfrom frequenz.channels.timer import SkipMissedAndDrift, Timer, TriggerAllMissed\n\n\nclass Command(Enum):\n    PING = auto()\n    STOP_SENDER = auto()\n\n\nclass ReplyCommand(Enum):\n    PONG = auto()\n\n\n@dataclass(frozen=True)\nclass Reply:\n    reply: ReplyCommand\n    source: str\n\n\nasync def send(\n    sender: Sender[str],\n    control_command: Receiver[Command],\n    control_reply: Sender[Reply],\n) -\u003e None:\n    \"\"\"Send a counter value every second, until a stop command is received.\"\"\"\n    print(f\"{sender}: Starting\")\n    timer = Timer(timedelta(seconds=1.0), TriggerAllMissed())\n    counter = 0\n    async for selected in select(timer, control_command):\n        if selected_from(selected, timer):\n            print(f\"{sender}: Sending {counter}\")\n            await sender.send(f\"{sender}: {counter}\")\n            counter += 1\n        elif selected_from(selected, control_command):\n            print(f\"{sender}: Received command: {selected.message.name}\")\n            match selected.message:\n                case Command.STOP_SENDER:\n                    print(f\"{sender}: Stopping\")\n                    break\n                case Command.PING:\n                    print(f\"{sender}: Ping received, reply with pong\")\n                    await control_reply.send(Reply(ReplyCommand.PONG, str(sender)))\n                case _ as unknown:\n                    assert_never(unknown)\n    print(f\"{sender}: Finished\")\n\n\nasync def receive(\n    receivers: list[Receiver[str]],\n    control_command: Receiver[Command],\n    control_reply: Sender[Reply],\n) -\u003e None:\n    \"\"\"Receive data from multiple channels, until no more data is received for 2 seconds.\"\"\"\n    print(\"receive: Starting\")\n    timer = Timer(timedelta(seconds=2.0), SkipMissedAndDrift())\n    print(f\"{timer=}\")\n    merged = merge(*receivers)\n    async for selected in select(merged, timer, control_command):\n        if selected_from(selected, merged):\n            message = selected.message\n            print(f\"receive: Received {message=}\")\n            timer.reset()\n            print(f\"{timer=}\")\n        elif selected_from(selected, control_command):\n            print(f\"receive: received command: {selected.message.name}\")\n            match selected.message:\n                case Command.PING:\n                    print(\"receive: Ping received, reply with pong\")\n                    await control_reply.send(Reply(ReplyCommand.PONG, \"receive\"))\n                case Command.STOP_SENDER:\n                    pass  # Ignore\n                case _ as unknown:\n                    assert_never(unknown)\n        elif selected_from(selected, timer):\n            drift = selected.message\n            print(\n                f\"receive: No data received for {timer.interval + drift} seconds, \"\n                \"giving up\"\n            )\n            break\n    print(\"receive: Finished\")\n\n\nasync def main() -\u003e None:\n    data_channel_1 = Anycast[str](name=\"data-channel-1\")\n    data_channel_2 = Anycast[str](name=\"data-channel-2\")\n    command_channel = Broadcast[Command](name=\"control-channel\")  # (1)!\n    reply_channel = Anycast[Reply](name=\"reply-channel\")\n\n    async with asyncio.TaskGroup() as tasks:\n        tasks.create_task(\n            send(\n                data_channel_1.new_sender(),\n                command_channel.new_receiver(),\n                reply_channel.new_sender(),\n            ),\n            name=\"send-channel-1\",\n        )\n        tasks.create_task(\n            send(\n                data_channel_2.new_sender(),\n                command_channel.new_receiver(),\n                reply_channel.new_sender(),\n            ),\n            name=\"send-channel-2\",\n        )\n        tasks.create_task(\n            receive(\n                [data_channel_1.new_receiver(), data_channel_2.new_receiver()],\n                command_channel.new_receiver(),\n                reply_channel.new_sender(),\n            ),\n            name=\"receive\",\n        )\n\n        control_sender = command_channel.new_sender()\n        reply_receiver = reply_channel.new_receiver()\n\n        # Send a ping command to all tasks and wait for the replies\n        await control_sender.send(Command.PING)\n        print(f\"main: {await reply_receiver.receive()}\")\n        print(f\"main: {await reply_receiver.receive()}\")\n        print(f\"main: {await reply_receiver.receive()}\")\n\n        await asyncio.sleep(5.0)\n\n        # Stop senders, after 2 seconds not receiving any data,\n        # the receiver will stop too\n        await control_sender.send(Command.STOP_SENDER)\n\n\nasyncio.run(main())\n```\n\n\u003c!-- /quick-start-showcase --\u003e\n\n## Documentation\n\nFor more information, please read the [documentation\nwebsite](https://frequenz-floss.github.io/frequenz-channels-python/).\n\n## Contributing\n\nIf you want to know how to build this project and contribute to it, please\ncheck out the [Contributing Guide](docs/CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrequenz-floss%2Ffrequenz-channels-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrequenz-floss%2Ffrequenz-channels-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrequenz-floss%2Ffrequenz-channels-python/lists"}