-
Notifications
You must be signed in to change notification settings - Fork 52
How to organize your plugin project
Used terminology:
- plugin - any of the Sardana plugin (controller, macro, recorder, widget, complete GUI)
- project - synonym to repository e.g. git, mercurial, etc. with eventual infrastructure like issue tracker, wiki, etc.
- python project - top level python package +
setup.py
, etc.
SEP16 leaves to the plugin developer the decission on how to organize the project. However based on years of experience of developing Sardana plugins we have observed common patterns and questions that emerge to the developer on how to organize the project. This document does not give neither clear answers nor rules but analyzes the possible scenarios and share the lessons learnt.
Let's analyze an example of the IcePAP motion controller. Currently we have developed one motor controller and one trigger/gate controller as well as a set of macros for the IcePAP motion controller. You could either organize this project(s) in the following way:
- one project for motor controller, another one for trigger/gate controller and one common for all the macros
- one project for controller type plugins (both motor and trigger/gate) and another one for macro type plugins
- only one project for all plugins related to IcePAP
We believe that having all related plugins in one project gives the following benefits
- reduces the overhead of project administration maintenance
- facilitates the related plugins discovery - everything in one place
- no need to handle version dependencies between projects (e.g. a given version of a macro depends on a given version of a controller)
So we could recommend to organize in one single project all types of plugins related to each other by some characteristic. The characteristic examples could be:
- hardware/instrument type e.g. IcePAP motor controller, HKL
- specific system e.g. beamline, laboratory
- generic plugins for a control system e.g. Tango, EPICS, Lima
- generic plugins for a given type (and family) e.g. pseudo motors, recorders
Currently some types of plugins, for example macros, can not be directly imported in python. This is because they are tightly bound to the Sardana plugin system. Furthermore the current plugin system in Sardana does not require the plugins to be python modules - these just need to be files with python code and .py
extension.
On the other hand, the controller plugins are actually directly usable in Python:
In [1]: from sardana.pool.poolcontrollers.DummyMotorController import DummyMotorController
In [2]: dmotctrl = DummyMotorController("test", {})
In [3]: dmotctrl.AddDevice(1)
In [4]: dmotctrl.ReadOne(1)
Out[4]: SardanaValue(value=0, timestamp=1563288438.98)
Taking into account our long term plan to unify the plugin system in Taurus and Sardana - see TEP13 and the current Proof-of-Concept implementation of the taurus.qt.qtgui
and taurus_pyqtgraph we belive that organizing the projects in the way that plugins itself will reside in orginary python modules is a good step forward.
The module naming is totally open but we strongly recommend to use the top level module name: sardana_<name>
where <name>
identifies your plugin or family of plugins e.g. sardana_icepap
. If in the project there are multiple types of plugins, e.g. macros, controllers and widgets we recommend an intermediate python sub-module per type of plugins:
sardana_<name>.macro
sardana_<name>.ctrl
sardana_<name>.recorder
sardana_<name>.gui
Following this recomendation, and the sardana-icepap project example we could imagine the following code:
from sardana_icepap.ctrl import IcePAPMotorController
from sardana_icepap.macro import ipap_homing
In order to enable this plugins in Sardana using the current plugin system you would need to configure the following properties (based on Debian directory structure):
PoolPath = /usr/lib/python/3.5/dist-packages/sardana_<name>/ctrl
MacroPath = /usr/lib/python/3.5/dist-packages/sardana_<name>/macro
RecorderPath = /usr/lib/python/3.5/dist-packages/sardana_<name>/recorder
or of the project is not installed but only cloned:
PoolPath = <workspace>/sardana-name/sardana_<name>/ctrl
MacroPath = <workspace>/sardana_<name>/macro
RecorderPath = <workspace>/sardana_<name>/recorder
where is the directory where you have cloned the project.
Sometimes, within the same project, different plugins organized in different files/modules, reuse the same code. A good design decision is to separate this common code into a dedicated python module and use it as a dependency of the plugins. These dependency modules could be still part of the plugin project. This will certainly reduce the project maintenance overhead. But in other cases, where these dependecy is also used by another applications (not Sardana plugins) it has more sense to move them to a dedicated project.
Taking into account the above discussion, we believe that the following project organization fits pretty well to many scanarios.
The project top directory name (as well as the project name) could be sardana-<name>
where is the characteristic which groups the plugins or any other identifying name e.g. sardana-bl01
, sardana-icepap
, etc.
├──setup.py
└──sardana_<name>
├──__init__.py
├──ctrl
│ ├──__init__.py
│ └──foo.py (with FooMotorCtrl class)
├──macro
│ ├──__init__.py
│ └──bar.py (with bar_procedure function)
├──recorder
│ ├──__init__.py
│ └──baz.py (with BazRecorder class)
├──gui
│ ├──__init__.py
│ └──qux.py (with QuxWidget class)
└──fred.py (optional)
For very simple cases, we could directly program plugins in the plugin type module e.g. ctrl or macro:
├──setup.py
└──sardana_<name>
├──__init__.py
├──ctrl.py (with FooMotorCtrl class)
└──macro.py (with bar_procedure function)