From ca1b40a9bf37f25c47a9e4713d54f4e13752436a Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:34:45 +0200 Subject: [PATCH 01/18] - ignore mr.developer artifact --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9277fbd0..c0c6c2e4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.pyo *.egg-info .installed.cfg +.mr.developer.cfg bin/ build/ develop-eggs/ From 8ebd99173d01991ea73a46324994ce0a1e940b5e Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:35:37 +0200 Subject: [PATCH 02/18] - work with the Zope master branch --- buildout.cfg | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/buildout.cfg b/buildout.cfg index 8ed55343..813a41cd 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -1,8 +1,18 @@ [buildout] -extends = https://raw.githubusercontent.com/zopefoundation/Zope/master/versions.cfg +[buildout] +extensions = + mr.developer +extends = + https://raw.githubusercontent.com/zopefoundation/Zope/master/sources.cfg + https://raw.githubusercontent.com/zopefoundation/Zope/master/versions-prod.cfg develop = . parts = interpreter test +auto-checkout = + Zope2 + +[sources] +Zope2 = git ${remotes:github}/Zope pushurl=${remotes:github_push}/Zope [versions] Products.ZCatalog = From d0f1c29d6ff16513d51ea659924954877327ea10 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:37:09 +0200 Subject: [PATCH 03/18] - replace map call with zip --- src/Products/PluginIndexes/DateRangeIndex/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/PluginIndexes/DateRangeIndex/tests.py b/src/Products/PluginIndexes/DateRangeIndex/tests.py index b416f239..4fffeea4 100644 --- a/src/Products/PluginIndexes/DateRangeIndex/tests.py +++ b/src/Products/PluginIndexes/DateRangeIndex/tests.py @@ -151,7 +151,7 @@ def test_retrieval(self): results, used = self._checkApply(index, {'work': value}, matches) matches = sorted(matches, key=lambda d: d[1].name()) - for result, match in map(None, results, matches): + for result, match in zip(results, matches): self.assertEqual(index.getEntryForObject(result), match[1].datum()) From cc96a5a57974ee5c5a469bc8757c6477675857b6 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:43:05 +0200 Subject: [PATCH 04/18] - wrap calls to map/filter/range/etc in list calls where needed --- .../PluginIndexes/BooleanIndex/tests.py | 28 +++++++------- .../tests/testCompositeIndex.py | 4 +- .../PluginIndexes/PathIndex/PathIndex.py | 4 +- src/Products/PluginIndexes/PathIndex/tests.py | 4 +- src/Products/PluginIndexes/unindex.py | 2 +- src/Products/ZCTextIndex/BaseIndex.py | 2 +- src/Products/ZCTextIndex/Lexicon.py | 2 +- src/Products/ZCTextIndex/NBest.py | 2 +- src/Products/ZCTextIndex/PipelineFactory.py | 6 +-- src/Products/ZCTextIndex/QueryParser.py | 4 +- .../ZCTextIndex/tests/testZCTextIndex.py | 8 ++-- src/Products/ZCatalog/tests/test_catalog.py | 38 +++++++++---------- 12 files changed, 51 insertions(+), 53 deletions(-) diff --git a/src/Products/PluginIndexes/BooleanIndex/tests.py b/src/Products/PluginIndexes/BooleanIndex/tests.py index db88ac15..f4ec8aa0 100644 --- a/src/Products/PluginIndexes/BooleanIndex/tests.py +++ b/src/Products/PluginIndexes/BooleanIndex/tests.py @@ -108,26 +108,26 @@ def test_index_many_true(self): for i in range(0, 100): obj = Dummy(i, i < 80 and True or False) index._index_object(obj.id, obj, attr='truth') - self.assertEqual(list(index._index), range(80, 100)) + self.assertEqual(list(index._index), list(range(80, 100))) self.assertEqual(len(index._unindex), 100) res, idx = index._apply_index({'truth': True}) - self.assertEqual(list(res), range(0, 80)) + self.assertEqual(list(res), list(range(0, 80))) res, idx = index._apply_index({'truth': False}) - self.assertEqual(list(res), range(80, 100)) + self.assertEqual(list(res), list(range(80, 100))) def test_index_many_false(self): index = self._makeOne() for i in range(0, 100): obj = Dummy(i, i >= 80 and True or False) index._index_object(obj.id, obj, attr='truth') - self.assertEqual(list(index._index), range(80, 100)) + self.assertEqual(list(index._index), list(range(80, 100))) self.assertEqual(len(index._unindex), 100) res, idx = index._apply_index({'truth': False}) - self.assertEqual(list(res), range(0, 80)) + self.assertEqual(list(res), list(range(0, 80))) res, idx = index._apply_index({'truth': True}) - self.assertEqual(list(res), range(80, 100)) + self.assertEqual(list(res), list(range(80, 100))) def test_index_many_change(self): index = self._makeOne() @@ -144,26 +144,26 @@ def add(i, value): # Now add an equal number of False values for i in range(4, 8): add(i, False) - self.assertEqual(list(index._index), range(4, 8)) + self.assertEqual(list(index._index), list(range(4, 8))) self.assertEqual(len(index._unindex), 8) # Once False gets to be more than 60% of the indexed set, we switch add(8, False) - self.assertEqual(list(index._index), range(4, 9)) + self.assertEqual(list(index._index), list(range(4, 9))) add(9, False) - self.assertEqual(list(index._index), range(0, 4)) + self.assertEqual(list(index._index), list(range(0, 4))) res, idx = index._apply_index({'truth': True}) - self.assertEqual(list(res), range(0, 4)) + self.assertEqual(list(res), list(range(0, 4))) res, idx = index._apply_index({'truth': False}) - self.assertEqual(list(res), range(4, 10)) + self.assertEqual(list(res), list(range(4, 10))) # and we can again switch if the percentages change again for i in range(6, 10): index.unindex_object(i) - self.assertEqual(list(index._index), range(4, 6)) + self.assertEqual(list(index._index), list(range(4, 6))) self.assertEqual(len(index._unindex), 6) res, idx = index._apply_index({'truth': True}) - self.assertEqual(list(res), range(0, 4)) + self.assertEqual(list(res), list(range(0, 4))) res, idx = index._apply_index({'truth': False}) - self.assertEqual(list(res), range(4, 6)) + self.assertEqual(list(res), list(range(4, 6))) def test_items(self): index = self._makeOne() diff --git a/src/Products/PluginIndexes/CompositeIndex/tests/testCompositeIndex.py b/src/Products/PluginIndexes/CompositeIndex/tests/testCompositeIndex.py index 0ec4991e..ef5e9a92 100644 --- a/src/Products/PluginIndexes/CompositeIndex/tests/testCompositeIndex.py +++ b/src/Products/PluginIndexes/CompositeIndex/tests/testCompositeIndex.py @@ -20,8 +20,8 @@ states = ['published', 'pending', 'private', 'intranet'] types = ['Document', 'News', 'File', 'Image'] default_pages = [True, False, False, False, False, False] -subjects = map(lambda x: 'subject_%s' % x, range(6)) -keywords = map(lambda x: 'keyword_%s' % x, range(6)) +subjects = list(map(lambda x: 'subject_%s' % x, range(6))) +keywords = list(map(lambda x: 'keyword_%s' % x, range(6))) class TestObject(object): diff --git a/src/Products/PluginIndexes/PathIndex/PathIndex.py b/src/Products/PluginIndexes/PathIndex/PathIndex.py index a4da8655..1c0b4b06 100644 --- a/src/Products/PluginIndexes/PathIndex/PathIndex.py +++ b/src/Products/PluginIndexes/PathIndex/PathIndex.py @@ -116,7 +116,7 @@ def index_object(self, docid, obj, threshold=100): if isinstance(path, (list, tuple)): path = '/' + '/'.join(path[1:]) - comps = filter(None, path.split('/')) + comps = list(filter(None, path.split('/'))) old_value = self._unindex.get(docid, None) if old_value == path: @@ -280,7 +280,7 @@ def _search(self, path, default_level=0): [self._search(path, level) for level in range(self._depth + 1)]) - comps = filter(None, path.split('/')) + comps = list(filter(None, path.split('/'))) if level + len(comps) - 1 > self._depth: # Our search is for a path longer than anything in the index diff --git a/src/Products/PluginIndexes/PathIndex/tests.py b/src/Products/PluginIndexes/PathIndex/tests.py index 06af1864..68b9571d 100644 --- a/src/Products/PluginIndexes/PathIndex/tests.py +++ b/src/Products/PluginIndexes/PathIndex/tests.py @@ -280,14 +280,14 @@ def test___apply_index_root_levelO_dict(self): _populateIndex(index) query = {'path': {'query': '/', 'level': 0}} res = index._apply_index(query) - self.assertEqual(list(res[0].keys()), range(1, 19)) + self.assertEqual(list(res[0].keys()), list(range(1, 19))) def test___apply_index_root_levelO_tuple(self): index = self._makeOne() _populateIndex(index) query = {'path': (('/', 0),)} res = index._apply_index(query) - self.assertEqual(list(res[0].keys()), range(1, 19)) + self.assertEqual(list(res[0].keys()), list(range(1, 19))) def test__apply_index_simple(self): index = self._makeOne() diff --git a/src/Products/PluginIndexes/unindex.py b/src/Products/PluginIndexes/unindex.py index 4a01da65..5c95388b 100644 --- a/src/Products/PluginIndexes/unindex.py +++ b/src/Products/PluginIndexes/unindex.py @@ -466,7 +466,7 @@ def query_index(self, record, resultset=None): record.keys = [k for k in index.keys() if k not in not_parm] else: # convert query arguments into indexed format - record.keys = map(self._convert, record.keys) + record.keys = list(map(self._convert, record.keys)) # Range parameter range_parm = record.get('range', None) diff --git a/src/Products/ZCTextIndex/BaseIndex.py b/src/Products/ZCTextIndex/BaseIndex.py index b3ecf6e5..e95227ea 100644 --- a/src/Products/ZCTextIndex/BaseIndex.py +++ b/src/Products/ZCTextIndex/BaseIndex.py @@ -219,7 +219,7 @@ def search_phrase(self, phrase): return result def _remove_oov_wids(self, wids): - return filter(self._wordinfo.has_key, wids) + return list(filter(self._wordinfo.has_key, wids)) # Subclass must override. # The workhorse. Return a list of (IIBucket, weight) pairs, one pair diff --git a/src/Products/ZCTextIndex/Lexicon.py b/src/Products/ZCTextIndex/Lexicon.py index c72b55a3..95e8f57f 100644 --- a/src/Products/ZCTextIndex/Lexicon.py +++ b/src/Products/ZCTextIndex/Lexicon.py @@ -71,7 +71,7 @@ def sourceToWordIds(self, text): last = _text2list(text) for element in self._pipeline: last = element.process(last) - return map(self._getWordIdCreate, last) + return list(map(self._getWordIdCreate, last)) def termToWordIds(self, text): last = _text2list(text) diff --git a/src/Products/ZCTextIndex/NBest.py b/src/Products/ZCTextIndex/NBest.py index c3c14109..e3fe970f 100644 --- a/src/Products/ZCTextIndex/NBest.py +++ b/src/Products/ZCTextIndex/NBest.py @@ -68,7 +68,7 @@ def addmany(self, sequence): assert n == len(scores) def getbest(self): - result = zip(self._items, self._scores) + result = list(zip(self._items, self._scores)) result.reverse() return result diff --git a/src/Products/ZCTextIndex/PipelineFactory.py b/src/Products/ZCTextIndex/PipelineFactory.py index 802bd1e3..846fcdfa 100644 --- a/src/Products/ZCTextIndex/PipelineFactory.py +++ b/src/Products/ZCTextIndex/PipelineFactory.py @@ -34,13 +34,11 @@ def registerFactory(self, group, name, factory): elements[name] = factory def getFactoryGroups(self): - groups = self._groups.keys() - groups.sort() + groups = sorted(self._groups.keys()) return groups def getFactoryNames(self, group): - names = self._groups[group].keys() - names.sort() + names = sorted(self._groups[group].keys()) return names def instantiate(self, group, name): diff --git a/src/Products/ZCTextIndex/QueryParser.py b/src/Products/ZCTextIndex/QueryParser.py index f1a67cfc..b047803f 100644 --- a/src/Products/ZCTextIndex/QueryParser.py +++ b/src/Products/ZCTextIndex/QueryParser.py @@ -193,7 +193,7 @@ def _parseOrExpr(self): L.append(self._parseAndExpr()) while self._check(_OR): L.append(self._parseAndExpr()) - L = filter(None, L) + L = list(filter(None, L)) if not L: return None # Only stopwords elif len(L) == 1: @@ -241,7 +241,7 @@ def _parseTerm(self): nodes = [self._parseAtom()] while self._peek(_ATOM): nodes.append(self._parseAtom()) - nodes = filter(None, nodes) + nodes = list(filter(None, nodes)) if not nodes: return None # Only stopwords structure = [(isinstance(nodes[i], ParseTree.NotNode), i, nodes[i]) diff --git a/src/Products/ZCTextIndex/tests/testZCTextIndex.py b/src/Products/ZCTextIndex/tests/testZCTextIndex.py index 51e78ba8..cd9e5699 100644 --- a/src/Products/ZCTextIndex/tests/testZCTextIndex.py +++ b/src/Products/ZCTextIndex/tests/testZCTextIndex.py @@ -480,27 +480,27 @@ def _checkRelativeScores(self): self.assertEqual(num, 9) self.assertEqual(len(r), 9) # The more twos in a doc, the better the score should be. - self.assertEqual([doc for doc, score in r], range(9, 0, -1)) + self.assertEqual([doc for doc, score in r], list(range(9, 0, -1))) # Search for "two" alone shouldn't make any difference to relative # results. r, num = self.zc_index.query("two") self.assertEqual(num, 9) self.assertEqual(len(r), 9) - self.assertEqual([doc for doc, score in r], range(9, 0, -1)) + self.assertEqual([doc for doc, score in r], list(range(9, 0, -1))) # Searching for xyz should skip doc 9, and favor the lower-numbered # docs (they have more instances of xyz). r, num = self.zc_index.query("xyz") self.assertEqual(num, 8) self.assertEqual(len(r), 8) - self.assertEqual([doc for doc, score in r], range(1, 9)) + self.assertEqual([doc for doc, score in r], list(range(1, 9))) # And relative results shouldn't change if we add "one". r, num = self.zc_index.query("xyz one") self.assertEqual(num, 8) self.assertEqual(len(r), 8) - self.assertEqual([doc for doc, score in r], range(1, 9)) + self.assertEqual([doc for doc, score in r], list(range(1, 9))) # But if we search for all the words, it's much muddier. The boost # in going from i instances to i+1 of a given word is smaller than diff --git a/src/Products/ZCatalog/tests/test_catalog.py b/src/Products/ZCatalog/tests/test_catalog.py index 27054f9d..c9116dee 100644 --- a/src/Products/ZCatalog/tests/test_catalog.py +++ b/src/Products/ZCatalog/tests/test_catalog.py @@ -203,7 +203,7 @@ class TestCatalog(unittest.TestCase): upper = 10 - nums = range(upper) + nums = list(range(upper)) for i in range(upper): j = random.randrange(0, upper) tmp = nums[i] @@ -355,7 +355,7 @@ class TestCatalogSortBatch(unittest.TestCase): upper = 100 - nums = range(upper) + nums = list(range(upper)) for i in range(upper): j = random.randrange(0, upper) tmp = nums[i] @@ -427,7 +427,7 @@ def test_sortResults(self): rs = IISet([b.getRID() for b in brains]) si = catalog.getIndex('num') result = catalog.sortResults(rs, si) - self.assertEqual([r.num for r in result], range(100)) + self.assertEqual([r.num for r in result], list(range(100))) def test_sortResults_reversed(self): catalog = self._make_one() @@ -445,7 +445,7 @@ def test_sortResults_limit(self): result = catalog.sortResults(rs, si, limit=10) self.assertEqual(len(result), 10) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(10)) + self.assertEqual([r.num for r in result], list(range(10))) def test_sortResults_limit_reversed(self): catalog = self._make_one() @@ -469,7 +469,7 @@ def testLargeSortedResultSetWithSmallIndex(self): def testSortLimit(self): catalog = self._make_one() - full = catalog(att1='att1', sort_on='num') + full = list(catalog(att1='att1', sort_on='num')) a = catalog(att1='att1', sort_on='num', sort_limit=10) self.assertEqual([r.num for r in a], [r.num for r in full[:10]]) self.assertEqual(a.actual_result_count, self.upper) @@ -496,42 +496,42 @@ def testSortLimitViaBatchingArgsBeforeStart(self): query = dict(att1='att1', sort_on='num', b_start=-5, b_size=8) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(0, 3)) + self.assertEqual([r.num for r in result], list(range(0, 3))) def testSortLimitViaBatchingArgsStart(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=0, b_size=5) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(0, 5)) + self.assertEqual([r.num for r in result], list(range(0, 5))) def testSortLimitViaBatchingEarlyFirstHalf(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=11, b_size=17) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(11, 28)) + self.assertEqual([r.num for r in result], list(range(11, 28))) def testSortLimitViaBatchingArgsLateFirstHalf(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=30, b_size=15) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(30, 45)) + self.assertEqual([r.num for r in result], list(range(30, 45))) def testSortLimitViaBatchingArgsLeftMiddle(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=45, b_size=8) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(45, 53)) + self.assertEqual([r.num for r in result], list(range(45, 53))) def testSortLimitViaBatchingArgsRightMiddle(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=48, b_size=8) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(48, 56)) + self.assertEqual([r.num for r in result], list(range(48, 56))) def testSortLimitViaBatchingArgsRightMiddleSortOnTwoSecond(self): catalog = self._make_one() @@ -539,14 +539,14 @@ def testSortLimitViaBatchingArgsRightMiddleSortOnTwoSecond(self): sort_order=('', 'reverse'), b_start=48, b_size=8) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(51, 43, -1)) + self.assertEqual([r.num for r in result], list(range(51, 43, -1))) def testSortLimitViaBatchingArgsEarlySecondHalf(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=55, b_size=15) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(55, 70)) + self.assertEqual([r.num for r in result], list(range(55, 70))) def testSortLimitViaBatchingArgsEarlySecondHalfSortOnTwoFirst(self): catalog = self._make_one() @@ -554,7 +554,7 @@ def testSortLimitViaBatchingArgsEarlySecondHalfSortOnTwoFirst(self): sort_order=('reverse', ''), b_start=55, b_size=15) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(55, 70)) + self.assertEqual([r.num for r in result], list(range(55, 70))) def testSortLimitViaBatchingArgsEarlySecondHalfSortOnTwoSecond(self): catalog = self._make_one() @@ -562,7 +562,7 @@ def testSortLimitViaBatchingArgsEarlySecondHalfSortOnTwoSecond(self): sort_order=('', 'reverse'), b_start=55, b_size=15) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(44, 29, -1)) + self.assertEqual([r.num for r in result], list(range(44, 29, -1))) def testSortLimitViaBatchingArgsEarlySecondHalfSortOnTwoBoth(self): catalog = self._make_one() @@ -570,28 +570,28 @@ def testSortLimitViaBatchingArgsEarlySecondHalfSortOnTwoBoth(self): sort_order=('reverse', 'reverse'), b_start=55, b_size=15) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(44, 29, -1)) + self.assertEqual([r.num for r in result], list(range(44, 29, -1))) def testSortLimitViaBatchingArgsSecondHalf(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=70, b_size=15) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(70, 85)) + self.assertEqual([r.num for r in result], list(range(70, 85))) def testSortLimitViaBatchingArgsEnd(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=90, b_size=10) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(90, 100)) + self.assertEqual([r.num for r in result], list(range(90, 100))) def testSortLimitViaBatchingArgsOverEnd(self): catalog = self._make_one() query = dict(att1='att1', sort_on='num', b_start=90, b_size=15) result = catalog(query) self.assertEqual(result.actual_result_count, 100) - self.assertEqual([r.num for r in result], range(90, 100)) + self.assertEqual([r.num for r in result], list(range(90, 100))) def testSortLimitViaBatchingArgsOutside(self): catalog = self._make_one() From c8e6745e953f4c1f0a0d19b2cc4f3eed1663ba4a Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:44:43 +0200 Subject: [PATCH 05/18] - range barfs when handed a float --- src/Products/ZCTextIndex/ZCTextIndex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/ZCTextIndex/ZCTextIndex.py b/src/Products/ZCTextIndex/ZCTextIndex.py index dc9ba3f5..9f35e959 100644 --- a/src/Products/ZCTextIndex/ZCTextIndex.py +++ b/src/Products/ZCTextIndex/ZCTextIndex.py @@ -379,7 +379,7 @@ def queryLexicon(self, REQUEST, words=None, page=0, rows=20, cols=4): end_word=end, word_count=word_count, page_count=page_count, - page_range=range(page_count), + page_range=range(int(page_count)), page_columns=columns) if REQUEST is not None: From 51450f5cb025ec87a1e862eb6b3eb19bbdc807e8 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:46:00 +0200 Subject: [PATCH 06/18] - fix thread-related imports --- src/Products/ZCatalog/plan.py | 6 +----- src/Products/ZCatalog/tests/test_plan.py | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Products/ZCatalog/plan.py b/src/Products/ZCatalog/plan.py index be504517..d7685a63 100644 --- a/src/Products/ZCatalog/plan.py +++ b/src/Products/ZCatalog/plan.py @@ -17,6 +17,7 @@ from collections import namedtuple from logging import getLogger from os import environ +from six.moves._thread import allocate_lock from Acquisition import aq_base @@ -25,11 +26,6 @@ from Products.PluginIndexes.interfaces import IUniqueValueIndex -try: - from _thread import allocate_lock -except ImportError: # PY2 - from thread import allocate_lock - MAX_DISTINCT_VALUES = 10 REFRESH_RATE = 100 diff --git a/src/Products/ZCatalog/tests/test_plan.py b/src/Products/ZCatalog/tests/test_plan.py index 28167bc7..dc0ddba0 100644 --- a/src/Products/ZCatalog/tests/test_plan.py +++ b/src/Products/ZCatalog/tests/test_plan.py @@ -13,7 +13,7 @@ import os import os.path -from thread import LockType +from six.moves._thread import LockType import time import unittest From 07d2951ebf21f7ed991710ca966f509154055257 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:46:46 +0200 Subject: [PATCH 07/18] - Python 3 no longer has sys.maxint --- src/Products/ZCatalog/tests/test_zodb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/ZCatalog/tests/test_zodb.py b/src/Products/ZCatalog/tests/test_zodb.py index 2c8d7b1f..0fe6b68e 100644 --- a/src/Products/ZCatalog/tests/test_zodb.py +++ b/src/Products/ZCatalog/tests/test_zodb.py @@ -57,7 +57,7 @@ def tearDown(cls): @staticmethod def _make_dummy(): - num = random.randint(0, sys.maxint) + num = random.randint(0, sys.maxsize) return ZDummy(num) @staticmethod From 6eb03d9d8f9898634519ec3e27102f9ddc7b8ffa Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:48:18 +0200 Subject: [PATCH 08/18] - Under Python 3, the locale flag cannot be used on string patterns --- src/Products/ZCTextIndex/HTMLSplitter.py | 4 ++-- src/Products/ZCTextIndex/Lexicon.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Products/ZCTextIndex/HTMLSplitter.py b/src/Products/ZCTextIndex/HTMLSplitter.py index a0324d2e..59514b17 100644 --- a/src/Products/ZCTextIndex/HTMLSplitter.py +++ b/src/Products/ZCTextIndex/HTMLSplitter.py @@ -22,7 +22,7 @@ @implementer(ISplitter) class HTMLWordSplitter(object): - def process(self, text, wordpat=r"(?L)\w+"): + def process(self, text, wordpat=r"\w+"): splat = [] for t in text: splat += self._split(t, wordpat) @@ -30,7 +30,7 @@ def process(self, text, wordpat=r"(?L)\w+"): def processGlob(self, text): # see Lexicon.globToWordIds() - return self.process(text, r"(?L)\w+[\w*?]*") + return self.process(text, r"\w+[\w*?]*") def _split(self, text, wordpat): text = text.lower() diff --git a/src/Products/ZCTextIndex/Lexicon.py b/src/Products/ZCTextIndex/Lexicon.py index 95e8f57f..34debf60 100644 --- a/src/Products/ZCTextIndex/Lexicon.py +++ b/src/Products/ZCTextIndex/Lexicon.py @@ -181,8 +181,8 @@ def _text2list(text): class Splitter(object): import re - rx = re.compile(br"(?L)\w+") - rxGlob = re.compile(br"(?L)\w+[\w*?]*") # See globToWordIds() above + rx = re.compile(r"\w+") + rxGlob = re.compile(r"\w+[\w*?]*") # See globToWordIds() above def process(self, lst): result = [] From 2b47570d577f1264cf0a17e66330de95ef4b9be1 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:49:51 +0200 Subject: [PATCH 09/18] - convert test tring to bytes --- src/Products/ZCTextIndex/tests/testQueryParser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Products/ZCTextIndex/tests/testQueryParser.py b/src/Products/ZCTextIndex/tests/testQueryParser.py index 6cd4cd10..243806be 100644 --- a/src/Products/ZCTextIndex/tests/testQueryParser.py +++ b/src/Products/ZCTextIndex/tests/testQueryParser.py @@ -12,6 +12,7 @@ # ############################################################################## +import six from unittest import TestCase @@ -221,7 +222,7 @@ def test024(self): # Split by UTF-8 fullwidth space from Products.ZCTextIndex.ParseTree import AndNode from Products.ZCTextIndex.ParseTree import AtomNode - self.expect("foo\xe3\x80\x80bar", + self.expect(six.b("foo\xe3\x80\x80bar"), AndNode([AtomNode("foo"), AtomNode("bar")])) def test025(self): From 357206d74ce4ce45a155f0e51472527b46369fb6 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sat, 13 May 2017 18:50:38 +0200 Subject: [PATCH 10/18] - fix sorting --- src/Products/ZCTextIndex/tests/testSetOps.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Products/ZCTextIndex/tests/testSetOps.py b/src/Products/ZCTextIndex/tests/testSetOps.py index b23f1dee..fa2ef5b4 100644 --- a/src/Products/ZCTextIndex/tests/testSetOps.py +++ b/src/Products/ZCTextIndex/tests/testSetOps.py @@ -98,8 +98,7 @@ def testMany(self): t[key] = N * i + j L.append((t, i + 1)) random.shuffle(L) - allkeys = allkeys.keys() - allkeys.sort() + allkeys = sorted(allkeys.keys()) # Test the union. expected = [] From 94eb5e2d91f382c0ad5ad09c3fbc3209c57afcdf Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sun, 14 May 2017 10:36:19 +0200 Subject: [PATCH 11/18] - work around sorting behavior change between Python 2 and Python 3 --- src/Products/ZCatalog/ZCatalog.py | 2 +- src/Products/ZCatalog/plan.py | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Products/ZCatalog/ZCatalog.py b/src/Products/ZCatalog/ZCatalog.py index f3785913..0a3097d9 100644 --- a/src/Products/ZCatalog/ZCatalog.py +++ b/src/Products/ZCatalog/ZCatalog.py @@ -882,7 +882,7 @@ def getCatalogPlan(self): output.append('queryplan = {') for cid, plan in sorted(pmap.items()): output.append(' %s: {' % repr(cid)) - for querykey, details in sorted(plan.items()): + for querykey, details in plan.items(): if isinstance(details, (frozenset, set)): output.append(' %r: %r,' % (querykey, details)) else: diff --git a/src/Products/ZCatalog/plan.py b/src/Products/ZCatalog/plan.py index d7685a63..a6fd2925 100644 --- a/src/Products/ZCatalog/plan.py +++ b/src/Products/ZCatalog/plan.py @@ -229,22 +229,28 @@ def make_key(self, query): return None valueindexes = self.valueindexes() - key = keys = query.keys() + query_keys = set(query.keys()) + key = set() - values = [name for name in keys if name in valueindexes] + # Only consider elements common to the valueindexes and the keys + values = query_keys & valueindexes if values: # If we have indexes whose values should be considered, we first # preserve all normal indexes and then add the keys whose values # matter including their value into the key - key = [name for name in keys if name not in values] + key = query_keys - values for name in values: v = query.get(name, []) # We need to make sure the key is immutable, # repr() is an easy way to do this without imposing # restrictions on the types of values. - key.append((name, repr(v))) + key.add((name, repr(v))) - return tuple(sorted(key)) + # Workaround: Python 2.x accepted different types as sort key + # for the sorted builtin. Python 3 only sorts on identical types. + tuple_keys = key - set([x for x in key if not isinstance(x, tuple)]) + str_keys = key - tuple_keys + return tuple(sorted(str_keys)) + tuple(sorted(tuple_keys)) def plan(self): benchmark = PriorityMap.get_entry(self.cid, self.key) From 1df89e8e3afb31f380c2ddfbf7179a27674b2831 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sun, 14 May 2017 12:07:08 +0200 Subject: [PATCH 12/18] - Python 2-compatible workaround for regex locale flag change --- src/Products/ZCTextIndex/HTMLSplitter.py | 13 +++++++++++-- src/Products/ZCTextIndex/Lexicon.py | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Products/ZCTextIndex/HTMLSplitter.py b/src/Products/ZCTextIndex/HTMLSplitter.py index 59514b17..53dabc96 100644 --- a/src/Products/ZCTextIndex/HTMLSplitter.py +++ b/src/Products/ZCTextIndex/HTMLSplitter.py @@ -12,17 +12,26 @@ # ############################################################################## import re +import six from zope.interface import implementer from Products.ZCTextIndex.interfaces import ISplitter from Products.ZCTextIndex.PipelineFactory import element_factory +if six.PY2: + word_pattern = r"(?L)\w+" + glob_pattern = r"(?L)\w+[\w*?]*" +else: + # in Python 3, the locale flag can only be applied to bytes patterns + word_pattern = r"\w+" + glob_pattern = r"\w+[\w*?]*" + @implementer(ISplitter) class HTMLWordSplitter(object): - def process(self, text, wordpat=r"\w+"): + def process(self, text, wordpat=word_pattern): splat = [] for t in text: splat += self._split(t, wordpat) @@ -30,7 +39,7 @@ def process(self, text, wordpat=r"\w+"): def processGlob(self, text): # see Lexicon.globToWordIds() - return self.process(text, r"\w+[\w*?]*") + return self.process(text, glob_pattern) def _split(self, text, wordpat): text = text.lower() diff --git a/src/Products/ZCTextIndex/Lexicon.py b/src/Products/ZCTextIndex/Lexicon.py index 34debf60..d2597712 100644 --- a/src/Products/ZCTextIndex/Lexicon.py +++ b/src/Products/ZCTextIndex/Lexicon.py @@ -16,6 +16,7 @@ from random import randrange import re +import six from BTrees.IOBTree import IOBTree from BTrees.OIBTree import OIBTree @@ -181,8 +182,12 @@ def _text2list(text): class Splitter(object): import re - rx = re.compile(r"\w+") - rxGlob = re.compile(r"\w+[\w*?]*") # See globToWordIds() above + if six.PY2: + rx = re.compile(br"(?L)\w+") + rxGlob = re.compile(br"(?L)\w+[\w*?]*") + else: + rx = re.compile(r"\w+") + rxGlob = re.compile(r"\w+[\w*?]*") # See globToWordIds() above def process(self, lst): result = [] From fa4527457b5db1f98b4072026357a7306277a925 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Sun, 14 May 2017 12:07:56 +0200 Subject: [PATCH 13/18] - fix broken plan.make_key by only keeping the sorting workaround --- src/Products/ZCatalog/plan.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Products/ZCatalog/plan.py b/src/Products/ZCatalog/plan.py index a6fd2925..e342458a 100644 --- a/src/Products/ZCatalog/plan.py +++ b/src/Products/ZCatalog/plan.py @@ -229,27 +229,25 @@ def make_key(self, query): return None valueindexes = self.valueindexes() - query_keys = set(query.keys()) - key = set() + key = keys = query.keys() - # Only consider elements common to the valueindexes and the keys - values = query_keys & valueindexes + values = [name for name in keys if name in valueindexes] if values: # If we have indexes whose values should be considered, we first # preserve all normal indexes and then add the keys whose values # matter including their value into the key - key = query_keys - values + key = [name for name in keys if name not in values] for name in values: v = query.get(name, []) # We need to make sure the key is immutable, # repr() is an easy way to do this without imposing # restrictions on the types of values. - key.add((name, repr(v))) + key.append((name, repr(v))) # Workaround: Python 2.x accepted different types as sort key # for the sorted builtin. Python 3 only sorts on identical types. - tuple_keys = key - set([x for x in key if not isinstance(x, tuple)]) - str_keys = key - tuple_keys + tuple_keys = set(key) - set([x for x in key if not isinstance(x, tuple)]) + str_keys = set(key) - tuple_keys return tuple(sorted(str_keys)) + tuple(sorted(tuple_keys)) def plan(self): From 7b9bf747a2ec5e84d84d134a2fe8db7f6040d04e Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Mon, 15 May 2017 19:34:06 +0200 Subject: [PATCH 14/18] - add a tox configuration --- .gitignore | 1 + buildout.cfg | 8 ++++++-- tox.ini | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index c0c6c2e4..0ae1f69f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.egg-info .installed.cfg .mr.developer.cfg +.tox bin/ build/ develop-eggs/ diff --git a/buildout.cfg b/buildout.cfg index 813a41cd..afa2e4cd 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -1,5 +1,4 @@ [buildout] -[buildout] extensions = mr.developer extends = @@ -7,7 +6,7 @@ extends = https://raw.githubusercontent.com/zopefoundation/Zope/master/versions-prod.cfg develop = . -parts = interpreter test +parts = interpreter test tox auto-checkout = Zope2 @@ -26,3 +25,8 @@ eggs = Products.ZCatalog [test] recipe = zc.recipe.testrunner eggs = Products.ZCatalog + +[tox] +recipe = zc.recipe.egg +eggs = + tox diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..19997c9e --- /dev/null +++ b/tox.ini @@ -0,0 +1,15 @@ +[tox] +envlist = + py27, + py34, + py35, + py36, + +[testenv] +commands = + {envbindir}/buildout -c {toxinidir}/buildout.cfg tox:env={envname} bootstrap + {envbindir}/buildout -c {toxinidir}/buildout.cfg tox:env={envname} install test + {toxinidir}/bin/test +skip_install = true +deps = + zc.buildout From 0dea78bdb8b60348a70a8b93531b038c4442542b Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Mon, 15 May 2017 19:40:31 +0200 Subject: [PATCH 15/18] - wrap map calls in list calls --- src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py | 2 +- src/Products/PluginIndexes/DateRangeIndex/tests.py | 2 +- src/Products/PluginIndexes/FieldIndex/tests.py | 4 ++-- src/Products/PluginIndexes/KeywordIndex/tests.py | 4 ++-- src/Products/PluginIndexes/TopicIndex/FilteredSet.py | 2 +- src/Products/PluginIndexes/unindex.py | 4 ++-- src/Products/ZCTextIndex/WidCode.py | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py b/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py index 07f6a59e..d9ec0f1c 100644 --- a/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py +++ b/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py @@ -99,7 +99,7 @@ def update(self, d): self.__setitem__(key, val) def values(self): - return map(self.get, self._keys) + return list(map(self.get, self._keys)) class Component(object): diff --git a/src/Products/PluginIndexes/DateRangeIndex/tests.py b/src/Products/PluginIndexes/DateRangeIndex/tests.py index 4fffeea4..979f8bc8 100644 --- a/src/Products/PluginIndexes/DateRangeIndex/tests.py +++ b/src/Products/PluginIndexes/DateRangeIndex/tests.py @@ -94,7 +94,7 @@ def checkApply(): result = result.keys() assert used == (index._since_field, index._until_field) assert len(result) == len(expectedValues), \ - '%s | %s' % (map(None, result), expectedValues) + '%s | %s' % (list(result), expectedValues) for k, v in expectedValues: assert k in result return result, used diff --git a/src/Products/PluginIndexes/FieldIndex/tests.py b/src/Products/PluginIndexes/FieldIndex/tests.py index c1a4192c..b5b3c05e 100644 --- a/src/Products/PluginIndexes/FieldIndex/tests.py +++ b/src/Products/PluginIndexes/FieldIndex/tests.py @@ -114,9 +114,9 @@ def checkApply(): result = result.keys() assert used == ('foo', ) assert len(result) == len(expectedValues), \ - '%s | %s' % (map(None, result), expectedValues) + '%s | %s' % (list(result), expectedValues) for k, v in expectedValues: - assert k in result + self.assertTrue(k in result) index = self._index diff --git a/src/Products/PluginIndexes/KeywordIndex/tests.py b/src/Products/PluginIndexes/KeywordIndex/tests.py index 1fd35a1f..415059cc 100644 --- a/src/Products/PluginIndexes/KeywordIndex/tests.py +++ b/src/Products/PluginIndexes/KeywordIndex/tests.py @@ -101,8 +101,8 @@ def checkApply(): result, used = self._index._apply_index(req) assert used == ('foo', ) assert len(result) == len(expectedValues), \ - '%s | %s' % (map(None, result), - map(lambda x: x[0], expectedValues)) + '%s | %s' % (list(result), + list(map(lambda x: x[0], expectedValues))) if hasattr(result, 'keys'): result = result.keys() diff --git a/src/Products/PluginIndexes/TopicIndex/FilteredSet.py b/src/Products/PluginIndexes/TopicIndex/FilteredSet.py index 857fd806..5b7cac28 100644 --- a/src/Products/PluginIndexes/TopicIndex/FilteredSet.py +++ b/src/Products/PluginIndexes/TopicIndex/FilteredSet.py @@ -65,7 +65,7 @@ def setExpression(self, expr): self.expr = expr def __repr__(self): - return '%s: (%s) %s' % (self.id, self.expr, map(None, self.ids)) + return '%s: (%s) %s' % (self.id, self.expr, list(map(None, self.ids))) __str__ = __repr__ diff --git a/src/Products/PluginIndexes/unindex.py b/src/Products/PluginIndexes/unindex.py index 5c95388b..409f401f 100644 --- a/src/Products/PluginIndexes/unindex.py +++ b/src/Products/PluginIndexes/unindex.py @@ -453,7 +453,7 @@ def query_index(self, record, resultset=None): cached = IISet((cached, )) if not_parm: - not_parm = map(self._convert, not_parm) + not_parm = list(map(self._convert, not_parm)) exclude = self._apply_not(not_parm, resultset) cached = difference(cached, exclude) @@ -461,7 +461,7 @@ def query_index(self, record, resultset=None): if not record.keys and not_parm: # convert into indexed format - not_parm = map(self._convert, not_parm) + not_parm = list(map(self._convert, not_parm)) # we have only a 'not' query record.keys = [k for k in index.keys() if k not in not_parm] else: diff --git a/src/Products/ZCTextIndex/WidCode.py b/src/Products/ZCTextIndex/WidCode.py index 3df325e9..0176dee3 100644 --- a/src/Products/ZCTextIndex/WidCode.py +++ b/src/Products/ZCTextIndex/WidCode.py @@ -98,11 +98,11 @@ def _decode(s): # See comment in decode(). This is here to allow a trick to work. return 0 if len(s) == 3: - a, b, c = map(ord, s) + a, b, c = list(map(ord, s)) assert a & 0x80 == 0x80 and not b & 0x80 and not c & 0x80 return ((a & 0x7F) << 14) | (b << 7) | c assert len(s) == 4, repr(s) - a, b, c, d = map(ord, s) + a, b, c, d = list(map(ord, s)) assert a & 0x80 == 0x80 and not b & 0x80 and not c & 0x80 and not d & 0x80 return ((a & 0x7F) << 21) | (b << 14) | (c << 7) | d From a067aec48773410b8908960a8f308ce71da0c552 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Mon, 15 May 2017 19:53:55 +0200 Subject: [PATCH 16/18] Run tests with single type of key only to prevent Python 3 sorting blowups --- .../PluginIndexes/FieldIndex/tests.py | 15 +++-------- .../PluginIndexes/KeywordIndex/tests.py | 27 +++++-------------- src/Products/PluginIndexes/UUIDIndex/tests.py | 8 +++--- 3 files changed, 14 insertions(+), 36 deletions(-) diff --git a/src/Products/PluginIndexes/FieldIndex/tests.py b/src/Products/PluginIndexes/FieldIndex/tests.py index b5b3c05e..1b8c0fa0 100644 --- a/src/Products/PluginIndexes/FieldIndex/tests.py +++ b/src/Products/PluginIndexes/FieldIndex/tests.py @@ -67,7 +67,7 @@ def setUp(self): (4, Dummy('abcd')), (5, Dummy('abce')), (6, Dummy('abce')), - (7, Dummy(0))] + (7, Dummy('0'))] self._forward = {} self._backward = {} for k, v in self._values: @@ -85,7 +85,7 @@ def setUp(self): 'range': 'max'}} self._max_req_n = {'foo': {'query': 'abc', 'range': 'max', - 'not': ['a', 'b', 0]}} + 'not': ['a', 'b', '0']}} self._range_req = {'foo': {'query': ('abc', 'abcd'), 'range': 'min:max'}} self._range_ren = {'foo': {'query': ('abc', 'abcd'), @@ -94,11 +94,11 @@ def setUp(self): self._range_non = {'foo': {'query': ('a', 'aa'), 'range': 'min:max', 'not': 'a'}} - self._zero_req = {'foo': 0} + self._zero_req = {'foo': '0'} self._not_1 = {'foo': {'query': 'a', 'not': 'a'}} self._not_2 = {'foo': {'query': ['a', 'ab'], 'not': 'a'}} self._not_3 = {'foo': {'not': 'a'}} - self._not_4 = {'foo': {'not': [0]}} + self._not_4 = {'foo': {'not': ['0']}} self._not_5 = {'foo': {'not': ['a', 'b']}} self._not_6 = {'foo': 'a', 'bar': {'query': 123, 'not': 1}} @@ -210,13 +210,6 @@ def testPopulated(self): self._checkApply(self._not_5, values[1:]) self._checkApply(self._not_6, values[0:1]) - def testZero(self): - # Make sure 0 gets indexed. - self._populateIndex() - values = self._values - self._checkApply(self._zero_req, values[-1:]) - assert 0 in self._index.uniqueValues('foo') - def testNone(self): # Make sure None is ignored. self._index.index_object(10, Dummy(None)) diff --git a/src/Products/PluginIndexes/KeywordIndex/tests.py b/src/Products/PluginIndexes/KeywordIndex/tests.py index 415059cc..44216913 100644 --- a/src/Products/PluginIndexes/KeywordIndex/tests.py +++ b/src/Products/PluginIndexes/KeywordIndex/tests.py @@ -31,15 +31,6 @@ def __str__(self): __repr__ = __str__ -def sortedUnique(seq): - unique = {} - for i in seq: - unique[i] = None - unique = unique.keys() - unique.sort() - return unique - - class TestKeywordIndex(unittest.TestCase): _old_log_write = None @@ -75,20 +66,20 @@ def setUp(self): (4, Dummy(['a', 'b', 'c', 'd'])), (5, Dummy(['a', 'b', 'c', 'e'])), (6, Dummy(['a', 'b', 'c', 'e', 'f'])), - (7, Dummy([0])), + (7, Dummy(['0'])), ] self._noop_req = {'bar': 123} self._all_req = {'foo': ['a']} self._some_req = {'foo': ['e']} self._overlap_req = {'foo': ['c', 'e']} self._string_req = {'foo': 'a'} - self._zero_req = {'foo': [0]} + self._zero_req = {'foo': ['0']} self._not_1 = {'foo': {'query': 'f', 'not': 'f'}} self._not_2 = {'foo': {'query': ['e', 'f'], 'not': 'f'}} - self._not_3 = {'foo': {'not': 0}} - self._not_4 = {'foo': {'not': [0, 'e']}} - self._not_5 = {'foo': {'not': [0, 'no-value']}} + self._not_3 = {'foo': {'not': '0'}} + self._not_4 = {'foo': {'not': ['0', 'e']}} + self._not_5 = {'foo': {'not': ['0', 'no-value']}} self._not_6 = {'foo': 'c', 'bar': {'query': 123, 'not': 1}} def _populateIndex(self): @@ -176,7 +167,7 @@ def testPopulated(self): for k, v in values: entry = self._index.getEntryForObject(k) entry.sort() - kw = sortedUnique(v.foo()) + kw = sorted(set(v.foo())) self.assertEqual(entry, kw) assert len(list(self._index.uniqueValues('foo'))) == len(values) - 1 @@ -194,12 +185,6 @@ def testPopulated(self): self._checkApply(self._not_5, values[:7]) self._checkApply(self._not_6, values[2:7]) - def testZero(self): - self._populateIndex() - values = self._values - self._checkApply(self._zero_req, values[-1:]) - assert 0 in self._index.uniqueValues('foo') - def testReindexChange(self): self._populateIndex() values = self._values diff --git a/src/Products/PluginIndexes/UUIDIndex/tests.py b/src/Products/PluginIndexes/UUIDIndex/tests.py index 7934b966..e7dee9c5 100644 --- a/src/Products/PluginIndexes/UUIDIndex/tests.py +++ b/src/Products/PluginIndexes/UUIDIndex/tests.py @@ -60,9 +60,9 @@ def setUp(self): self._values = [ (0, Dummy('a')), (1, Dummy('ab')), - (2, Dummy(123)), - (3, Dummy(234)), - (4, Dummy(0))] + (2, Dummy('123')), + (3, Dummy('234')), + (4, Dummy('0'))] self._forward = {} self._backward = {} for k, v in self._values: @@ -137,7 +137,7 @@ def test_populated(self): self.assertEqual(self._index.getEntryForObject(k), v.foo()) self._checkApply({'foo': 'a'}, [values[0]]) - self._checkApply({'foo': 0}, [values[4]]) + self._checkApply({'foo': '0'}, [values[4]]) self._checkApply({'foo': ['a', 'ab']}, values[:2]) def test_none(self): From 7ab5ff83c6cd6c6efd0e1b756a20fccb4d8a3b01 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Mon, 15 May 2017 19:54:25 +0200 Subject: [PATCH 17/18] Package maintenance - Note Python 3 compatibility in package classifiers - Add Python 3 environments to travis configuration - Update Manifest file - Note Zope 4 / Python 3 compatibility in the Changelog --- .travis.yml | 4 ++++ CHANGES.rst | 4 ++++ MANIFEST.in | 4 ++++ setup.py | 8 +++++++- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a6682104..6c5eb98c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,11 @@ language: python sudo: false python: - 2.7 + - 3.4 + - 3.5 + - 3.6 install: + - pip install -U setuptools==33.1.1 - python bootstrap.py - bin/buildout script: diff --git a/CHANGES.rst b/CHANGES.rst index cd28aa3b..009a25b8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,10 @@ Changelog 4.0a4 (unreleased) ------------------ +- Python 3 compatibility + +- Target use with Zope 4: no longer support 2.13.x. + - `five.globalrequest` got merged into Zope2 itself. - Use aq_inner before aq_parent at some places to safely get the parent diff --git a/MANIFEST.in b/MANIFEST.in index 03f7cb18..db3099f0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,11 @@ include *.txt include *.rst +include *.cfg +exclude .*.cfg recursive-include src * global-exclude *.pyc global-exclude *.pyo + +include *.py diff --git a/setup.py b/setup.py index 0dcec80c..a270de10 100644 --- a/setup.py +++ b/setup.py @@ -34,9 +34,15 @@ "License :: OSI Approved :: Zope Public License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 2 :: Only", + "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Internet :: WWW/HTTP :: Indexing/Search", ], install_requires=[ 'setuptools', From 43c332aa9616024d0992068d874b11e32d690358 Mon Sep 17 00:00:00 2001 From: Jens Vagelpohl Date: Mon, 15 May 2017 20:00:29 +0200 Subject: [PATCH 18/18] Remove PyPy classifier [ci skip] --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index a270de10..ef25916d 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,6 @@ "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP :: Indexing/Search", ], install_requires=[