Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4020 from pypeclub/feature/OP-4285_Convertors-for…
Browse files Browse the repository at this point in the history
…-legacy-instances
  • Loading branch information
mkolar authored Oct 26, 2022
2 parents 443d359 + 698fe83 commit 6d75023
Show file tree
Hide file tree
Showing 11 changed files with 994 additions and 235 deletions.
186 changes: 186 additions & 0 deletions openpype/pipeline/create/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
Creator,
AutoCreator,
discover_creator_plugins,
discover_convertor_plugins,
CreatorError,
)

Expand Down Expand Up @@ -70,6 +71,41 @@ def __init__(self, host, missing_methods):
super(HostMissRequiredMethod, self).__init__(msg)


class ConvertorsOperationFailed(Exception):
def __init__(self, msg, failed_info):
super(ConvertorsOperationFailed, self).__init__(msg)
self.failed_info = failed_info


class ConvertorsFindFailed(ConvertorsOperationFailed):
def __init__(self, failed_info):
msg = "Failed to find incompatible subsets"
super(ConvertorsFindFailed, self).__init__(
msg, failed_info
)


class ConvertorsConversionFailed(ConvertorsOperationFailed):
def __init__(self, failed_info):
msg = "Failed to convert incompatible subsets"
super(ConvertorsConversionFailed, self).__init__(
msg, failed_info
)


def prepare_failed_convertor_operation_info(identifier, exc_info):
exc_type, exc_value, exc_traceback = exc_info
formatted_traceback = "".join(traceback.format_exception(
exc_type, exc_value, exc_traceback
))

return {
"convertor_identifier": identifier,
"message": str(exc_value),
"traceback": formatted_traceback
}


class CreatorsOperationFailed(Exception):
"""Raised when a creator process crashes in 'CreateContext'.
Expand Down Expand Up @@ -926,6 +962,37 @@ def apply_changes(self, changes):
self[key] = new_value


class ConvertorItem(object):
"""Item representing convertor plugin.
Args:
identifier (str): Identifier of convertor.
label (str): Label which will be shown in UI.
"""

def __init__(self, identifier, label):
self._id = str(uuid4())
self.identifier = identifier
self.label = label

@property
def id(self):
return self._id

def to_data(self):
return {
"id": self.id,
"identifier": self.identifier,
"label": self.label
}

@classmethod
def from_data(cls, data):
obj = cls(data["identifier"], data["label"])
obj._id = data["id"]
return obj


class CreateContext:
"""Context of instance creation.
Expand Down Expand Up @@ -991,6 +1058,9 @@ def __init__(
# Manual creators
self.manual_creators = {}

self.convertors_plugins = {}
self.convertor_items_by_id = {}

self.publish_discover_result = None
self.publish_plugins_mismatch_targets = []
self.publish_plugins = []
Expand Down Expand Up @@ -1071,6 +1141,7 @@ def reset(self, discover_publish_plugins=True):

with self.bulk_instances_collection():
self.reset_instances()
self.find_convertor_items()
self.execute_autocreators()

self.reset_finalization()
Expand Down Expand Up @@ -1125,6 +1196,12 @@ def reset_plugins(self, discover_publish_plugins=True):
Reloads creators from preregistered paths and can load publish plugins
if it's enabled on context.
"""

self._reset_publish_plugins(discover_publish_plugins)
self._reset_creator_plugins()
self._reset_convertor_plugins()

def _reset_publish_plugins(self, discover_publish_plugins):
import pyblish.logic

from openpype.pipeline import OpenPypePyblishPluginMixin
Expand Down Expand Up @@ -1166,6 +1243,7 @@ def reset_plugins(self, discover_publish_plugins=True):
self.publish_plugins = plugins_by_targets
self.plugins_with_defs = plugins_with_defs

def _reset_creator_plugins(self):
# Prepare settings
system_settings = get_system_settings()
project_settings = get_project_settings(self.project_name)
Expand Down Expand Up @@ -1217,6 +1295,27 @@ def reset_plugins(self, discover_publish_plugins=True):

self.creators = creators

def _reset_convertor_plugins(self):
convertors_plugins = {}
for convertor_class in discover_convertor_plugins():
if inspect.isabstract(convertor_class):
self.log.info(
"Skipping abstract Creator {}".format(str(convertor_class))
)
continue

convertor_identifier = convertor_class.identifier
if convertor_identifier in convertors_plugins:
self.log.warning((
"Duplicated Converter identifier. "
"Using first and skipping following"
))
continue

convertors_plugins[convertor_identifier] = convertor_class(self)

self.convertors_plugins = convertors_plugins

def reset_context_data(self):
"""Reload context data using host implementation.
Expand Down Expand Up @@ -1346,6 +1445,14 @@ def creator_removed_instance(self, instance):

self._instances_by_id.pop(instance.id, None)

def add_convertor_item(self, convertor_identifier, label):
self.convertor_items_by_id[convertor_identifier] = ConvertorItem(
convertor_identifier, label
)

def remove_convertor_item(self, convertor_identifier):
self.convertor_items_by_id.pop(convertor_identifier, None)

@contextmanager
def bulk_instances_collection(self):
"""Validate context of instances in bulk.
Expand Down Expand Up @@ -1413,6 +1520,37 @@ def reset_instances(self):
if failed_info:
raise CreatorsCollectionFailed(failed_info)

def find_convertor_items(self):
"""Go through convertor plugins to look for items to convert.
Raises:
ConvertorsFindFailed: When one or more convertors fails during
finding.
"""

self.convertor_items_by_id = {}

failed_info = []
for convertor in self.convertors_plugins.values():
try:
convertor.find_instances()

except:
failed_info.append(
prepare_failed_convertor_operation_info(
convertor.identifier, sys.exc_info()
)
)
self.log.warning(
"Failed to find instances of convertor \"{}\"".format(
convertor.identifier
),
exc_info=True
)

if failed_info:
raise ConvertorsFindFailed(failed_info)

def execute_autocreators(self):
"""Execute discovered AutoCreator plugins.
Expand Down Expand Up @@ -1668,3 +1806,51 @@ def collection_shared_data(self):
"Accessed Collection shared data out of collection phase"
)
return self._collection_shared_data

def run_convertor(self, convertor_identifier):
"""Run convertor plugin by it's idenfitifier.
Conversion is skipped if convertor is not available.
Args:
convertor_identifier (str): Identifier of convertor.
"""

convertor = self.convertors_plugins.get(convertor_identifier)
if convertor is not None:
convertor.convert()

def run_convertors(self, convertor_identifiers):
"""Run convertor plugins by idenfitifiers.
Conversion is skipped if convertor is not available. It is recommended
to trigger reset after conversion to reload instances.
Args:
convertor_identifiers (Iterator[str]): Identifiers of convertors
to run.
Raises:
ConvertorsConversionFailed: When one or more convertors fails.
"""

failed_info = []
for convertor_identifier in convertor_identifiers:
try:
self.run_convertor(convertor_identifier)

except:
failed_info.append(
prepare_failed_convertor_operation_info(
convertor_identifier, sys.exc_info()
)
)
self.log.warning(
"Failed to convert instances of convertor \"{}\"".format(
convertor_identifier
),
exc_info=True
)

if failed_info:
raise ConvertorsConversionFailed(failed_info)
Loading

0 comments on commit 6d75023

Please sign in to comment.