- Joined
- Jul 10, 2007
- Messages
- 6,306
This is useful for binding
- Properties
- Values
- Methods
- Functions
Python:
#
# bind(obj, name = None, prop = None)
# - binds a field, method, function, or property to a given class instance
# this enables the class to use the field
#
# Example: bind(instance, "hello", myFunction)
#
# instance.myFunction()
#
# - this keeps a history of all binding. When something is unbound, it loads
# up the previous binding.
#
# - may not bind None
#
# bindProperty(obj, name, fget = None, fset = None)
# - this converts the getter and setter to a property
# and then binds it. The getter and setter may be a function
# or a method.
#
# - this is essentially a wrapper for bind
#
# Example: def getter(self):
# return self._some_field_in_object
#
# def setter(self, value):
# self._some_field_in_object = value
#
# bind(object, "_some_field_in_object", 9)
# bindProperty(object, "myField", getter, setter)
#
# print object.myField -> 9
# object.myField = 11
# print object.myField -> 11
#
# unbind(obj, name, prop)
# - this will unbind the thing that was bound and revert the binding
# back to what it previously was if the thing that was bound is the active
# binding
#
# - for fields, if the field was a scalar, then that specific value must
# be unbound
#
# Example: bind(instance, "field", 7)
#
# print str(t.field) -> 7
#
# unbind(instance, "field", 7)
#
# print str(t.field) -> previous value
#
# unbindProperty(obj, name, fget = None, fset = None)
#
import types
import inspect
def _getCallerName():
fback = inspect.currentframe().f_back.f_back
locs = fback.f_locals
if ('self' in locs):
locs['self'].__class__.__name__
return fback.f_globals['__file__']
def _getHidden(name, callername):
return "_" + name + "_" + callername + "_hidden"
def _getName(name, prop):
if (name != None):
return name
if (prop == None):
return None
return prop.__name__
def _getArgs(prop):
if (prop == None):
return None
if (inspect.isfunction(prop) or inspect.ismethod(prop)):
args = prop.func_code.co_varnames
if (args == []):
return None
return args
return None
def _toFunction(prop):
if (prop == None):
return None
if (inspect.ismethod(prop)):
return prop.im_func
return prop
def _clearProperties(objectType, name):
props = []
objs = []
objs = inspect.getmro(objectType)
for obj in reversed(objs):
if (hasattr(obj, name)):
props.insert(0, getattr(obj, name))
delattr(obj, name)
else:
props.insert(0, None)
return (props, objs)
def _setProperties(props, objs, name):
if (props == None):
return
for i in range(len(props)):
if (props[i] != None):
setattr(objs[i], name, props[i])
# each object has a binding table
#
# bindingTable.localBinding[name].local[index] = property
# bindingTable.globalBinding[name][index] = property
#
# the binding at the top of the table is the current binding
# if there is no binding, the original binding is used
#
class _BindingTable:
def __init__(self, obj, localBinding, globalBinding):
self.obj = obj
self.localBinding = localBinding
self.globalBinding = globalBinding
@staticmethod
def get(obj, name):
self = None
if (hasattr(obj, "__bindingtable")):
self = getattr(obj, "__bindingtable")
else:
globalBinding = None
if (hasattr(obj.__class__, "__bindingtableGlobal")):
globalBinding = getattr(obj.__class__, "__bindingtableGlobal").globalBinding
else:
globalBinding = {}
setattr(obj.__class__, "__bindingtableGlobal", _BindingTable(obj.__class__, None, globalBinding))
self = _BindingTable(obj, {}, globalBinding)
setattr(obj, "__bindingtable", self)
globalBinding = self.globalBinding.get(name)
if (globalBinding == None):
globalBinding = _Binding(obj.__class__, name)
self.globalBinding[name] = globalBinding
binding = self.localBinding.get(name)
if (binding == None):
binding = _Binding(obj, name, globalBinding)
self.localBinding[name] = binding
return binding
@staticmethod
def has(obj, name, prop):
if (not hasattr(obj, "__bindingtable")):
return False
self = getattr(obj, "__bindingtable")
return name in self.localBinding and prop in self.localBinding[name].binding
class _Binding:
def _update(self):
props = None
objs = None
if (self.globalBinding != None):
props, objs = _clearProperties(self.obj.__class__, self.name)
if (hasattr(self.obj, self.name)):
if (self.binding[0] == None and (len(self.binding) > 1 or not self.has)):
'''
if (self.globalBinding == None):
print "deleting global (", self.obj.__name__, "): ", self.name, " as was virtual property"
else:
print "deleting local (", self.obj.__class__.__name__, "): ", self.name, " as was virtual property"
'''
delattr(self.obj, self.name)
elif (self.isProperty[0] and self.globalBinding != None):
'''
if (self.globalBinding == None):
print "deleting global (", self.obj.__name__, "): ", self.name, " for being a local property"
else:
print "deleting local (", self.obj.__class__.__name__, "): ", self.name, " for being a local property"
'''
delattr(self.obj, self.name)
else:
'''
if (self.globalBinding == None):
print "setting global (", self.obj.__name__, "): ", self.name, " to ", self.binding[0]
else:
print "setting local (", self.obj.__class__.__name__, "): ", self.name, " to ", self.binding[0]
'''
setattr(self.obj, self.name, self.binding[0])
elif (not self.isProperty[0] or self.globalBinding == None):
if (self.binding[0] != None or (len(self.binding) == 1 and self.has)):
'''
if (self.globalBinding == None):
print "setting global (", self.obj.__name__, "): ", self.name, " to ", self.binding[0]
else:
print "setting local (", self.obj.__class__.__name__, "): ", self.name, " to ", self.binding[0]
'''
setattr(self.obj, self.name, self.binding[0])
_setProperties(props, objs, self.name)
def add(self, prop):
# add property to history
self.binding.insert(0, prop)
self.isProperty.insert(0, isinstance(prop, property))
# if it was a property and on an instance, add it to the class
if (self.initialized and self.globalBinding != None and self.isProperty[0]):
self.globalBinding.add(prop)
# update object state
self._update()
'''
if (self.globalBinding != None):
print "binded local (", self.obj.__class__.__name__, "): ", self.binding
else:
print "binded global (", self.obj.__name__, "): ", self.binding
'''
def remove(self, prop):
if (prop == None or len(self.binding) == 1):
return
if (not prop in self.binding):
return
# remove property from history
if (self.globalBinding != None and isinstance(prop, property)):
self.globalBinding.remove(prop)
i = self.binding.index(prop)
self.binding.pop(i)
self.isProperty.pop(i)
self._update()
'''
if (self.globalBinding != None):
print "unbinded local (", self.obj.__class__.__name__, "): ", self.binding
else:
print "unbinded global (", self.obj.__name__, "): ", self.binding
'''
def __init__(self, obj, name, globalBinding = None):
self.obj = obj
self.name = name
self.has = False
self.initialized = False
if (globalBinding == None):
self.table = getattr(obj, "__bindingtableGlobal")
else:
self.table = getattr(obj, "__bindingtable")
self.globalBinding = globalBinding
self.binding = []
self.isProperty = []
if (globalBinding != None):
if (self.globalBinding.isProperty[0]):
if (self.globalBinding.isProperty[-1]):
self.add(self.globalBinding.binding[-1])
else:
props, objs = _clearProperties(self.obj.__class__, self.name)
if (hasattr(obj, name)):
self.add(getattr(obj, name))
else:
self.binding.append(None)
self.isProperty.append(None)
_setProperties(props, objs, self.name)
elif (hasattr(obj, name)):
self.has = True
self.add(getattr(obj, name))
else:
self.binding.append(None)
self.isProperty.append(None)
elif (hasattr(obj, name)):
self.has = True
self.add(getattr(obj, name))
else:
self.binding.append(None)
self.isProperty.append(None)
self.initialized = True
class _PropertyValue:
def __init__(self):
self.stack = []
class _Property(property):
def __init__(self, obj, name):
if (name == None):
raise "_Poperty requires a name to be created"
property.__init__(self)
self.obj = obj
self._dic = {}
self.name = name
self.base = None
self.isMethod = False
setattr(obj.__class__, "___propertytable___" + name, self)
if (hasattr(obj.__class__, name)):
prop = getattr(obj.__class__, name)
self.base = prop
self._set(obj, prop)
setattr(obj.__class__, name, self)
if (hasattr(obj, name)):
if (inspect.ismethod(getattr(obj, name))):
self.isMethod = True
elif (hasattr(obj, name)):
prop = getattr(obj, name)
self.base = prop
self._set(obj, prop)
setattr(obj.__class__, name, self)
def _set(self, obj, prop):
props = self._dic.get(obj)
if (props == None):
props = _PropertyValue()
self._dic[obj] = props
props.stack.insert(0, prop)
def _rem(self, obj, prop):
props = self._dic.get(obj)
if (props == None):
return
props.stack.remove(prop)
if (len(props.stack) == 0):
self._dic.pop(obj)
def _convert(self, obj, prop):
if (not isinstance(prop, property)):
return prop
props = self._dic.get(obj)
if (props == None):
return None
for p in props.stack:
if p.fget == prop.fget and p.fset == prop.fset:
return p
return None
# self, obj, type
def __get__(self, instance, owner):
name = self.name
if (instance == None):
return self
binding = _BindingTable.get(instance, name)
if (_callable(binding.binding[0])):
return binding.binding[0]
# attempt to get the property from _Property
props = self._dic.get(instance)
if (props != None):
if (len(props.stack) > 0):
prop = props.stack[0]
if (isinstance(prop, property)):
return prop.fget(instance)
else:
return prop
# attempt to get the local field
props, objs = _clearProperties(owner, name)
value = None
gotValue = False
if (hasattr(instance, name)):
gotValue = True
value = getattr(instance, name)
_setProperties(props, objs, name)
if (gotValue):
return value
# attempt to get the base property (one defined by class)
if (self.base != None):
if (isinstance(self.base, property)):
return self.base.fget(instance)
else:
if (self.isMethod):
return types.MethodType(self.base, self.obj, self.obj.__class__)
else:
return self.base
# the property does not exist
raise AttributeError("'" + instance.__class__.__name__ + "' object has no attribute '" + name + "'")
def __set__(self, instance, value):
if (instance == None):
return
# attempt to set the property from _Property
props = self._dic.get(instance)
if (props != None):
if (len(props.stack) > 0):
prop = props.stack[0]
if (isinstance(prop, property)):
prop.fset(instance, value)
return
else:
props.stack[0] = value
return
# attempt to set the local field
props, objs = _clearProperties(self.obj.__class__, self.name)
setValue = False
if (hasattr(instance, self.name)):
setattr(instance, self.name, value)
setValue = True
_setProperties(props, objs, self.name)
if (setValue):
return
# attempt to set the base property (one defined by class)
if (self.base != None):
if (isinstance(self.base, property)):
self.base.fset(instance, value)
return
# the field does not exist for this class, so register it
binding = _BindingTable.get(instance, self.name)
binding.has = not binding.globalBinding.isProperty[-1]
binding.binding[0] = value
prop = getattr(self.obj.__class__, self.name)
delattr(self.obj.__class__, self.name)
setattr(instance, self.name, value)
setattr(self.obj.__class__, self.name, prop)
self._set(instance, value)
def _getlen(prop):
if (prop == None):
return 0
return len(prop)
def _callable(prop):
if (prop == None):
return False
return callable(prop) and not isinstance(prop, property)
def _bindProperty(obj, binding, prop):
p = None
if (hasattr(obj.__class__, "___propertytable___" + binding.name)):
p = getattr(obj.__class__, "___propertytable___" + binding.name)
else:
p = _Property(obj, binding.name)
p._set(obj, prop)
binding.add(p)
def _bindCode(obj, binding, prop):
binding.add(types.MethodType(_toFunction(prop), obj, obj.__class__))
def bind(obj, name = None, prop = None):
if (obj == None or (prop == None and name == None)):
return
if (name != None):
if (not isinstance(name, str)):
return
if (not hasattr(obj, "__class__")):
return
name = _getName(name, prop)
if (name == None):
return
binding = _BindingTable.get(obj, name)
if (inspect.ismethod(prop) or inspect.isfunction(prop)):
_bindCode(obj, binding, prop)
else:
_bindProperty(obj, binding, prop)
def bindProperty(obj, name, fget = None, fset = None):
if (name == None):
return
if (not isinstance(name, str)):
return
bind(obj, name, property(_toFunction(fget), _toFunction(fset), None, None))
def unbind(obj, name, prop):
if (obj == None or name == None or prop == None):
return
if (not isinstance(name, str)):
return
if (not hasattr(obj, "__class__")):
return
p = None
if (not inspect.ismethod(prop) and not inspect.isfunction(prop)):
if (not hasattr(obj.__class__, "___propertytable___" + name)):
return
p = getattr(obj.__class__, "___propertytable___" + name)
if (isinstance(prop, property)):
prop = p._convert(obj, prop)
if (prop == None):
return
elif (inspect.ismethod(prop) or inspect.isfunction(prop)):
val = prop
prop = types.MethodType(_toFunction(val), obj, obj.__class__)
if (inspect.ismethod(prop) or inspect.isfunction(prop)):
if (not _BindingTable.has(obj, name, prop)):
return
else:
if (not _BindingTable.has(obj, name, p)):
return
binding = _BindingTable.get(obj, name)
if (not inspect.ismethod(prop) and not inspect.isfunction(prop)):
p._rem(obj, prop)
binding.remove(p)
else:
binding.remove(prop)
def unbindProperty(obj, name, fget = None, fset = None):
unbind(obj, name, property(_toFunction(fget), _toFunction(fset), None, None))
Last edited: