def get_getter(cls, prop_name, user_getter=None, getter_takes_name=False): """This implementation returns the PROP_NAME value if there exists such property. Otherwise there must exist a logical getter (user_getter) which the value is taken from. If no getter is found, None is returned (i.e. the property cannot be created)""" has_prop_variable = cls.has_prop_attribute(prop_name) # WARNING! Deprecated has_specific_getter = hasattr(cls, GET_PROP_NAME % {'prop_name' : prop_name}) has_general_getter = hasattr(cls, GET_GENERIC_NAME) if not (has_prop_variable or has_specific_getter or has_general_getter or user_getter): return None # when property variable is given, it overrides all the getters if has_prop_variable: if has_specific_getter or user_getter: logger.warning("In class %s.%s ignoring custom logical getter " "for property '%s' as a corresponding " "attribute exists" \ % (cls.__module__, cls.__name__, prop_name)) pass # user_getter is ignored here, so it has not to be passed up user_getter = None; getter_takes_name = False else: # uses logical getter. Sees if the getter needs to receive # the property name (i.e. if the getter is used for multiple # properties) if user_getter: pass else: if has_specific_getter: # this is done to delay getter call, to have # bound methods to allow overloading of getter in # derived classes def __getter(self): _getter = getattr(self, GET_PROP_NAME % {'prop_name' : prop_name}) return _getter() #previously it was simply: #user_getter = getattr(cls, GET_PROP_NAME % {'prop_name' : prop_name}) user_getter = __getter getter_takes_name = False else: assert has_general_getter def __getter(self, name): _getter = getattr(self, GET_GENERIC_NAME) return _getter(name) #user_getter = getattr(cls, GET_GENERIC_NAME) user_getter = __getter getter_takes_name = True pass pass pass return PropertyMeta.get_getter(cls, prop_name, user_getter, getter_takes_name)
def __get_observables_sets__(cls): """Returns a pair of frozensets. First set of strings is the set of concrete properties, obtained by expanding wildcards found in class field __observables__. Expansion works only with names not prefixed with __. Second set of strings contains the names of the logical properties. This set may still contain logical properties which have not been associated with a getter (and optionally with a setter). """ conc_prop_set = set() log_prop_set = set() not_found = [] names = cls.__dict__.get(OBS_TUPLE_NAME, tuple()) if not isinstance(names, types.ListType) and \ not isinstance(names, types.TupleType): raise TypeError("In class %s.%s attribute '%s' must be a list or tuple" % (cls.__module__, cls.__name__, OBS_TUPLE_NAME)) for name in names: if type(name) != types.StringType: raise TypeError("In class %s.%s attribute '%s' must contain"\ " only strings (found %s)" % (cls.__module__, cls.__name__, OBS_TUPLE_NAME, type(name))) if (cls.__dict__.has_key(name) and not isinstance(getattr(cls, name), types.MethodType)): conc_prop_set.add(name) else: not_found.append(name) pass # now searches all possible matches for those that have not # been found, and collects all logical properties as well # (those which do not match, and do not contain patterns) concrete_members = [x for x,v in cls.__dict__.iteritems() if (not x.startswith("__") and not isinstance(v, types.FunctionType) and not isinstance(v, types.MethodType) and type(v) is not classmethod and x not in conc_prop_set)] for pat in not_found: if frozenset(pat) & WILDCARDS: matches = fnmatch.filter(concrete_members, pat) if 0 == len(matches): logger.warning("In class %s.%s observable pattern '%s' " \ "did not match any existing attribute." % \ (cls.__module__, cls.__name__, pat)) else: conc_prop_set |= set(matches) else: # here pat has to be a logical property log_prop_set.add(pat) pass pass return (frozenset(conc_prop_set), frozenset(log_prop_set))
def get_getter_source(cls, getter_name, prop_name): """This implementation returns the PROP_NAME value if there exists such property. If there exist a pair of methods __get_<prop_name>_value__ and __set_<prop_name>_value__ the value is taken from the getter. Otherwise if there exists the generic pair __get_value__ and __set_value__ the gettter is called. The getter method (specific or general) is called _only_ if there exists no variable called PROP_NAME (see the user manual)""" has_prop_variable = cls.has_prop_attribute(prop_name) has_specific_getter = hasattr( cls, GET_PROP_NAME % {"prop_name": prop_name} ) and hasattr( # has property getter and setter cls, SET_PROP_NAME % {"prop_name": prop_name} ) has_general_getter = hasattr(cls, GET_GENERIC_NAME) and hasattr( # has generic getter and setter cls, SET_GENERIC_NAME ) assert has_prop_variable or has_specific_getter or has_general_getter, "no var/methods for '%s'" % prop_name # when property variable is given, it overrides all getters if has_prop_variable: getter = "self." + PROP_NAME if has_specific_getter: logger.warning( "In class %s.%s ignoring custom getter/setter pair for property '%s' as a corresponding attribute exists" % (cls.__module__, cls.__name__, prop_name) ) # specific getter ovverides the general one elif has_specific_getter: getter = "self." + GET_PROP_NAME + "()" # generic getter else: getter = "self." + GET_GENERIC_NAME + "('%(prop_name)s')" # general getter ovverides the general one return ("def %(getter_name)s(self): return " + getter) % {"getter_name": getter_name, "prop_name": prop_name}
def __create_prop_accessors__(cls, prop_name, default_val): """Private method that creates getter and setter, and the corresponding property""" getter_name = "get_prop_%s" % prop_name setter_name = "set_prop_%s" % prop_name members_names = cls.__dict__.keys() # checks if accessors are already defined: if getter_name not in members_names: src = type(cls).get_getter_source(cls, getter_name, prop_name) func = get_function_from_source(src) setattr(cls, getter_name, func) else: logger.debug("Custom member '%s' overloads generated getter of property '%s'", getter_name, prop_name) pass if setter_name not in members_names: src = type(cls).get_setter_source(cls, setter_name, prop_name) func = get_function_from_source(src) setattr(cls, setter_name, func) else: logger.warning("Custom member '%s' overloads generated setter of property '%s'", setter_name, prop_name) pass prop = property(getattr(cls, getter_name), getattr(cls, setter_name)) setattr(cls, prop_name, prop) has_prop_variable = hasattr(cls, prop_name) or props.has_key(prop_name) if has_prop_variable: varname = PROP_NAME % {"prop_name": prop_name} if not varname in members_names: cls.__create_property(varname, default_val) else: logger.warning( "In class %s.%s automatic property builder found a possible clashing with attribute '%s'", cls.__module__, cls.__name__, varname, ) pass return
def __create_conc_prop_accessors__(cls, prop_name, default_val): """Private method that creates getter and setter, and the corresponding property. This is used for concrete properties.""" getter_name = "get_prop_%s" % prop_name setter_name = "set_prop_%s" % prop_name members_names = cls.__dict__.keys() # checks if accessors are already defined: if getter_name not in members_names: _getter = type(cls).get_getter(cls, prop_name) setattr(cls, getter_name, _getter) else: logger.debug("Custom member '%s' overloads generated getter of property '%s'" \ % (getter_name, prop_name)) pass if setter_name not in members_names: _setter = type(cls).get_setter(cls, prop_name) setattr(cls, setter_name, _setter) else: logger.warning("Custom member '%s' overloads generated setter of property '%s'" \ % (setter_name, prop_name)) pass # creates the property prop = property(getattr(cls, getter_name), getattr(cls, setter_name)) setattr(cls, prop_name, prop) # creates the underlaying variable if needed varname = PROP_NAME % {'prop_name' : prop_name} if varname not in members_names: setattr(cls, varname, cls.create_value(varname, default_val)) else: logger.warning("In class %s.%s automatic property builder found a " "possible clashing with attribute '%s'" \ % (cls.__module__, cls.__name__, varname)) return
def __get_observables_array__(cls): """Returns a set of strings by expanding wilcards found in class field __observables__. Expansion works only with names not prefixed with __""" import fnmatch res_set = set() not_found = [] names = getattr(cls, OBS_TUPLE_NAME, tuple()) if not isinstance(names, types.ListType) and not isinstance(names, types.TupleType): raise TypeError( "In class %s.%s attribute '%s' must be a list or tuple" % (cls.__module__, cls.__name__, OBS_TUPLE_NAME) ) for name in names: if type(name) != types.StringType: raise TypeError( "In class %s.%s attribute '%s' must contain" " only strings (found %s)" % (cls.__module__, cls.__name__, OBS_TUPLE_NAME, type(name)) ) if hasattr(cls, name) and getattr(cls, name) != types.MethodType: res_set.add(name) else: not_found.append(name) pass # now searches all possible matches for those that have not been found: for name in ( x for x, v in cls.__dict__.iteritems() if not x.startswith("__") and type(v) != types.MethodType and x not in res_set ): for pat, i in zip(not_found, range(len(not_found))): if fnmatch.fnmatch(name, pat): res_set.add(name) pass pass # finally, there might entries that have no corresponding # value, but there exist getter and setter methods. These # entries are valid only if they do not contain wilcards wilcards = frozenset("[]!*?") for name, nameset in zip(not_found, (frozenset(x) for x in not_found)): if len(nameset & wilcards) == 0: # no wilcards in the name # has property getter and setter : if ( hasattr(cls, GET_PROP_NAME % {"prop_name": name}) and hasattr(cls, SET_PROP_NAME % {"prop_name": name}) ) or ( # has generic getter and setter hasattr(cls, GET_GENERIC_NAME) and hasattr(cls, SET_GENERIC_NAME) ): res_set.add(name) else: # the observable was not found! logger.warning( "In class %s.%s ignoring observable '%s' " "which has no corresponding attribute" " or custom getter/setter pair" % (cls.__module__, cls.__name__, name) ) pass pass pass return res_set
def get_setter(cls, prop_name, user_setter=None, setter_takes_name=False, user_getter=None, getter_takes_name=False): """The setter follows the rules of the getter. First search for property variable, then logical custom setter. If no setter is found, None is returned (i.e. the property is read-only.)""" has_prop_variable = cls.has_prop_attribute(prop_name) # WARNING! These are deprecated has_specific_setter = hasattr(cls, SET_PROP_NAME % {'prop_name' : prop_name}) has_general_setter = hasattr(cls, SET_GENERIC_NAME) if not (has_prop_variable or has_specific_setter or has_general_setter or user_setter): return None if has_prop_variable: if has_specific_setter or user_setter: logger.warning("In class %s.%s ignoring custom logical setter " "for property '%s' as a corresponding " "attribute exists" \ % (cls.__module__, cls.__name__, prop_name)) pass user_setter = user_getter = None setter_takes_name = getter_takes_name = False else: if user_setter: pass else: if has_specific_setter: def __setter(self, val): _setter = getattr(self, SET_PROP_NAME % {'prop_name' : prop_name}) _setter(val) pass user_setter = __setter #user_setter = getattr(cls, SET_PROP_NAME % {'prop_name' : prop_name}) setter_takes_name = False else: assert has_general_setter def __setter(self, name, val): _setter = getattr(self, SET_GENERIC_NAME) _setter(name, val) pass user_setter = __setter #user_setter = getattr(cls, SET_GENERIC_NAME) setter_takes_name = True pass pass pass # the final setter is a combination of a basic setter, and # the getter (see how inner_{getter,setter} are used in # _setter below) _inner_setter = PropertyMeta.get_setter(cls, prop_name, user_setter, setter_takes_name, user_getter, getter_takes_name) _inner_getter = type(cls).get_getter(cls, prop_name, user_getter, getter_takes_name) def _setter(self, val): old = _inner_getter(self) new = type(self).create_value(prop_name, val, self) _inner_setter(self, new) if type(self).check_value_change(old, new): self._reset_property_notification(prop_name, old) pass self.notify_property_value_change(prop_name, old, val) return return _setter
def __create_log_props(cls, log_props, _getdict, _setdict): """Creates all the logical property. The list of names of properties to be created is passed with frozenset log_props. The getter/setter information is taken from _{get,set}dict. This method resolves also wildcards in names, and performs all checks to ensure correctness. Returns the frozen set of the actually created properties (as not log_props may be really created, e.g. when no getter is provided, and a warning is issued). """ real_log_props = set() resolved_getdict = {} resolved_setdict = {} for _dict_name, _dict, _resolved_dict in (("getter", _getdict, resolved_getdict), ("setter", _setdict, resolved_setdict)): # first resolve all wildcards for pat, ai in ((pat, ai) for pat, ai in _dict.iteritems() if frozenset(pat) & WILDCARDS): matches = fnmatch.filter(log_props, pat) for match in matches: if match in _resolved_dict: raise NameError("In class %s.%s %s property '%s' " "is matched multiple times by patterns" % \ (cls.__module__, cls.__name__, _dict_name, match)) _resolved_dict[match] = ai pass if not matches: logger.warning("In class %s.%s %s pattern '%s' " "did not match any existing logical property." % \ (cls.__module__, cls.__name__, _dict_name, pat)) pass pass # now adds the exact matches (no wilcards) which override # the pattern-matches _resolved_dict.update((name, ai) for name, ai in _dict.iteritems() if name in log_props) # checks that all getter/setter have a corresponding logical property not_found = [name for name in _resolved_dict if name not in log_props] if not_found: logger.warning("In class %s.%s logical %s were declared for"\ "non-existant observables: %s" % \ (cls.__module__, cls.__name__, _dict_name, str(not_found))) pass pass # creates the properties for name in log_props: # finds the getter ai_get = resolved_getdict.get(name, None) if ai_get: # decorator-based _getter = type(cls).get_getter(cls, name, ai_get.func, ai_get.has_args) else: # old style _getter = type(cls).get_getter(cls, name) if _getter is None: raise RuntimeError("In class %s.%s logical observable '%s' "\ "has no getter method" % \ (cls.__module__, cls.__name__, name)) pass # finds the setter ai_set = resolved_setdict.get(name, None) if ai_set: # decorator-based if ai_get: _setter = type(cls).get_setter(cls, name, ai_set.func, ai_set.has_args, ai_get.func, ai_get.has_args) else: # the getter is old style. _getter is already # resolved wrt the name it may take, so # getter_takes_name is False _setter = type(cls).get_setter(cls, name, ai_set.func, ai_set.has_args, _getter, False) pass else: # old style setter if ai_get: _setter = type(cls).get_setter(cls, name, None, None, ai_get.func, ai_get.has_args) else: _setter = type(cls).get_setter(cls, name) pass # here _setter can be None prop = property(_getter, _setter) setattr(cls, name, prop) real_log_props.add(name) pass # checks that all setters have a getter setters_no_getters = (set(resolved_setdict) - real_log_props) & log_props if setters_no_getters: logger.warning("In class %s.%s logical setters have no " "getters: %s" % \ (cls.__module__, cls.__name__, ", ".join(setters_no_getters))) pass return frozenset(real_log_props)