diff options
| -rw-r--r-- | sources/pyside-tools/deploy.py | 5 | ||||
| -rw-r--r-- | sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py | 131 |
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() |
