Skip to main content

ghpython_componentizer

Build Grasshopper .ghuser components out of plain Python source bundles. 🐵

Write your Grasshopper components as real Python files, in a real editor, under version control, and compile them into .ghuser components for the CPython (Python 3) interpreter of Rhino 8.

This package is the standalone version of the componentizer behind the compas-actions.ghpython_components GitHub action: same tool, usable from your own scripts, build systems or CI, without going through the action.

Requirements

  • Python 3.9 or newer
  • pythonnet (installed automatically) and a .NET runtime, i.e. Windows
  • The GH_IO.dll assembly, which is downloaded automatically from NuGet unless you point at a local copy
  • On macOS, Mono and libgdiplus: brew install mono mono-libgdiplus

Installation

pip install ghpython_componentizer

Usage

From the command line

ghpython-componentizer <source> <target>

For example, to build all component bundles under components into a build folder:

ghpython-componentizer components build

Optionally, tag the components with a version, which replaces the {{version}} template variable in the code:

ghpython-componentizer components build --version 0.1.2

An optional name prefix can help tell components apart from other similarly named ones:

ghpython-componentizer components build --prefix "(PACKAGE-NAME)"

If you already have a copy of GH_IO.dll (e.g. from a NuGet restore), point the tool at the folder containing it to skip the download:

ghpython-componentizer components build --ghio ./lib

The tool is also runnable as a module, which is handy when the scripts folder is not on PATH:

python -m ghpython_componentizer components build

From Python

from ghpython_componentizer import build_components

build_components("components", "build", version="0.1.2", prefix="(COMPAS)")

Or one component at a time:

from ghpython_componentizer import create_ghuser_component

create_ghuser_component("components/MyComponent", "build/MyComponent.ghuser")

Both functions download GH_IO.dll from NuGet on first use, unless the ghio_dir argument points at a folder that contains it.

From a build script, on any platform

from ghpython_componentizer import run_componentizer

run_componentizer("components", "build", version="0.1.2", prefix="(COMPAS)")

This does the same as build_components, but in a subprocess started with the environment the platform needs. On macOS that is the only thing that works from a process that is already running, see below. Everywhere else the two are interchangeable.

macOS

Only Mono can load the net48 assemblies this tool needs, and Mono draws the component icons through the native libgdiplus library. Homebrew installs libgdiplus in a prefix the dynamic loader does not search by default, so without help the build fails with a System.TypeInitializationException while embedding the icon.

DYLD_LIBRARY_PATH is only read when a process starts, so it cannot be fixed from inside a running process. The command line therefore relaunches itself once, with the right environment, and everything works out of the box:

brew install mono mono-libgdiplus
ghpython-componentizer components build

From Python, build_components and create_ghuser_component cannot fix the environment of the process calling them: they raise a RuntimeError explaining what to do. Use run_componentizer instead, or start your own subprocess with the environment returned by componentizer_env:

import subprocess
import sys

from ghpython_componentizer import componentizer_env

# Note: no shell in between, macOS strips DYLD_* variables when it starts one.
subprocess.run(
    [sys.executable, "-m", "ghpython_componentizer", "components", "build"],
    env=componentizer_env(),
    check=True,
)

The same caveat applies to the interpreter itself: macOS strips DYLD_* variables when starting a protected binary, so build with a Python from python.org, Homebrew or uv rather than /usr/bin/python3.

How to create components

  1. Create a folder to contain your components.
  2. Each component goes into its own folder, called a source bundle.
  3. The name of the folder determines the name of the .ghuser file created.
  4. Inside the component folder:
    1. Create a metadata.json file containing all required details of the component.
    2. Add a lovely icon named icon.png (24x24).
    3. Add a code.py file with the Python script of the component.
components/
└── My_Component/
    ├── code.py
    ├── icon.png
    └── metadata.json

Specification

Icon

  • Icon name should be icon.png
  • Icon dimensions should be 24x24

Python code

Supports a small set of templated variables that can be used in code:

  • {{version}}: Gets replaced with the version, if specified.
  • {{name}}: Gets replaced with the name of the component as defined in the metadata file.
  • {{ghuser_name}}: Gets replaced with the name of the .ghuser file being generated.

Metadata

  • name: Name of the component. Keep it short, single words are best.
  • nickname: Abbreviation of the component. Keep it short, 1~5 character words are best.
  • category: Category of the component. The category controls in which tab the component will end up.
  • subcategory: Subcategory for this component. The subcategory controls in which panel the component will end up.
  • description: (optional) Description of the component. Be succinct but clear.
  • exposure: (optional) Controls where the component will be exposed. Defaults to 2 (primary). Accepts one of the following integer values:
    • -1: Hidden. Do not expose the object anywhere.
    • 2: Primary. Expose the object in the first section on the toolbar.
    • 4: Secondary. Expose the object in the second section on the toolbar.
    • 8, 16, 32, 64, 128: Expose the object in the third to seventh section on the toolbar.
  • instanceGuid: (optional) Statically define a GUID for this instance. Defaults to a new Guid.
  • ghpython
    • marshalGuids: (optional) Defines whether input Guids will be looked up or not. Defaults to True. Change to False to preserve input Guids.
    • iconDisplay: (optional) Defines whether to display the icon or not. Defaults to 0.
      • 0: Application setting
      • 1: Text display
      • 2: Icon display
    • inputParameters: List of input parameters.
      • name: Name of the input parameter.
      • nickname: (optional) Abbreviation of the input parameter. Defaults to the same as name.
      • description: (optional) Description of the input parameter.
      • optional: (optional) Defines whether the input parameter is optional or not. Defaults to True.
      • allowTreeAccess: (optional) Defines whether to allow tree access for this input parameter. Defaults to True.
      • showTypeHints: (optional) Defines whether to show type hints for this input parameter. Defaults to True.
      • scriptParamAccess: (optional) Defines access type of the parameter. Defaults to item. Accepts either integer value or string value.
        • 0 / item: item access
        • 1 / list: list access
        • 2 / tree: tree access
      • wireDisplay: (optional) Defines wire display type. Accepts either integer value or string value.
        • 0 / default: Wire display is controlled by the application settings.
        • 1 / faint: Wires are displayed faintly while the parameter is not selected.
        • 2 / hidden: Wires are not displayed at all while the parameter is not selected.
      • typeHintID: (optional) Defines the type hint of the input parameter. Defaults to ghdoc. Accepts either a Guid value or one of the following string values: none, ghdoc, float, bool, int, complex, str, datetime, guid, color, point, vector, plane, interval, uvinterval, box, transform, line, circle, arc, polyline, rectangle, curve, mesh, surface, subd, brep, pointcloud, geometrybase.
      • reverse: (optional) Defines whether data inside the parameter is reversed. Defaults to False.
      • simplify: (optional) Defines whether data inside the parameter is simplified. Defaults to False.
      • flatten: (optional) Defines whether data inside the parameter is flattened. Mutually exclusive with graft. Defaults to False.
      • graft: (optional) Defines whether data inside the parameter is grafted. Mutually exclusive with flatten. Defaults to False.
    • outputParameters: List of output parameters.
      • name: Name of the output parameter.
      • nickname: (optional) Abbreviation of the output parameter. Defaults to the same as name.
      • description: (optional) Description of the output parameter.
      • optional: (optional) Defines whether the output parameter is optional or not. Defaults to False.
      • reverse: (optional) Defines whether data inside the parameter is reversed. Defaults to False.
      • simplify: (optional) Defines whether data inside the parameter is simplified. Defaults to False.
      • flatten: (optional) Defines whether data inside the parameter is flattened. Mutually exclusive with graft. Defaults to False.
      • graft: (optional) Defines whether data inside the parameter is grafted. Mutually exclusive with flatten. Defaults to False.

Caveats

GHUser components have one important limitation: once used in a document, they forget who they are. They don't know they were created out of a ghuser component, they will be simple script components. This has an important consequence: if you update the ghuser components, those already in use will NOT be automatically updated.

IronPython components

This package builds components for the CPython (Python 3) interpreter of Rhino 8 only. Components for the IronPython interpreters of Rhino 7 and Rhino 8 are still built with the scripts of the GitHub action.

Development

pip install -e ".[dev]"
pytest
ruff check .
ruff format .

The test suite covers the parts that do not require a .NET runtime, so it runs on any platform. Linting and formatting follow the same ruff rules as the other COMPAS packages, configured in pyproject.toml.

Releasing

Releases are prepared and published by pull request, with the shared compas-actions.

  1. Describe the changes under ## Unreleased in CHANGELOG.md, as they are merged.
  2. Run the prepare release workflow from the Actions tab, choosing patch, minor or major. It bumps the version, closes the changelog section and opens a release/vX.Y.Z pull request.
  3. Review and merge that pull request.

Merging it runs the release workflow: it builds the distributions, publishes them to PyPI through trusted publishing and creates the tagged GitHub release, with the changelog section as release notes. No API token is involved: PyPI needs a publisher configured for this repository, the release.yml workflow and the pypi environment.

License

This package is maintained by Gramazio Kohler Research @gramaziokohler and it is published under an MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ghpython_componentizer-0.1.0.tar.gz (19.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ghpython_componentizer-0.1.0-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

File details

Details for the file ghpython_componentizer-0.1.0.tar.gz.

File metadata

  • Download URL: ghpython_componentizer-0.1.0.tar.gz
  • Upload date:
  • Size: 19.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ghpython_componentizer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 997dcb3a82a5187f48006ed2e880adc181aede9b029712521dac3517d96139dc
MD5 3c80b88a5d677cfdda8af0ce30b3ff3b
BLAKE2b-256 9ee6c2f983ede20bbec674f6e5639e19f1dd6313ea5277f68d532fc0a400047c

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghpython_componentizer-0.1.0.tar.gz:

Publisher: release.yml on compas-dev/ghpython_componentizer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ghpython_componentizer-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ghpython_componentizer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 97c055c56bacdf6d08ab401401c552deb6cce540bc26bfba1e63b6e15a4fe57a
MD5 67fc815991d5df2cd427b089c48052ed
BLAKE2b-256 c5dff6e09ab04f1035d046c632403eddd1d9e94d1cb35e6412ca6bafdf3c1a2f

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghpython_componentizer-0.1.0-py3-none-any.whl:

Publisher: release.yml on compas-dev/ghpython_componentizer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page