@@ -144,6 +144,17 @@ def __call__(cls, index, caching=None):
144
144
# We are done, let's return the instance
145
145
return obj
146
146
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
+
147
158
@property
148
159
def caching (cls ):
149
160
"""Returns whether this class is caching its instances by default.
@@ -208,54 +219,52 @@ def __hash__(self):
208
219
209
220
def __getattr__ (self , attr ):
210
221
"""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 ))
219
227
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 )
222
231
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 ):
226
234
227
- # Return the attribute's value
228
- return value
235
+ # Cache the value
236
+ with suppress (AttributeError ):
237
+ object .__setattr__ (self , attr , value )
229
238
230
- # If the attribute is not found, raise an error
231
- raise AttributeError ('Attribute "{0}" not found' .format (attr ))
239
+ return value
232
240
233
241
def __setattr__ (self , attr , value ):
234
242
"""Find if the attribute is valid and sets its value."""
235
243
# 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__
244
246
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 ):
247
252
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__
250
256
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 ):
253
262
254
- # No need to go further
255
- return
263
+ # Set the attribute to the given value
264
+ return object . __setattr__ ( self , attr , value )
256
265
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 )
259
268
260
269
def __dir__ (self ):
261
270
"""Return an alphabetized list of attributes for the instance."""
@@ -317,9 +326,21 @@ def server_classes(self):
317
326
"""Yield all server classes for the entity."""
318
327
return {
319
328
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
+ )
321
332
}
322
333
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
+
323
344
@cached_property
324
345
def properties (self ):
325
346
"""Iterate over all descriptors available for the entity."""
0 commit comments