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.