diff --git a/CHANGELOG.md b/CHANGELOG.md index c0ac707d38da..33c09eb80881 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,7 +95,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fixed error in `circle_to_compas` from Rhino. * Fixed Rhino to Rhino brep serialization. * Upated `compas.scene.Group.add()` to pass on group kwargs as default for child items. -* Fixed bug in context detection, which wrongly defaults to `Viewer` instead of `None`. +* Fixed `compas.scene.context.detect_current_context()` to return `None` by default instead of randomly selecting the first registered context, ensuring predictable behavior when creating scene objects without an explicit context. * Fixed bug in calculation of `compas.geometry.Polyhedron.edges` if geometry is computed using numpy. * Fixed bug in `Grpah.from_pointcloud` which uses degree parameter wrongly. diff --git a/src/compas/scene/context.py b/src/compas/scene/context.py index c53052a4a233..b9edd1aa3a2d 100644 --- a/src/compas/scene/context.py +++ b/src/compas/scene/context.py @@ -94,15 +94,15 @@ def register(item_type, sceneobject_type, context=None): def detect_current_context(): """Chooses an appropriate context depending on available contexts and open instances. with the following priority: - 1. Viewer - 2. Plotter - 3. Rhino / GH - checked explicitly since SceneObjects for both get registered when code is run from either. - 4. Other + 1. Rhino / GH - checked explicitly since SceneObjects for both get registered when code is run from either. + 2. Blender + 3. None (default) - returns the base scene object type Returns ------- - str - Name of an available context, used as key in :attr:`SceneObject.ITEM_SCENEOBJECT` + str or None + Name of an available context, used as key in :attr:`SceneObject.ITEM_SCENEOBJECT`, + or None if no explicit context is detected. """ @@ -112,9 +112,6 @@ def detect_current_context(): return "Rhino" if compas.is_blender(): return "Blender" - # other_contexts = [v for v in ITEM_SCENEOBJECT.keys()] - # if other_contexts: - # return other_contexts[0] return None diff --git a/tests/compas/scene/test_scene.py b/tests/compas/scene/test_scene.py index fe09c341e078..3cb9c73d3095 100644 --- a/tests/compas/scene/test_scene.py +++ b/tests/compas/scene/test_scene.py @@ -66,21 +66,47 @@ def test_get_sceneobject_cls_with_out_of_order_registration(): sceneobject = SceneObject(item, context="fake") assert isinstance(sceneobject, FakeSubSceneObject) - def test_sceneobject_auto_context_discovery(mocker): - register_fake_context() + def test_sceneobject_requires_explicit_context_when_registered(mocker): + """Test that when a context is registered, it must be explicitly specified.""" + register_fake_context() - item = FakeItem() + item = FakeItem() + # Without explicit context, should fail since context is None and FakeItem is not registered for None + with pytest.raises(SceneObjectNotRegisteredError): sceneobject = SceneObject(item) - assert isinstance(sceneobject, FakeSceneObject) + # With explicit context, should work + sceneobject = SceneObject(item, context="fake") + assert isinstance(sceneobject, FakeSceneObject) + + def test_sceneobject_auto_context_discovery_no_context(mocker): + mocker.patch("compas.scene.context.compas.is_grasshopper", return_value=False) + mocker.patch("compas.scene.context.compas.is_rhino", return_value=False) + mocker.patch("compas.scene.context.compas.is_blender", return_value=False) + + with pytest.raises(SceneObjectNotRegisteredError): + item = FakeSubItem() + _ = SceneObject(item) - def test_sceneobject_auto_context_discovery_no_context(mocker): - mocker.patch("compas.scene.context.compas.is_grasshopper", return_value=False) - mocker.patch("compas.scene.context.compas.is_rhino", return_value=False) + def test_detect_current_context_returns_none_by_default(mocker): + """Test that detect_current_context returns None when no explicit context is detected.""" + from compas.scene.context import detect_current_context - with pytest.raises(SceneObjectNotRegisteredError): - item = FakeSubItem() - _ = SceneObject(item) + mocker.patch("compas.scene.context.compas.is_grasshopper", return_value=False) + mocker.patch("compas.scene.context.compas.is_rhino", return_value=False) + mocker.patch("compas.scene.context.compas.is_blender", return_value=False) + + context = detect_current_context() + assert context is None + + def test_scene_default_context_is_none(mocker): + """Test that Scene has context=None when no explicit context is detected.""" + mocker.patch("compas.scene.context.compas.is_grasshopper", return_value=False) + mocker.patch("compas.scene.context.compas.is_rhino", return_value=False) + mocker.patch("compas.scene.context.compas.is_blender", return_value=False) + + scene = Scene() + assert scene.context is None def test_sceneobject_transform(): scene = Scene()