Commit 1f64ef16 authored by Jérome Perrin's avatar Jérome Perrin

wip py3 sort categories

parent b91cf5bd
......@@ -28,6 +28,7 @@
from past.builtins import cmp
import string
import warnings
from Products.ERP5Type.Globals import InitializeClass, DTMLFile
from AccessControl import ClassSecurityInfo
......@@ -282,6 +283,7 @@ class Category(Folder):
def getCategoryChildValueList(self, recursive=1, include_if_child=1,
is_self_excluded=1, sort_on=None,
sort_order=None, local_sort_method=None,
local_sort_key=None,
local_sort_id=None, checked_permission=None,
**kw):
"""
......@@ -305,8 +307,13 @@ class Category(Folder):
WARNING: using these parameters can slow down
significantly, because this is written in Python
local_sort_key - When using the default preorder traversal, use
this function as sort key for objects of the same
depth.
local_sort_method - When using the default preorder traversal, use
this function to sort objects of the same depth.
DEPRECATED, use local_sort_key
local_sort_id - When using the default preorder traversal, sort
objects of the same depth by comparing their
......@@ -325,24 +332,23 @@ class Category(Folder):
child_value_list = self.objectValues(self.allowed_types)
if local_sort_id:
if isinstance(local_sort_id, (tuple, list)):
def sort_method(a, b):
for sort_id in local_sort_id:
diff = cmp(a.getProperty(sort_id, 0), b.getProperty(sort_id, 0))
if diff != 0:
return diff
return 0
local_sort_method = sort_method
else:
local_sort_method = lambda a, b: cmp(a.getProperty(local_sort_id, 0),
b.getProperty(local_sort_id, 0))
if not isinstance(local_sort_id, (tuple, list)):
local_sort_id = (local_sort_id, )
def sort_key(c):
return [c.getProperty(sort_id, 0) for sort_id in local_sort_id]
local_sort_key = sort_key
if local_sort_method:
# sort objects at the current level
warnings.warn(
"`local_sort_method` argument is deprecated, use `local_sort_key` instead",
DeprecationWarning)
child_value_list = list(child_value_list)
if six.PY2:
child_value_list.sort(local_sort_method)
else:
child_value_list.sort(key=cmp_to_key(local_sort_method))
local_sort_key = cmp_to_key(local_sort_method)
if local_sort_key:
child_value_list = sorted(child_value_list, key=local_sort_key)
if recursive:
for c in child_value_list:
# Do not pass sort_on / sort_order parameters intentionally, because
......@@ -351,6 +357,7 @@ class Category(Folder):
is_self_excluded=0,
include_if_child=include_if_child,
local_sort_method=local_sort_method,
local_sort_key=local_sort_key,
local_sort_id=local_sort_id))
else:
for c in child_value_list:
......@@ -838,7 +845,7 @@ class BaseCategory(Category):
'getCategoryChildValueList')
def getCategoryChildValueList(self, is_self_excluded=1, recursive=1,
include_if_child=1, sort_on=None, sort_order=None,
local_sort_method=None, local_sort_id=None,
local_sort_method=None, local_sort_key=None, local_sort_id=None,
checked_permission=None, **kw):
"""
List the child objects of this category and all its subcategories.
......@@ -860,8 +867,13 @@ class BaseCategory(Category):
the 'sort_on' attribute. The default is to do a preorder tree
traversal on all subobjects.
local_sort_key - When using the default preorder traversal, use
this function as sort key for objects of the same
depth.
local_sort_method - When using the default preorder traversal, use
this function to sort objects of the same depth.
DEPRECATED, use local_sort_key
local_sort_id - When using the default preorder traversal, sort
objects of the same depth by comparing their
......@@ -879,32 +891,31 @@ class BaseCategory(Category):
child_value_list = self.objectValues(self.allowed_types)
if local_sort_id:
if isinstance(local_sort_id, (tuple, list)):
def sort_method(a, b):
for sort_id in local_sort_id:
diff = cmp(a.getProperty(sort_id, 0), b.getProperty(sort_id, 0))
if diff != 0:
return diff
return 0
local_sort_method = sort_method
else:
local_sort_method = lambda a, b: cmp(a.getProperty(local_sort_id, 0),
b.getProperty(local_sort_id, 0))
if not isinstance(local_sort_id, (tuple, list)):
local_sort_id = (local_sort_id, )
def sort_key(c):
return [c.getProperty(sort_id, 0) for sort_id in local_sort_id]
local_sort_key = sort_key
if local_sort_method:
# sort objects at the current level
warnings.warn(
"`local_sort_method` argument is deprecated, use `local_sort_key` instead",
DeprecationWarning)
child_value_list = list(child_value_list)
if six.PY2:
child_value_list.sort(local_sort_method)
else:
child_value_list.sort(key=cmp_to_key(local_sort_method))
local_sort_key = cmp_to_key(local_sort_method)
if local_sort_key:
child_value_list = sorted(child_value_list, key=local_sort_key)
if recursive:
for c in child_value_list:
value_list.extend(c.getCategoryChildValueList(recursive=1,
is_self_excluded=0,
include_if_child=include_if_child,
local_sort_id=local_sort_id,
local_sort_method=local_sort_method))
local_sort_method=local_sort_method,
local_sort_key=local_sort_key,
local_sort_id=local_sort_id))
else:
for c in child_value_list:
if include_if_child:
......
......@@ -30,6 +30,9 @@
from Products.CMFCategory.Filter import Filter
from ZODB.POSException import ConflictError
from zLOG import LOG, PROBLEM
import six
if six.PY3:
from functools import cmp_to_key
class Renderer(Filter):
"""
......@@ -42,7 +45,7 @@ class Renderer(Filter):
def __init__(self, spec = None, filter = None, portal_type = None,
display_id = None, sort_id = None,
display_method = None, sort_method = None, filter_method = None,
display_method = None, sort_key = None, sort_method = None, filter_method = None,
filter_node=0, disable_node=0,
filter_leave=0, disable_leave=0,
is_right_display = 0, translate_display = 0,
......@@ -74,7 +77,10 @@ class Renderer(Filter):
foo2 5
display order will be (foo1, foo, foo2)
- *sort_method*: a callable method which provides a sort function (?la cmp)
- *sort_key*: a callable method used to sort the values, as in sort(key=sort_key)
- *sort_method*: a callable method used to sort the values, as in python2 cmp.
DEPRECATED, use sort_key.
- *is_right_display*: use the right value in the couple as the display value.
......@@ -109,6 +115,7 @@ class Renderer(Filter):
self.display_id = display_id
self.sort_id = sort_id
self.display_method = display_method
self.sort_key = sort_key
self.sort_method = sort_method
self.is_right_display = is_right_display
self.translate_display = translate_display
......@@ -135,7 +142,12 @@ class Renderer(Filter):
value_list = self.getObjectList(value_list)
value_list = self.filter(value_list)
if self.sort_method is not None:
value_list.sort(self.sort_method)
if six.PY2:
value_list.sort(self.sort_method)
else:
value_list.sort(key=cmp_to_key(self.sort_method))
elif self.sort_key is not None:
value_list.sort(key=self.sort_key)
elif self.sort_id is not None:
value_list.sort(key=lambda x: x.getProperty(self.sort_id))
......
......@@ -30,6 +30,7 @@
import mock
from collections import deque
import unittest
import warnings
from Acquisition import aq_base
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
......@@ -952,8 +953,8 @@ class TestCMFCategory(ERP5TypeTestCase):
self.assertSameSet(c1.getCategoryChildValueList(is_self_excluded=0),
(c1, c11, c111))
def test_24_getCategoryChildValueListLocalSortMethod(self):
'''Test getCategoryChildValueList local sort method'''
def test_24_getCategoryChildValueListLocalSort(self):
'''Test getCategoryChildValueList local sort'''
pc = self.getCategoriesTool()
bc = pc.newContent(portal_type='Base Category', id='child_test')
c1 = bc.newContent(portal_type='Category', id='1', int_index=10, title='C')
......@@ -974,17 +975,52 @@ class TestCMFCategory(ERP5TypeTestCase):
# sort_order which sort the whole list regardless of the original
# structure).
# This can be done either with a function (like cmp argument to python
# list sort)
sort_key_calls = []
def sort_key(c):
sort_key_calls.append(c)
return c.getTitle()
# here c1, c2, c3 are sorted by their titles
self.assertEqual(list(bc.getCategoryChildValueList(
local_sort_key=sort_key)),
[c3, c2, c1, c11, c111, c12])
self.assertTrue(sort_key_calls)
# here c11 & c12 are sorted by their titles
self.assertEqual(list(c1.getCategoryChildValueList(
local_sort_key=sort_key)), [c11, c111, c12])
self.assertTrue(sort_key_calls)
# This can be done with a function, using `local_sort_key` or
# `local_sort_method` (with is like cmp argument to python2 list sort)
sort_func_calls = []
def sort_func(a, b):
sort_func_calls.append((a, b))
return cmp(a.getTitle(), b.getTitle())
# `local_sort_method` is deprecated, so using it cause a warning to be
# emitted. Because the method exists on both category and base category
# there can be two warnings.
with warnings.catch_warnings(record=True) as warning_list:
c1.getCategoryChildValueList(local_sort_method=sort_func)
self.assertEqual(
[str(w.message) for w in warning_list],
['`local_sort_method` argument is deprecated, use `local_sort_key` instead'])
with warnings.catch_warnings(record=True) as warning_list:
bc.getCategoryChildValueList(local_sort_method=sort_func)
self.assertEqual(
[str(w.message) for w in warning_list],
['`local_sort_method` argument is deprecated, use `local_sort_key` instead'] * 2)
sort_func_calls.clear()
# here c1, c2, c3 are sorted by their titles
self.assertEqual(list(bc.getCategoryChildValueList(
local_sort_method=sort_func)),
[c3, c2, c1, c11, c111, c12])
self.assertTrue(sort_func_calls)
sort_func_calls.clear()
# here c11 & c12 are sorted by their titles
self.assertEqual(list(c1.getCategoryChildValueList(
local_sort_method=sort_func)), [c11, c111, c12])
self.assertTrue(sort_func_calls)
# This can also be done with a local_sort_id, then objects are sorted by
# comparing this 'sort_id' property (using getProperty())
......
......@@ -33,7 +33,7 @@ from six import string_types as basestring
from six.moves import xrange
import six
if six.PY3:
from functools import cmp_to_key
from functools import cmp_to_key, total_ordering
import os
import re
import string
......@@ -146,13 +146,21 @@ else:
if six.PY2:
OrderableKey = lambda x: x
else:
@total_ordering
class OrderableKey(object):
def __init__(self, value):
self.value = value
def __lt__(self, other):
if not isinstance(other, OrderableKey):
raise TypeError
return cmp(self.value, other.value) != 1
def __eq__(self, other):
if not isinstance(other, OrderableKey):
raise TypeError
return self.value == other.value
def __repr__(self):
return 'OrderableKey(%r)' % self.value
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment