to_sequence
to_sequence
is a decorator to put a block of Python code into a sequence action.
(to_sequence source code)
In Pylatus Script editor the to_sequence
decorator is already imported into Python interpreter, but if you
have external .py
files, then you can import it as:
from auxygen_seq import to_sequence
Usages example https://git.3lp.cx/dyadkin/bm01macros/src/branch/master/musst.py
Explanation
Imagine we have a function which connects to spec
and runs a macro there. In our case, we have a spec
macro chan_on
which turns on a channel chan
on the MUSST card:
def musst_chan_on(chan):
spec('snbla2:rhmusst').run(f'chan_on {chan}')
If we call this function as is
musst_chan_on(8)
it will turn on TTL signal on our MUSST. But we may want to put our spec
command into the sequence, then we could
write it like:
@to_sequence
def musst_chan_on(chan):
'''Musst turns channel 'chan' ON'''
spec('snbla2:rhmusst').run(f'chan_on {chan}')
After that, calling
musst_chan_on(8)
it will create a sequence action instead of running it immediately.
Format doc strings
You can use Qt format strings to add some info into sequence action:
@to_sequence
def musst_chan_on(chan, time):
'''Musst turns channel '%1' for %2 seconds'''
spec('snbla2:rhmusst').run(f'chan_on {chan}')
from time import sleep
sleep(time)
spec('snbla2:rhmusst').run(f'chan_off {chan}')
which will generate an action as following:
%1
will be substituted with the first argument value, %2
with the second one, etc., up to 9 arguments are allowed.
Limitations
Due to fragile substance of the decorator there are certain limitations on its usage:
- Only a top level function with only positional arguments can be decorated. Classes, methods, metaclasses, lambdas, closures or other smart thing will not work, i.e.
@to_sequence
class TooSmart: # wrong! classes cannot be decorated
pass
class EvenSmarter(TooSmart):
@to_sequence
def cool_method(self): # wrong! methods cannot be decorated
pass
@to_sequence
def silly_function():
'''I am a very silly function''' # ok!
@to_sequence
def silly_function(a=1): # wrong! keyword arguments are not allowed
'''I am another very silly function'''
@to_sequence
def smart_function():
'''I am a very smart function'''
@to_sequence
def silly_function(): # wrong! closures cannot be decorated
'''I am a very silly function'''
-
The decorated function can get any number of positional arguments (no keywords), but they must be either:
- strings,
- integer numbers,
- float numbers.
More complicated data structures (i.e. dicts, lists, other objects) will not work:
@to_sequence
def spec_hello(greet, num): # <--- ok!
'''Prints `greet` `num` times in spec'''
spec('bm31spec2:experiment').run(f'for (i=0; i<{num}; i++) print({greet});')
@to_sequence
def spec_run_all(list_of_commands): # <--- wrong! python list (or any Iterable) cannot be an argument
'''Runs list of commands in spec'''
for command in list_of_commands:
spec('bm31spec2:experiment').run(f'{command}')
- The decorated function must have a doc-string, which will be used as a part of the sequence action name.
@to_sequence
def silly_function():
'''I am a very silly function''' # ok!
@to_sequence
def dummy_function():
return None # wrong! there is no doc string
- The decorated function cannot capture anything from the outer scope, i.e. neither imports, nor variables, nor other functions or types:
from time import sleep
sleep_time = 5
@to_sequence
def spec_sleep1():
'''Block spec prompt for 5 seconds'''
sleep(sleep_time) # wrong! sleep and sleep_time cannot be captured from the outer scope
@to_sequence
def spec_sleep2(sleep_time):
'''Block spec prompt for `%1` seconds'''
from time import sleep
sleep(sleep_time) # ok, sleep is imported inside the function, sleep_time is an argument
If you find these limitations too strict, your patches eliminating them are always welcome.