Writing a BLISS controller¶
Here you can find somt tips about the wrinting of a BLISS controller.
@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.
Note
“↹” represents the action of pressing the “Tab” key of the keyboard.
Example: 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
a special decorator called @autocomplete_property
must be used.
from bliss.common.utils import autocomplete_property class Lima(object): @autocomplete_property def counters(self): all_counters = [self.image] ...
Using this decorator would result in autocompletion suggestions:
BLISS [1]: lima_simulator.counters. ↹ _roi1_ _roi2_ _bpm_
The __info__()
method for Bliss shell¶
Info
- Any Bliss controller that is visible to the user in the command line
should have an
__info__()
function implemented! - The return type of
__info__()
must bestr
, otherwhise it fails and__repr__()
is used as fallback! - 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.
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:
- 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 [4]: my_detectors = {'my_lima':lima_simulator,'my_mca':simu1} LIMA [5]: my_detectors Out [5]: {'my_lima': <Lima Controller for Simulator (Lima Simulator)>, 'my_mca': <bliss.controllers.mca.simulation.SimulatedMCA object at 0x7f2f535b5f60>}
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).
The signature of __info__()
should be def __info__(self):
the return value
musst 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]
Warning
If, for any reason, there is an exception raised inside __info__
, the
fallback option will be used and __repr__
is evaluated in this case.
And this will hide the error.
So, any error musst be treated and displayed before returning.
Example:
def __info__(self): """Standard method called by BLISS Shell info helper.""" try: info_string = self.info(menu=False) except Exception: log_error( self, "An error happend during execution of __info__(), use .info() to get it.", ) return info_string
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'
__str__()
and __repr__()
¶
If implemented in a Python class, __repr__
and __str__
methods are
build-in functions Python to return information about an object instantiating this class.
__str__
should print a readable message-
__repr__
should print a short message obout the objec that is unambigous (e.g. name of an identifier, class name, etc). -
__str__
is called:- 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)
orf'some text {my_obj}'
)
- when the object is passed to the print() function (e.g.
__repr__
method is called:- 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)
)
By default when no __str__
or __repr__
methods are defined, the __repr__
returns the name of the class (Length) and __str__
calls __repr__
.