From 4a8dfc3db9988b61572aa307fe01b8fe1e011e8e Mon Sep 17 00:00:00 2001 From: Kaa Maurice Date: Thu, 16 Jun 2022 14:15:26 +0200 Subject: [PATCH 1/5] blender install pyside2 for all platforms --- .../hosts/blender/hooks/pre_pyside_install.py | 112 ++++++++++++------ 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/openpype/hosts/blender/hooks/pre_pyside_install.py b/openpype/hosts/blender/hooks/pre_pyside_install.py index a37f8f03793..aaa545b2cf3 100644 --- a/openpype/hosts/blender/hooks/pre_pyside_install.py +++ b/openpype/hosts/blender/hooks/pre_pyside_install.py @@ -1,6 +1,7 @@ import os import re import subprocess +from platform import system from openpype.lib import PreLaunchHook @@ -13,12 +14,9 @@ class InstallPySideToBlender(PreLaunchHook): For pipeline implementation is required to have Qt binding installed in blender's python packages. - - Prelaunch hook can work only on Windows right now. """ app_groups = ["blender"] - platforms = ["windows"] def execute(self): # Prelaunch hook is not crucial @@ -34,25 +32,28 @@ def inner_execute(self): # Get blender's python directory version_regex = re.compile(r"^[2-3]\.[0-9]+$") + platform = system().lower() executable = self.launch_context.executable.executable_path - if os.path.basename(executable).lower() != "blender.exe": + expected_executable = "blender" + if platform == "windows": + expected_executable += ".exe" + + if os.path.basename(executable).lower() != expected_executable: self.log.info(( - "Executable does not lead to blender.exe file. Can't determine" - " blender's python to check/install PySide2." + f"Executable does not lead to {expected_executable} file." + "Can't determine blender's python to check/install PySide2." )) return - executable_dir = os.path.dirname(executable) + versions_dir = os.path.dirname(executable) + if platform == "darwin": + versions_dir = os.path.join( + os.path.dirname(versions_dir), "Resources" + ) version_subfolders = [] - for name in os.listdir(executable_dir): - fullpath = os.path.join(name, executable_dir) - if not os.path.isdir(fullpath): - continue - - if not version_regex.match(name): - continue - - version_subfolders.append(name) + for dir_entry in os.scandir(versions_dir): + if dir_entry.is_dir() and version_regex.match(dir_entry.name): + version_subfolders.append(dir_entry.name) if not version_subfolders: self.log.info( @@ -72,16 +73,21 @@ def inner_execute(self): version_subfolder = version_subfolders[0] - pythond_dir = os.path.join( - os.path.dirname(executable), - version_subfolder, - "python" - ) + python_dir = os.path.join(versions_dir, version_subfolder, "python") + python_lib = os.path.join(python_dir, "lib") + python_version = "python" + + if platform != "windows": + for dir_entry in os.scandir(python_lib): + if dir_entry.is_dir() and dir_entry.name.startswith("python"): + python_lib = dir_entry.path + python_version = dir_entry.name + break # Change PYTHONPATH to contain blender's packages as first python_paths = [ - os.path.join(pythond_dir, "lib"), - os.path.join(pythond_dir, "lib", "site-packages"), + python_lib, + os.path.join(python_lib, "site-packages"), ] python_path = self.launch_context.env.get("PYTHONPATH") or "" for path in python_path.split(os.pathsep): @@ -91,7 +97,12 @@ def inner_execute(self): self.launch_context.env["PYTHONPATH"] = os.pathsep.join(python_paths) # Get blender's python executable - python_executable = os.path.join(pythond_dir, "bin", "python.exe") + python_bin = os.path.join(python_dir, "bin") + if platform == "windows": + python_executable = os.path.join(python_bin, "python.exe") + else: + python_executable = os.path.join(python_bin, python_version) + if not os.path.exists(python_executable): self.log.warning( "Couldn't find python executable for blender. {}".format( @@ -106,7 +117,15 @@ def inner_execute(self): return # Install PySide2 in blender's python - self.install_pyside_windows(python_executable) + if platform == "windows": + result = self.install_pyside_windows(python_executable) + else: + result = self.install_pyside(python_executable) + + if result: + self.log.info("Successfully installed PySide2 module to blender.") + else: + self.log.warning("Failed to install PySide2 module to blender.") def install_pyside_windows(self, python_executable): """Install PySide2 python module to blender's python. @@ -144,21 +163,38 @@ def install_pyside_windows(self, python_executable): lpDirectory=os.path.dirname(python_executable) ) process_handle = process_info["hProcess"] - obj = win32event.WaitForSingleObject( - process_handle, win32event.INFINITE - ) + win32event.WaitForSingleObject(process_handle, win32event.INFINITE) returncode = win32process.GetExitCodeProcess(process_handle) - if returncode == 0: - self.log.info( - "Successfully installed PySide2 module to blender." - ) - return + return returncode == 0 except pywintypes.error: pass - self.log.warning("Failed to install PySide2 module to blender.") + @staticmethod + def install_pyside(python_executable): + """Install PySide2 python module to blender's python.""" + try: + # Parameters + # - use "-m pip" as module pip to install PySide2 and argument + # "--ignore-installed" is to force install module to blender's + # site-packages and make sure it is binary compatible + args = [ + python_executable, + "-m", + "pip", + "install", + "--ignore-installed", + "PySide2", + ] + process = subprocess.Popen( + args, stdout=subprocess.PIPE, universal_newlines=True + ) + stdout, _ = process.communicate() + return process.returncode == 0 + except subprocess.SubprocessError: + pass - def is_pyside_installed(self, python_executable): + @staticmethod + def is_pyside_installed(python_executable): """Check if PySide2 module is in blender's pip list. Check that PySide2 is installed directly in blender's site-packages. @@ -167,9 +203,11 @@ def is_pyside_installed(self, python_executable): """ # Get pip list from blender's python executable args = [python_executable, "-m", "pip", "list"] - process = subprocess.Popen(args, stdout=subprocess.PIPE) + process = subprocess.Popen( + args, stdout=subprocess.PIPE, universal_newlines=True + ) stdout, _ = process.communicate() - lines = stdout.decode().split("\r\n") + lines = stdout.split(os.linesep) # Second line contain dashes that define maximum length of module name. # Second column of dashes define maximum length of module version. package_dashes, *_ = lines[1].split(" ") From b42505b430fa77608c722d91fce2f50ff38ba5e6 Mon Sep 17 00:00:00 2001 From: Kaa Maurice Date: Tue, 21 Jun 2022 15:19:34 +0200 Subject: [PATCH 2/5] fix pre_pyside_install for all platform --- openpype/hosts/blender/hooks/pre_pyside_install.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/blender/hooks/pre_pyside_install.py b/openpype/hosts/blender/hooks/pre_pyside_install.py index aaa545b2cf3..d2c07e860d0 100644 --- a/openpype/hosts/blender/hooks/pre_pyside_install.py +++ b/openpype/hosts/blender/hooks/pre_pyside_install.py @@ -203,11 +203,9 @@ def is_pyside_installed(python_executable): """ # Get pip list from blender's python executable args = [python_executable, "-m", "pip", "list"] - process = subprocess.Popen( - args, stdout=subprocess.PIPE, universal_newlines=True - ) + process = subprocess.Popen(args, stdout=subprocess.PIPE) stdout, _ = process.communicate() - lines = stdout.split(os.linesep) + lines = stdout.decode().split(os.linesep) # Second line contain dashes that define maximum length of module name. # Second column of dashes define maximum length of module version. package_dashes, *_ = lines[1].split(" ") From 07e67a14b76047129fa0dcf49ec1be452c6af79b Mon Sep 17 00:00:00 2001 From: Kaa Maurice Date: Thu, 23 Jun 2022 16:05:46 +0200 Subject: [PATCH 3/5] more exceptions for pre pisyde install --- openpype/hosts/blender/hooks/pre_pyside_install.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/blender/hooks/pre_pyside_install.py b/openpype/hosts/blender/hooks/pre_pyside_install.py index d2c07e860d0..9e1046453bc 100644 --- a/openpype/hosts/blender/hooks/pre_pyside_install.py +++ b/openpype/hosts/blender/hooks/pre_pyside_install.py @@ -169,8 +169,7 @@ def install_pyside_windows(self, python_executable): except pywintypes.error: pass - @staticmethod - def install_pyside(python_executable): + def install_pyside(self, python_executable): """Install PySide2 python module to blender's python.""" try: # Parameters @@ -188,13 +187,16 @@ def install_pyside(python_executable): process = subprocess.Popen( args, stdout=subprocess.PIPE, universal_newlines=True ) - stdout, _ = process.communicate() + process.communicate() return process.returncode == 0 + except PermissionError: + self.log.warning("Permission denied with command: \"{}\".".format(" ".join(args))) + except OSError as error: + self.log.warning("OS error has occurred with command: \"{error}\".") except subprocess.SubprocessError: pass - @staticmethod - def is_pyside_installed(python_executable): + def is_pyside_installed(self, python_executable): """Check if PySide2 module is in blender's pip list. Check that PySide2 is installed directly in blender's site-packages. From 262d179e15e1236f09fd86352a697eaff23a899d Mon Sep 17 00:00:00 2001 From: Kaa Maurice Date: Thu, 23 Jun 2022 16:09:50 +0200 Subject: [PATCH 4/5] fix last commit draft code --- openpype/hosts/blender/hooks/pre_pyside_install.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/blender/hooks/pre_pyside_install.py b/openpype/hosts/blender/hooks/pre_pyside_install.py index 9e1046453bc..d0f2b3d4178 100644 --- a/openpype/hosts/blender/hooks/pre_pyside_install.py +++ b/openpype/hosts/blender/hooks/pre_pyside_install.py @@ -190,9 +190,12 @@ def install_pyside(self, python_executable): process.communicate() return process.returncode == 0 except PermissionError: - self.log.warning("Permission denied with command: \"{}\".".format(" ".join(args))) + self.log.warning( + "Permission denied with command:" + "\"{}\".".format(" ".join(args)) + ) except OSError as error: - self.log.warning("OS error has occurred with command: \"{error}\".") + self.log.warning(f"OS error has occurred: \"{error}\".") except subprocess.SubprocessError: pass From b6312221b643f1c30a7e2dee67429564c8686ff1 Mon Sep 17 00:00:00 2001 From: kaa Date: Tue, 28 Jun 2022 14:36:45 +0200 Subject: [PATCH 5/5] Check for python with enabled pymalloc Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/blender/hooks/pre_pyside_install.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/blender/hooks/pre_pyside_install.py b/openpype/hosts/blender/hooks/pre_pyside_install.py index d0f2b3d4178..e5f66d2a26e 100644 --- a/openpype/hosts/blender/hooks/pre_pyside_install.py +++ b/openpype/hosts/blender/hooks/pre_pyside_install.py @@ -102,6 +102,9 @@ def inner_execute(self): python_executable = os.path.join(python_bin, "python.exe") else: python_executable = os.path.join(python_bin, python_version) + # Check for python with enabled 'pymalloc' + if not os.path.exists(python_executable): + python_executable += "m" if not os.path.exists(python_executable): self.log.warning(