0001"""
0002Bound attributes are attributes that are bound to a specific class and
0003a specific name. In SQLObject a typical example is a column object,
0004which knows its name and class.
0005
0006A bound attribute should define a method ``__addtoclass__(added_class,
0007name)`` (attributes without this method will simply be treated as
0008normal). The return value is ignored; if the attribute wishes to
0009change the value in the class, it must call ``setattr(added_class,
0010name, new_value)``.
0011
0012BoundAttribute is a class that facilitates lazy attribute creation.
0013"""
0014from __future__ import absolute_import
0015
0016from . import declarative
0017from . import events
0018
0019__all__ = ['BoundAttribute', 'BoundFactory']
0020
0021
0022class BoundAttribute(declarative.Declarative):
0023
0024 """
0025 This is a declarative class that passes all the values given to it
0026 to another object. So you can pass it arguments (via
0027 __init__/__call__) or give it the equivalent of keyword arguments
0028 through subclassing. Then a bound object will be added in its
0029 place.
0030
0031 To hook this other object in, override ``make_object(added_class,
0032 name, **attrs)`` and maybe ``set_object(added_class, name,
0033 **attrs)`` (the default implementation of ``set_object``
0034 just resets the attribute to whatever ``make_object`` returned).
0035
0036 Also see ``BoundFactory``.
0037 """
0038
0039 _private_variables = (
0040 '_private_variables',
0041 '_all_attributes',
0042 '__classinit__',
0043 '__addtoclass__',
0044 '_add_attrs',
0045 'set_object',
0046 'make_object',
0047 'clone_in_subclass',
0048 )
0049
0050 _all_attrs = ()
0051 clone_for_subclass = True
0052
0053 def __classinit__(cls, new_attrs):
0054 declarative.Declarative.__classinit__(cls, new_attrs)
0055 cls._all_attrs = cls._add_attrs(cls, new_attrs)
0056
0057 def __instanceinit__(self, new_attrs):
0058 declarative.Declarative.__instanceinit__(self, new_attrs)
0059 self.__dict__['_all_attrs'] = self._add_attrs(self, new_attrs)
0060
0061 @staticmethod
0062 def _add_attrs(this_object, new_attrs):
0063 private = this_object._private_variables
0064 all_attrs = list(this_object._all_attrs)
0065 for key in new_attrs.keys():
0066 if key.startswith('_') or key in private:
0067 continue
0068 if key not in all_attrs:
0069 all_attrs.append(key)
0070 return tuple(all_attrs)
0071
0072 @declarative.classinstancemethod
0073 def __addtoclass__(self, cls, added_class, attr_name):
0074 me = self or cls
0075 attrs = {}
0076 for name in me._all_attrs:
0077 attrs[name] = getattr(me, name)
0078 attrs['added_class'] = added_class
0079 attrs['attr_name'] = attr_name
0080 obj = me.make_object(**attrs)
0081
0082 if self.clone_for_subclass:
0083 def on_rebind(new_class_name, bases, new_attrs,
0084 post_funcs, early_funcs):
0085 def rebind(new_class):
0086 me.set_object(
0087 new_class, attr_name,
0088 me.make_object(**attrs))
0089 post_funcs.append(rebind)
0090 events.listen(receiver=on_rebind, soClass=added_class,
0091 signal=events.ClassCreateSignal, weak=False)
0092
0093 me.set_object(added_class, attr_name, obj)
0094
0095 @classmethod
0096 def set_object(cls, added_class, attr_name, obj):
0097 setattr(added_class, attr_name, obj)
0098
0099 @classmethod
0100 def make_object(cls, added_class, attr_name, *args, **attrs):
0101 raise NotImplementedError
0102
0103 def __setattr__(self, name, value):
0104 self.__dict__['_all_attrs'] = self._add_attrs(self, {name: value})
0105 self.__dict__[name] = value
0106
0107
0108class BoundFactory(BoundAttribute):
0109
0110 """
0111 This will bind the attribute to whatever is given by
0112 ``factory_class``. This factory should be a callable with the
0113 signature ``factory_class(added_class, attr_name, *args, **kw)``.
0114
0115 The factory will be reinvoked (and the attribute rebound) for
0116 every subclassing.
0117 """
0118
0119 factory_class = None
0120 _private_variables = (
0121 BoundAttribute._private_variables + ('factory_class',))
0122
0123 def make_object(cls, added_class, attr_name, *args, **kw):
0124 return cls.factory_class(added_class, attr_name, *args, **kw)