Skip to content

Commit 45ba3b7

Browse files
authored
Merge pull request #1391 from compas-dev/gh_python3
Fixed `compas_ghpython` is not importable in Rhino8
2 parents db0c5b0 + 85f002d commit 45ba3b7

File tree

9 files changed

+89
-48
lines changed

9 files changed

+89
-48
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Changed
1313

14+
* Fixed support for `compas_gpython` in Rhino 8 Grasshopper CPython components.
15+
1416
### Removed
1517

18+
* Removed deprecated module `compas_ghpython.utilities`. For drawing functions, use `compas_ghpython.drawing` directly.
1619

1720
## [2.4.2] 2024-09-17
1821

src/compas/plugins.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import pkgutil
2323
import threading
2424

25+
import compas
26+
2527
__all__ = [
2628
"pluggable",
2729
"plugin",
@@ -33,6 +35,17 @@
3335
"PluginDefaultNotAvailableError",
3436
]
3537

38+
if compas.RHINO:
39+
import System # type: ignore
40+
41+
DotNetException = System.Exception
42+
else:
43+
44+
class DotNetException(BaseException):
45+
"""Ensures DotNetException is always a valid exception class. Even when not in IPY."""
46+
47+
pass
48+
3649

3750
class PluginNotInstalledError(Exception):
3851
"""Exception raised when an extension point is invoked but no plugin is available."""
@@ -401,15 +414,15 @@ def try_import(self, module_name):
401414
If importable, it returns the imported module, otherwise ``None``.
402415
"""
403416
module = None
404-
405417
try:
406418
module = __import__(module_name, fromlist=["__name__"], level=0)
407419
self._cache[module_name] = True
408420

409421
# There are two types of possible failure modes:
410422
# 1) cannot be imported, or
411423
# 2) is a python 3 module and we're in IPY, which causes a SyntaxError
412-
except (ImportError, SyntaxError):
424+
# 3) in Rhino8 we may get a nasty DotNet Exception when trying to import a module
425+
except (ImportError, SyntaxError, DotNetException):
413426
self._cache[module_name] = False
414427

415428
return module

src/compas_ghpython/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111

1212
__version__ = "2.4.2"
1313

14-
if compas.is_rhino():
15-
from .utilities import * # noqa: F401 F403
16-
1714
__all__ = [
1815
"get_grasshopper_managedplugin_path",
1916
"get_grasshopper_library_path",

src/compas_ghpython/drawing.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
from Rhino.Geometry import Vector3f
2121

2222
from compas.geometry import centroid_points
23+
from compas.geometry import centroid_polygon
2324
from compas.itertools import pairwise
24-
from compas_rhino.drawing import _face_to_max_quad
2525

2626
try:
2727
from Rhino.Geometry import MeshNgon
@@ -31,6 +31,17 @@
3131
TOL = sc.doc.ModelAbsoluteTolerance
3232

3333

34+
def _face_to_max_quad(points, face):
35+
faces = []
36+
c = len(points)
37+
points.append(centroid_polygon(points))
38+
for i in range(-1, len(face) - 1):
39+
a = face[i]
40+
b = face[i + 1]
41+
faces.append([c, a, b, b])
42+
return faces
43+
44+
3445
def draw_frame(frame):
3546
"""Draw a frame."""
3647
pt = Point3d(*iter(frame.point))

src/compas_ghpython/scene/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def register_scene_objects():
8787
register(VolMesh, VolMeshObject, context="Grasshopper")
8888
register(Brep, BrepObject, context="Grasshopper")
8989

90-
# print("GH SceneObjects registered.")
90+
print("GH SceneObjects registered.")
9191

9292

9393
__all__ = [

src/compas_ghpython/scene/meshobject.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,20 @@
44

55
from compas.scene import MeshObject as BaseMeshObject
66
from compas_rhino import conversions
7-
from compas_rhino.scene.helpers import ngon
87

98
from .sceneobject import GHSceneObject
109

1110

11+
def _create_ngon(vertex_count):
12+
if vertex_count < 3:
13+
return
14+
if vertex_count == 3:
15+
return [0, 1, 2]
16+
if vertex_count == 4:
17+
return [0, 1, 2, 3]
18+
return list(range(vertex_count))
19+
20+
1221
class MeshObject(GHSceneObject, BaseMeshObject):
1322
"""Scene object for drawing mesh data structures.
1423
@@ -147,7 +156,7 @@ def draw_faces(self):
147156
for face in faces:
148157
color = self.facecolor[face] # type: ignore
149158
vertices = [self.mesh.vertex_attributes(vertex, "xyz") for vertex in self.mesh.face_vertices(face)] # type: ignore
150-
facet = ngon(len(vertices))
159+
facet = _create_ngon(len(vertices))
151160

152161
if facet:
153162
geometry = conversions.vertices_and_faces_to_rhino(vertices, [facet], color=color)

src/compas_ghpython/scene/volmeshobject.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,20 @@
44

55
from compas.scene import VolMeshObject as BaseVolMeshObject
66
from compas_rhino import conversions
7-
from compas_rhino.scene.helpers import ngon
87

98
from .sceneobject import GHSceneObject
109

1110

11+
def _create_ngon(vertex_count):
12+
if vertex_count < 3:
13+
return
14+
if vertex_count == 3:
15+
return [0, 1, 2]
16+
if vertex_count == 4:
17+
return [0, 1, 2, 3]
18+
return list(range(vertex_count))
19+
20+
1221
class VolMeshObject(GHSceneObject, BaseVolMeshObject):
1322
"""Scene object for drawing volmesh data structures."""
1423

@@ -84,7 +93,7 @@ def draw_faces(self):
8493
for face in faces:
8594
color = self.facecolor[face]
8695
vertices = [self.vertex_xyz[vertex] for vertex in self.volmesh.face_vertices(face)]
87-
facet = ngon(len(vertices))
96+
facet = _create_ngon(len(vertices))
8897
if facet:
8998
meshes.append(conversions.vertices_and_faces_to_rhino(vertices, [facet], color=color))
9099

src/compas_ghpython/utilities/__init__.py

Lines changed: 0 additions & 36 deletions
This file was deleted.

tests/compas/test_plugins.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from abc import abstractmethod
22

33
import pytest
4-
4+
import compas
55
from compas.plugins import IncompletePluginImplError
66
from compas.plugins import PluginValidator
77

@@ -36,3 +36,38 @@ def test_ensure_implementations_fails_with_incomplete_impl():
3636

3737
def test_ensure_implementations_with_valid_impl():
3838
PluginValidator.ensure_implementations(CompleteImpl)
39+
40+
41+
def test_dot_net_exception_with_rhino():
42+
if not compas.RHINO:
43+
return
44+
45+
from compas.plugins import DotNetException
46+
47+
assert DotNetException is not None
48+
49+
import System
50+
51+
assert DotNetException == System.Exception
52+
53+
54+
def test_dot_net_exception_without_rhino():
55+
if compas.RHINO:
56+
return
57+
58+
from compas.plugins import DotNetException
59+
60+
assert DotNetException is not None
61+
assert issubclass(DotNetException, BaseException)
62+
63+
64+
def test_importer_fail_silently():
65+
from compas.plugins import Importer
66+
67+
importer = Importer()
68+
69+
is_importable = importer.check_importable("compas")
70+
assert is_importable
71+
72+
is_importable = importer.check_importable("module_which_does_not_exist")
73+
assert not is_importable

0 commit comments

Comments
 (0)