Skip to content

Commit b3c80d9

Browse files
authored
Merge pull request #909 from compas-dev/artist-factory-plugins
Artist factory plugins
2 parents 3e010b2 + 34a73b7 commit b3c80d9

File tree

5 files changed

+50
-12
lines changed

5 files changed

+50
-12
lines changed

docs/devguide.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,12 @@ Advanced options
390390

391391
There are a few additional options that plugins can use:
392392

393-
* ``requires``: List of required python modules. COMPAS will filter out plugins if their
393+
* ``requires``: List of requirements. COMPAS will filter out plugins if their
394394
requirements list is not satisfied at runtime. This allows to have multiple implementations
395-
of the same operation and have them selected based on which packages are installed.
396-
on the system. Eg. `requires=['scipy']`.
395+
of the same operation and have them selected based on different criteria.
396+
The requirement can either be a package name string (e.g. ``requires=['scipy']``) or
397+
a ``callable`` with a boolean return value, in which any arbitrary check can be implemented
398+
(e.g. ``requires=[lambda: is_rhino_active()]``).
397399
* ``tryfirst`` and ``trylast``: Plugins cannot control the exact priority they will have
398400
but they can indicate whether to try to prioritize them or demote them as fallback using
399401
these two boolean parameters.

src/compas/plugins.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,11 @@ def plugin(method=None, category=None, requires=None, tryfirst=False, trylast=Fa
321321
The method to decorate as ``plugin``.
322322
category : str, optional
323323
An optional string to group or categorize plugins.
324-
requires : list of str, optional
325-
Optionally defines a list of packages that should be importable
326-
for this plugin to be used.
324+
requires : list, optional
325+
Optionally defines a list of requirements that should be fulfilled
326+
for this plugin to be used. The requirement can either be a package
327+
name (``str``) or a ``callable`` with a boolean return value,
328+
in which any arbitrary check can be implemented.
327329
tryfirst : bool, optional
328330
Plugins can declare a preferred priority by setting this to ``True``.
329331
By default ``False``.
@@ -414,9 +416,16 @@ def check_importable(self, module_name):
414416
return self._cache[module_name]
415417

416418

419+
def verify_requirement(manager, requirement):
420+
if callable(requirement):
421+
return requirement()
422+
423+
return manager.importer.check_importable(requirement)
424+
425+
417426
def is_plugin_selectable(plugin, manager):
418427
if plugin.opts['requires']:
419-
importable_requirements = (manager.importer.check_importable(name) for name in plugin.opts['requires'])
428+
importable_requirements = (verify_requirement(manager, requirement) for requirement in plugin.opts['requires'])
420429

421430
if not all(importable_requirements):
422431
if manager.DEBUG:

src/compas_ghpython/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,9 @@ def _get_grasshopper_special_folder(version, folder_name):
6969
return grasshopper_library_path
7070

7171

72-
__all_plugins__ = ['compas_ghpython.install', 'compas_ghpython.uninstall']
72+
__all_plugins__ = [
73+
'compas_ghpython.install',
74+
'compas_ghpython.uninstall',
75+
'compas_ghpython.artists',
76+
]
7377
__all__ = [name for name in dir() if not name.startswith('_')]

src/compas_ghpython/artists/__init__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
from compas.datastructures import Network
7272
from compas.datastructures import VolMesh
7373

74+
from compas.robots import RobotModel
75+
7476
from .artist import GHArtist
7577
from .circleartist import CircleArtist
7678
from .frameartist import FrameArtist
@@ -99,7 +101,17 @@
99101
VolMeshArtist.default_cellcolor = (255, 0, 0)
100102

101103

102-
@plugin(category='factories', pluggable_name='new_artist', requires=['ghpythonlib'])
104+
def verify_gh_context():
105+
try:
106+
import Rhino
107+
import scriptcontext as sc
108+
109+
return not isinstance(sc.doc, Rhino.RhinoDoc)
110+
except:
111+
return False
112+
113+
114+
@plugin(category='factories', pluggable_name='new_artist', requires=['ghpythonlib', verify_gh_context])
103115
def new_artist_gh(cls, *args, **kwargs):
104116
# "lazy registration" seems necessary to avoid item-artist pairs to be overwritten unintentionally
105117

@@ -111,6 +123,7 @@ def new_artist_gh(cls, *args, **kwargs):
111123
GHArtist.register(Mesh, MeshArtist)
112124
GHArtist.register(Network, NetworkArtist)
113125
GHArtist.register(VolMesh, VolMeshArtist)
126+
GHArtist.register(RobotModel, RobotModelArtist)
114127

115128
data = args[0]
116129

@@ -127,7 +140,7 @@ def new_artist_gh(cls, *args, **kwargs):
127140
for name, value in inspect.getmembers(cls):
128141
if inspect.ismethod(value):
129142
if hasattr(value, '__isabstractmethod__'):
130-
raise Exception('Abstract method not implemented')
143+
raise Exception('Abstract method not implemented: {}'.format(value))
131144

132145
return super(Artist, cls).__new__(cls)
133146

src/compas_rhino/artists/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,17 @@
141141
VolMeshArtist.default_cellcolor = (255, 0, 0)
142142

143143

144-
@plugin(category='factories', pluggable_name='new_artist', requires=['Rhino'])
144+
def verify_rhino_context():
145+
try:
146+
import Rhino
147+
import scriptcontext as sc
148+
149+
return isinstance(sc.doc, Rhino.RhinoDoc)
150+
except:
151+
return False
152+
153+
154+
@plugin(category='factories', pluggable_name='new_artist', requires=['Rhino', verify_rhino_context])
145155
def new_artist_rhino(cls, *args, **kwargs):
146156
# "lazy registration" seems necessary to avoid item-artist pairs to be overwritten unintentionally
147157

@@ -180,7 +190,7 @@ def new_artist_rhino(cls, *args, **kwargs):
180190
for name, value in inspect.getmembers(cls):
181191
if inspect.ismethod(value):
182192
if hasattr(value, '__isabstractmethod__'):
183-
raise Exception('Abstract method not implemented')
193+
raise Exception('Abstract method not implemented: {}'.format(value))
184194

185195
return super(Artist, cls).__new__(cls)
186196

0 commit comments

Comments
 (0)