Skip to content

Commit cdc3518

Browse files
committed
devguide stubs
1 parent 02c9cd2 commit cdc3518

File tree

14 files changed

+249
-233
lines changed

14 files changed

+249
-233
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.

docs/devguide/__old/plugins.rst

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
*******
2+
Plugins
3+
*******
4+
5+
What are plugins?
6+
-----------------
7+
8+
COMPAS has an extensible architecture based on plugins that allows to
9+
customize and extend the functionality of the core framework.
10+
11+
For a plugin to work, there needs to exist a counterpart to be connected to.
12+
This means there are two components involved:
13+
14+
* :meth:`compas.plugins.pluggable` interface: the *extension point* that COMPAS defines
15+
as the counterpart for plugins to connect to.
16+
* :meth:`compas.plugins.plugin` implementation: a *concrete implementation* of the
17+
``pluggable`` interface.
18+
19+
Both of these components are declared using decorators:
20+
21+
.. code-block:: python
22+
23+
@pluggable
24+
def do_hard_stuff(input):
25+
pass
26+
27+
@plugin(pluggable_name='do_hard_stuff')
28+
def do_hard_stuff_numpy(input):
29+
# NOTE: Here use the power of numpy to do hard stuff very fast
30+
# ..
31+
32+
Once these parts are implemented, the program could simply
33+
call the function ``do_hard_stuff`` and the appropriate plugin
34+
implementation using ``numpy`` would be called automatically.
35+
36+
37+
Why are plugins important?
38+
--------------------------
39+
40+
The example above is just a single code block, but the power of plugins comes
41+
from the ability to split those two parts -the :meth:`compas.plugins.pluggable`
42+
and the :meth:`compas.plugins.plugin`- into completely different files, folders
43+
or even entire projects and still work the same way.
44+
45+
Additionally, COMPAS is able to pick the most suitable plugin implementation
46+
for its current execution context. For instance, one could have two implementations
47+
of the same :meth:`compas.plugins.pluggable` definition, one using ``numpy`` and
48+
another one using *Rhino SDK* and have the correct one automatically selected
49+
based on where your script is executing.
50+
51+
52+
How to make plugins discoverable?
53+
---------------------------------
54+
55+
COMPAS plugin discovery is based on naming conventions. This is mainly due to
56+
the need to support IronPython inside Rhino, which lacks ``setuptools``
57+
infrastructure. For more details, check
58+
`these python guidelines <https://packaging.python.org/guides/creating-and-discovering-plugins/#using-naming-convention>`_.
59+
60+
A COMPAS plugin needs to fulfill two conditions:
61+
62+
* **Name**: The package name should be prefixed with ``compas``, eg. ``compas_cgal``.
63+
* **Metadata**: The package should define a bit of metadata listing the modules that contain plugins.
64+
This is done declaring a variable called ``__all_plugins__``,
65+
eg. ``__all_plugins__ = ['compas_cgal.booleans']``.
66+
67+
COMPAS automatically discovers plugins searching over all available packages in the system,
68+
and picks up those prefixed with the ``compas`` word.
69+
All packages are included in the search: packages installed with ``pip``, packages made
70+
available through the ``PYTHONPATH`` / ``IRONPYTHONPATH``, local packages, etc.
71+
72+
Once a package is found, the metadata in ``__all_plugins__`` is read and all modules
73+
listed are analyzed to look for functions decorated with the :meth:`compas.plugins.plugin`
74+
decorator.
75+
76+
77+
Two kinds of extension points
78+
-----------------------------
79+
80+
An extension point, or *pluggable* interface can be declared as being one of two types
81+
based on how they select which implementation to pick if there are multiple available.
82+
83+
* ``selector='first_match'``: this type of extension point will pick the first plugin
84+
implementation that satisfies the requirements.
85+
* ``selector='collect_all'``: extension points defined with this selector will instead
86+
collect all plugin implementations and execute them all, collecting the return
87+
values into a list. An example of this is the Rhino install extension
88+
point: :meth:`compas_rhino.install.installable_rhino_packages`.
89+
90+
91+
A complete example
92+
------------------
93+
94+
Let's explore a complete example to gain a better understanding.
95+
96+
97+
Extension point
98+
^^^^^^^^^^^^^^^
99+
100+
For the sake of example, we are going to assume that ``compas`` core defines
101+
the following :meth:`compas.plugins.pluggable` interface in
102+
103+
**compas/geometry/booleans/__init__.py**
104+
105+
.. code-block:: python
106+
107+
@pluggable(category='booleans')
108+
def boolean_union_mesh_mesh(A, B):
109+
pass
110+
111+
112+
Plugin
113+
^^^^^^
114+
115+
Now let's write a plugin that implements this interface:
116+
117+
**compas_plugin_sample/__init__.py**
118+
119+
.. code-block:: python
120+
121+
__all_plugins__ = ['compas_plugin_sample.boolean_trimesh']
122+
123+
124+
**compas_plugin_sample/boolean_trimesh.py**
125+
126+
.. code-block:: python
127+
128+
import trimesh
129+
130+
@plugin(category='booleans', requires=['trimesh'])
131+
def boolean_union_mesh_mesh(A, B):
132+
va, fa = A
133+
at = trimesh.Trimesh(vertices=va, faces=fa)
134+
135+
vb, fb = B
136+
bt = trimesh.Trimesh(vertices=vb, faces=fb)
137+
138+
r = at.union(bt, engine='scad')
139+
140+
return r.vertices, r.faces
141+
142+
Voilà! We have a trimesh-based boolean union plugin!
143+
144+
145+
Advanced options
146+
----------------
147+
148+
There are a few additional options that plugins can use:
149+
150+
* ``requires``: List of requirements. COMPAS will filter out plugins if their
151+
requirements list is not satisfied at runtime. This allows to have multiple implementations
152+
of the same operation and have them selected based on different criteria.
153+
The requirement can either be a package name string (e.g. ``requires=['scipy']``) or
154+
a ``callable`` with a boolean return value, in which any arbitrary check can be implemented
155+
(e.g. ``requires=[lambda: is_rhino_active()]``).
156+
* ``tryfirst`` and ``trylast``: Plugins cannot control the exact priority they will have
157+
but they can indicate whether to try to prioritize them or demote them as fallback using
158+
these two boolean parameters.
159+
* ``pluggable_name``: Usually, the name of the decorated plugin method matches that of the
160+
pluggable interface. When that is not the case, the pluggable name can be specified via
161+
this parameter.
162+
* ``domain``: extension points are unambiguously identified by a URL that combines domain,
163+
category and pluggable name. All COMPAS core plugins use the same domain, but other
164+
packages could potentially decide to use a different domain to ensure collision-free
165+
naming of pluggable extension points.
166+
167+
While developing plugins, it is also possible to enable print output to understand what
168+
how plugin selection works behind the scenes. To enable that, set ``DEBUG`` flag
169+
accordingly:
170+
171+
.. code-block:: python
172+
173+
from compas.plugins import plugin_manager
174+
plugin_manager.DEBUG = True
175+
File renamed without changes.

docs/devguide/__old/workflow.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
********
2+
Workflow
3+
********
4+
5+
Setup
6+
=====
7+
8+
To set up a developer environment
9+
10+
1. Fork `the repository <https://github.com/compas-dev/compas>`_ and clone the fork.
11+
2. Create a virtual environment using your tool of choice (e.g. `virtualenv`, `conda`, etc).
12+
13+
.. code-block:: bash
14+
15+
conda create -n compas-dev python=3.11 --yes
16+
conda activate compas-dev
17+
18+
3. Install development dependencies:
19+
20+
.. code-block:: bash
21+
22+
cd path/to/compas
23+
pip install -r requirements-dev.txt
24+
25+
4. Make sure all tests pass and the code is free of lint:
26+
27+
.. code-block:: bash
28+
29+
invoke lint
30+
invoke test
31+
32+
5. Create a branch for your contributions.
33+
34+
.. code-block::
35+
36+
git branch title-proposed-changes
37+
git checkout title-proposed-changes
38+
39+
6. Start making changes!
40+
41+
Submitting a PR
42+
===============
43+
44+
Once you are done making changes, you have to submit your contribution through a pull request (PR).
45+
The procedure for submitting a PR is the following.
46+
47+
1. Make sure all tests still pass, the code is free of lint, and the docstrings compile correctly:
48+
49+
.. code-block:: bash
50+
51+
invoke lint
52+
invoke test
53+
invoke docs
54+
55+
2. Add yourself to ``AUTHORS.md``.
56+
3. Summarize the changes you made in ``CHANGELOG.md``.
57+
4. Commit your changes and push your branch to GitHub.
58+
5. Create a `pull request <https://help.github.com/articles/about-pull-requests/>`_.

docs/devguide/code.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Code Constributions
2+
===================

docs/devguide/documentation.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Contributing to the Documentation
2+
=================================

docs/devguide/dtypes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Implementing a New Data Type
2+
============================

docs/devguide/extensions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Developing an Extension
2+
=======================

0 commit comments

Comments
 (0)