diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 19761235..00000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,25 +0,0 @@ -[bumpversion] -current_version = 2.0.0 -message = Bump version to {new_version} -commit = True -tag = True - -[bumpversion:file:docs/installation.rst] -search = {current_version} -replace = {new_version} - -[bumpversion:file:setup.py] -search = version="{current_version}" -replace = version="{new_version}" - -[bumpversion:file:src/compas_eve/__init__.py] -search = __version__ = "{current_version}" -replace = __version__ = "{new_version}" - -[bumpversion:file:CHANGELOG.md] -search = Unreleased -replace = [{new_version}] {now:%Y-%m-%d} - -[bumpversion:glob:src/compas_eve/ghpython/components/**/code.py] -search = v{current_version} -replace = v{new_version} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3976fc45..b51dac56 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python: ['3.10', '3.11'] + python: ['3.10', '3.11', '3.12'] steps: - uses: compas-dev/compas-actions.build@v4 @@ -49,3 +49,24 @@ jobs: - name: Tear down docker container run: | docker rm -f nanomq + + build-cpython-components: + runs-on: windows-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e .[dev] + + - name: Create CPython Grasshopper user objects + run: | + invoke build-cpython-ghuser-components + + - uses: actions/upload-artifact@v5 + with: + name: compas_eve_components + path: src/compas_eve/ghpython/components/ghuser diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7a04c2a7..d7f3b1c4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,6 +14,7 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: compas-dev/compas-actions.docs@v2 + - uses: compas-dev/compas-actions.docs@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} + use_conda: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e15e9b0..ff979e33 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,19 +6,21 @@ on: - 'v*' jobs: - build: + Build: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - python: ['3.10', '3.11'] + os: [ubuntu-latest] + python: ['3.10'] steps: - uses: compas-dev/compas-actions.build@v4 with: - python: ${{ matrix.python }} invoke_lint: true + check_import: false + use_conda: false invoke_test: false + python: ${{ matrix.python }} - name: Run unit tests run: | pytest tests/unit @@ -44,81 +46,12 @@ jobs: docker rm -f nanomq Publish: - needs: build - runs-on: windows-latest + needs: Build + runs-on: ubuntu-latest steps: - # The steps should rely on compas-actions.publish - # but this bug is blocking it: https://github.com/compas-dev/compas-actions.publish/issues/1 - # so atm, it's a copy of the steps - - # - uses: compas-dev/compas-actions.publish@v2 - # with: - # pypi_token: ${{ secrets.PYPI }} - # github_token: ${{ secrets.TOKEN }} - # build_ghpython_components: true - # gh_source: src/compas_eve/ghpython/components - # gh_target: src/compas_eve/ghpython/components/ghuser - # release_name_prefix: COMPAS EVE v - - - uses: actions/checkout@v3 - - - name: Get Version From Tag - id: tag_name - run: | - echo "current_version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - shell: bash - - - name: Get Changelog Entry - id: changelog_reader - uses: mindsers/changelog-reader-action@v2 - with: - version: ${{ steps.tag_name.outputs.current_version }} - path: ./CHANGELOG.md - - - name: Assemble Release Name - id: assemble_release_name - shell: bash - run: | - release_name="COMPAS EVE v${{ steps.tag_name.outputs.current_version }}" - echo Using release name: $release_name - echo "release_name=$release_name" >> $GITHUB_OUTPUT - - - name: Create Release - id: create_release - uses: ncipollo/release-action@v1 + - uses: compas-dev/compas-actions.publish@v3 with: - body: ${{ steps.changelog_reader.outputs.changes }} - token: ${{ secrets.TOKEN }} - name: ${{ steps.assemble_release_name.outputs.release_name }} - - - name: Setup Python 3.10 - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - - name: Install CPython dependencies - run: | - python -m pip install --upgrade pip - python -m pip install wheel - - - uses: NuGet/setup-nuget@v1.0.5 - - name: Install dependencies - run: | - choco install ironpython --version=2.7.8.1 - - - uses: compas-dev/compas-actions.ghpython_components@v4 - with: - source: src/compas_eve/ghpython/components - target: src/compas_eve/ghpython/components/ghuser - prefix: "" - - - shell: bash - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - python setup.py clean --all sdist bdist_wheel - twine check dist/* - twine upload dist/* --skip-existing - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI }} + publish_to_pypi: true + pypi_token: ${{ secrets.PYPI }} + github_token: ${{ secrets.GITHUB_TOKEN }} + python: '3.10' diff --git a/.github/workflows/yak_publish.yml b/.github/workflows/yak_publish.yml new file mode 100644 index 00000000..0e29b314 --- /dev/null +++ b/.github/workflows/yak_publish.yml @@ -0,0 +1,59 @@ +name: publish_yak + +on: + workflow_dispatch: + inputs: + environment: + description: "Choose deployment environment" + required: true + type: choice + options: + - test + - prod + +jobs: + + publish_test_yak: + runs-on: windows-latest + + steps: + + - name: Set test flag based on input + shell: pwsh + run: | + if ("${{ github.event.inputs.environment }}" -eq "test") { + echo "TEST_FLAG=--test-server" | Out-File -FilePath $env:GITHUB_ENV -Append + } + else { + echo "TEST_FLAG=" | Out-File -FilePath $env:GITHUB_ENV -Append + } + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e .[dev] + + - name: Create CPython Grasshopper user objects + run: | + invoke build-cpython-ghuser-components + + - name: Create Rhino8 Yak package + shell: pwsh + run: | + invoke yakerize -m $Env:YAK_TEMPLATE\manifest.yml -l $Env:YAK_TEMPLATE\icon.png -g $Env:USER_OBJECTS -t rh8 + env: + USER_OBJECTS: src\compas_eve\ghpython\components\ghuser + YAK_TEMPLATE: src\compas_eve\ghpython\yak_template + + - name: Publish to Yak server (Rhino 8) + shell: pwsh + run: | + $test_flag = if ($Env:TEST_FLAG) { $Env:TEST_FLAG } else { "" } + $file = Get-ChildItem -Path dist\yak_package\*rh8*.yak -File | Select-Object -ExpandProperty Name + $command = "invoke publish-yak -y dist\yak_package\$file $test_flag".Trim() + Invoke-Expression $command + env: + YAK_TOKEN: ${{ secrets.YAK_DF_TOKEN }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 489c47f7..e8f90899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +* Removed Rhino7 Grasshopper components and replaced them with Rhino8 ones. ## [2.0.0] 2025-10-30 diff --git a/conftest.py b/conftest.py index 371bdb6d..d0f59dcf 100644 --- a/conftest.py +++ b/conftest.py @@ -1,19 +1,20 @@ -import pytest -import compas -import compas_eve +from pathlib import Path import math import numpy +import pytest +import compas +import compas_eve -def pytest_ignore_collect(path): - if "rhino" in str(path): - return True - if "blender" in str(path): +def pytest_ignore_collect(collection_path: Path, config): + # Skip anything under rhino/blender/ghpython + parts_lower = {p.lower() for p in collection_path.parts} + if {"rhino", "blender", "ghpython"} & parts_lower: return True - if "ghpython" in str(path): - return True + # return None -> don't ignore + return None @pytest.fixture(autouse=True) diff --git a/pyproject.toml b/pyproject.toml index 27a6e842..e86bb08d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=66.0"] build-backend = "setuptools.build_meta" # ============================================================================ -# project info +# Project info # ============================================================================ [project] @@ -33,6 +33,11 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] +[tool.setuptools.dynamic] +version = { attr = "compas_eve.__version__" } +dependencies = { file = "requirements.txt" } +optional-dependencies = { dev = { file = "requirements-dev.txt" } } + [project.entry-points.'compas_pb.plugins'] serializers = 'compas_eve.codecs.conversions' @@ -46,7 +51,7 @@ Forum = "https://forum.compas-framework.org/" # ============================================================================ -# setuptools config +# Linting and formatting # ============================================================================ [tool.ruff] @@ -99,4 +104,34 @@ max-doc-length = 179 [tool.ruff.format] docstring-code-format = true -docstring-code-line-length = "dynamic" \ No newline at end of file +docstring-code-line-length = "dynamic" + +# ============================================================================ +# Release automation +# ============================================================================ + +[tool.bumpversion] +current_version = "2.0.0" +message = "Bump version to {new_version}" +commit = true +tag = true + +[[tool.bumpversion.files]] +filename = "src/compas_eve/__init__.py" +search = "{current_version}" +replace = "{new_version}" + +[[tool.bumpversion.files]] +filename = "docs/installation.rst" +search = "{current_version}" +replace = "{new_version}" + +[[tool.bumpversion.files]] +glob = "src/compas_eve/ghpython/components/**/code.py" +search = "# r: compas_eve>={current_version}" +replace = "# r: compas_eve>={new_version}" + +[[tool.bumpversion.files]] +filename = "CHANGELOG.md" +search = "Unreleased" +replace = "[{new_version}] {now:%Y-%m-%d}" diff --git a/requirements-dev.txt b/requirements-dev.txt index e9337a79..1772fe59 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -9,4 +9,5 @@ pytest-mock ruff sphinx_compas2_theme twine -wheel \ No newline at end of file +wheel +pythonnet \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index f4a31c22..00000000 --- a/setup.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- -# flake8: noqa -from __future__ import absolute_import -from __future__ import print_function - -import io -from os import path - -from setuptools import setup -from setuptools.command.develop import develop -from setuptools.command.install import install - - -here = path.abspath(path.dirname(__file__)) - - -def read(*names, **kwargs): - return io.open(path.join(here, *names), encoding=kwargs.get("encoding", "utf8")).read() - - -long_description = read("README.md") -requirements = read("requirements.txt").split("\n") - -# Read dev requirements for optional dependencies -dev_requirements = [ - line.strip() - for line in read("requirements-dev.txt").split("\n") - if line.strip() and not line.strip().startswith("#") and line.strip() != "-e ." -] - -optional_requirements = { - "dev": dev_requirements, -} - -setup( - name="compas_eve", - version="2.0.0", - description="COMPAS Event Extensions: adds event-based communication infrastructure to the COMPAS framework.", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/compas-dev/compas_eve", - author="Gonzalo Casas", - author_email="casas@arch.ethz.ch", - license="MIT license", - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "Topic :: Scientific/Engineering", - "License :: OSI Approved :: MIT License", - "Operating System :: Unix", - "Operating System :: POSIX", - "Operating System :: Microsoft :: Windows", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: Implementation :: CPython", - ], - keywords=[], - project_urls={}, - packages=["compas_eve"], - package_dir={"": "src"}, - package_data={}, - data_files=[], - include_package_data=True, - zip_safe=False, - install_requires=requirements, - python_requires=">=3.8", - extras_require=optional_requirements, - entry_points={ - "console_scripts": [], - }, - ext_modules=[], -) diff --git a/src/compas_eve/codecs/conversions.py b/src/compas_eve/codecs/conversions.py index 806602c8..a36ac41f 100644 --- a/src/compas_eve/codecs/conversions.py +++ b/src/compas_eve/codecs/conversions.py @@ -3,8 +3,8 @@ from compas_pb.registry import pb_deserializer from compas_pb.registry import pb_serializer -from compas_eve.proto import message_pb2 from compas_eve import Message +from compas_eve.proto import message_pb2 @pb_serializer(Message) diff --git a/src/compas_eve/ghpython/__init__.py b/src/compas_eve/ghpython/__init__.py index 299aa33d..ad054b8b 100644 --- a/src/compas_eve/ghpython/__init__.py +++ b/src/compas_eve/ghpython/__init__.py @@ -16,6 +16,65 @@ """ +try: + import Grasshopper # type: ignore +except (ImportError, SyntaxError): + pass + + from .background import BackgroundWorker -__all__ = ["BackgroundWorker"] + +def warning(component, message): + """Add a warning message to the component. + + Parameters + ---------- + component : Grasshopper.Kernel.IGH_Component + The component instance. Pre-Rhino8 use `self`. Post-Rhino8 use `ghenv.Component`. + message : str + The message to display. + """ + component.AddRuntimeMessage(Grasshopper.Kernel.GH_RuntimeMessageLevel.Warning, message) + + +def error(component, message): + """Add an error message to the component. + + Parameters + ---------- + component : Grasshopper.Kernel.IGH_Component + The component instance. Pre-Rhino8 use `self`. Post-Rhino8 use `ghenv.Component`. + message : str + The message to display. + """ + component.AddRuntimeMessage(Grasshopper.Kernel.GH_RuntimeMessageLevel.Error, message) + + +def remark(component, message): + """Add a remark message to the component. + + Parameters + ---------- + component : Grasshopper.Kernel.IGH_Component + The component instance. Pre-Rhino8 use `self`. Post-Rhino8 use `ghenv.Component`. + message : str + The message to display. + """ + component.AddRuntimeMessage(Grasshopper.Kernel.GH_RuntimeMessageLevel.Remark, message) + + +def message(component, message): + """Add a text that will appear under the component. + + Parameters + ---------- + component : Grasshopper.Kernel.IGH_Component + The component instance. Pre-Rhino8 use `self`. Post-Rhino8 use `ghenv.Component`. + message : str + The message to display. + """ + component.Message = message + + +__all__ = ["BackgroundWorker", "warning", "error", "remark", "message"] diff --git a/src/compas_eve/ghpython/components/Ce_BackgroundTask/code.py b/src/compas_eve/ghpython/components/Ce_BackgroundTask/code.py index 5d947493..59d3578d 100644 --- a/src/compas_eve/ghpython/components/Ce_BackgroundTask/code.py +++ b/src/compas_eve/ghpython/components/Ce_BackgroundTask/code.py @@ -1,3 +1,4 @@ +# r: compas_eve>=2.0.0 """ Background Task component. @@ -7,16 +8,16 @@ """ import threading + +import Grasshopper import scriptcontext as sc from compas_eve.ghpython import BackgroundWorker -from ghpythonlib.componentbase import executingcomponent as component - DEBUG = False -class BackgroundTaskComponent(component): +class BackgroundTaskComponent(Grasshopper.Kernel.GH_ScriptInstance): def RunScript(self, task, reset, on): if not on: BackgroundWorker.stop_instance_by_component(ghenv) # noqa: F821 diff --git a/src/compas_eve/ghpython/components/Ce_Message/code.py b/src/compas_eve/ghpython/components/Ce_Message/code.py index 897adeab..243d23de 100644 --- a/src/compas_eve/ghpython/components/Ce_Message/code.py +++ b/src/compas_eve/ghpython/components/Ce_Message/code.py @@ -1,18 +1,16 @@ +# r: compas_eve>=2.0.0 """ Create a message. - -COMPAS EVE v2.0.0 """ -import System - import Rhino.Geometry as rg import rhinoscriptsyntax as rs - +import System from compas.geometry import Brep -from compas_eve import Message from compas_rhino import conversions +from compas_eve import Message + component = ghenv.Component # noqa: F821 local_values = locals() diff --git a/src/compas_eve/ghpython/components/Ce_MqttConnect/code.py b/src/compas_eve/ghpython/components/Ce_MqttConnect/code.py index cd324acb..e7a208d9 100644 --- a/src/compas_eve/ghpython/components/Ce_MqttConnect/code.py +++ b/src/compas_eve/ghpython/components/Ce_MqttConnect/code.py @@ -1,26 +1,25 @@ +# r: compas_eve>=2.0.0 """ Connect or disconnect to an MQTT broker. - -COMPAS EVE v2.0.0 """ from threading import Event +import Grasshopper from compas_ghpython import create_id -from ghpythonlib.componentbase import executingcomponent as component from scriptcontext import sticky as st from compas_eve.mqtt import MqttTransport -class MqttConnectComponent(component): - def RunScript(self, host, port, connect): +class MqttConnectComponent(Grasshopper.Kernel.GH_ScriptInstance): + def RunScript(self, host: str, port: int, connect: bool): mqtt_transport = None host = host or "127.0.0.1" port = port or 1883 - key = create_id(self, "mqtt_transport") + key = create_id(ghenv.Component, "mqtt_transport") # noqa: F821 mqtt_transport = st.get(key, None) if mqtt_transport: diff --git a/src/compas_eve/ghpython/components/Ce_Publish/code.py b/src/compas_eve/ghpython/components/Ce_Publish/code.py index 3386e60a..ec098bf4 100644 --- a/src/compas_eve/ghpython/components/Ce_Publish/code.py +++ b/src/compas_eve/ghpython/components/Ce_Publish/code.py @@ -1,29 +1,30 @@ +# r: compas_eve>=2.0.0 """ Publish messages to a topic. - -COMPAS EVE v2.0.0 """ import time -from ghpythonlib.componentbase import executingcomponent as component +import Grasshopper +from compas_ghpython import create_id from scriptcontext import sticky as st -from compas_eve import Topic from compas_eve import Publisher -from compas_ghpython import create_id +from compas_eve import Topic +from compas_eve.ghpython import warning -class PublishComponent(component): - def RunScript(self, transport, topic_name, message, on): +class PublishComponent(Grasshopper.Kernel.GH_ScriptInstance): + def RunScript(self, transport, topic_name: str, message, on: bool): if not topic_name: - raise ValueError("Please specify the name of the topic") + warning(ghenv.Component, "Please specify the name of the topic") # noqa: F821 + return None if on is None: on = True - key = create_id(self, "publisher_{}".format(id(transport))) - key_count = create_id(self, "publisher_count_{}".format(id(transport))) + key = create_id(ghenv.Component, "publisher_{}".format(id(transport))) # noqa: F821 + key_count = create_id(ghenv.Component, "publisher_count_{}".format(id(transport))) # noqa: F821 publisher = st.get(key, None) if not publisher: diff --git a/src/compas_eve/ghpython/components/Ce_Subscribe/code.py b/src/compas_eve/ghpython/components/Ce_Subscribe/code.py index 62b5ed72..846d22ac 100644 --- a/src/compas_eve/ghpython/components/Ce_Subscribe/code.py +++ b/src/compas_eve/ghpython/components/Ce_Subscribe/code.py @@ -1,20 +1,21 @@ +# r: compas_eve>=2.0.0 """ Subscribe to a topic to receive messages. - -COMPAS EVE v2.0.0 """ -from ghpythonlib.componentbase import executingcomponent as component +import Grasshopper -from compas_eve import Topic from compas_eve import Subscriber +from compas_eve import Topic from compas_eve.ghpython import BackgroundWorker +from compas_eve.ghpython import warning -class SubscribeComponent(component): - def RunScript(self, transport, topic_name, start, on): +class SubscribeComponent(Grasshopper.Kernel.GH_ScriptInstance): + def RunScript(self, transport, topic_name: str, start: bool, on: bool): if not topic_name: - raise ValueError("Please specify the name of the topic") + warning(ghenv.Component, "Please specify the name of the topic") # noqa: F821 + return None if on is None: on = True diff --git a/src/compas_eve/ghpython/yak_template/icon.png b/src/compas_eve/ghpython/yak_template/icon.png new file mode 100644 index 00000000..e3ff78cd Binary files /dev/null and b/src/compas_eve/ghpython/yak_template/icon.png differ diff --git a/src/compas_eve/ghpython/yak_template/manifest.yml b/src/compas_eve/ghpython/yak_template/manifest.yml new file mode 100644 index 00000000..9375fdeb --- /dev/null +++ b/src/compas_eve/ghpython/yak_template/manifest.yml @@ -0,0 +1,27 @@ +name: compas_eve +version: {{ version }} +authors: + - Gramazio Kohler Research, ETH Zurich +description: "compas_eve adds event-based communication infrastructure to the COMPAS framework." +url: "https://github.com/gramaziokohler/compas_eve" +keywords: [ + "COMPAS", + "compas_eve", + "compas", + "framework", + "architecture", + "engineering", + "digital", + "fabrication", + "construction", + "mqtt", + "events", + "ipc", + "IPC", + "publish", + "subscribe", + "topic", + "event-driven", + "distributed systems" +] +icon: "icon.png" diff --git a/src/compas_eve/mqtt/mqtt_paho.py b/src/compas_eve/mqtt/mqtt_paho.py index 5ac217c8..a154e723 100644 --- a/src/compas_eve/mqtt/mqtt_paho.py +++ b/src/compas_eve/mqtt/mqtt_paho.py @@ -1,8 +1,9 @@ -from ..core import Transport -from ..event_emitter import EventEmitterMixin +import uuid import paho.mqtt.client as mqtt -import uuid + +from ..core import Transport +from ..event_emitter import EventEmitterMixin try: from paho.mqtt.enums import CallbackAPIVersion diff --git a/tasks.py b/tasks.py index f47c87e5..941f5dc5 100644 --- a/tasks.py +++ b/tasks.py @@ -7,6 +7,7 @@ from compas_invocations2 import docs from compas_invocations2 import style from compas_invocations2 import tests +from compas_invocations2 import grasshopper from invoke import Collection import compas_pb @@ -21,16 +22,18 @@ docs.linkcheck, tests.test, tests.testdocs, - build.build_ghuser_components, + build.build_cpython_ghuser_components, build.prepare_changelog, build.clean, build.release, + grasshopper.yakerize, + grasshopper.update_gh_header, generate_proto_classes, ) ns.configure( { "base_folder": os.path.dirname(__file__), - "ghuser": { + "ghuser_cpython": { "prefix": "COMPAS EVE: ", "source_dir": "src/compas_eve/ghpython/components", "target_dir": "src/compas_eve/ghpython/components/ghuser",