aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2023-10-14 11:37:51 +0200
committerChristian Tismer <tismer@stackless.com>2023-10-18 12:56:57 +0200
commitd6d3729c0f0ebbfcb6b38ba343ff5dc6a3d19bc7 (patch)
treee3b069602fb5e65b936561078e2da99bce4fc693
parent0f8c63342fd03805c5b1d20e3536cceb2a28163b (diff)
deploy: Apply fixes when using pyenv and provide readable errors
When the patch for pyenv was applied, some tests in test_pyside6_deploy.py were broken, which shows up locally but not (yet) in CI. For better understanding, the test classes were further broken up into three groups (irrelevant, might be undone). Things became clearer by writing a special version of unittest.TestCase that handles long strings as lists. REMARK: We are at Python 3.8 and can use ":=" now :=) Task-number: PYSIDE-1612 Change-Id: I3a479f48b96dd5f95864b8a94af6d01b42ffc196 Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io> Reviewed-by: Shyamnath Premnadh <Shyamnath.Premnadh@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--sources/pyside-tools/deploy.py5
-rw-r--r--sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py131
2 files changed, 89 insertions, 47 deletions
diff --git a/sources/pyside-tools/deploy.py b/sources/pyside-tools/deploy.py
index 0c75350d3..342e5ef40 100644
--- a/sources/pyside-tools/deploy.py
+++ b/sources/pyside-tools/deploy.py
@@ -74,8 +74,9 @@ def main(main_file: Path = None, name: str = None, config_file: Path = None, ini
packages="packages")
# required by Nuitka for pyenv Python
- if python.is_pyenv_python():
- config.extra_args += " --static-libpython=no"
+ add_arg = " --static-libpython=no"
+ if python.is_pyenv_python() and add_arg not in config.extra_args:
+ config.extra_args += add_arg
# writing config file
# in the case of --dry-run, we use default.spec as reference. Do not save the changes
diff --git a/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py b/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
index 3a54e2b88..ce292d786 100644
--- a/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
+++ b/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
@@ -23,34 +23,47 @@ class ConfigFile:
return str(self.parser.get(section, key))
-class TestPySide6Deploy(unittest.TestCase):
+def is_pyenv_python():
+ pyenv_root = os.environ.get("PYENV_ROOT")
+
+ if pyenv_root and (resolved_exe := str(Path(sys.executable).resolve())):
+ return resolved_exe.startswith(pyenv_root)
+ return False
+
+
+class LongSortedOptionTest(unittest.TestCase):
+ @staticmethod
+ def _option_prepare(s):
+ """
+ Take a string and return a list obtained by text.split().
+ Options starting with "--" are also sorted."
+ """
+ items = s.split()
+ for idx in range(len(items)):
+ if items[idx].startswith("--"):
+ return items[:idx] + sorted(items[idx:])
+ return items
+
+ def assertEqual(self, text_a, text_b):
+ if (not isinstance(text_a, str) or not isinstance(text_b, str)
+ or (len(text_a) < 50 and len(text_b) < 50)):
+ return super().assertEqual(text_a, text_b)
+ sort_a = self._option_prepare(text_a)
+ sort_b = self._option_prepare(text_b)
+ return super().assertEqual(sort_a, sort_b)
+
+
+class DeployTestBase(LongSortedOptionTest):
@classmethod
def setUpClass(cls):
- # PYSIDE-2230: A temporary patch that avoids the pyenv error.
- # The final solution is too much for this quick fix.
- if os.environ.get("PYENV_ROOT"):
- del os.environ["PYENV_ROOT"]
cls.pyside_root = Path(__file__).parents[5].resolve()
- example_root = cls.pyside_root / "examples"
- example_widgets = example_root / "widgets" / "widgets" / "tetrix"
- example_qml = example_root / "qml" / "editingmodel"
- example_webenginequick = example_root / "webenginequick" / "nanobrowser"
+ cls.example_root = cls.pyside_root / "examples"
cls.temp_dir = tempfile.mkdtemp()
- cls.temp_example_widgets = Path(
- shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
- ).resolve()
- cls.temp_example_qml = Path(
- shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
- ).resolve()
- cls.temp_example_webenginequick = Path(
- shutil.copytree(example_webenginequick, Path(cls.temp_dir) / "nanobrowser")
- ).resolve()
cls.current_dir = Path.cwd()
- cls.linux_onefile_icon = (
- cls.pyside_root / "sources" / "pyside-tools" / "deploy_lib" / "pyside_icon.jpg"
- )
-
- sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
+ tools_path = cls.pyside_root / "sources" / "pyside-tools"
+ cls.linux_onefile_icon = tools_path / "deploy_lib" / "pyside_icon.jpg"
+ if tools_path not in sys.path:
+ sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
cls.deploy_lib = importlib.import_module("deploy_lib")
cls.deploy = importlib.import_module("deploy")
sys.modules["deploy"] = cls.deploy
@@ -61,7 +74,25 @@ class TestPySide6Deploy(unittest.TestCase):
# print no outputs to stdout
sys.stdout = mock.MagicMock()
- def setUpWidgets(self):
+ @classmethod
+ def tearDownClass(cls) -> None:
+ shutil.rmtree(Path(cls.temp_dir))
+
+ def tearDown(self) -> None:
+ super().tearDown()
+ os.chdir(self.current_dir)
+
+
+class TestPySide6DeployWidgets(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_widgets = cls.example_root / "widgets" / "widgets" / "tetrix"
+ cls.temp_example_widgets = Path(
+ shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
+ ).resolve()
+
+ def setUp(self):
os.chdir(self.temp_example_widgets)
self.main_file = self.temp_example_widgets / "tetrix.py"
self.deployment_files = self.temp_example_widgets / "deployment"
@@ -72,20 +103,20 @@ class TestPySide6Deploy(unittest.TestCase):
)
if sys.platform.startswith("linux"):
self.expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+ if is_pyenv_python():
+ self.expected_run_cmd += " --static-libpython=no"
self.config_file = self.temp_example_widgets / "pysidedeploy.spec"
def testWidgetDryRun(self):
# Checking for dry run commands is equivalent to mocking the
# subprocess.check_call() in commands.py as the the dry run command
# is the command being run.
- self.setUpWidgets()
original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
self.assertEqual(original_output, self.expected_run_cmd)
def testWidgetConfigFile(self):
# includes both dry run and config_file tests
- self.setUpWidgets()
# init
init_result = self.deploy.main(self.main_file, init=True, force=True)
self.assertEqual(init_result, None)
@@ -101,13 +132,23 @@ class TestPySide6Deploy(unittest.TestCase):
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.5.4,ordered_set,zstandard")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "")
- self.assertEqual(
- config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True"
- )
+ equ_base = "--quiet --noinclude-qt-translations=True"
+ equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
+ self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
self.assertEqual(config_obj.get_value("qt", "excluded_qml_plugins"), "")
self.config_file.unlink()
- def setUpQml(self):
+
+class TestPySide6DeployQml(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_qml = cls.example_root / "qml" / "editingmodel"
+ cls.temp_example_qml = Path(
+ shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
+ ).resolve()
+
+ def setUp(self):
os.chdir(self.temp_example_qml)
self.main_file = self.temp_example_qml / "main.py"
self.deployment_files = self.temp_example_qml / "deployment"
@@ -137,11 +178,11 @@ class TestPySide6Deploy(unittest.TestCase):
if sys.platform.startswith("linux"):
self.expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+ if is_pyenv_python():
+ self.expected_run_cmd += " --static-libpython=no"
self.config_file = self.temp_example_qml / "pysidedeploy.spec"
def testQmlConfigFile(self):
- self.setUpQml()
-
# create config file
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
@@ -155,9 +196,9 @@ class TestPySide6Deploy(unittest.TestCase):
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.5.4,ordered_set,zstandard")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "main.qml,MovingRectangle.qml")
- self.assertEqual(
- config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True"
- )
+ equ_base = "--quiet --noinclude-qt-translations=True"
+ equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
+ self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
self.assertEqual(
config_obj.get_value("qt", "excluded_qml_plugins"),
"QtCharts,QtQuick3D,QtSensors,QtTest,QtWebEngine",
@@ -165,7 +206,6 @@ class TestPySide6Deploy(unittest.TestCase):
self.config_file.unlink()
def testQmlDryRun(self):
- self.setUpQml()
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
@@ -173,13 +213,22 @@ class TestPySide6Deploy(unittest.TestCase):
self.assertEqual(mock_qmlimportscanner.call_count, 1)
def testMainFileDryRun(self):
- self.setUpQml()
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
original_output = self.deploy.main(Path.cwd() / "main.py", dry_run=True, force=True)
self.assertEqual(original_output, self.expected_run_cmd)
self.assertEqual(mock_qmlimportscanner.call_count, 1)
+
+class TestPySide6DeployWebEngine(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_webenginequick = cls.example_root / "webenginequick" / "nanobrowser"
+ cls.temp_example_webenginequick = Path(
+ shutil.copytree(example_webenginequick, Path(cls.temp_dir) / "nanobrowser")
+ ).resolve()
+
# this test case retains the QtWebEngine dlls
def testWebEngineQuickDryRun(self):
# setup
@@ -245,14 +294,6 @@ class TestPySide6Deploy(unittest.TestCase):
"QtCharts,QtQuick3D,QtSensors,QtTest",
)
- def tearDown(self) -> None:
- super().tearDown()
- os.chdir(self.current_dir)
-
- @classmethod
- def tearDownClass(cls) -> None:
- shutil.rmtree(Path(cls.temp_dir))
-
if __name__ == "__main__":
unittest.main()