Make scheduler filters/weighers only load once
Right now, filters/weighers are instantiated on every invocation of the scheduler. This is both time consuming and unnecessary. In cases where a filter/weigher tries to be smart and store/cache something in between invocations this actually prohibits that. This change make base filter/weigher functions take objects instead of classes and then let schedulers create objects only once and then reuse them. This fixes a known bug in trusted_filter that tries to cache things. Related to blueprint scheduler-optimization Change-Id: I3174ab7968b51c43c0711033bac5d4bc30938b95 Closes-Bug: #1223450
This commit is contained in:
parent
551cf196df
commit
c126d36640
|
@ -73,11 +73,13 @@ class CellsScheduler(base.Base):
|
|||
self.compute_api = compute.API()
|
||||
self.compute_task_api = conductor.ComputeTaskAPI()
|
||||
self.filter_handler = filters.CellFilterHandler()
|
||||
self.filter_classes = self.filter_handler.get_matching_classes(
|
||||
filter_classes = self.filter_handler.get_matching_classes(
|
||||
CONF.cells.scheduler_filter_classes)
|
||||
self.filters = [cls() for cls in filter_classes]
|
||||
self.weight_handler = weights.CellWeightHandler()
|
||||
self.weigher_classes = self.weight_handler.get_matching_classes(
|
||||
weigher_classes = self.weight_handler.get_matching_classes(
|
||||
CONF.cells.scheduler_weight_classes)
|
||||
self.weighers = [cls() for cls in weigher_classes]
|
||||
|
||||
def _create_instances_here(self, ctxt, instance_uuids, instance_properties,
|
||||
instance_type, image, security_groups, block_device_mapping):
|
||||
|
@ -142,8 +144,7 @@ class CellsScheduler(base.Base):
|
|||
|
||||
def _grab_target_cells(self, filter_properties):
|
||||
cells = self._get_possible_cells()
|
||||
cells = self.filter_handler.get_filtered_objects(self.filter_classes,
|
||||
cells,
|
||||
cells = self.filter_handler.get_filtered_objects(self.filters, cells,
|
||||
filter_properties)
|
||||
# NOTE(comstud): I know this reads weird, but the 'if's are nested
|
||||
# this way to optimize for the common case where 'cells' is a list
|
||||
|
@ -156,7 +157,7 @@ class CellsScheduler(base.Base):
|
|||
raise exception.NoCellsAvailable()
|
||||
|
||||
weighted_cells = self.weight_handler.get_weighed_objects(
|
||||
self.weigher_classes, cells, filter_properties)
|
||||
self.weighers, cells, filter_properties)
|
||||
LOG.debug("Weighted cells: %(weighted_cells)s",
|
||||
{'weighted_cells': weighted_cells})
|
||||
target_cells = [cell.obj for cell in weighted_cells]
|
||||
|
|
|
@ -64,20 +64,15 @@ class BaseFilterHandler(loadables.BaseLoader):
|
|||
This class should be subclassed where one needs to use filters.
|
||||
"""
|
||||
|
||||
def get_filtered_objects(self, filter_classes, objs,
|
||||
filter_properties, index=0):
|
||||
def get_filtered_objects(self, filters, objs, filter_properties, index=0):
|
||||
list_objs = list(objs)
|
||||
LOG.debug("Starting with %d host(s)", len(list_objs))
|
||||
for filter_cls in filter_classes:
|
||||
cls_name = filter_cls.__name__
|
||||
filter = filter_cls()
|
||||
|
||||
for filter in filters:
|
||||
if filter.run_filter_for_index(index):
|
||||
objs = filter.filter_all(list_objs,
|
||||
filter_properties)
|
||||
cls_name = filter.__class__.__name__
|
||||
objs = filter.filter_all(list_objs, filter_properties)
|
||||
if objs is None:
|
||||
LOG.debug("Filter %(cls_name)s says to stop filtering",
|
||||
{'cls_name': cls_name})
|
||||
LOG.debug("Filter %s says to stop filtering", cls_name)
|
||||
return
|
||||
list_objs = list(objs)
|
||||
if not list_objs:
|
||||
|
|
|
@ -281,11 +281,15 @@ class HostManager(object):
|
|||
def __init__(self):
|
||||
self.host_state_map = {}
|
||||
self.filter_handler = filters.HostFilterHandler()
|
||||
self.filter_classes = self.filter_handler.get_matching_classes(
|
||||
filter_classes = self.filter_handler.get_matching_classes(
|
||||
CONF.scheduler_available_filters)
|
||||
self.filter_cls_map = dict(
|
||||
(cls.__name__, cls) for cls in filter_classes)
|
||||
self.filter_obj_map = {}
|
||||
self.weight_handler = weights.HostWeightHandler()
|
||||
self.weight_classes = self.weight_handler.get_matching_classes(
|
||||
weigher_classes = self.weight_handler.get_matching_classes(
|
||||
CONF.scheduler_weight_classes)
|
||||
self.weighers = [cls() for cls in weigher_classes]
|
||||
|
||||
def _choose_host_filters(self, filter_cls_names):
|
||||
"""Since the caller may specify which filters to use we need
|
||||
|
@ -297,14 +301,17 @@ class HostManager(object):
|
|||
filter_cls_names = CONF.scheduler_default_filters
|
||||
if not isinstance(filter_cls_names, (list, tuple)):
|
||||
filter_cls_names = [filter_cls_names]
|
||||
cls_map = dict((cls.__name__, cls) for cls in self.filter_classes)
|
||||
|
||||
good_filters = []
|
||||
bad_filters = []
|
||||
for filter_name in filter_cls_names:
|
||||
if filter_name not in cls_map:
|
||||
bad_filters.append(filter_name)
|
||||
continue
|
||||
good_filters.append(cls_map[filter_name])
|
||||
if filter_name not in self.filter_obj_map:
|
||||
if filter_name not in self.filter_cls_map:
|
||||
bad_filters.append(filter_name)
|
||||
continue
|
||||
filter_cls = self.filter_cls_map[filter_name]
|
||||
self.filter_obj_map[filter_name] = filter_cls()
|
||||
good_filters.append(self.filter_obj_map[filter_name])
|
||||
if bad_filters:
|
||||
msg = ", ".join(bad_filters)
|
||||
raise exception.SchedulerHostFilterNotFound(filter_name=msg)
|
||||
|
@ -357,7 +364,7 @@ class HostManager(object):
|
|||
"'force_nodes' value of '%s'")
|
||||
LOG.audit(msg % forced_nodes_str)
|
||||
|
||||
filter_classes = self._choose_host_filters(filter_class_names)
|
||||
filters = self._choose_host_filters(filter_class_names)
|
||||
ignore_hosts = filter_properties.get('ignore_hosts', [])
|
||||
force_hosts = filter_properties.get('force_hosts', [])
|
||||
force_nodes = filter_properties.get('force_nodes', [])
|
||||
|
@ -381,12 +388,12 @@ class HostManager(object):
|
|||
return name_to_cls_map.values()
|
||||
hosts = name_to_cls_map.itervalues()
|
||||
|
||||
return self.filter_handler.get_filtered_objects(filter_classes,
|
||||
return self.filter_handler.get_filtered_objects(filters,
|
||||
hosts, filter_properties, index)
|
||||
|
||||
def get_weighed_hosts(self, hosts, weight_properties):
|
||||
"""Weigh the hosts."""
|
||||
return self.weight_handler.get_weighed_objects(self.weight_classes,
|
||||
return self.weight_handler.get_weighed_objects(self.weighers,
|
||||
hosts, weight_properties)
|
||||
|
||||
def get_all_host_states(self, context):
|
||||
|
|
|
@ -43,13 +43,14 @@ class _FilterTestClass(test.NoDBTestCase):
|
|||
self.scheduler = self.msg_runner.scheduler
|
||||
self.my_cell_state = self.msg_runner.state_manager.get_my_state()
|
||||
self.filter_handler = filters.CellFilterHandler()
|
||||
self.filter_classes = self.filter_handler.get_matching_classes(
|
||||
filter_classes = self.filter_handler.get_matching_classes(
|
||||
[self.filter_cls_name])
|
||||
self.filters = [cls() for cls in filter_classes]
|
||||
self.context = context.RequestContext('fake', 'fake',
|
||||
is_admin=True)
|
||||
|
||||
def _filter_cells(self, cells, filter_properties):
|
||||
return self.filter_handler.get_filtered_objects(self.filter_classes,
|
||||
return self.filter_handler.get_filtered_objects(self.filters,
|
||||
cells,
|
||||
filter_properties)
|
||||
|
||||
|
|
|
@ -51,11 +51,13 @@ class FakeFilterClass2(filters.BaseCellFilter):
|
|||
|
||||
|
||||
class FakeWeightClass1(weights.BaseCellWeigher):
|
||||
pass
|
||||
def _weigh_object(self, obj, weight_properties):
|
||||
pass
|
||||
|
||||
|
||||
class FakeWeightClass2(weights.BaseCellWeigher):
|
||||
pass
|
||||
def _weigh_object(self, obj, weight_properties):
|
||||
pass
|
||||
|
||||
|
||||
class CellsSchedulerTestCase(test.TestCase):
|
||||
|
@ -360,8 +362,8 @@ class CellsSchedulerTestCase(test.TestCase):
|
|||
def fake_rpc_build_instances(ctxt, **host_sched_kwargs):
|
||||
call_info['host_sched_kwargs'] = host_sched_kwargs
|
||||
|
||||
def fake_get_filtered_objs(filter_classes, cells, filt_properties):
|
||||
call_info['filt_classes'] = filter_classes
|
||||
def fake_get_filtered_objs(filters, cells, filt_properties):
|
||||
call_info['filt_objects'] = filters
|
||||
call_info['filt_cells'] = cells
|
||||
call_info['filt_props'] = filt_properties
|
||||
return cells
|
||||
|
@ -411,7 +413,7 @@ class CellsSchedulerTestCase(test.TestCase):
|
|||
'instance_type': 'fake_type'}
|
||||
self.assertEqual(expected_filt_props, call_info['filt_props'])
|
||||
self.assertEqual([FakeFilterClass1, FakeFilterClass2],
|
||||
call_info['filt_classes'])
|
||||
[obj.__class__ for obj in call_info['filt_objects']])
|
||||
self.assertEqual([self.my_cell_state], call_info['filt_cells'])
|
||||
|
||||
def test_cells_filter_returning_none(self):
|
||||
|
@ -475,8 +477,8 @@ class CellsSchedulerTestCase(test.TestCase):
|
|||
def fake_rpc_build_instances(ctxt, **host_sched_kwargs):
|
||||
call_info['host_sched_kwargs'] = host_sched_kwargs
|
||||
|
||||
def fake_get_weighed_objs(weight_classes, cells, filt_properties):
|
||||
call_info['weight_classes'] = weight_classes
|
||||
def fake_get_weighed_objs(weighers, cells, filt_properties):
|
||||
call_info['weighers'] = weighers
|
||||
call_info['weight_cells'] = cells
|
||||
call_info['weight_props'] = filt_properties
|
||||
return [weights.WeightedCell(cells[0], 0.0)]
|
||||
|
@ -526,5 +528,5 @@ class CellsSchedulerTestCase(test.TestCase):
|
|||
'instance_type': 'fake_type'}
|
||||
self.assertEqual(expected_filt_props, call_info['weight_props'])
|
||||
self.assertEqual([FakeWeightClass1, FakeWeightClass2],
|
||||
call_info['weight_classes'])
|
||||
[obj.__class__ for obj in call_info['weighers']])
|
||||
self.assertEqual([self.my_cell_state], call_info['weight_cells'])
|
||||
|
|
|
@ -78,11 +78,12 @@ class _WeigherTestClass(test.NoDBTestCase):
|
|||
def setUp(self):
|
||||
super(_WeigherTestClass, self).setUp()
|
||||
self.weight_handler = weights.CellWeightHandler()
|
||||
self.weight_classes = self.weight_handler.get_matching_classes(
|
||||
weigher_classes = self.weight_handler.get_matching_classes(
|
||||
[self.weigher_cls_name])
|
||||
self.weighers = [cls() for cls in weigher_classes]
|
||||
|
||||
def _get_weighed_cells(self, cells, weight_properties):
|
||||
return self.weight_handler.get_weighed_objects(self.weight_classes,
|
||||
return self.weight_handler.get_weighed_objects(self.weighers,
|
||||
cells, weight_properties)
|
||||
|
||||
|
||||
|
|
|
@ -110,11 +110,9 @@ class FiltersTestCase(test.NoDBTestCase):
|
|||
self.mox.StubOutWithMock(filt2_mock, 'run_filter_for_index')
|
||||
self.mox.StubOutWithMock(filt2_mock, 'filter_all')
|
||||
|
||||
Filter1().AndReturn(filt1_mock)
|
||||
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
||||
filt1_mock.filter_all(filter_objs_initial,
|
||||
filter_properties).AndReturn(filter_objs_second)
|
||||
Filter2().AndReturn(filt2_mock)
|
||||
filt2_mock.run_filter_for_index(0).AndReturn(True)
|
||||
filt2_mock.filter_all(filter_objs_second,
|
||||
filter_properties).AndReturn(filter_objs_last)
|
||||
|
@ -122,8 +120,8 @@ class FiltersTestCase(test.NoDBTestCase):
|
|||
self.mox.ReplayAll()
|
||||
|
||||
filter_handler = filters.BaseFilterHandler(filters.BaseFilter)
|
||||
filter_classes = [Filter1, Filter2]
|
||||
result = filter_handler.get_filtered_objects(filter_classes,
|
||||
filter_mocks = [filt1_mock, filt2_mock]
|
||||
result = filter_handler.get_filtered_objects(filter_mocks,
|
||||
filter_objs_initial,
|
||||
filter_properties)
|
||||
self.assertEqual(filter_objs_last, result)
|
||||
|
@ -154,19 +152,17 @@ class FiltersTestCase(test.NoDBTestCase):
|
|||
self.mox.StubOutWithMock(filt2_mock, 'run_filter_for_index')
|
||||
self.mox.StubOutWithMock(filt2_mock, 'filter_all')
|
||||
|
||||
Filter1().AndReturn(filt1_mock)
|
||||
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
||||
filt1_mock.filter_all(filter_objs_initial,
|
||||
filter_properties).AndReturn(filter_objs_second)
|
||||
Filter2().AndReturn(filt2_mock)
|
||||
# return false so filter_all will not be called
|
||||
filt2_mock.run_filter_for_index(0).AndReturn(False)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
filter_handler = filters.BaseFilterHandler(filters.BaseFilter)
|
||||
filter_classes = [Filter1, Filter2]
|
||||
filter_handler.get_filtered_objects(filter_classes,
|
||||
filter_mocks = [filt1_mock, filt2_mock]
|
||||
filter_handler.get_filtered_objects(filter_mocks,
|
||||
filter_objs_initial,
|
||||
filter_properties)
|
||||
|
||||
|
@ -192,15 +188,14 @@ class FiltersTestCase(test.NoDBTestCase):
|
|||
use_mock_anything=True)
|
||||
self.mox.StubOutWithMock(filt2_mock, 'filter_all')
|
||||
|
||||
Filter1().AndReturn(filt1_mock)
|
||||
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
||||
filt1_mock.filter_all(filter_objs_initial,
|
||||
filter_properties).AndReturn(None)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
filter_handler = filters.BaseFilterHandler(filters.BaseFilter)
|
||||
filter_classes = [Filter1, Filter2]
|
||||
result = filter_handler.get_filtered_objects(filter_classes,
|
||||
filter_mocks = [filt1_mock, filt2_mock]
|
||||
result = filter_handler.get_filtered_objects(filter_mocks,
|
||||
filter_objs_initial,
|
||||
filter_properties)
|
||||
self.assertIsNone(result)
|
||||
|
|
|
@ -49,6 +49,9 @@ class HostManagerTestCase(test.NoDBTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(HostManagerTestCase, self).setUp()
|
||||
self.flags(scheduler_available_filters=['%s.%s' % (__name__, cls) for
|
||||
cls in ['FakeFilterClass1',
|
||||
'FakeFilterClass2']])
|
||||
self.host_manager = host_manager.HostManager()
|
||||
self.fake_hosts = [host_manager.HostState('fake_host%s' % x,
|
||||
'fake-node') for x in xrange(1, 5)]
|
||||
|
@ -57,20 +60,16 @@ class HostManagerTestCase(test.NoDBTestCase):
|
|||
|
||||
def test_choose_host_filters_not_found(self):
|
||||
self.flags(scheduler_default_filters='FakeFilterClass3')
|
||||
self.host_manager.filter_classes = [FakeFilterClass1,
|
||||
FakeFilterClass2]
|
||||
self.assertRaises(exception.SchedulerHostFilterNotFound,
|
||||
self.host_manager._choose_host_filters, None)
|
||||
|
||||
def test_choose_host_filters(self):
|
||||
self.flags(scheduler_default_filters=['FakeFilterClass2'])
|
||||
self.host_manager.filter_classes = [FakeFilterClass1,
|
||||
FakeFilterClass2]
|
||||
|
||||
# Test we returns 1 correct function
|
||||
filter_classes = self.host_manager._choose_host_filters(None)
|
||||
self.assertEqual(len(filter_classes), 1)
|
||||
self.assertEqual(filter_classes[0].__name__, 'FakeFilterClass2')
|
||||
host_filters = self.host_manager._choose_host_filters(None)
|
||||
self.assertEqual(len(host_filters), 1)
|
||||
self.assertEqual(host_filters[0].__class__.__name__,
|
||||
'FakeFilterClass2')
|
||||
|
||||
def _mock_get_filtered_hosts(self, info, specified_filters=None):
|
||||
self.mox.StubOutWithMock(self.host_manager, '_choose_host_filters')
|
||||
|
@ -85,7 +84,7 @@ class HostManagerTestCase(test.NoDBTestCase):
|
|||
|
||||
self.stubs.Set(FakeFilterClass1, '_filter_one', fake_filter_one)
|
||||
self.host_manager._choose_host_filters(specified_filters).AndReturn(
|
||||
[FakeFilterClass1])
|
||||
[FakeFilterClass1()])
|
||||
|
||||
def _verify_result(self, info, result, filters=True):
|
||||
for x in info['got_fprops']:
|
||||
|
|
|
@ -207,6 +207,9 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(IronicHostManagerTestFilters, self).setUp()
|
||||
self.flags(scheduler_available_filters=['%s.%s' % (__name__, cls) for
|
||||
cls in ['FakeFilterClass1',
|
||||
'FakeFilterClass2']])
|
||||
self.host_manager = ironic_host_manager.IronicHostManager()
|
||||
self.fake_hosts = [ironic_host_manager.IronicNodeState(
|
||||
'fake_host%s' % x, 'fake-node') for x in range(1, 5)]
|
||||
|
@ -215,20 +218,17 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
|||
|
||||
def test_choose_host_filters_not_found(self):
|
||||
self.flags(scheduler_default_filters='FakeFilterClass3')
|
||||
self.host_manager.filter_classes = [FakeFilterClass1,
|
||||
FakeFilterClass2]
|
||||
self.assertRaises(exception.SchedulerHostFilterNotFound,
|
||||
self.host_manager._choose_host_filters, None)
|
||||
|
||||
def test_choose_host_filters(self):
|
||||
self.flags(scheduler_default_filters=['FakeFilterClass2'])
|
||||
self.host_manager.filter_classes = [FakeFilterClass1,
|
||||
FakeFilterClass2]
|
||||
|
||||
# Test we returns 1 correct function
|
||||
filter_classes = self.host_manager._choose_host_filters(None)
|
||||
self.assertEqual(1, len(filter_classes))
|
||||
self.assertEqual('FakeFilterClass2', filter_classes[0].__name__)
|
||||
host_filters = self.host_manager._choose_host_filters(None)
|
||||
self.assertEqual(1, len(host_filters))
|
||||
self.assertEqual('FakeFilterClass2',
|
||||
host_filters[0].__class__.__name__)
|
||||
|
||||
def _mock_get_filtered_hosts(self, info, specified_filters=None):
|
||||
self.mox.StubOutWithMock(self.host_manager, '_choose_host_filters')
|
||||
|
@ -243,7 +243,7 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
|||
|
||||
self.stubs.Set(FakeFilterClass1, '_filter_one', fake_filter_one)
|
||||
self.host_manager._choose_host_filters(specified_filters).AndReturn(
|
||||
[FakeFilterClass1])
|
||||
[FakeFilterClass1()])
|
||||
|
||||
def _verify_result(self, info, result, filters=True):
|
||||
for x in info['got_fprops']:
|
||||
|
|
|
@ -46,12 +46,12 @@ class RamWeigherTestCase(test.NoDBTestCase):
|
|||
def setUp(self):
|
||||
super(RamWeigherTestCase, self).setUp()
|
||||
self.weight_handler = weights.HostWeightHandler()
|
||||
self.weight_classes = [ram.RAMWeigher]
|
||||
self.weighers = [ram.RAMWeigher()]
|
||||
|
||||
def _get_weighed_host(self, hosts, weight_properties=None):
|
||||
if weight_properties is None:
|
||||
weight_properties = {}
|
||||
return self.weight_handler.get_weighed_objects(self.weight_classes,
|
||||
return self.weight_handler.get_weighed_objects(self.weighers,
|
||||
hosts, weight_properties)[0]
|
||||
|
||||
def _get_all_hosts(self):
|
||||
|
@ -118,7 +118,7 @@ class RamWeigherTestCase(test.NoDBTestCase):
|
|||
# negativehost: free_ram_mb=-512
|
||||
|
||||
# so, host4 should win
|
||||
weights = self.weight_handler.get_weighed_objects(self.weight_classes,
|
||||
weights = self.weight_handler.get_weighed_objects(self.weighers,
|
||||
hostinfo_list, {})
|
||||
|
||||
weighed_host = weights[0]
|
||||
|
@ -135,13 +135,14 @@ class MetricsWeigherTestCase(test.NoDBTestCase):
|
|||
def setUp(self):
|
||||
super(MetricsWeigherTestCase, self).setUp()
|
||||
self.weight_handler = weights.HostWeightHandler()
|
||||
self.weight_classes = [metrics.MetricsWeigher]
|
||||
self.weighers = [metrics.MetricsWeigher()]
|
||||
|
||||
def _get_weighed_host(self, hosts, setting, weight_properties=None):
|
||||
if not weight_properties:
|
||||
weight_properties = {}
|
||||
self.flags(weight_setting=setting, group='metrics')
|
||||
return self.weight_handler.get_weighed_objects(self.weight_classes,
|
||||
self.weighers[0]._parse_setting()
|
||||
return self.weight_handler.get_weighed_objects(self.weighers,
|
||||
hosts, weight_properties)[0]
|
||||
|
||||
def _get_all_hosts(self):
|
||||
|
@ -227,7 +228,7 @@ class MetricsWeigherTestCase(test.NoDBTestCase):
|
|||
self.assertIn(item, weigher.setting)
|
||||
|
||||
def test_parse_setting(self):
|
||||
weigher = self.weight_classes[0]()
|
||||
weigher = self.weighers[0]
|
||||
self._check_parsing_result(weigher,
|
||||
['foo=1'],
|
||||
[('foo', 1.0)])
|
||||
|
@ -270,12 +271,12 @@ class IoOpsWeigherTestCase(test.NoDBTestCase):
|
|||
def setUp(self):
|
||||
super(IoOpsWeigherTestCase, self).setUp()
|
||||
self.weight_handler = weights.HostWeightHandler()
|
||||
self.weight_classes = [io_ops.IoOpsWeigher]
|
||||
self.weighers = [io_ops.IoOpsWeigher()]
|
||||
|
||||
def _get_weighed_host(self, hosts, io_ops_weight_multiplier):
|
||||
if io_ops_weight_multiplier is not None:
|
||||
self.flags(io_ops_weight_multiplier=io_ops_weight_multiplier)
|
||||
return self.weight_handler.get_weighed_objects(self.weight_classes,
|
||||
return self.weight_handler.get_weighed_objects(self.weighers,
|
||||
hosts, {})[0]
|
||||
|
||||
def _get_all_hosts(self):
|
||||
|
|
|
@ -121,16 +121,14 @@ class BaseWeigher(object):
|
|||
class BaseWeightHandler(loadables.BaseLoader):
|
||||
object_class = WeighedObject
|
||||
|
||||
def get_weighed_objects(self, weigher_classes, obj_list,
|
||||
weighing_properties):
|
||||
def get_weighed_objects(self, weighers, obj_list, weighing_properties):
|
||||
"""Return a sorted (descending), normalized list of WeighedObjects."""
|
||||
|
||||
if not obj_list:
|
||||
return []
|
||||
|
||||
weighed_objs = [self.object_class(obj, 0.0) for obj in obj_list]
|
||||
for weigher_cls in weigher_classes:
|
||||
weigher = weigher_cls()
|
||||
for weigher in weighers:
|
||||
weights = weigher.weigh_objects(weighed_objs, weighing_properties)
|
||||
|
||||
# Normalize the weights
|
||||
|
|
Loading…
Reference in New Issue