@@ -144,6 +144,17 @@ def __call__(cls, index, caching=None):
144144 # We are done, let's return the instance
145145 return obj
146146
147+ @cached_property (unbound = True )
148+ def attributes (cls ):
149+ """Returns all the attributes available for this class.
150+
151+ :rtype: dict
152+ """
153+ attributes = {}
154+ for cls in reversed (cls .mro ()):
155+ attributes .update (vars (cls ))
156+ return attributes
157+
147158 @property
148159 def caching (cls ):
149160 """Returns whether this class is caching its instances by default.
@@ -208,54 +219,52 @@ def __hash__(self):
208219
209220 def __getattr__ (self , attr ):
210221 """Find if the attribute is valid and returns the appropriate value."""
211- # Loop through all of the entity's server classes
212- for instance in self .server_classes .values ():
213-
214- try :
215- # Get the attribute's value
216- value = getattr (instance , attr )
217- except AttributeError :
218- continue
222+ # Try to resolve the dynamic attribute
223+ try :
224+ instance , value = self .dynamic_attributes [attr ]
225+ except KeyError :
226+ raise AttributeError ('Attribute "{0}" not found' .format (attr ))
219227
220- # Is the value a dynamic function?
221- if isinstance (value , MemberFunction ):
228+ # Is the attribute a property descriptor?
229+ with suppress (AttributeError ):
230+ value = value .__get__ (instance )
222231
223- # Cache the value
224- with suppress (AttributeError ):
225- object .__setattr__ (self , attr , value )
232+ # Is the value a dynamic function?
233+ if isinstance (value , MemberFunction ):
226234
227- # Return the attribute's value
228- return value
235+ # Cache the value
236+ with suppress (AttributeError ):
237+ object .__setattr__ (self , attr , value )
229238
230- # If the attribute is not found, raise an error
231- raise AttributeError ('Attribute "{0}" not found' .format (attr ))
239+ return value
232240
233241 def __setattr__ (self , attr , value ):
234242 """Find if the attribute is valid and sets its value."""
235243 # Is the given attribute a property?
236- if (attr in super ().__dir__ () and isinstance (
237- getattr (self .__class__ , attr , None ), property )):
238-
239- # Set the property's value
240- object .__setattr__ (self , attr , value )
241-
242- # No need to go further
243- return
244+ try :
245+ setter = type (self ).attributes [attr ].__set__
244246
245- # Loop through all of the entity's server classes
246- for server_class , instance in self .server_classes .items ():
247+ # KeyError:
248+ # The attribute does not exist.
249+ # AttributeError:
250+ # The attribute is not a descriptor.
251+ except (KeyError , AttributeError ):
247252
248- # Does the current server class contain the given attribute?
249- if hasattr (server_class , attr ):
253+ # Try to resolve a dynamic attribute
254+ try :
255+ self , setter = self .dynamic_attributes [attr ].__set__
250256
251- # Set the attribute's value
252- setattr (instance , attr , value )
257+ # KeyError:
258+ # The attribute does not exist.
259+ # AttributeError:
260+ # The attribute is not a descriptor.
261+ except (KeyError , AttributeError ):
253262
254- # No need to go further
255- return
263+ # Set the attribute to the given value
264+ return object . __setattr__ ( self , attr , value )
256265
257- # If the attribute is not found, just set the attribute
258- super (). __setattr__ ( attr , value )
266+ # Set the attribute's value
267+ setter ( self , value )
259268
260269 def __dir__ (self ):
261270 """Return an alphabetized list of attributes for the instance."""
@@ -317,9 +326,21 @@ def server_classes(self):
317326 """Yield all server classes for the entity."""
318327 return {
319328 server_class : make_object (server_class , self .pointer ) for
320- server_class in server_classes .get_entity_server_classes (self )
329+ server_class in reversed (
330+ server_classes .get_entity_server_classes (self )
331+ )
321332 }
322333
334+ @cached_property
335+ def dynamic_attributes (self ):
336+ """Returns the dynamic attributes for this entities."""
337+ attributes = {}
338+ for cls , instance in self .server_classes .items ():
339+ attributes .update (
340+ {attr :(instance , getattr (cls , attr )) for attr in dir (cls )}
341+ )
342+ return attributes
343+
323344 @cached_property
324345 def properties (self ):
325346 """Iterate over all descriptors available for the entity."""
0 commit comments