Testing: Validate Maya Logs (#5775)

* Working version

* Improve launched app communication

* Move imports to methods.

* Update tests/integration/hosts/maya/test_publish_in_maya.py

Co-authored-by: Roy Nieterau <roy_nieterau@hotmail.com>

* Collect errors from process

* fix startup scripts arguments

* Update openpype/lib/applications.py

Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com>

* Fix application polling

* Docstring

* Revert stdout and stderr

* Revert subprocess.PIPE

* Added missed imports

If we are moving these because of testing, lets move all of them

---------

Co-authored-by: Roy Nieterau <roy_nieterau@hotmail.com>
Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com>
Co-authored-by: kalisp <petr.kalis@gmail.com>
This commit is contained in:
Toke Jepsen 2023-11-07 15:51:54 +00:00 committed by GitHub
parent 4bafde2641
commit f7d76617c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 121 additions and 56 deletions

View file

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Class for handling Render Settings.""" """Class for handling Render Settings."""
from maya import cmds # noqa
import maya.mel as mel
import six import six
import sys import sys
@ -63,6 +61,10 @@ class RenderSettings(object):
def set_default_renderer_settings(self, renderer=None): def set_default_renderer_settings(self, renderer=None):
"""Set basic settings based on renderer.""" """Set basic settings based on renderer."""
# Not all hosts can import this module.
from maya import cmds
import maya.mel as mel
if not renderer: if not renderer:
renderer = cmds.getAttr( renderer = cmds.getAttr(
'defaultRenderGlobals.currentRenderer').lower() 'defaultRenderGlobals.currentRenderer').lower()

View file

@ -5,8 +5,6 @@ This is resolving index of server lists stored in `deadlineServers` instance
attribute or using default server if that attribute doesn't exists. attribute or using default server if that attribute doesn't exists.
""" """
from maya import cmds
import pyblish.api import pyblish.api
from openpype.pipeline.publish import KnownPublishError from openpype.pipeline.publish import KnownPublishError
@ -44,7 +42,8 @@ class CollectDeadlineServerFromInstance(pyblish.api.InstancePlugin):
str: Selected Deadline Webservice URL. str: Selected Deadline Webservice URL.
""" """
# Not all hosts can import this module.
from maya import cmds
deadline_settings = ( deadline_settings = (
render_instance.context.data render_instance.context.data
["system_settings"] ["system_settings"]

View file

@ -6,8 +6,6 @@ import getpass
import attr import attr
from datetime import datetime from datetime import datetime
import bpy
from openpype.lib import is_running_from_build from openpype.lib import is_running_from_build
from openpype.pipeline import legacy_io from openpype.pipeline import legacy_io
from openpype.pipeline.farm.tools import iter_expected_files from openpype.pipeline.farm.tools import iter_expected_files
@ -142,6 +140,9 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
return job_info return job_info
def get_plugin_info(self): def get_plugin_info(self):
# Not all hosts can import this module.
import bpy
plugin_info = BlenderPluginInfo( plugin_info = BlenderPluginInfo(
SceneFile=self.scene_path, SceneFile=self.scene_path,
Version=bpy.app.version_string, Version=bpy.app.version_string,

View file

@ -3,7 +3,6 @@ import json
from datetime import datetime from datetime import datetime
import requests import requests
import hou
import pyblish.api import pyblish.api
@ -31,6 +30,8 @@ class HoudiniSubmitPublishDeadline(pyblish.api.ContextPlugin):
targets = ["deadline"] targets = ["deadline"]
def process(self, context): def process(self, context):
# Not all hosts can import this module.
import hou
# Ensure no errors so far # Ensure no errors so far
assert all( assert all(

View file

@ -1,9 +1,8 @@
import hou
import os import os
import attr import attr
import getpass import getpass
from datetime import datetime from datetime import datetime
import pyblish.api import pyblish.api
from openpype.pipeline import legacy_io from openpype.pipeline import legacy_io
@ -119,6 +118,8 @@ class HoudiniSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
return job_info return job_info
def get_plugin_info(self): def get_plugin_info(self):
# Not all hosts can import this module.
import hou
instance = self._instance instance = self._instance
context = instance.context context = instance.context

View file

@ -1,8 +1,8 @@
import os import os
import getpass import getpass
import copy import copy
import attr import attr
from openpype.lib import ( from openpype.lib import (
TextDef, TextDef,
BoolDef, BoolDef,
@ -15,11 +15,6 @@ from openpype.pipeline import (
from openpype.pipeline.publish.lib import ( from openpype.pipeline.publish.lib import (
replace_with_published_scene_path replace_with_published_scene_path
) )
from openpype.hosts.max.api.lib import (
get_current_renderer,
get_multipass_setting
)
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
from openpype.lib import is_running_from_build from openpype.lib import is_running_from_build
@ -191,6 +186,13 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
self.submit(self.assemble_payload(job_info, plugin_info)) self.submit(self.assemble_payload(job_info, plugin_info))
def _use_published_name(self, data, project_settings): def _use_published_name(self, data, project_settings):
# Not all hosts can import these modules.
from openpype.hosts.max.api.lib import (
get_current_renderer,
get_multipass_setting
)
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
instance = self._instance instance = self._instance
job_info = copy.deepcopy(self.job_info) job_info = copy.deepcopy(self.job_info)
plugin_info = copy.deepcopy(self.plugin_info) plugin_info = copy.deepcopy(self.plugin_info)

View file

@ -28,8 +28,6 @@ from collections import OrderedDict
import attr import attr
from maya import cmds
from openpype.pipeline import ( from openpype.pipeline import (
legacy_io, legacy_io,
OpenPypePyblishPluginMixin OpenPypePyblishPluginMixin
@ -246,6 +244,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
return job_info return job_info
def get_plugin_info(self): def get_plugin_info(self):
# Not all hosts can import this module.
from maya import cmds
instance = self._instance instance = self._instance
context = instance.context context = instance.context
@ -288,7 +288,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
return plugin_payload return plugin_payload
def process_submission(self): def process_submission(self):
from maya import cmds
instance = self._instance instance = self._instance
filepath = self.scene_path # publish if `use_publish` else workfile filepath = self.scene_path # publish if `use_publish` else workfile
@ -675,7 +675,7 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
str str
""" """
from maya import cmds
# "vrayscene/<Scene>/<Scene>_<Layer>/<Layer>" # "vrayscene/<Scene>/<Scene>_<Layer>/<Layer>"
vray_settings = cmds.ls(type="VRaySettingsNode") vray_settings = cmds.ls(type="VRaySettingsNode")
node = vray_settings[0] node = vray_settings[0]

View file

@ -2,8 +2,6 @@ import os
import attr import attr
from datetime import datetime from datetime import datetime
from maya import cmds
from openpype import AYON_SERVER_ENABLED from openpype import AYON_SERVER_ENABLED
from openpype.pipeline import legacy_io, PublishXmlValidationError from openpype.pipeline import legacy_io, PublishXmlValidationError
from openpype.tests.lib import is_in_tests from openpype.tests.lib import is_in_tests
@ -127,7 +125,8 @@ class MayaSubmitRemotePublishDeadline(
job_info.EnvironmentKeyValue[key] = value job_info.EnvironmentKeyValue[key] = value
def get_plugin_info(self): def get_plugin_info(self):
# Not all hosts can import this module.
from maya import cmds
scene = self._instance.context.data["currentFile"] scene = self._instance.context.data["currentFile"]
plugin_info = MayaPluginInfo() plugin_info = MayaPluginInfo()

View file

@ -7,8 +7,6 @@ from datetime import datetime
import requests import requests
import pyblish.api import pyblish.api
import nuke
from openpype import AYON_SERVER_ENABLED from openpype import AYON_SERVER_ENABLED
from openpype.pipeline import legacy_io from openpype.pipeline import legacy_io
from openpype.pipeline.publish import ( from openpype.pipeline.publish import (
@ -498,6 +496,9 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
Returning: Returning:
list: captured groups list list: captured groups list
""" """
# Not all hosts can import this module.
import nuke
captured_groups = [] captured_groups = []
for lg_name, list_node_class in self.limit_groups.items(): for lg_name, list_node_class in self.limit_groups.items():
for node_class in list_node_class: for node_class in list_node_class:

View file

@ -5,15 +5,9 @@ Requires:
masterLayer -> instance data attribute masterLayer -> instance data attribute
otioClipRange -> instance data attribute otioClipRange -> instance data attribute
""" """
# import os
import opentimelineio as otio
import pyblish.api
from pprint import pformat from pprint import pformat
from openpype.pipeline.editorial import (
get_media_range_with_retimes, import pyblish.api
otio_range_to_frame_range,
otio_range_with_handles
)
class CollectOtioFrameRanges(pyblish.api.InstancePlugin): class CollectOtioFrameRanges(pyblish.api.InstancePlugin):
@ -27,6 +21,14 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin):
hosts = ["resolve", "hiero", "flame", "traypublisher"] hosts = ["resolve", "hiero", "flame", "traypublisher"]
def process(self, instance): def process(self, instance):
# Not all hosts can import these modules.
import opentimelineio as otio
from openpype.pipeline.editorial import (
get_media_range_with_retimes,
otio_range_to_frame_range,
otio_range_with_handles
)
# get basic variables # get basic variables
otio_clip = instance.data["otioClip"] otio_clip = instance.data["otioClip"]
workfile_start = instance.data["workfileFrameStart"] workfile_start = instance.data["workfileFrameStart"]

View file

@ -11,10 +11,10 @@ Provides:
instance -> families (adding ["review", "ftrack"]) instance -> families (adding ["review", "ftrack"])
""" """
import opentimelineio as otio
import pyblish.api
from pprint import pformat from pprint import pformat
import pyblish.api
class CollectOtioReview(pyblish.api.InstancePlugin): class CollectOtioReview(pyblish.api.InstancePlugin):
"""Get matching otio track from defined review layer""" """Get matching otio track from defined review layer"""
@ -25,6 +25,9 @@ class CollectOtioReview(pyblish.api.InstancePlugin):
hosts = ["resolve", "hiero", "flame"] hosts = ["resolve", "hiero", "flame"]
def process(self, instance): def process(self, instance):
# Not all hosts can import this module.
import opentimelineio as otio
# get basic variables # get basic variables
otio_review_clips = [] otio_review_clips = []
otio_timeline = instance.context.data["otioTimeline"] otio_timeline = instance.context.data["otioTimeline"]

View file

@ -6,18 +6,15 @@ Provides:
instance -> otioReviewClips instance -> otioReviewClips
""" """
import os import os
import clique import clique
import opentimelineio as otio
import pyblish.api import pyblish.api
from openpype.pipeline.editorial import (
get_media_range_with_retimes,
range_from_frames,
make_sequence_collection
)
from openpype.pipeline.publish import ( from openpype.pipeline.publish import (
get_publish_template_name get_publish_template_name
) )
class CollectOtioSubsetResources(pyblish.api.InstancePlugin): class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
"""Get Resources for a subset version""" """Get Resources for a subset version"""
@ -26,8 +23,14 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin):
families = ["clip"] families = ["clip"]
hosts = ["resolve", "hiero", "flame"] hosts = ["resolve", "hiero", "flame"]
def process(self, instance): def process(self, instance):
# Not all hosts can import these modules.
import opentimelineio as otio
from openpype.pipeline.editorial import (
get_media_range_with_retimes,
range_from_frames,
make_sequence_collection
)
if "audio" in instance.data["family"]: if "audio" in instance.data["family"]:
return return

View file

@ -1,11 +1,12 @@
import os import os
import tempfile
import pyblish import pyblish
from openpype.lib import ( from openpype.lib import (
get_ffmpeg_tool_args, get_ffmpeg_tool_args,
run_subprocess run_subprocess
) )
import tempfile
import opentimelineio as otio
class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): class ExtractOtioAudioTracks(pyblish.api.ContextPlugin):
@ -155,6 +156,9 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin):
Returns: Returns:
list: list of audio clip dictionaries list: list of audio clip dictionaries
""" """
# Not all hosts can import this module.
import opentimelineio as otio
output = [] output = []
# go trough all audio tracks # go trough all audio tracks
for otio_track in otio_timeline.tracks: for otio_track in otio_timeline.tracks:

View file

@ -1,6 +1,6 @@
import os import os
import pyblish.api import pyblish.api
import opentimelineio as otio
from openpype.pipeline import publish from openpype.pipeline import publish
@ -16,6 +16,9 @@ class ExtractOTIOFile(publish.Extractor):
hosts = ["resolve", "hiero", "traypublisher"] hosts = ["resolve", "hiero", "traypublisher"]
def process(self, instance): def process(self, instance):
# Not all hosts can import this module.
import opentimelineio as otio
if not instance.context.data.get("otioTimeline"): if not instance.context.data.get("otioTimeline"):
return return
# create representation data # create representation data

View file

@ -15,8 +15,8 @@ Provides:
""" """
import os import os
import clique import clique
import opentimelineio as otio
from pyblish import api from pyblish import api
from openpype.lib import ( from openpype.lib import (
@ -24,13 +24,6 @@ from openpype.lib import (
run_subprocess, run_subprocess,
) )
from openpype.pipeline import publish from openpype.pipeline import publish
from openpype.pipeline.editorial import (
otio_range_to_frame_range,
trim_media_range,
range_from_frames,
frames_to_seconds,
make_sequence_collection
)
class ExtractOTIOReview(publish.Extractor): class ExtractOTIOReview(publish.Extractor):
@ -62,6 +55,13 @@ class ExtractOTIOReview(publish.Extractor):
output_ext = ".jpg" output_ext = ".jpg"
def process(self, instance): def process(self, instance):
# Not all hosts can import these modules.
import opentimelineio as otio
from openpype.pipeline.editorial import (
otio_range_to_frame_range,
make_sequence_collection
)
# TODO: convert resulting image sequence to mp4 # TODO: convert resulting image sequence to mp4
# get otio clip and other time info from instance clip # get otio clip and other time info from instance clip
@ -281,6 +281,12 @@ class ExtractOTIOReview(publish.Extractor):
Returns: Returns:
otio.time.TimeRange: trimmed available range otio.time.TimeRange: trimmed available range
""" """
# Not all hosts can import these modules.
from openpype.pipeline.editorial import (
trim_media_range,
range_from_frames
)
avl_start = int(avl_range.start_time.value) avl_start = int(avl_range.start_time.value)
src_start = int(avl_start + start) src_start = int(avl_start + start)
avl_durtation = int(avl_range.duration.value) avl_durtation = int(avl_range.duration.value)
@ -338,6 +344,8 @@ class ExtractOTIOReview(publish.Extractor):
Returns: Returns:
otio.time.TimeRange: trimmed available range otio.time.TimeRange: trimmed available range
""" """
# Not all hosts can import this module.
from openpype.pipeline.editorial import frames_to_seconds
# create path and frame start to destination # create path and frame start to destination
output_path, out_frame_start = self._get_ffmpeg_output() output_path, out_frame_start = self._get_ffmpeg_output()

View file

@ -15,7 +15,6 @@ from openpype.lib import (
run_subprocess, run_subprocess,
) )
from openpype.pipeline import publish from openpype.pipeline import publish
from openpype.pipeline.editorial import frames_to_seconds
class ExtractOTIOTrimmingVideo(publish.Extractor): class ExtractOTIOTrimmingVideo(publish.Extractor):
@ -75,6 +74,8 @@ class ExtractOTIOTrimmingVideo(publish.Extractor):
otio_range (opentime.TimeRange): range to trim to otio_range (opentime.TimeRange): range to trim to
""" """
# Not all hosts can import this module.
from openpype.pipeline.editorial import frames_to_seconds
# create path to destination # create path to destination
output_path = self._get_ffmpeg_output(input_file_path) output_path = self._get_ffmpeg_output(input_file_path)

View file

@ -33,7 +33,7 @@ class MayaHostFixtures(HostFixtures):
yield dest_path yield dest_path
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def startup_scripts(self, monkeypatch_session): def startup_scripts(self, monkeypatch_session, download_test_data):
"""Points Maya to userSetup file from input data""" """Points Maya to userSetup file from input data"""
startup_path = os.path.join( startup_path = os.path.join(
os.path.dirname(__file__), "input", "startup" os.path.dirname(__file__), "input", "startup"
@ -44,6 +44,11 @@ class MayaHostFixtures(HostFixtures):
"{}{}{}".format(startup_path, os.pathsep, original_pythonpath) "{}{}{}".format(startup_path, os.pathsep, original_pythonpath)
) )
monkeypatch_session.setenv(
"MAYA_CMD_FILE_OUTPUT",
os.path.join(download_test_data, "output.log")
)
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def skip_compare_folders(self): def skip_compare_folders(self):
yield [] yield []

View file

@ -1,3 +1,6 @@
import re
import os
from tests.lib.assert_classes import DBAssert from tests.lib.assert_classes import DBAssert
from tests.integration.hosts.maya.lib import MayaLocalPublishTestClass from tests.integration.hosts.maya.lib import MayaLocalPublishTestClass
@ -35,6 +38,32 @@ class TestPublishInMaya(MayaLocalPublishTestClass):
TIMEOUT = 120 # publish timeout TIMEOUT = 120 # publish timeout
def test_publish(
self,
dbcon,
publish_finished,
download_test_data
):
"""Testing Pyblish and Python logs within Maya."""
# All maya output via MAYA_CMD_FILE_OUTPUT env var during test run
logging_path = os.path.join(download_test_data, "output.log")
with open(logging_path, "r") as f:
logging_output = f.read()
print(("-" * 50) + "LOGGING" + ("-" * 50))
print(logging_output)
# Check for pyblish errors.
error_regex = r"pyblish \(ERROR\)((.|\n)*?)((pyblish \())"
matches = re.findall(error_regex, logging_output)
assert not matches, matches[0][0]
# Check for python errors.
error_regex = r"// Error((.|\n)*)"
matches = re.findall(error_regex, logging_output)
assert not matches, matches[0][0]
def test_db_asserts(self, dbcon, publish_finished): def test_db_asserts(self, dbcon, publish_finished):
"""Host and input data dependent expected results in DB.""" """Host and input data dependent expected results in DB."""
print("test_db_asserts") print("test_db_asserts")

View file

@ -10,6 +10,7 @@ import glob
import platform import platform
import requests import requests
import re import re
import time
from tests.lib.db_handler import DBHandler from tests.lib.db_handler import DBHandler
from tests.lib.file_handler import RemoteFileHandler from tests.lib.file_handler import RemoteFileHandler
@ -334,7 +335,7 @@ class PublishTest(ModuleUnitTest):
print("Creating only setup for test, not launching app") print("Creating only setup for test, not launching app")
yield False yield False
return return
import time
time_start = time.time() time_start = time.time()
timeout = timeout or self.TIMEOUT timeout = timeout or self.TIMEOUT
timeout = float(timeout) timeout = float(timeout)