diff --git a/last_commit.txt b/last_commit.txt index 9bc3551071..e3168c28cb 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,36 +1,43 @@ -Repository: plone.folder +Repository: plone.z3cform Branch: refs/heads/master -Date: 2020-05-19T18:20:06+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.folder/commit/ffab7d1fe4684d0a273f946e529be3d86b1846c2 +Date: 2020-05-20T23:31:29+02:00 +Author: ale-rt (ale-rt) +Commit: https://github.com/plone/plone.z3cform/commit/d8c2a35b262eff5dd2c5cdc5a8fe1adfbc66d031 + +Zope 5 compatibility -optimize loop in moveObjectsByDelta +Copy the HTTPRequest._decode from Zope4 because it is has been removed +in Zope5 + +Fixes #13 Files changed: -A news/15.bugfix -M src/plone/folder/default.py -M src/plone/folder/partial.py +A news/.gitkeep +A news/13.bugfix +M src/plone/z3cform/tests.py +M src/plone/z3cform/z2.py -b'diff --git a/news/15.bugfix b/news/15.bugfix\nnew file mode 100644\nindex 0000000..a5ee223\n--- /dev/null\n+++ b/news/15.bugfix\n@@ -0,0 +1,3 @@\n+Micro-optimization of often called loop in moveObjectsByDelta.\n+``x in y`` is up to 1000 times faster if y is a set and not a list.\n+[jensens]\n\\ No newline at end of file\ndiff --git a/src/plone/folder/default.py b/src/plone/folder/default.py\nindex 768911b..84d4c8d 100644\n--- a/src/plone/folder/default.py\n+++ b/src/plone/folder/default.py\n@@ -81,8 +81,12 @@ def moveObjectsByDelta(\n if delta > 0:\n subset_ids.reverse()\n idx = 0\n+ # micro-optimization 1: set is 1000 time faster on contains than list\n+ subset_ids_as_set = set(subset_ids)\n+ # micro-optimization 2: speedup on lookup in bytecode\n+ order_getitem = order.__getitem__\n for i in range(len(order)):\n- if order[i] not in subset_ids:\n+ if order_getitem(i) not in subset_ids_as_set:\n continue\n obj_id = subset_ids[idx]\n try:\ndiff --git a/src/plone/folder/partial.py b/src/plone/folder/partial.py\nindex 6891dd2..5ef625d 100644\n--- a/src/plone/folder/partial.py\n+++ b/src/plone/folder/partial.py\n@@ -15,7 +15,7 @@\n \n @implementer(IExplicitOrdering)\n class PartialOrdering(object):\n- """ this implementation uses a list ot store order information on a\n+ """ this implementation uses a list to store order information on a\n regular attribute of the folderish object; explicit ordering\n is supported """\n adapts(IOrderableFolder)\n@@ -96,8 +96,10 @@ def moveObjectsByDelta(self, ids, delta, subset_ids=None,\n if delta > 0:\n subset_ids.reverse()\n idx = 0\n+ # micro-optimization: set is 1000 time faster on contains than list\n+ subset_ids_as_set = set(subset_ids)\n for i, value in enumerate(self.order):\n- if value in subset_ids:\n+ if value in subset_ids_as_set:\n id = subset_ids[idx]\n try:\n self.order[i] = id\n' +b'diff --git a/news/.gitkeep b/news/.gitkeep\nnew file mode 100644\nindex 0000000..8b13789\n--- /dev/null\n+++ b/news/.gitkeep\n@@ -0,0 +1 @@\n+\ndiff --git a/news/13.bugfix b/news/13.bugfix\nnew file mode 100644\nindex 0000000..3e3f1ae\n--- /dev/null\n+++ b/news/13.bugfix\n@@ -0,0 +1 @@\n+Copy the HTTPRequest._decode from Zope4 because it is going away in Zope5\ndiff --git a/src/plone/z3cform/tests.py b/src/plone/z3cform/tests.py\nindex 846ee2f..493a18f 100644\n--- a/src/plone/z3cform/tests.py\n+++ b/src/plone/z3cform/tests.py\n@@ -179,6 +179,39 @@ def check_output(self, want, got, optionflags):\n return doctest.OutputChecker.check_output(self, want, got, optionflags)\n \n \n+class Z2TestCase(unittest.TestCase):\n+ def test_recursive_decode(self):\n+ from plone.z3cform.z2 import _recursive_decode\n+\n+ form = _recursive_decode(\n+ {\n+ "foo": b"fo\\xc3\\xb8",\n+ "foo_list": [b"fo\\xc3\\xb8", "SPAM"],\n+ "foo_tuple": (b"fo\\xc3\\xb8", "HAM"),\n+ "foo_dict": {"foo": b"fo\\xc3\\xb8", "bar": "EGGS"},\n+ },\n+ "utf-8",\n+ )\n+ self.assertIsInstance(form["foo"], six.text_type)\n+ self.assertEqual(form["foo"], u"fo\xc3\xb8")\n+ self.assertIsInstance(form["foo_list"], list)\n+ self.assertIsInstance(form["foo_list"][0], six.text_type)\n+ self.assertIsInstance(form["foo_list"][1], six.text_type)\n+ self.assertEqual(form["foo_list"][0], u"fo\xc3\xb8")\n+ self.assertEqual(form["foo_list"][1], "SPAM")\n+ self.assertIsInstance(form["foo_tuple"], tuple)\n+ self.assertIsInstance(form["foo_tuple"][0], six.text_type)\n+ self.assertIsInstance(form["foo_tuple"][1], six.text_type)\n+ self.assertEqual(form["foo_tuple"][0], u"fo\xc3\xb8")\n+ self.assertEqual(form["foo_tuple"][1], "HAM")\n+ self.assertIsInstance(form["foo_dict"], dict)\n+ self.assertIsInstance(form["foo_dict"]["foo"], six.text_type)\n+ self.assertIsInstance(form["foo_dict"]["bar"], six.text_type)\n+ self.assertEqual(form["foo_dict"]["foo"], u"fo\xc3\xb8")\n+ self.assertEqual(form["foo_dict"]["bar"], "EGGS")\n+\n+\n+\n def test_suite():\n layout_txt = layered(\n doctest.DocFileSuite(\'layout.rst\', checker=Py23DocChecker()),\n@@ -214,8 +247,8 @@ def test_suite():\n doctest.DocFileSuite(\'traversal.txt\', checker=Py23DocChecker()),\n layer=FUNCTIONAL_TESTING,\n )\n-\n- return unittest.TestSuite(\n+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(Z2TestCase)\n+ suite.addTests(\n [\n layout_txt,\n inputs_txt,\n@@ -225,3 +258,4 @@ def test_suite():\n crud_py,\n ]\n )\n+ return suite\ndiff --git a/src/plone/z3cform/z2.py b/src/plone/z3cform/z2.py\nindex d575f98..abe9db8 100644\n--- a/src/plone/z3cform/z2.py\n+++ b/src/plone/z3cform/z2.py\n@@ -62,11 +62,25 @@ def processInputs(request, charsets=None):\n interface.alsoProvides(request, IProcessedRequest)\n \n \n+def _recursive_decode(value, charset):\n+ """Recursively look for string values and decode.\n+ """\n+ if isinstance(value, list):\n+ return [_recursive_decode(v, charset) for v in value]\n+ elif isinstance(value, tuple):\n+ return tuple(_recursive_decode(v, charset) for v in value)\n+ elif isinstance(value, dict):\n+ return {k: _recursive_decode(v, charset) for k, v in value.items()}\n+ elif isinstance(value, six.binary_type):\n+ return six.text_type(value, charset, \'replace\')\n+ return value\n+\n+\n def _decode(text, charsets):\n for charset in charsets:\n try:\n # decode recursively\n- return HTTPRequest._decode(text, charset)\n+ return _recursive_decode(text, charset)\n except (UnicodeError, AttributeError):\n pass\n return text\n' -Repository: plone.folder +Repository: plone.z3cform Branch: refs/heads/master -Date: 2020-05-19T20:24:04+02:00 -Author: agitator (agitator) -Commit: https://github.com/plone/plone.folder/commit/658ab7c020d9e8a856d64b68006eb7d39dbdfd18 +Date: 2020-05-21T23:37:26+02:00 +Author: Jens W. Klein (jensens) +Commit: https://github.com/plone/plone.z3cform/commit/93eb0f1be50f0318f7b03276d5f9617883028da4 -Merge pull request #15 from plone/optimize-moveObjectsByDelta +Merge pull request #14 from plone/13-recursive-decode -optimize loop in moveObjectsByDelta +Zope 5 compatibility Files changed: -A news/15.bugfix -M src/plone/folder/default.py -M src/plone/folder/partial.py +A news/.gitkeep +A news/13.bugfix +M src/plone/z3cform/tests.py +M src/plone/z3cform/z2.py -b'diff --git a/news/15.bugfix b/news/15.bugfix\nnew file mode 100644\nindex 0000000..a5ee223\n--- /dev/null\n+++ b/news/15.bugfix\n@@ -0,0 +1,3 @@\n+Micro-optimization of often called loop in moveObjectsByDelta.\n+``x in y`` is up to 1000 times faster if y is a set and not a list.\n+[jensens]\n\\ No newline at end of file\ndiff --git a/src/plone/folder/default.py b/src/plone/folder/default.py\nindex 768911b..84d4c8d 100644\n--- a/src/plone/folder/default.py\n+++ b/src/plone/folder/default.py\n@@ -81,8 +81,12 @@ def moveObjectsByDelta(\n if delta > 0:\n subset_ids.reverse()\n idx = 0\n+ # micro-optimization 1: set is 1000 time faster on contains than list\n+ subset_ids_as_set = set(subset_ids)\n+ # micro-optimization 2: speedup on lookup in bytecode\n+ order_getitem = order.__getitem__\n for i in range(len(order)):\n- if order[i] not in subset_ids:\n+ if order_getitem(i) not in subset_ids_as_set:\n continue\n obj_id = subset_ids[idx]\n try:\ndiff --git a/src/plone/folder/partial.py b/src/plone/folder/partial.py\nindex 6891dd2..5ef625d 100644\n--- a/src/plone/folder/partial.py\n+++ b/src/plone/folder/partial.py\n@@ -15,7 +15,7 @@\n \n @implementer(IExplicitOrdering)\n class PartialOrdering(object):\n- """ this implementation uses a list ot store order information on a\n+ """ this implementation uses a list to store order information on a\n regular attribute of the folderish object; explicit ordering\n is supported """\n adapts(IOrderableFolder)\n@@ -96,8 +96,10 @@ def moveObjectsByDelta(self, ids, delta, subset_ids=None,\n if delta > 0:\n subset_ids.reverse()\n idx = 0\n+ # micro-optimization: set is 1000 time faster on contains than list\n+ subset_ids_as_set = set(subset_ids)\n for i, value in enumerate(self.order):\n- if value in subset_ids:\n+ if value in subset_ids_as_set:\n id = subset_ids[idx]\n try:\n self.order[i] = id\n' +b'diff --git a/news/.gitkeep b/news/.gitkeep\nnew file mode 100644\nindex 0000000..8b13789\n--- /dev/null\n+++ b/news/.gitkeep\n@@ -0,0 +1 @@\n+\ndiff --git a/news/13.bugfix b/news/13.bugfix\nnew file mode 100644\nindex 0000000..3e3f1ae\n--- /dev/null\n+++ b/news/13.bugfix\n@@ -0,0 +1 @@\n+Copy the HTTPRequest._decode from Zope4 because it is going away in Zope5\ndiff --git a/src/plone/z3cform/tests.py b/src/plone/z3cform/tests.py\nindex 846ee2f..493a18f 100644\n--- a/src/plone/z3cform/tests.py\n+++ b/src/plone/z3cform/tests.py\n@@ -179,6 +179,39 @@ def check_output(self, want, got, optionflags):\n return doctest.OutputChecker.check_output(self, want, got, optionflags)\n \n \n+class Z2TestCase(unittest.TestCase):\n+ def test_recursive_decode(self):\n+ from plone.z3cform.z2 import _recursive_decode\n+\n+ form = _recursive_decode(\n+ {\n+ "foo": b"fo\\xc3\\xb8",\n+ "foo_list": [b"fo\\xc3\\xb8", "SPAM"],\n+ "foo_tuple": (b"fo\\xc3\\xb8", "HAM"),\n+ "foo_dict": {"foo": b"fo\\xc3\\xb8", "bar": "EGGS"},\n+ },\n+ "utf-8",\n+ )\n+ self.assertIsInstance(form["foo"], six.text_type)\n+ self.assertEqual(form["foo"], u"fo\xc3\xb8")\n+ self.assertIsInstance(form["foo_list"], list)\n+ self.assertIsInstance(form["foo_list"][0], six.text_type)\n+ self.assertIsInstance(form["foo_list"][1], six.text_type)\n+ self.assertEqual(form["foo_list"][0], u"fo\xc3\xb8")\n+ self.assertEqual(form["foo_list"][1], "SPAM")\n+ self.assertIsInstance(form["foo_tuple"], tuple)\n+ self.assertIsInstance(form["foo_tuple"][0], six.text_type)\n+ self.assertIsInstance(form["foo_tuple"][1], six.text_type)\n+ self.assertEqual(form["foo_tuple"][0], u"fo\xc3\xb8")\n+ self.assertEqual(form["foo_tuple"][1], "HAM")\n+ self.assertIsInstance(form["foo_dict"], dict)\n+ self.assertIsInstance(form["foo_dict"]["foo"], six.text_type)\n+ self.assertIsInstance(form["foo_dict"]["bar"], six.text_type)\n+ self.assertEqual(form["foo_dict"]["foo"], u"fo\xc3\xb8")\n+ self.assertEqual(form["foo_dict"]["bar"], "EGGS")\n+\n+\n+\n def test_suite():\n layout_txt = layered(\n doctest.DocFileSuite(\'layout.rst\', checker=Py23DocChecker()),\n@@ -214,8 +247,8 @@ def test_suite():\n doctest.DocFileSuite(\'traversal.txt\', checker=Py23DocChecker()),\n layer=FUNCTIONAL_TESTING,\n )\n-\n- return unittest.TestSuite(\n+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(Z2TestCase)\n+ suite.addTests(\n [\n layout_txt,\n inputs_txt,\n@@ -225,3 +258,4 @@ def test_suite():\n crud_py,\n ]\n )\n+ return suite\ndiff --git a/src/plone/z3cform/z2.py b/src/plone/z3cform/z2.py\nindex d575f98..abe9db8 100644\n--- a/src/plone/z3cform/z2.py\n+++ b/src/plone/z3cform/z2.py\n@@ -62,11 +62,25 @@ def processInputs(request, charsets=None):\n interface.alsoProvides(request, IProcessedRequest)\n \n \n+def _recursive_decode(value, charset):\n+ """Recursively look for string values and decode.\n+ """\n+ if isinstance(value, list):\n+ return [_recursive_decode(v, charset) for v in value]\n+ elif isinstance(value, tuple):\n+ return tuple(_recursive_decode(v, charset) for v in value)\n+ elif isinstance(value, dict):\n+ return {k: _recursive_decode(v, charset) for k, v in value.items()}\n+ elif isinstance(value, six.binary_type):\n+ return six.text_type(value, charset, \'replace\')\n+ return value\n+\n+\n def _decode(text, charsets):\n for charset in charsets:\n try:\n # decode recursively\n- return HTTPRequest._decode(text, charset)\n+ return _recursive_decode(text, charset)\n except (UnicodeError, AttributeError):\n pass\n return text\n'