Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 3 additions & 23 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,6 @@ jobs:
run: |
pytest tests/unit

build-ironpython:
name: windows-ironpython
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
shell: cmd
run: |
curl -o compas.tar.gz -LJO https://pypi.debian.net/compas/latest
curl -o ironpython-pytest.tar.gz -LJO https://pypi.debian.net/ironpython-pytest/latest
choco install ironpython --version=2.7.8.1
ipy -X:Frames -m ensurepip
ipy -X:Frames -m pip install --no-deps ironpython-pytest.tar.gz

rem untar and rename, these cannot be installed using ironpip because they not longer have a setup.py
tar -xf compas.tar.gz && for /d %%i in (compas-*) do ren "%%i" compas

- name: Run tests
env:
IRONPYTHONPATH: ./src;./compas/src
run: |
ipy -m pytest tests/unit

integration_tests:
if: "!contains(github.event.pull_request.labels.*.name, 'docs-only')"
runs-on: 'ubuntu-latest'
Expand All @@ -63,6 +40,9 @@ jobs:
python: '3.11'
invoke_lint: false
invoke_test: false
- name: Install test dependencies
run: |
pip install -r tests/integration/requirements.txt
- name: Run integration tests
run: |
pytest tests/integration
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

* Added support for MQTT-PAHO 2.0 versioned callbacks.
* Added `MessageCodec` abstract base class for extensible message serialization.
* Added `JsonMessageCodec` for JSON-based message serialization (default).
* Added `ProtobufMessageCodec` for binary message serialization using Protocol Buffers (requires `compas_pb`).
* Added `codec` parameter to `Transport`, `InMemoryTransport`, and `MqttTransport` classes.

### Changed

* Updated dependency on `paho-mqtt` to support `>=1, <3` to include version `2.x` with backward compatibility.

### Removed

* **BREAKING**: Removed IronPython support and `mqtt_cli` implementation.
* **BREAKING**: Removed support for Rhino 7 (IronPython-based).


## [1.0.0] 2024-05-27

Expand Down
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Or using `conda`:
* Publisher/subscriber communication model (N-to-N communication)
* In-process events
* MQTT support
* CPython & IronPython support
* Extensible codec system for message serialization (JSON, Protocol Buffers)

## Examples

Expand Down Expand Up @@ -78,13 +78,28 @@ for i in range(10):
This example shows how to send and receive from a single script, but
running publishers and subscribers on different scripts, different processes, or even different computers will work the exact same way.

### Using different codecs

### Usage from Rhinoceros 3D
By default, COMPAS EVE uses JSON for message serialization. However, you can use different codecs for more efficient serialization:

It is possible to use the same code from within Rhino/Grasshopper.
```python
import compas_eve as eve
from compas_eve import JsonMessageCodec
from compas_eve.codecs import ProtobufMessageCodec
from compas_eve.mqtt import MqttTransport

Make sure you have installed it to Rhino using the COMPAS installation mechanism:
# Use JSON codec (default)
json_codec = JsonMessageCodec()
tx = MqttTransport("broker.hivemq.com", codec=json_codec)

```bash
python -m compas_rhino.install -v 7.0
# Or use Protocol Buffers for binary serialization (requires compas_pb)
pb_codec = ProtobufMessageCodec()
tx = MqttTransport("broker.hivemq.com", codec=pb_codec)
```


### Usage from Rhinoceros 3D

It is possible to use the same code from within Rhino/Grasshopper.

To install `compas_eve`, use the the syntax `# r: compas_eve` at the top of any Python 3.x script in Rhino/Grasshopper.
3 changes: 2 additions & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ API Reference
:maxdepth: 1

api/compas_eve
api/compas_eve.mqtt
api/compas_eve.codecs
api/compas_eve.memory
api/compas_eve.mqtt
api/compas_eve.ghpython

2 changes: 2 additions & 0 deletions docs/api/compas_eve.codecs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

.. automodule:: compas_eve.codecs
4 changes: 0 additions & 4 deletions docs/api/compas_eve.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
********************************************************************************
compas_eve
********************************************************************************

.. automodule:: compas_eve
117 changes: 117 additions & 0 deletions examples/codec_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""
Example demonstrating custom codec usage with COMPAS EVE.

This example shows how to:
1. Use the default JsonMessageCodec
2. Create a custom codec
3. Use ProtobufMessageCodec (if compas_pb is installed)
"""

import json

import compas_eve as eve
from compas_eve import MessageCodec
from compas_eve.codecs import JsonMessageCodec


# Example 1: Using the default JSON codec (implicit)
print("=" * 60)
print("Example 1: Default JSON Codec (implicit)")
print("=" * 60)

pub = eve.Publisher("/example/default")
sub = eve.EchoSubscriber("/example/default")
sub.subscribe()

pub.publish(eve.Message(text="Hello with default JSON codec", count=1))
print()


# Example 2: Explicitly using JSON codec
print("=" * 60)
print("Example 2: Explicit JSON Codec")
print("=" * 60)

json_codec = JsonMessageCodec()
transport = eve.InMemoryTransport(codec=json_codec)

pub = eve.Publisher("/example/json", transport=transport)
sub = eve.EchoSubscriber("/example/json", transport=transport)
sub.subscribe()

pub.publish(eve.Message(text="Hello with explicit JSON codec", count=2))
print()


# Example 3: Custom codec
print("=" * 60)
print("Example 3: Custom Codec")
print("=" * 60)


class UpperCaseCodec(MessageCodec):
"""A simple custom codec that converts all string values to uppercase."""

def encode(self, message):
"""Encode message by converting all string values to uppercase."""
# Assume message is a Message instance
data = message.data

# Convert string values to uppercase
encoded_data = {}
for key, value in data.items():
if isinstance(value, str):
encoded_data[key] = value.upper()
else:
encoded_data[key] = value
return json.dumps(encoded_data)

def decode(self, encoded_data, message_type):
"""Decode data (strings remain uppercase)."""
data = json.loads(encoded_data)
return message_type.parse(data)


custom_codec = UpperCaseCodec()
custom_transport = eve.InMemoryTransport(codec=custom_codec)

pub = eve.Publisher("/example/custom", transport=custom_transport)

# Create a custom subscriber that prints the message
class PrintSubscriber(eve.Subscriber):
def message_received(self, message):
print(f"Received: {message}")

sub = PrintSubscriber("/example/custom", transport=custom_transport)
sub.subscribe()

pub.publish(eve.Message(text="hello world", count=3))
print()


# Example 4: Protocol Buffers codec (if available)
print("=" * 60)
print("Example 4: Protocol Buffers Codec (if available)")
print("=" * 60)

try:
from compas_eve.codecs import ProtobufMessageCodec

pb_codec = ProtobufMessageCodec()
pb_transport = eve.InMemoryTransport(codec=pb_codec)

pub = eve.Publisher("/example/protobuf", transport=pb_transport)
sub = eve.EchoSubscriber("/example/protobuf", transport=pb_transport)
sub.subscribe()

pub.publish(eve.Message(text="Hello with Protocol Buffers", count=4))
print("βœ“ Protocol Buffers codec is working!")

except ImportError as e:
print(f"Protocol Buffers codec not available: {e}")
print("Install with: pip install compas_pb")

print()
print("=" * 60)
print("All examples completed!")
print("=" * 60)
51 changes: 51 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,54 @@
[build-system]
requires = ["setuptools>=66.0"]
build-backend = "setuptools.build_meta"

# ============================================================================
# project info
# ============================================================================

[project]
name = "compas_eve"
description = "COMPAS Event Extensions: adds event-based communication infrastructure to the COMPAS framework."
keywords = ["events", "event-driven", "compas", "architecture", "distributed systems"]
authors = [
{ name = "Gonzalo Casas", email = "[email protected]" },
{ name = "Chen Kasirer", email = "[email protected]" },
]
license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.9"
dynamic = ['dependencies', 'optional-dependencies', 'version']
classifiers = [
"Development Status :: 4 - Beta",
"Topic :: Scientific/Engineering",
"Operating System :: Unix",
"Operating System :: POSIX",
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]

[project.entry-points.'compas_pb.plugins']
serializers = 'compas_eve.codecs.conversions'

[project.urls]
Homepage = "https://github.com/compas-dev/compas_eve"
Documentation = "https://compas.dev/compas_eve"
Repository = "https://github.com/compas-dev/compas_eve"
Changelog = "https://github.com/compas-dev/compas_eve/blob/main/CHANGELOG.md"
Issues = "https://github.com/compas-dev/compas_eve/issues"
Forum = "https://forum.compas-framework.org/"


# ============================================================================
# setuptools config
# ============================================================================

[tool.black]
line-length = 120

Expand Down
20 changes: 8 additions & 12 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
attrs >=17.4
autopep8
black
bump2version >=1.0.1
check-manifest >=0.36
compas_invocations
doc8
flake8
black >=22.12.0
build
bump-my-version
compas_invocations2
compas_pb >= 0.4.4
invoke >=0.14
isort
pydocstyle
pytest >=3.2
pytest-mock
ruff
sphinx_compas2_theme
twine
wheel
-e .
wheel
3 changes: 3 additions & 0 deletions src/compas_eve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
EchoSubscriber
Transport
InMemoryTransport
MessageCodec
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot don't import the JsonMessageCodec to this level, leave it where it belongs, and just import it from .codecs where used, otherwise it's a bit asymmetric/odd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed JsonMessageCodec from top-level exports in commit [pending]. It now stays in the codecs submodule and should be imported as from compas_eve.codecs import JsonMessageCodec. The import in core.py is now lazy to avoid unnecessary top-level dependencies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed JsonMessageCodec from top-level exports in commit 490749a. It now stays in the codecs submodule and should be imported as from compas_eve.codecs import JsonMessageCodec. The import in core.py is now lazy to avoid unnecessary top-level dependencies.

get_default_transport
set_default_transport

Expand Down Expand Up @@ -47,6 +48,7 @@
get_default_transport,
set_default_transport,
)
from .codecs import MessageCodec
from .memory import InMemoryTransport

HERE = os.path.dirname(__file__)
Expand All @@ -62,6 +64,7 @@
"EchoSubscriber",
"Topic",
"Transport",
"MessageCodec",
"get_default_transport",
"set_default_transport",
"InMemoryTransport",
Expand Down
Loading