dev_write_ctrl.md 6.92 KB
Newer Older
Cyril Guilloud's avatar
Cyril Guilloud committed
1
2
3
4
# Writing a BLISS controller

Here you can find somt tips about the wrinting of a BLISS controller.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
## @autocomplete_property decorator

in many controllers the `@property` decorator is heavily used to protect certain attributes of the instance or to limit the access to read-only. When using the bliss command line interface the autocompletion will __not__ suggeste any completion based on the return value of the method underneath the property. This is a wanted behavior e.g. in case this would trigger hardware communication. There are however also usecases where a _deeper_ autocompletion is wanted. E.g. the `.counter` namespace of a controller. If implemented as `@property`

    BLISS [1]: lima_simulator.counters.

would not show any autocompletion suggestions. To enable _deeper_ autocompletion there is a special decorator called `@autocomplete_property` that can be imported via `from bliss.common.utils import autocomplete_property`. Using the `@autocomplete_property` decorator befor the `def counters(self):` method of the controller would e.g. result in 

    BLISS [1]: lima_simulator.counters.
                                      _roi1_
                                      _roi2_
                                      _bpm_

autocompletion suggestions. 
Cyril Guilloud's avatar
Cyril Guilloud committed
19

Linus Pithan's avatar
Linus Pithan committed
20
21
22
23
## The `__info__` methode for Bliss shell
!!! info

    - Any Bliss controller that is visible to the user in the command line should have an `__info__` function implemented!
Linus Pithan's avatar
Linus Pithan committed
24
    - The return type of `__info__` must be `str`, otherwhise it fails and `repr` is used as fallback!
Linus Pithan's avatar
Linus Pithan committed
25
26
27
    - As a rule of thumb: the retrun value of a custom `__repr__` implementation should not contain `\n` and should be inspired by the standard implementation of `__repr__` in python.

In Bliss `__info__` is used by the command line interface (Bliss shell or Bliss repl) to enquire information of the internal state of any object / controller in case it is available.
Linus Pithan's avatar
Linus Pithan committed
28
This is used to have simple way to get (detailed) information that is needed from a __user point of view__ to use the object. This is in contrast to the build-in python function `__repr__`, which should return a short summary of the concerned object from the __developer point of view__. The Protocol that is put in place in the Bliss shell is the following:
Linus Pithan's avatar
Linus Pithan committed
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
* if the return value of a statement entered into the Bliss shel is a python object with `__info__` implemented this `__info__` function will be called by the Bliss shell to display the output. As a fallback option (`__info__` not implemented) the standard behavior of the interactive python interpreter involving `__repr__` is used. (For details about `__repr__` see next section.)

Here is an example for the lima controller that is using `__info__`:
```
LIMA_TEST_SESSION [3]: lima_simulator          
              Out [3]: Simulator - Generator (Simulator) - Lima Simulator
                       
                       Image:
                       bin = [1 1]
                       flip = [False False]
                       height = 1024
                       roi = <0,0> <1024 x 1024>
                       rotation = rotation_enum.NONE
                       sizes = [   0    4 1024 1024]
                       type = Bpp32
                       width = 1024
                       
                       Acquisition:
                       expo_time = 1.0
                       mode = mode_enum.SINGLE
                       nb_frames = 1
                       status = Ready
                       status_fault_error = No error
                       trigger_mode = trigger_mode_enum.INTERNAL_TRIGGER
                       
                       ROI Counters:
                       [default]
                       
                       Name  ROI (<X, Y> <W x H>)
                       ----  ------------------
                         r1  <0, 0> <100 x 200>
```
the information given above is usefull from a __user point of view__. As a __developer__ one might want to work in the Bliss shell with live object e.g.

```
LIMA_TEST_SESSION [4]: my_detectors = {'my_lima':lima_simulator,'my_mca':simu1}                
LIMA_TEST_SESSION [5]: my_detectors            
              Out [5]: {'my_lima': <Lima Controller for Simulator (Lima Simulator)>, 
                        'my_mca': <bliss.controllers.mca.simulation.SimulatedMCA object at 0x7f2f535b5f60>}
```
Linus Pithan's avatar
Linus Pithan committed
69
in this case it is desirable that the python objects themselves are clearly represented, which is exactly the role of `__repr__` (in this example the `lima_simulator` has a custom `__repr__` while in `simu1` there is no `__repr__` implemented so the bulid in python implementation is used). 
Linus Pithan's avatar
Linus Pithan committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

The signature of `__info__` should be `def __info__(self):` the return value should be a string.

```
BLISS [1]: class A(object): 
      ...:     def __repr__(self): 
      ...:         return "my repl" 
      ...:     def __str__(self): 
      ...:         return "my str" 
      ...:     def __info__(self): 
      ...:         return "my info"            

BLISS [2]: a=A()                               

BLISS [3]: a                                   
  Out [3]: my info

BLISS [4]: [a]                                 
  Out [4]: [my repl]
```

Note : if for any reason there is an exception raised inside `__info__` the fallback option will be used and `__repr__` is evaluated in this case.

Linus Pithan's avatar
Linus Pithan committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
The equivalent of `repr(obj)` or `str(obj)` is also availabe in `bliss.common.standard` as `info(obj)` which can be used also outside the Bliss shell.

```
Python 3.7.3 (default, Mar 27 2019, 22:11:17) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> from bliss.common.standard import info

>>> class A(object):
...     def __repr__(self):
...          return "my repl"
...     def __info__(self):
...          return "my info"
... 
>>> info(A())
'my info'

>>> class B(object):
...     def __repr__(self):
...          return "my repl"
... 

>>> info(B())
'my repl'
```

Linus Pithan's avatar
Linus Pithan committed
120
### Recap `__str__` and `__repr__` methods
Cyril Guilloud's avatar
Cyril Guilloud committed
121
122

If implemented in a Python class, `__repr__` and `__str__` methods are
Linus Pithan's avatar
Linus Pithan committed
123
build-in functions Python to return information about an object instantiating this class.
Cyril Guilloud's avatar
Cyril Guilloud committed
124
125

* `__str__` should print a readable message
Linus Pithan's avatar
Linus Pithan committed
126
* `__repr__` should print a __short__ message obout the objec that is unambigous (e.g. name of an identifier, class name, etc).
Cyril Guilloud's avatar
Cyril Guilloud committed
127
128
129

`__str__` is called:

Linus Pithan's avatar
Linus Pithan committed
130
131
* when the object is passed to the print() function (e.g. `print(my_obj)`).
* wheh the object is used in string operations (e.g. `str(my_obj)` or `'{}'.format(my_obj)` or `f'some text {my_obj}'`)
Cyril Guilloud's avatar
Cyril Guilloud committed
132
133
134

`__repr__` method is called:

Linus Pithan's avatar
Linus Pithan committed
135
136
137
* when user type the name of the object in an interpreter session (a python shell).
* when displaying containers like lists and dicts (the result of `__repr__` is used to represent the objects they contain)
* when explicitly asking for it in the print() function. (e.g. `print("%r" % my_object)`)
Cyril Guilloud's avatar
Cyril Guilloud committed
138
139
140


By default when no `__str__` or `__repr__` methods are defined, the
Linus Pithan's avatar
Linus Pithan committed
141
142
143
144
`__repr__` returns the name of the class (Length) and `__str__` calls `__repr__`.