From 1f0afd17a7ce79ea89b3e1789f86202f5e38e883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 18 Sep 2024 13:46:10 +0200 Subject: [PATCH] Iterate only once to sort records by order clauses Instead of sorting the list multiple times applying order clause one by one in reverse order, the list is sorted only once using a key composed of all the order clauses. An operator is used to reverse the comparison instead of using the reverse keyword argument of sorted. Closes #13525 --- trytond/trytond/model/order.py | 43 +++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/trytond/trytond/model/order.py b/trytond/trytond/model/order.py index bd9fa98d30..3287780334 100644 --- a/trytond/trytond/model/order.py +++ b/trytond/trytond/model/order.py @@ -1,6 +1,8 @@ # This file is part of Tryton. The COPYRIGHT file at the toplevel of this # repository contains the full copyright notices and license terms. +from functools import total_ordering + from sql import Column from trytond.i18n import lazy_gettext @@ -32,12 +34,31 @@ def sequence_ordered( return SequenceOrderedMixin -class _attrgetter: - __slots__ = ('_attr', '_null') +@total_ordering +class _reversor: + __slots__ = ('_obj',) - def __init__(self, attr, null=None): + def __init__(self, obj): + self._obj = obj + + def __eq__(self, other): + if not isinstance(other, _reversor): + return NotImplemented + return self._obj == other._obj + + def __lt__(self, other): + if not isinstance(other, _reversor): + return NotImplemented + return self._obj > other._obj + + +class _attrgetter: + __slots__ = ('_attr', '_null', '_reverse') + + def __init__(self, attr, null=None, reverse=False): self._attr = attr self._null = null + self._reverse = reverse def __call__(self, obj): for name in self._attr.split('.'): @@ -60,14 +81,18 @@ class _attrgetter: null = self._null else: null = not self._null - return (null, obj) + value = (null, obj) + if self._reverse: + value = _reversor(value) + return value def sort(records, order): "Return a new list of records ordered" if not order: return records - for oexpr, otype in reversed(order): + getters = [] + for oexpr, otype in order: try: otype, null_ordering = otype.split(' ', 1) except ValueError: @@ -79,6 +104,8 @@ def sort(records, order): null = not reverse else: null = None - records = sorted( - records, key=_attrgetter(oexpr, null), reverse=reverse) - return records + getters.append(_attrgetter(oexpr, null, reverse)) + + def func(obj): + return tuple(getter(obj) for getter in getters) + return sorted(records, key=func)