labrad / pylabrad

python interface for labrad
51 stars 31 forks source link

How to pass in @setting decorator a list of user-defined classes (inherited from the base 'object' class) #395

Closed tristanlorriaux closed 1 year ago

tristanlorriaux commented 1 year ago

Hi there 🚀,

I've been stuck on a little problem for a while now. I want to pass in parameters to the @settings decorator a successive list of datasets. The structure of the object is as follows:

list = [Dataset 1, Dataset 2...]

And I want to enter it as a parameter to the decorator:

@settings(..., dataset_list = '*_', ..):

I specify that Dataset is a class of its own, which interacts with the datavault, and which is in another modulus dataset.py. This Dataset class is thus defined in a dataset.py file as follows:

class Dataset(object):
    def __init__(self, path, name, independents, dependents,
                 params=None, mkdir=True, delay=10, lazy=True, cxn=None):
        self.cxn = cxn
        self.path = path
        self.name = name
        ...

The problem is that this list of classes not known by LabRAD leads logically enough to an error of the type:

TypeError: No LabRAD type for: <dataking.dataset.Dataset object at 0x000002595fb5db78> (type <class 'dataking.dataset.Dataset'>)

and this even if I put in the decorator the tag which seems to me the most adequate, namely '*_'.

So I tried to fill in the type in labrad.types.py, in which I found the dictionary _types= {} which I think allows to associate a python class to a LabRAD type. But writing in this dict _types= {Dataset: TAny} or _types= {Dataset: TNone} doesn't seem to work since these classes TAny and TNone are defined further down. Any idea how to solve this problem?

Thanks in advance for the answer to my question :)

Tristan

maffoo commented 1 year ago

The @setting decorator is for marking labrad server methods that are meant to be remotely callable. This means that any data you send to such a setting has to be "serialized" as labrad data (see https://github.com/labrad/labrad/wiki/Data-Types-and-Encoding). For pylabrad, this means the data needs to plain tuples, lists, strings, etc, not arbitrary classes. If you want to send other types through such remote calls, you could add functions to convert to and from labrad data, something like:

class Dataset:
    ...

    def to_labrad_data(self):
        # Convert to data types that labrad can handle

    @classmethod
    def from_labrad_data(cls, data):
        # Create Dataset instance from labrad data

Then in the server you can do something like:

class MyServer(LabradServer):
    @setting('foo')
    def foo(self, datasets_list):
        datasets = [Dataset.from_labrad_data(data) for data in datasets_list]

And you could call this remotely like:

# datasets has type list[Dataset]
cxn.myserver.foo([dataset.to_labrad_data() for dataset in datasets])