Skip to content

Commit 89b5b3f

Browse files
committed
Improved performance of Entity.__getattr__ and Entity.__setattr__.
Added Entity.attributes and Entity.dynamic_attributes. Fixed resolution order of Entity.server_classes.
1 parent 45b24d8 commit 89b5b3f

File tree

1 file changed

+57
-36
lines changed
  • addons/source-python/packages/source-python/entities

1 file changed

+57
-36
lines changed

addons/source-python/packages/source-python/entities/_base.py

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)