0

I am trying to be all fancy with sub element attribute access in a custom class hierarchy. My fanciness works in that I can successfully use descriptors to do this. I want to be even more fancy and make the class RefHolder (shown below in the testcase) use slots to save space.

When I try to use slots though, I get RuntimeError: maximum recursion depth exceeded

Note that I have already tried looking at existing solutions for this, the most closely matching I could find being this one:

https://stackoverflow.com/a/19566973/1671693

I have tried this in the testcase below but I am still get the runtimeerror.

Note that in the testcase, if the commented lines are used instead of the ones directly beneath them and __slots__ is removed from RefHolder, The testcase passes.

Any suggestions? Additionally, I am creating an object for every attribute access which seems expensive, are there any suggestions on a more efficient way of achieving the same behavior? Thanks!

import unittest

class RefHolder():
    __slots__ = ['__obj', 'get_value']
    def __init__(self, obj, get_value=False):
        self.__dict__['__obj'] = obj
        self.__dict__['get_value']=get_value

    def get_sub(self, name):
        #attr = self.__dict__['__obj'].find_by_name(name)
        attr = self.__dict__['__obj'].__get__(self, RefHolder).find_by_name(name)
        if attr is None:
            raise AttributeError("Can't find field {}".format(name))
        return attr

    def __getattr__(self, name):
        attr = self.get_sub(name)

        #if self.__dict__['get_value']:
        if self.__dict__['get_value'].__get__(self, RefHolder):
            return attr.Value
        else:
            return attr

    def __setattr__(self, name, value):
        attr = self.get_sub(name)

        #if self.__dict__['get_value']:
        if self.__dict__['get_value'].__get__(self, RefHolder):
            attr.Value = value
        else:
            raise AttributeError("{} is read only in this context".format(name))

class ContainerAccess():
    __slots__ = ['get_value']
    def __init__(self, get_value=False):
        self.get_value = get_value
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return RefHolder(obj, self.get_value)
    def __set__(self, obj, value):
        raise AttributeError("Read Only attribute".format(value))

class PropVal():
    def __init__(self, val):
        self.Value = val
    @property
    def Value(self):
        return self._value
    @Value.setter
    def Value(self, value):
        self._value = value


class T():
    get = ContainerAccess()
    getv = ContainerAccess(get_value=True)
    def __init__(self):
        self.store = {}
        self._value = 0
    def find_by_name(self, name):
        return self.store.get(name)

class T2(T):
    pass

class TestDesc(unittest.TestCase):
    def test_it(self):
        t = T()
        t2 = T2()
        t.store['my_val'] = PropVal(5)
        t.store['my_val2'] = PropVal(6)
        t2.store['my_val'] = PropVal(1)
        self.assertEqual(t.get.my_val.Value, 5)
        self.assertEqual(t.get.my_val2.Value, 6)
        self.assertEqual(t2.get.my_val.Value, 1)
        t.get.my_val.Value = 6
        self.assertEqual(t.get.my_val.Value, 6)
        with self.assertRaises(AttributeError):
            t.get.blah.Value = 6
            #self.assertEqual(t.get.my_other_val.Value, None)

        self.assertEqual(t.getv.my_val, 6)
        t.getv.my_val = 7
        self.assertEqual(t.getv.my_val, 7)
        with self.assertRaises(AttributeError):
            t.get.my_val = 7
Community
  • 1
  • 1
Gregory Kuhn
  • 1,627
  • 2
  • 22
  • 34
  • 1
    It would be helpful to those trying to answer your question, if you could create a Minimal, Complete, and Verifiable [example](http://stackoverflow.com/help/mcve). – ilyas patanam Jan 11 '16 at 21:32
  • I had hoped the provided test case would serve this purpose. As mentioned, using the commented lines instead of the lines beneath them, and removing the `__slots__` line results in a working test case – Gregory Kuhn Jan 11 '16 at 23:14

0 Answers0