From 063537ee3cbfd41134dfbea816d7b396838438f9 Mon Sep 17 00:00:00 2001 From: Rafael Pastrana Date: Sat, 18 Jul 2026 10:01:18 -0400 Subject: [PATCH 1/2] Register scene objects via plugin discovery, not eager import Eager import-time registration populated COMPAS's global ITEM_SCENEOBJECT, which suppressed the lazy discovery trigger in compas.scene.context and left other backends unregistered. Declare __all_plugins__ so the factory is found through the standard collect_all path instead. --- CHANGELOG.md | 11 +++++++++++ src/compas_plotter/__init__.py | 8 +++----- src/compas_plotter/scene/__init__.py | 5 ----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59f0ecc..94ec3b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* Created `compas_plotter.__all_plugins__`, so COMPAS's plugin manager discovers the + `register_scene_objects` factory through the standard `collect_all` path (the + same mechanism `compas_viewer` uses). + ### Changed ### Removed +* Removed Eager, import-time `register_scene_objects()` call in + `compas_plotter.scene`, and the `from . import scene` side-effect import in + `compas_plotter/__init__.py`. These populated COMPAS's global + `ITEM_SCENEOBJECT` at import time, which suppressed the lazy + `if not ITEM_SCENEOBJECT` discovery trigger in `compas.scene.context` and left + every other backend unregistered. + ## [1.0.0] 2026-07-11 ### Added diff --git a/src/compas_plotter/__init__.py b/src/compas_plotter/__init__.py index 78ffdc2..38c097b 100644 --- a/src/compas_plotter/__init__.py +++ b/src/compas_plotter/__init__.py @@ -10,10 +10,8 @@ from .__version__ import __version__ from .plotter import Plotter -# Importing the scene subpackage triggers registration of the Plotter scene -# objects. When this package is pip-installed, COMPAS would also discover the -# @plugin-decorated factory through the entry point; importing here guarantees -# registration works in editable / non-installed setups as well. -from . import scene # noqa: E402,F401 +# COMPAS plugin discovery imports the modules listed here to find the +# ``@plugin``-decorated ``register_scene_objects`` factory. +__all_plugins__ = ["compas_plotter.scene"] __all__ = ["Plotter", "__version__"] diff --git a/src/compas_plotter/scene/__init__.py b/src/compas_plotter/scene/__init__.py index 71ef78a..9596979 100644 --- a/src/compas_plotter/scene/__init__.py +++ b/src/compas_plotter/scene/__init__.py @@ -68,11 +68,6 @@ def register_scene_objects(): # objects are implemented (Breps additionally need e.g. compas_occ). -# Eager registration so that the Plotter works in editable / non-installed setups -# (where the entry-point-based plugin discovery does not run). -register_scene_objects() - - __all__ = [ "PlotterScene", "PlotterSceneObject", From 6853b5afa75ef303566992339b6cfd98136df384 Mon Sep 17 00:00:00 2001 From: Rafael Pastrana Date: Sat, 18 Jul 2026 10:01:35 -0400 Subject: [PATCH 2/2] Test that import has no eager registration side effect Add test_import_does_not_populate_registry_eagerly, run in a subprocess for a pristine interpreter (ITEM_SCENEOBJECT is a process-global latch). Hoist the module-level imports it shares with existing tests to the top of the file. --- tests/test_plotter.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/test_plotter.py b/tests/test_plotter.py index 129ed1d..2c5fa0e 100644 --- a/tests/test_plotter.py +++ b/tests/test_plotter.py @@ -1,3 +1,7 @@ +import subprocess +import sys +import textwrap + import matplotlib matplotlib.use("Agg") @@ -19,6 +23,8 @@ from compas.geometry import Sphere from compas.geometry import Torus from compas.geometry import Vector +from compas.geometry import Translation +from compas.scene import Scene from compas_plotter import Plotter from compas_plotter.scene import BoxObject @@ -33,6 +39,7 @@ from compas_plotter.scene import PolylineObject from compas_plotter.scene import ShapeObject from compas_plotter.scene import VectorObject +from compas_plotter.scene import PlotterScene def test_add_point_returns_pointobject(): @@ -128,9 +135,6 @@ def test_redraw_clears_and_redraws(): def test_scene_is_a_compas_scene(): - from compas.scene import Scene - - from compas_plotter.scene import PlotterScene plotter = Plotter() assert isinstance(plotter.scene, PlotterScene) @@ -139,7 +143,6 @@ def test_scene_is_a_compas_scene(): def test_scene_hierarchy_and_world_transform(): - from compas.geometry import Translation plotter = Plotter() parent = plotter.add(Point(0, 0, 0)) @@ -217,3 +220,26 @@ def frame(f): assert obj.vertex_xyz[1][1] == 3.0 assert obj.vertex_xyz[3][2] == 3.0 + + +def test_import_does_not_populate_registry_eagerly(): + script = textwrap.dedent( + """ + import compas.scene.context as ctx + import compas_plotter # must not register anything on its own + assert not ctx.ITEM_SCENEOBJECT, ctx.ITEM_SCENEOBJECT + + # Discovery through the plugin path still finds the Plotter backend and, + # critically, the base COMPAS context too. + from compas.scene.context import register_scene_objects + register_scene_objects() + from compas.datastructures import Mesh + assert "Plotter" in ctx.ITEM_SCENEOBJECT + assert Mesh in ctx.ITEM_SCENEOBJECT["Plotter"] + assert Mesh in ctx.ITEM_SCENEOBJECT[None] + print("OK") + """ + ) + result = subprocess.run([sys.executable, "-c", script], capture_output=True, text=True) + assert result.returncode == 0, result.stderr + assert "OK" in result.stdout