Strange bug with trying to cache server calls (same as this post, but different type of error).
When testing the following code with
m = Menu(name="test")
print(m)
print(m.name)
I get the output from anvil
name: test
klass
but when run directly on python I get (as expected)
name: test
test
I can only assume this is a bug in Sklupt to do with the getter function in Data as it prints the entire object fine.
from time import time
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 Data:
def getter(self, name):
if time() - self._last_update > self.__class__.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(self.__class__.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):
for a in self.__class__.attributes:
setattr(self, "_" + a, None)
self._last_update = 0
self.set_multiple(**kwargs)
def __str__(self):
output = ""
for a in self.__class__.attributes:
value = getattr(self, "_" + a)
output += ", " + a + ": " + str(value)
return output[2:]
class Menu(Data):
attributes = [ "name"]
update_time = 30
def _update(self):
pass
def __init__(self, **kwargs):
Data.__init__(self, **kwargs)
for a in Menu.attributes:
setattr(Menu, a, property(partial(Menu.getter, name=a), partial(Menu.setter, name=a)))
This is a bug in Skulpt - the name
property is not properly escaped, and so conflicts with bits of the underlying Javascript.
Moved to bug reports.
In the meantime, I suspect you can probably accomplish what you want a little more simply, using __getattr__
and __setattr__
and a dictionary to store your values…
1 Like
Are you suggesting something like this?
This is giving me the same error but is simpler
from time import time
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 Data:
def __getattr__(self, name):
if time() - self._last_update > self.__class__.update_time:
self._update()
self._last_update = time()
return self._data.get(name, None)
def __setattr__(self, key, value):
if key not in self.__class__.attributes:
self.__dict__[key] = value
self.set_multiple(**{key: value})
def set_multiple(self, **new_values):
if set(self.__class__.attributes) == new_values.keys():
self._last_update = time()
for k in new_values:
self._data[k]= new_values[k]
def _update(self):
raise NotImplementedError("Must override _update")
def __init__(self, **kwargs):
self.__dict__['_data'] = {}
self._last_update = 0
self.set_multiple(**kwargs)
def __str__(self):
output = ""
for a in self.__class__.attributes:
value = self.__getattr__(a)
output += ", " + a + ": " + str(value)
return output[2:]
class Menu(Data):
attributes = ["name"]
update_time = 30
def _update(self):
# should be done as part of the meal
pass
def __init__(self, **kwargs):
Data.__init__(self, **kwargs)
Yeah, that’s a bit easier on the eyes 
Workaround for now: The bug with the name
attribute happens in object.__getattribute__
, which triggers before __getattr__
. So actually, if you override __getattribute__
rather than __getattr__
, you should dodge this bug.
1 Like
I’ve added
def __getattribute__(self, key):
if key == 'name':
return self._data[key]
v = object.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
This does fix it
Thanks for the help
I don’t think that hasattr()
is necessary - object.__getattribute__
already does the descriptor lookup for you. But yes, that’s a tidy workaround.
1 Like