Trying to cache server calls

I am trying to create an object which is passed from form to form with some data got from the server modules.
This caches the data for a set ammount of time then requests the data again, reducing server calls.

However when running this code I get the following error
When calling

g = Groups(1,2)
g.groups

SuspensionError: Cannot call a function that blocks or suspends here
at property.py, line 20 column 8

This error does not happen if I remove the line calling the server.
And when the same line is run in the form init (Where g.groups is) it runs fine

from time import time
import anvil.server


def merge_two_dicts(x, y):
  x.update(y)
  return x


def partial(fun, *part_args, **part_kwargs):
  pk = part_kwargs
  pa = part_args
  def wrapper(*extra_args, **extra_kwargs):
    args = list(pa)
    args.extend(extra_args)
    kwargs = merge_two_dicts(pk, extra_kwargs)
    return fun(*args, **kwargs)
  return wrapper


class APIException(Exception):
  pass


class Data:

  def getter(self, name):
    if time() - self._last_update > Groups.update_time:
      self._update()
      self._last_update = time()
    return getattr(self, "_" + name)

  def setter(self, value, name):
    self.set_multiple(**{name: value})

  def set_multiple(self, **values):
    if set(Groups.attributes) == values.keys():
      self._last_update = time()
    for k in values:
      setattr(self, "_" + k, values[k])

  def _update(self):
    raise NotImplementedError("Must override _update")

  def __init__(self, **kwargs):
    self._last_update = 0
    self.set_multiple(**kwargs)


class Groups(Data):
  attributes = ["groups"]
  update_time = 30

  def _update(self):
    resp = anvil.server.call("site_groups", method="GET", eventID=self.eventID, meal_date=self.date)
    status = resp[0]
    data = resp[1]
    if status > 299:
      raise APIException
    self._groups = data

  def __init__(self, eventID, date, **kwargs):
    Data.__init__(self, **kwargs)
    for a in Groups.attributes:
      setattr(self, a, None)
    self.eventID = eventID
    self.date = date

for a in Groups.attributes:
  setattr(Groups, a, property(partial(Groups.getter, name=a), partial(Groups.setter, name=a)))

Hey, looks like the property() infrastructure doesn’t support blocking calls. There are still some corners of Skulpt that don’t, and this piece of code seems determined to exercise every piece of the Python attribute infrastructure it can find :smiley:

As I mentioned in a prior bug report, I’m going to suggest you do something simpler with __getattr__ and __setattr__ (which do support blocking calls). I’ve added this code sample as a feature request for our Skulpt support, and I’ll come back and update this thread when we ship an update that makes it work.

Thanks for the suggestion,
I’ll give it a go now

Update:
The new solution does work