cache PropertyHolder.property_list and use O(1) property name lookups

This commit is contained in:
qubesuser 2017-11-09 03:15:37 +01:00
parent f2b8ad7d38
commit d183ab1c18

View File

@ -496,7 +496,7 @@ class PropertyHolder(qubes.events.Emitter):
propvalues = {}
all_names = set(prop.__name__ for prop in self.property_list())
all_names = self.property_dict()
for key in list(kwargs):
if not key in all_names:
continue
@ -509,8 +509,6 @@ class PropertyHolder(qubes.events.Emitter):
if self.xml is not None:
# check if properties are appropriate
all_names = set(prop.__name__ for prop in self.property_list())
for node in self.xml.xpath('./properties/property'):
name = node.get('name')
if name not in all_names:
@ -518,6 +516,39 @@ class PropertyHolder(qubes.events.Emitter):
'property {!r} not applicable to {!r}'.format(
name, self.__class__.__name__))
# pylint: disable=too-many-nested-blocks
@classmethod
def property_dict(cls, load_stage=None):
'''List all properties attached to this VM's class
:param load_stage: Filter by load stage
:type load_stage: :py:func:`int` or :py:obj:`None`
'''
# use cls.__dict__ since we must not look at parent classes
if "_property_dict" not in cls.__dict__:
cls._property_dict = {}
memo = cls._property_dict
if load_stage not in memo:
props = dict()
if load_stage is None:
for class_ in cls.__mro__:
for name in class_.__dict__:
# don't overwrite props with those from base classes
if name not in props:
prop = class_.__dict__[name]
if isinstance(prop, property):
assert name == prop.__name__
props[name] = prop
else:
for prop in cls.property_dict().values():
if prop.load_stage == load_stage:
props[prop.__name__] = prop
memo[load_stage] = props
return memo[load_stage]
@classmethod
def property_list(cls, load_stage=None):
'''List all properties attached to this VM's class
@ -526,14 +557,15 @@ class PropertyHolder(qubes.events.Emitter):
:type load_stage: :py:func:`int` or :py:obj:`None`
'''
props = set()
for class_ in cls.__mro__:
props.update(prop for prop in class_.__dict__.values()
if isinstance(prop, property))
if load_stage is not None:
props = set(prop for prop in props
if prop.load_stage == load_stage)
return sorted(props)
# use cls.__dict__ since we must not look at parent classes
if "_property_list" not in cls.__dict__:
cls._property_list = {}
memo = cls._property_list
if load_stage not in memo:
memo[load_stage] = sorted(cls.property_dict(load_stage).values())
return memo[load_stage]
def _property_init(self, prop, value):
'''Initialise property to a given value, without side effects.
@ -588,9 +620,9 @@ class PropertyHolder(qubes.events.Emitter):
if isinstance(prop, qubes.property):
return prop
for p in cls.property_list():
if p.__name__ == prop:
return p
props = cls.property_dict()
if prop in props:
return props[prop]
raise AttributeError('No property {!r} found in {!r}'.format(
prop, cls))