aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/tests/pysidetest/enum_test.py
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2022-06-21 10:22:04 +0200
committerChristian Tismer <tismer@stackless.com>2022-07-14 17:21:31 +0200
commitf92b7dcac9537fb091dc15d3ee218f1984d8abef (patch)
tree1bad4ea3cfa9d7c5eeb88450df38a103a7402e99 /sources/pyside6/tests/pysidetest/enum_test.py
parentda2cf031521815a9559ca784beadb70c7a2852d9 (diff)
PyEnum: Increase compatibility by allowing defaults and old flag names
This patch supports to write Qt.Alignment() instead of Qt.AlignmentFlag(0) Also supported is Qt.AlignmentFlag() which is mapped to Qt.AlignmentFlag(0) This trickery was quite involved since the Python opcodes needed to be analyzed if we have a parameterless call. Only in that case, we insert a partial object which supplies the missing value=0 default. Changing the implementation of PyEnum was not desired because this is highly complicated, not portable and even not possible. The change has been tested with Python 3.6 to 3.11.0b3 . [ChangeLog][shiboken6] The new Python enums are made as compatible to the old ones as possible. It is again allowed to use Qt.Alignment() instead of Qt.AlignmentFlag(0), and a default of 0 is always allowed. Change-Id: If6a93f8210ff6cae4e38251420e1ad5fffbe42cb Pick-to: 6.3 Task-number: PYSIDE-1735 Reviewed-by: Christian Tismer <tismer@stackless.com>
Diffstat (limited to 'sources/pyside6/tests/pysidetest/enum_test.py')
-rw-r--r--sources/pyside6/tests/pysidetest/enum_test.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/sources/pyside6/tests/pysidetest/enum_test.py b/sources/pyside6/tests/pysidetest/enum_test.py
index 34f90702a..158faf37c 100644
--- a/sources/pyside6/tests/pysidetest/enum_test.py
+++ b/sources/pyside6/tests/pysidetest/enum_test.py
@@ -13,6 +13,7 @@ init_test_paths(True)
from PySide6.QtCore import Qt
from testbinding import Enum1, TestObjectWithoutNamespace
+import dis
class ListConnectionTest(unittest.TestCase):
@@ -44,6 +45,120 @@ class ListConnectionTest(unittest.TestCase):
self.assertFalse(Qt.AlignBottom < Qt.AlignHCenter)
self.assertTrue(Qt.AlignBottom > Qt.AlignHCenter)
+# PYSIDE-1735: We are testing that opcodes do what they are supposed to do.
+# This is needed in the PyEnum forgiveness mode where we need
+# to introspect the code if an Enum was called with no args.
+class InvestigateOpcodesTest(unittest.TestCase):
+
+ def probe_function1(self):
+ x = Qt.Alignment
+
+ def probe_function2(self):
+ x = Qt.Alignment()
+
+ @staticmethod
+ def read_code(func, **kw):
+ return list(instr[:3] for instr in dis.Bytecode(func, **kw))
+
+ @staticmethod
+ def get_sizes(func, **kw):
+ ops = list((instr.opname, instr.offset) for instr in dis.Bytecode(func, **kw))
+ res = []
+ for idx in range(1, len(ops)):
+ res.append((ops[idx - 1][0], ops[idx][1] - ops[idx - 1][1]))
+ return sorted(res, key=lambda x: (x[1], x[0]))
+
+ def testByteCode(self):
+ # opname, opcode, arg
+ result_1 = [('LOAD_GLOBAL', 116, 0),
+ ('LOAD_ATTR', 106, 1),
+ ('STORE_FAST', 125, 1),
+ ('LOAD_CONST', 100, 0),
+ ('RETURN_VALUE', 83, None)]
+
+ result_2 = [('LOAD_GLOBAL', 116, 0),
+ ('LOAD_METHOD', 160, 1),
+ ('CALL_METHOD', 161, 0),
+ ('STORE_FAST', 125, 1),
+ ('LOAD_CONST', 100, 0),
+ ('RETURN_VALUE', 83, None)]
+
+ if sys.version_info[:2] <= (3, 6):
+
+ result_2 = [('LOAD_GLOBAL', 116, 0),
+ ('LOAD_ATTR', 106, 1),
+ ('CALL_FUNCTION', 131, 0),
+ ('STORE_FAST', 125, 1),
+ ('LOAD_CONST', 100, 0),
+ ('RETURN_VALUE', 83, None)]
+
+ if sys.version_info[:2] >= (3, 11):
+ # Note: Python 3.11 is a bit more complex because it can optimize itself.
+ # Opcodes are a bit different, and a hidden second code object is used.
+ # We investigate this a bit, because we want to be warned when things change.
+ QUICKENING_WARMUP_DELAY = 8
+
+ result_1 = [('RESUME', 151, 0),
+ ('LOAD_GLOBAL', 116, 0),
+ ('LOAD_ATTR', 106, 1),
+ ('STORE_FAST', 125, 1),
+ ('LOAD_CONST', 100, 0),
+ ('RETURN_VALUE', 83, None)]
+
+ result_2 = [('RESUME', 151, 0),
+ ('LOAD_GLOBAL', 116, 1),
+ ('LOAD_ATTR', 106, 1),
+ ('PRECALL', 166, 0),
+ ('CALL', 171, 0),
+ ('STORE_FAST', 125, 1),
+ ('LOAD_CONST', 100, 0),
+ ('RETURN_VALUE', 83, None)]
+
+ sizes_2 = [('LOAD_CONST', 2),
+ ('RESUME', 2),
+ ('STORE_FAST', 2),
+ ('PRECALL', 4),
+ ('CALL', 10),
+ ('LOAD_ATTR', 10),
+ ('LOAD_GLOBAL', 12)]
+
+ self.assertEqual(self.read_code(self.probe_function2, adaptive=True), result_2)
+ self.assertEqual(self.get_sizes(self.probe_function2, adaptive=True), sizes_2)
+
+ @staticmethod
+ def code_quicken(f, times):
+ # running the code triggers acceleration after some runs.
+ for _ in range(times):
+ f()
+
+ code_quicken(self.probe_function2, QUICKENING_WARMUP_DELAY-1)
+ self.assertEqual(self.read_code(self.probe_function2, adaptive=True), result_2)
+ self.assertEqual(self.get_sizes(self.probe_function2, adaptive=True), sizes_2)
+
+ result_3 = [('RESUME_QUICK', 150, 0),
+ ('LOAD_GLOBAL_MODULE', 55, 1),
+ ('LOAD_ATTR_ADAPTIVE', 39, 1),
+ ('PRECALL_ADAPTIVE', 64, 0),
+ ('CALL_ADAPTIVE', 22, 0),
+ ('STORE_FAST', 125, 1),
+ ('LOAD_CONST', 100, 0),
+ ('RETURN_VALUE', 83, None)]
+
+ sizes_3 = [('LOAD_CONST', 2),
+ ('RESUME_QUICK', 2),
+ ('STORE_FAST', 2),
+ ('PRECALL_ADAPTIVE', 4),
+ ('CALL_ADAPTIVE', 10),
+ ('LOAD_ATTR_ADAPTIVE', 10),
+ ('LOAD_GLOBAL_MODULE', 12)]
+
+ code_quicken(self.probe_function2, 1)
+ self.assertEqual(self.read_code(self.probe_function2, adaptive=True), result_3)
+ self.assertEqual(self.get_sizes(self.probe_function2, adaptive=True), sizes_3)
+
+ self.assertEqual(self.read_code(self.probe_function1), result_1)
+ self.assertEqual(self.read_code(self.probe_function2), result_2)
+
if __name__ == '__main__':
unittest.main()