Pylint cleanup (#74)

* Misc fixes

* Misc cleanup

* Add all_versions to blender_downloader.py

* More cleanup

* Fix issue with status not reporting engine info

* Misc fixes

* Misc cleanup

* Add all_versions to blender_downloader.py

* More cleanup

* Fix issue with status not reporting engine info
This commit is contained in:
2024-01-28 10:30:57 -06:00
committed by GitHub
parent d673d7d4bf
commit 9757ba9276
14 changed files with 79 additions and 57 deletions

View File

@@ -1,4 +1,4 @@
[MASTER] [MASTER]
max-line-length = 120 max-line-length = 120
[MESSAGES CONTROL] [MESSAGES CONTROL]
disable = missing-docstring, invalid-name, import-error disable = missing-docstring, invalid-name, import-error, logging-fstring-interpolation

View File

@@ -18,4 +18,5 @@ setup(
data_files=DATA_FILES, data_files=DATA_FILES,
options={'py2app': OPTIONS}, options={'py2app': OPTIONS},
setup_requires=['py2app'], setup_requires=['py2app'],
name='Zordon'
) )

View File

@@ -60,7 +60,6 @@ def jobs_json():
job_cache_token = num_to_alphanumeric(job_cache_int) job_cache_token = num_to_alphanumeric(job_cache_int)
if hash_token and hash_token == job_cache_token: if hash_token and hash_token == job_cache_token:
return [], 204 # no need to update return [], 204 # no need to update
else:
return {'jobs': all_jobs, 'token': job_cache_token} return {'jobs': all_jobs, 'token': job_cache_token}
except Exception as e: except Exception as e:
logger.exception(f"Exception fetching all_jobs_cached: {e}") logger.exception(f"Exception fetching all_jobs_cached: {e}")
@@ -343,10 +342,10 @@ def clear_history():
def status(): def status():
renderer_data = {} renderer_data = {}
for render_class in EngineManager.supported_engines(): for render_class in EngineManager.supported_engines():
if EngineManager.all_versions_for_engine(render_class.name): # only return renderers installed on host if EngineManager.all_versions_for_engine(render_class.name()): # only return renderers installed on host
renderer_data[render_class.engine.name()] = \ renderer_data[render_class.name()] = \
{'versions': EngineManager.all_versions_for_engine(render_class.engine.name()), {'versions': EngineManager.all_versions_for_engine(render_class.name()),
'is_available': RenderQueue.is_available_for_job(render_class.engine.name()) 'is_available': RenderQueue.is_available_for_job(render_class.name())
} }
# Get system info # Get system info
@@ -376,8 +375,8 @@ def renderer_info():
if installed_versions: if installed_versions:
# fixme: using system versions only because downloaded versions may have permissions issues # fixme: using system versions only because downloaded versions may have permissions issues
system_installed_versions = [x for x in installed_versions if x['type'] == 'system'] system_installed_versions = [x for x in installed_versions if x['type'] == 'system']
install_path = system_installed_versions[0]['path'] if system_installed_versions else ( install_path = system_installed_versions[0]['path'] if system_installed_versions \
installed_versions)[0]['path'] else (installed_versions)[0]['path']
renderer_data[engine.name()] = {'is_available': RenderQueue.is_available_for_job(engine.name()), renderer_data[engine.name()] = {'is_available': RenderQueue.is_available_for_job(engine.name()),
'versions': installed_versions, 'versions': installed_versions,
'supported_extensions': engine.supported_extensions(), 'supported_extensions': engine.supported_extensions(),

View File

@@ -17,7 +17,7 @@ class ServerProxyManager:
pub.subscribe(cls.__zeroconf_state_change, 'zeroconf_state_change') pub.subscribe(cls.__zeroconf_state_change, 'zeroconf_state_change')
@classmethod @classmethod
def __zeroconf_state_change(cls, hostname, state_change, info): def __zeroconf_state_change(cls, hostname, state_change):
if state_change == ServiceStateChange.Added or state_change == ServiceStateChange.Updated: if state_change == ServiceStateChange.Added or state_change == ServiceStateChange.Updated:
cls.get_proxy_for_hostname(hostname) cls.get_proxy_for_hostname(hostname)
else: else:

View File

@@ -1,5 +1,6 @@
import logging import logging
import re import re
import threading
import requests import requests
@@ -15,6 +16,7 @@ supported_formats = ['.zip', '.tar.xz', '.dmg']
class BlenderDownloader(EngineDownloader): class BlenderDownloader(EngineDownloader):
engine = Blender engine = Blender
@staticmethod @staticmethod
@@ -79,6 +81,31 @@ class BlenderDownloader(EngineDownloader):
return lts_versions return lts_versions
@classmethod
def all_versions(cls, system_os=None, cpu=None):
majors = cls.__get_major_versions()
all_versions = []
threads = []
results = [[] for _ in majors]
def thread_function(major_version, index, system_os, cpu):
results[index] = cls.__get_minor_versions(major_version, system_os, cpu)
for i, m in enumerate(majors):
thread = threading.Thread(target=thread_function, args=(m, i, system_os, cpu))
threads.append(thread)
thread.start()
# Wait for all threads to complete
for thread in threads:
thread.join()
# Extend all_versions with the results from each thread
for result in results:
all_versions.extend(result)
return all_versions
@classmethod @classmethod
def find_most_recent_version(cls, system_os=None, cpu=None, lts_only=False): def find_most_recent_version(cls, system_os=None, cpu=None, lts_only=False):
try: try:
@@ -108,8 +135,6 @@ class BlenderDownloader(EngineDownloader):
major_version = '.'.join(version.split('.')[:2]) major_version = '.'.join(version.split('.')[:2])
minor_versions = [x for x in cls.__get_minor_versions(major_version, system_os, cpu) if minor_versions = [x for x in cls.__get_minor_versions(major_version, system_os, cpu) if
x['version'] == version] x['version'] == version]
# we get the URL instead of calculating it ourselves. May change this
cls.download_and_extract_app(remote_url=minor_versions[0]['url'], download_location=download_location, cls.download_and_extract_app(remote_url=minor_versions[0]['url'], download_location=download_location,
timeout=timeout) timeout=timeout)
except IndexError: except IndexError:

View File

@@ -12,8 +12,7 @@ class BlenderRenderWorker(BaseRenderWorker):
engine = Blender engine = Blender
def __init__(self, input_path, output_path, engine_path, args=None, parent=None, name=None): def __init__(self, input_path, output_path, engine_path, args=None, parent=None, name=None):
super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, engine_path=engine_path, args=args, parent=parent, name=name)
engine_path=engine_path, args=args, parent=parent, name=name)
# Args # Args
self.blender_engine = self.args.get('engine', 'BLENDER_EEVEE').upper() self.blender_engine = self.args.get('engine', 'BLENDER_EEVEE').upper()

View File

@@ -157,14 +157,14 @@ class BaseRenderWorker(Base):
if not os.path.exists(self.input_path): if not os.path.exists(self.input_path):
self.status = RenderStatus.ERROR self.status = RenderStatus.ERROR
msg = 'Cannot find input path: {}'.format(self.input_path) msg = f'Cannot find input path: {self.input_path}'
logger.error(msg) logger.error(msg)
self.errors.append(msg) self.errors.append(msg)
return return
if not os.path.exists(self.renderer_path): if not os.path.exists(self.renderer_path):
self.status = RenderStatus.ERROR self.status = RenderStatus.ERROR
msg = 'Cannot find render engine path for {}'.format(self.engine.name()) msg = f'Cannot find render engine path for {self.engine.name()}'
logger.error(msg) logger.error(msg)
self.errors.append(msg) self.errors.append(msg)
return return

View File

@@ -239,7 +239,7 @@ class EngineManager:
@classmethod @classmethod
def engine_for_project_path(cls, path): def engine_for_project_path(cls, path):
name, extension = os.path.splitext(path) _, extension = os.path.splitext(path)
extension = extension.lower().strip('.') extension = extension.lower().strip('.')
for engine in cls.supported_engines(): for engine in cls.supported_engines():
if extension in engine.supported_extensions(): if extension in engine.supported_extensions():

View File

@@ -90,7 +90,7 @@ class FFMPEGDownloader(EngineDownloader):
return releases return releases
@classmethod @classmethod
def __all_versions(cls, system_os=None, cpu=None): def all_versions(cls, system_os=None, cpu=None):
system_os = system_os or current_system_os() system_os = system_os or current_system_os()
cpu = cpu or current_system_cpu() cpu = cpu or current_system_cpu()
versions_per_os = {'linux': cls.__get_linux_versions, 'macos': cls.__get_macos_versions, versions_per_os = {'linux': cls.__get_linux_versions, 'macos': cls.__get_macos_versions,
@@ -131,14 +131,14 @@ class FFMPEGDownloader(EngineDownloader):
try: try:
system_os = system_os or current_system_os() system_os = system_os or current_system_os()
cpu = cpu or current_system_cpu() cpu = cpu or current_system_cpu()
return cls.__all_versions(system_os, cpu)[0] return cls.all_versions(system_os, cpu)[0]
except (IndexError, requests.exceptions.RequestException): except (IndexError, requests.exceptions.RequestException):
logger.error(f"Cannot get most recent version of ffmpeg") logger.error(f"Cannot get most recent version of ffmpeg")
return {} return {}
@classmethod @classmethod
def version_is_available_to_download(cls, version, system_os=None, cpu=None): def version_is_available_to_download(cls, version, system_os=None, cpu=None):
for ver in cls.__all_versions(system_os, cpu): for ver in cls.all_versions(system_os, cpu):
if ver['version'] == version: if ver['version'] == version:
return ver return ver
return None return None
@@ -149,7 +149,7 @@ class FFMPEGDownloader(EngineDownloader):
cpu = cpu or current_system_cpu() cpu = cpu or current_system_cpu()
# Verify requested version is available # Verify requested version is available
found_version = [item for item in cls.__all_versions(system_os, cpu) if item['version'] == version] found_version = [item for item in cls.all_versions(system_os, cpu) if item['version'] == version]
if not found_version: if not found_version:
logger.error(f"Cannot find FFMPEG version {version} for {system_os} and {cpu}") logger.error(f"Cannot find FFMPEG version {version} for {system_os} and {cpu}")
return return

View File

@@ -25,7 +25,7 @@ class FFMPEG(BaseRenderEngine):
def supported_extensions(cls): def supported_extensions(cls):
help_text = (subprocess.check_output([cls().renderer_path(), '-h', 'full'], stderr=subprocess.STDOUT) help_text = (subprocess.check_output([cls().renderer_path(), '-h', 'full'], stderr=subprocess.STDOUT)
.decode('utf-8')) .decode('utf-8'))
found = re.findall('extensions that .* is allowed to access \(default "(.*)"', help_text) found = re.findall(r'extensions that .* is allowed to access \(default "(.*)"', help_text)
found_extensions = set() found_extensions = set()
for match in found: for match in found:
found_extensions.update(match.split(',')) found_extensions.update(match.split(','))
@@ -36,7 +36,7 @@ class FFMPEG(BaseRenderEngine):
try: try:
ver_out = subprocess.check_output([self.renderer_path(), '-version'], ver_out = subprocess.check_output([self.renderer_path(), '-version'],
timeout=SUBPROCESS_TIMEOUT).decode('utf-8') timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
match = re.match(".*version\s*([\w.*]+)\W*", ver_out) match = re.match(r".*version\s*([\w.*]+)\W*", ver_out)
if match: if match:
version = match.groups()[0] version = match.groups()[0]
except Exception as e: except Exception as e:
@@ -50,8 +50,8 @@ class FFMPEG(BaseRenderEngine):
'ffprobe', '-v', 'quiet', '-print_format', 'json', 'ffprobe', '-v', 'quiet', '-print_format', 'json',
'-show_streams', '-select_streams', 'v', project_path '-show_streams', '-select_streams', 'v', project_path
] ]
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
video_info = json.loads(result.stdout) video_info = json.loads(output)
# Extract the necessary information # Extract the necessary information
video_stream = video_info['streams'][0] video_stream = video_info['streams'][0]
@@ -94,7 +94,7 @@ class FFMPEG(BaseRenderEngine):
try: try:
formats_raw = subprocess.check_output([self.renderer_path(), '-formats'], stderr=subprocess.DEVNULL, formats_raw = subprocess.check_output([self.renderer_path(), '-formats'], stderr=subprocess.DEVNULL,
timeout=SUBPROCESS_TIMEOUT).decode('utf-8') timeout=SUBPROCESS_TIMEOUT).decode('utf-8')
pattern = '(?P<type>[DE]{1,2})\s+(?P<id>\S{2,})\s+(?P<name>.*)' pattern = r'(?P<type>[DE]{1,2})\s+(?P<id>\S{2,})\s+(?P<name>.*)'
all_formats = [m.groupdict() for m in re.finditer(pattern, formats_raw)] all_formats = [m.groupdict() for m in re.finditer(pattern, formats_raw)]
return all_formats return all_formats
except Exception as e: except Exception as e:
@@ -124,6 +124,7 @@ class FFMPEG(BaseRenderEngine):
if match: if match:
frame_number = int(match[-1]) frame_number = int(match[-1])
return frame_number return frame_number
return -1
def get_arguments(self): def get_arguments(self):
help_text = (subprocess.check_output([self.renderer_path(), '-h', 'long'], stderr=subprocess.STDOUT) help_text = (subprocess.check_output([self.renderer_path(), '-h', 'long'], stderr=subprocess.STDOUT)
@@ -154,4 +155,4 @@ class FFMPEG(BaseRenderEngine):
if __name__ == "__main__": if __name__ == "__main__":
print(FFMPEG().supported_extensions()) print(FFMPEG().get_all_formats())

View File

@@ -12,7 +12,7 @@ from PyQt6.QtWidgets import (
from src.api.server_proxy import RenderServerProxy from src.api.server_proxy import RenderServerProxy
from src.engines.engine_manager import EngineManager from src.engines.engine_manager import EngineManager
from src.utilities.misc_helper import is_localhost from src.utilities.misc_helper import is_localhost, launch_url
class EngineBrowserWindow(QMainWindow): class EngineBrowserWindow(QMainWindow):
@@ -98,7 +98,7 @@ class EngineBrowserWindow(QMainWindow):
return return
table_data = [] # convert the data into a flat list table_data = [] # convert the data into a flat list
for engine_name, engine_data in raw_server_data.items(): for _, engine_data in raw_server_data.items():
table_data.extend(engine_data['versions']) table_data.extend(engine_data['versions'])
self.engine_data = table_data self.engine_data = table_data
@@ -144,15 +144,7 @@ class EngineBrowserWindow(QMainWindow):
def launch_button_click(self): def launch_button_click(self):
engine_info = self.engine_data[self.table_widget.currentRow()] engine_info = self.engine_data[self.table_widget.currentRow()]
path = engine_info['path'] launch_url(engine_info['path'])
if sys.platform.startswith('darwin'):
subprocess.run(['open', path])
elif sys.platform.startswith('win32'):
os.startfile(path)
elif sys.platform.startswith('linux'):
subprocess.run(['xdg-open', path])
else:
raise OSError("Unsupported operating system")
def install_button_click(self): def install_button_click(self):
self.update_download_status() self.update_download_status()

View File

@@ -29,6 +29,7 @@ from .widgets.proportional_image_label import ProportionalImageLabel
from .widgets.statusbar import StatusBar from .widgets.statusbar import StatusBar
from .widgets.toolbar import ToolBar from .widgets.toolbar import ToolBar
from src.api.serverproxy_manager import ServerProxyManager from src.api.serverproxy_manager import ServerProxyManager
from src.utilities.misc_helper import launch_url
logger = logging.getLogger() logger = logging.getLogger()
@@ -72,7 +73,7 @@ class MainWindow(QMainWindow):
# Create a QLabel widget to display the image # Create a QLabel widget to display the image
self.image_label = ProportionalImageLabel() self.image_label = ProportionalImageLabel()
self.image_label.setMaximumSize(700, 500) self.image_label.setMaximumSize(700, 500)
self.image_label.setFixedHeight(500) self.image_label.setFixedHeight(300)
self.image_label.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter) self.image_label.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter)
self.load_image_path(os.path.join(resources_dir(), 'Rectangle.png')) self.load_image_path(os.path.join(resources_dir(), 'Rectangle.png'))
@@ -305,7 +306,7 @@ class MainWindow(QMainWindow):
except ConnectionError as e: except ConnectionError as e:
logger.error(f"Connection error fetching image: {e}") logger.error(f"Connection error fetching image: {e}")
except Exception as e: except Exception as e:
logger.exception(f"Error fetching image: {e}") logger.error(f"Error fetching image: {e}")
job_id = self.selected_job_ids()[0] if self.selected_job_ids() else None job_id = self.selected_job_ids()[0] if self.selected_job_ids() else None
local_server = is_localhost(self.current_hostname) local_server = is_localhost(self.current_hostname)
@@ -563,15 +564,7 @@ class MainWindow(QMainWindow):
for job_id in job_ids: for job_id in job_ids:
job_info = self.current_server_proxy.get_job_info(job_id) job_info = self.current_server_proxy.get_job_info(job_id)
path = os.path.dirname(job_info['output_path']) path = os.path.dirname(job_info['output_path'])
launch_url(path)
if sys.platform.startswith('darwin'):
subprocess.run(['open', path])
elif sys.platform.startswith('win32'):
os.startfile(path)
elif sys.platform.startswith('linux'):
subprocess.run(['xdg-open', path])
else:
raise OSError("Unsupported operating system")
def new_job(self) -> None: def new_job(self) -> None:

View File

@@ -1 +0,0 @@
''' app/ui/widgets/dialog.py '''

View File

@@ -11,14 +11,27 @@ logger = logging.getLogger()
def launch_url(url): def launch_url(url):
if subprocess.run(['which', 'xdg-open'], capture_output=True).returncode == 0: logger = logging.getLogger(__name__)
subprocess.run(['xdg-open', url]) # linux
elif subprocess.run(['which', 'open'], capture_output=True).returncode == 0: if shutil.which('xdg-open'):
subprocess.run(['open', url]) # macos opener = 'xdg-open'
elif subprocess.run(['which', 'start'], capture_output=True).returncode == 0: elif shutil.which('open'):
subprocess.run(['start', url]) # windows - need to validate this works opener = 'open'
elif shutil.which('cmd'):
opener = 'start'
else: else:
logger.error(f"No valid launchers found to launch url: {url}") error_message = f"No valid launchers found to launch URL: {url}"
logger.error(error_message)
raise OSError(error_message)
try:
if opener == 'start':
# For Windows, use 'cmd /c start'
subprocess.run(['cmd', '/c', 'start', url], shell=False)
else:
subprocess.run([opener, url])
except Exception as e:
logger.error(f"Failed to launch URL: {url}. Error: {e}")
def file_exists_in_mounts(filepath): def file_exists_in_mounts(filepath):