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:

action_doc_string.png

%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.