Skip to content

Commit 520e240

Browse files
committed
Add install/uninstall extension points
1 parent ae188d5 commit 520e240

File tree

5 files changed

+125
-11
lines changed

5 files changed

+125
-11
lines changed

docs/plugins.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ Category: ``booleans``
2424
Category: ``install``
2525
^^^^^^^^^^^^^^^^^^^^^
2626

27-
.. currentmodule:: compas_rhino.install
27+
.. currentmodule::
2828

2929
* :func:`installable_rhino_packages`
30+
* :func:`after_rhino_install`
31+
32+
.. currentmodule:: compas_rhino.uninstall
33+
* :func:`after_rhino_uninstall`
3034

3135

3236
Category: ``intersections``

src/compas/plugins.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,12 +285,12 @@ def wrapper(*args, **kwargs):
285285

286286
# Collect all matching plugins
287287
elif selector == 'collect_all':
288-
results = []
289-
290288
for plugin_impl in _collect_plugins(extension_point_url):
291-
results.append(plugin_impl.method(*args, **kwargs))
292-
293-
return results
289+
try:
290+
result = plugin_impl.method(*args, **kwargs)
291+
yield result
292+
except Exception as e:
293+
yield e
294294
else:
295295
raise ValueError('Unexpected selector type. Must be either: first_match or collect_all')
296296

src/compas_rhino/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,5 @@ def _try_remove_bootstrapper(path):
211211
__all_plugins__ = [
212212
'compas_rhino.geometry.booleans',
213213
'compas_rhino.install',
214+
'compas_rhino.uninstall',
214215
]

src/compas_rhino/install.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@
1212
import compas._os
1313
import compas.plugins
1414

15-
__all__ = ['install']
15+
__all__ = [
16+
'install',
17+
'installable_rhino_packages',
18+
'after_rhino_install',
19+
'after_rhino_uninstall',
20+
]
1621

1722

1823
def install(version=None, packages=None):
@@ -84,8 +89,13 @@ def install(version=None, packages=None):
8489
symlinks = [(link['source_path'], link['link']) for link in symlinks_to_install]
8590
install_results = compas._os.create_symlinks(symlinks)
8691

92+
installed_packages = []
8793
for install_data, success in zip(symlinks_to_install, install_results):
88-
result = 'OK' if success else 'ERROR: Cannot create symlink, try to run as administrator.'
94+
if success:
95+
installed_packages.append(install_data['name'])
96+
result = 'OK'
97+
else:
98+
result = 'ERROR: Cannot create symlink, try to run as administrator.'
8999
results.append((install_data['name'], result))
90100

91101
if not all(install_results):
@@ -106,10 +116,38 @@ def install(version=None, packages=None):
106116
if status != 'OK':
107117
exit_code = -1
108118

119+
if len(installed_packages):
120+
print()
121+
print('Running post-installation steps...')
122+
print()
123+
_run_post_execution_steps(after_rhino_install(installed_packages))
124+
109125
print('\nCompleted.')
110126
if exit_code != 0:
111127
sys.exit(exit_code)
112128

129+
def _run_post_execution_steps(steps_generator):
130+
post_execution_errors = []
131+
for result in steps_generator:
132+
if isinstance(result, Exception):
133+
post_execution_errors.append(result)
134+
continue
135+
136+
for item in result:
137+
try:
138+
package, message, success = item
139+
status = 'OK' if success else 'ERROR'
140+
print(' {} {}: {}'.format(package.ljust(20), status, message))
141+
except ValueError:
142+
post_execution_errors.append(ValueError('Step successful, but result is wrongly formatted: {}'.format(str(result))))
143+
144+
if post_execution_errors:
145+
print()
146+
print('One or more errors occurred:')
147+
print()
148+
for error in post_execution_errors:
149+
print(' - {}'.format(repr(error)))
150+
113151

114152
@compas.plugins.plugin(category='install', pluggable_name='installable_rhino_packages', tryfirst=True)
115153
def default_installable_rhino_packages():
@@ -143,6 +181,35 @@ def installable_rhino_packages():
143181
pass
144182

145183

184+
@compas.plugins.pluggable(category='install', selector='collect_all')
185+
def after_rhino_install(installed_packages):
186+
"""Allows extensions to execute actions after install to Rhino is done.
187+
188+
Extensions providing Rhino or Grasshopper features
189+
can implement this pluggable interface to perform
190+
additional steps after an installation to Rhino has
191+
been completed.
192+
193+
Parameters
194+
----------
195+
installed_packages : :obj:`list` of :obj:`str`
196+
List of packages that have been installed successfully.
197+
198+
Examples
199+
--------
200+
>>> import compas.plugins
201+
>>> @compas.plugins.plugin(category='install')
202+
... def after_rhino_install(installed_packages):
203+
... # Do something after package is installed to Rhino, eg, copy components, etc
204+
... return [('compas_ghpython', 'GH Components installed', True)]
205+
206+
Returns
207+
-------
208+
:obj:`list` of 3-tuple (str, str, bool)
209+
List containing a 3-tuple with component name, message and ``True``/``False`` success flag.
210+
"""
211+
pass
212+
146213
def _update_bootstrapper(install_path, packages):
147214
# Take either the CONDA environment directory or the current Python executable's directory
148215
python_directory = os.environ.get('CONDA_PREFIX', None) or os.path.dirname(sys.executable)

src/compas_rhino/uninstall.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
import os
77
import sys
88

9+
import compas._os
10+
import compas.plugins
911
import compas_rhino
12+
from compas_rhino.install import _run_post_execution_steps
1013
from compas_rhino.install import installable_rhino_packages
1114

12-
import compas._os
13-
1415
__all__ = ['uninstall']
1516

1617

@@ -68,8 +69,13 @@ def uninstall(version=None, packages=None):
6869
symlinks = [link['link'] for link in symlinks_to_uninstall]
6970
uninstall_results = compas._os.remove_symlinks(symlinks)
7071

72+
uninstalled_packages = []
7173
for uninstall_data, success in zip(symlinks_to_uninstall, uninstall_results):
72-
result = 'OK' if success else 'ERROR: Cannot remove symlink, try to run as administrator.'
74+
if success:
75+
uninstalled_packages.append(uninstall_data['name'])
76+
result = 'OK'
77+
else:
78+
result = 'ERROR: Cannot remove symlink, try to run as administrator.'
7379
results.append((uninstall_data['name'], result))
7480

7581
if not all(uninstall_results):
@@ -92,6 +98,12 @@ def uninstall(version=None, packages=None):
9298
if status != 'OK':
9399
exit_code = -1
94100

101+
if len(uninstalled_packages):
102+
print()
103+
print('Running post-uninstallation steps...')
104+
print()
105+
_run_post_execution_steps(after_rhino_uninstall(uninstalled_packages))
106+
95107
print('\nUninstall completed.')
96108
if exit_code != 0:
97109
sys.exit(exit_code)
@@ -126,6 +138,36 @@ def _filter_installed_packages(version, packages):
126138
return packages
127139

128140

141+
@compas.plugins.pluggable(category='install', selector='collect_all')
142+
def after_rhino_uninstall(uninstalled_packages):
143+
"""Allows extensions to execute actions after uninstall from Rhino is done.
144+
145+
Extensions providing Rhino or Grasshopper features
146+
can implement this pluggable interface to perform
147+
additional steps after the uninstall from Rhino has
148+
been completed.
149+
150+
Parameters
151+
----------
152+
uninstalled_packages : :obj:`list` of :obj:`str`
153+
List of packages that have been uninstalled.
154+
155+
Examples
156+
--------
157+
>>> import compas.plugins
158+
>>> @compas.plugins.plugin(category='install')
159+
... def after_rhino_uninstall(uninstalled_packages):
160+
... # Do something cleanup, eg remove copied files.
161+
... return [('compas_ghpython', 'GH Components uninstalled', True)]
162+
163+
Returns
164+
-------
165+
:obj:`list` of 3-tuple (str, str, bool)
166+
List containing a 3-tuple with component name, message and ``True``/``False`` success flag.
167+
"""
168+
pass
169+
170+
129171
# ==============================================================================
130172
# Main
131173
# ==============================================================================

0 commit comments

Comments
 (0)