added few docstrings

This commit is contained in:
iLLiCiTiT 2021-10-05 17:49:27 +02:00
parent 8eac18e4ef
commit b964d119c4
3 changed files with 119 additions and 27 deletions

View file

@ -275,16 +275,19 @@ class PublishAttributes:
return output
def plugin_names_order(self):
"""Plugin names order by their 'order' attribute."""
for name in self._plugin_names_order:
yield name
def data_to_store(self):
"""Convert attribute values to "data to store"."""
output = {}
for key, attr_value in self._data.items():
output[key] = attr_value.data_to_store()
return output
def changes(self):
"""Return changes per each key."""
changes = {}
for key, attr_val in self._data.items():
attr_changes = attr_val.changes()
@ -299,6 +302,7 @@ class PublishAttributes:
return changes
def set_publish_plugins(self, attr_plugins):
"""Set publish plugins attribute definitions."""
self._plugin_names_order = []
self._missing_plugins = []
self.attr_plugins = attr_plugins or []
@ -439,6 +443,7 @@ class CreatedInstance:
data=str(self._data)
)
# --- Dictionary like methods ---
def __getitem__(self, key):
return self._data[key]
@ -472,6 +477,7 @@ class CreatedInstance:
def items(self):
return self._data.items()
# ------
@property
def family(self):
@ -569,14 +575,14 @@ class CreatedInstance:
changes[key] = (old_value, None)
return changes
@property
def creator_attribute_defs(self):
return self.creator_attributes.attr_defs
@property
def creator_attributes(self):
return self._data["creator_attributes"]
@property
def creator_attribute_defs(self):
return self.creator_attributes.attr_defs
@property
def publish_attributes(self):
return self._data["publish_attributes"]
@ -611,6 +617,7 @@ class CreatedInstance:
self.publish_attributes.set_publish_plugins(attr_plugins)
def add_members(self, members):
"""Currently unused method."""
for member in members:
if member not in self._members:
self._members.append(member)
@ -621,6 +628,16 @@ class CreateContext:
Context itself also can store data related to whole creation (workfile).
- those are mainly for Context publish plugins
Args:
host(ModuleType): Host implementation which handles implementation and
global metadata.
dbcon(AvalonMongoDB): Connection to mongo with context (at least
project).
headless(bool): Context is created out of UI (Current not used).
reset(bool): Reset context on initialization.
discover_publish_plugins(bool): Discover publish plugins during reset
phase.
"""
# Methods required in host implementaion to be able create instances
# or change context data.
@ -633,6 +650,7 @@ class CreateContext:
self, host, dbcon=None, headless=False, reset=True,
discover_publish_plugins=True
):
# Create conncetion if is not passed
if dbcon is None:
import avalon.api
@ -641,12 +659,17 @@ class CreateContext:
dbcon.install()
self.dbcon = dbcon
self.host = host
# Prepare attribute for logger (Created on demand in `log` property)
self._log = None
# Publish context plugins attributes and it's values
self._publish_attributes = PublishAttributes(self, {})
self._original_context_data = {}
self.host = host
# Validate host implementation
# - defines if context is capable of handling context data
host_is_valid = True
missing_methods = self.get_host_misssing_methods(host)
if missing_methods:
@ -660,6 +683,7 @@ class CreateContext:
).format(joined_methods))
self._host_is_valid = host_is_valid
# Currently unused variable
self.headless = headless
# TODO convert to dictionary instance by id to validate duplicates
@ -669,6 +693,7 @@ class CreateContext:
self.creators = {}
# Prepare categories of creators
self.autocreators = {}
# TODO rename 'ui_creators' to something more suitable
self.ui_creators = {}
self.publish_discover_result = None
@ -676,18 +701,29 @@ class CreateContext:
self.plugins_with_defs = []
self._attr_plugins_by_family = {}
# Helpers for validating context of collected instances
# - they can be validation for multiple instances at one time
# using context manager which will trigger validation
# after leaving of last context manager scope
self._bulk_counter = 0
self._bulk_instances_to_process = []
# Trigger reset if was enabled
if reset:
self.reset(discover_publish_plugins)
@property
def publish_attributes(self):
"""Access to global publish attributes."""
return self._publish_attributes
@classmethod
def get_host_misssing_methods(cls, host):
"""Collect missing methods from host.
Args:
host(ModuleType): Host implementaion.
"""
missing = set()
for attr_name in cls.required_methods:
if not hasattr(host, attr_name):
@ -696,15 +732,21 @@ class CreateContext:
@property
def host_is_valid(self):
"""Is host valid for creation."""
return self._host_is_valid
@property
def log(self):
"""Dynamic access to logger."""
if self._log is None:
self._log = logging.getLogger(self.__class__.__name__)
return self._log
def reset(self, discover_publish_plugins=True):
"""Reset context with all plugins and instances.
All changes will be lost if were not saved explicitely.
"""
self.reset_plugins(discover_publish_plugins)
self.reset_context_data()
@ -713,6 +755,11 @@ class CreateContext:
self.execute_autocreators()
def reset_plugins(self, discover_publish_plugins=True):
"""Reload plugins.
Reloads creators from preregistered paths and can load publish plugins
if it's enabled on context.
"""
import avalon.api
import pyblish.logic
@ -780,6 +827,11 @@ class CreateContext:
self.creators = creators
def reset_context_data(self):
"""Reload context data using host implementation.
These data are not related to any instance but may be needed for whole
publishing.
"""
if not self.host_is_valid:
self._original_context_data = {}
self._publish_attributes = PublishAttributes(self, {})
@ -796,11 +848,16 @@ class CreateContext:
)
def context_data_to_store(self):
"""Data that should be stored by host function.
The same data should be returned on loading.
"""
return {
"publish_attributes": self._publish_attributes.data_to_store()
}
def context_data_changes(self):
"""Changes of attributes."""
changes = {}
publish_attribute_changes = self._publish_attributes.changes()
if publish_attribute_changes:
@ -808,12 +865,26 @@ class CreateContext:
return changes
def creator_adds_instance(self, instance):
"""Creator adds new instance to context.
Instances should be added only from creators.
Args:
instance(CreatedInstance): Instance with prepared data from
creator.
TODO: Rename method to more suit.
"""
# Add instance to instances list
self.instances.append(instance)
# Prepare publish plugin attributes and set it on instance
attr_plugins = self._get_publish_plugins_with_attr_for_family(
instance.creator.family
)
instance.set_publish_plugins(attr_plugins)
# Add instance to be validated inside 'bulk_instances_collection'
# context manager if is inside bulk
with self.bulk_instances_collection():
self._bulk_instances_to_process.append(instance)
@ -826,6 +897,8 @@ class CreateContext:
This can be used for single instance or for adding multiple instances
which is helpfull on reset.
Should not be executed from multiple threads.
"""
self._bulk_counter += 1
try:
@ -846,6 +919,7 @@ class CreateContext:
self.validate_instances_context(instances_to_validate)
def reset_instances(self):
"""Reload instances"""
self.instances = []
# Collect instances
@ -869,9 +943,12 @@ class CreateContext:
self.log.warning(msg, exc_info=True)
def validate_instances_context(self, instances=None):
"""Validate 'asset' and 'task' instance context."""
# Use all instances from context if 'instances' are not passed
if instances is None:
instances = self.instances
# Skip if instances are empty
if not instances:
return
@ -921,6 +998,7 @@ class CreateContext:
instance.set_task_invalid(True)
def save_changes(self):
"""Save changes. Update all changed values."""
if not self.host_is_valid:
missing_methods = self.get_host_misssing_methods(self.host)
raise HostMissRequiredMethod(self.host, missing_methods)
@ -929,12 +1007,14 @@ class CreateContext:
self._save_instance_changes()
def _save_context_changes(self):
"""Save global context values."""
changes = self.context_data_changes()
if changes:
data = self.context_data_to_store()
self.host.update_context_data(data, changes)
def _save_instance_changes(self):
"""Save instance specific values."""
instances_by_identifier = collections.defaultdict(list)
for instance in self.instances:
identifier = instance.creator_identifier
@ -968,6 +1048,14 @@ class CreateContext:
creator.remove_instances(creator_instances)
def _get_publish_plugins_with_attr_for_family(self, family):
"""Publish plugin attributes for passed family.
Attribute definitions for specific family are cached.
Args:
family(str): Instance family for which should be attribute
definitions returned.
"""
if family not in self._attr_plugins_by_family:
import pyblish.logic
@ -983,6 +1071,7 @@ class CreateContext:
return self._attr_plugins_by_family[family]
def _get_publish_plugins_with_attr_for_context(self):
"""Publish plugins attributes for Context plugins."""
plugins = []
for plugin in self.plugins_with_defs:
if not plugin.__instanceEnabled__:

View file

@ -55,7 +55,7 @@ class BaseCreator:
@abstractproperty
def identifier(self):
"""Family that plugin represents."""
"""Identifier of creator (must be unique)."""
pass
@abstractproperty
@ -70,6 +70,7 @@ class BaseCreator:
return self._log
def _add_instance_to_context(self, instance):
"""Helper method to ad d"""
self.create_context.creator_adds_instance(instance)
def _remove_instance_from_context(self, instance):
@ -116,6 +117,11 @@ class BaseCreator:
def get_dynamic_data(
self, variant, task_name, asset_doc, project_name, host_name
):
"""Dynamic data for subset name filling.
These may be get dynamically created based on current context of
workfile.
"""
return {}
def get_subset_name(

View file

@ -2,17 +2,19 @@
Creation is process defying what and how will be published. May work in a different way based on host implementation.
## CreateContext
Entry point of creation. All data and metadata are stored to create context. Context hold all global data and instances. Is responsible for loading of plugins (create, publish), loading data from host, validation of host implementation and emitting changes to host implementation.
Entry point of creation. All data and metadata are handled through create context. Context hold all global data and instances. Is responsible for loading of plugins (create, publish), triggering creator methods, validation of host implementation and emitting changes to creators and host.
Discovers Create plugins to be able create new instances and convert existing instances. Creators may have defined attributes that are specific for the family. Attributes definition can enhance behavior of instance during publishing.
Discovers Creator plugins to be able create new instances and convert existing instances. Creators may have defined attributes that are specific for their instances. Attributes definition can enhance behavior of instance during publishing.
Publish plugins are loaded because they can also define attributes definitions. These are less family specific To be able define attributes Publish plugin must inherit from `OpenPypePyblishPluginMixin` and must override `get_attribute_defs` class method which must return list of attribute definitions. Values of publish plugin definitions are stored per plugin name under `publish_attributes`.
Possible attribute definitions can be found in `openpype/pipeline/lib/attribute_definitions.py`.
Except creating and removing instances are all changes not automatically propagated to host context (scene/workfile/...) to propagate changes call `save_changes` which trigger update of all instances in context using Creators implementation.
## CreatedInstance
Product of creation is "instance" which holds basic data defying it. Core data are `family` and `subset`. Other data can be keys used to fill subset name or metadata modifying publishing process of the instance (more described later).
Product of creation is "instance" which holds basic data defying it. Core data are `creator_identifier`, `family` and `subset`. Other data can be keys used to fill subset name or metadata modifying publishing process of the instance (more described later). All instances have `id` which holds constant `pyblish.avalon.instance` and `uuid` which is identifier of the instance.
Family tells how should be instance processed and subset what name will published item have.
- There are cases when subset is not fully filled during creation and may change during publishing. That is in most of cases caused because instance is related to other instance or instance data do not represent final product.
@ -35,8 +37,10 @@ Family tells how should be instance processed and subset what name will publishe
"active": True,
## Version of instance
"version": 1,
# Identifier of creator (is unique)
"creator_identifier": "",
## Creator specific attributes (defined by Creator)
"family_attributes": {...},
"creator_attributes": {...},
## Publish plugin specific plugins (defined by Publish plugin)
"publish_attributes": {
# Attribute values are stored by publish plugin name
@ -50,26 +54,19 @@ Family tells how should be instance processed and subset what name will publishe
```
## Creator
To be able create instance there must be defined a creator. Creator represents a family and handling of it's instances. Is not responsible only about creating new instances but also about updating existing. Family is identifier of creator so there can be only one Creator with same family at a time which helps to handle changes in creation of specific family.
To be able create, update, remove or collect existing instances there must be defined a creator. Creator must have unique identifier and can represents a family. There can be multiple Creators for single family. Identifier of creator should contain family (advise).
Creator does not have strictly defined how is new instance created but result be approachable from host implementation and host must have ability to remove the instance metadata without the Creator. That is host specific logic and can't be handled generally.
Creator has abstract methods to handle instances. For new instance creation is used `create` which should create metadata in host context and add new instance object to `CreateContext`. To collect existing instances is used `collect_instances` which should find all existing instances related to creator and add them to `CreateContext`. To update data of instance is used `update_instances` which is called from `CreateContext` on `save_changes`. To remove instance use `remove_instances` which should remove metadata from host context and remove instance from `CreateContext`.
Creator has access to `CreateContext` which created object of the creator. All new instances or removed instances must be told to context. To do so use methods `_add_instance_to_context` and `_remove_instance_from_context` where `CreatedInstance` is passed. They should be called from `create` if new instance was created and from `remove_instances` if instance was removed.
Creators don't have strictly defined how are instances handled but it is good practice to define a way which is host specific. It is not strict because there are cases when host implementation just can't handle all requirements of all creators.
### AutoCreator
Auto-creators are automatically executed when CreateContext is reset. They can be used to create instances that should be always available and may not require artist's manual creation (e.g. `workfile`). Should not create duplicated instance and should raise `AutoCreationSkipped` exception when did not create any instance to speed up resetting of context.
Auto-creators are automatically executed when `CreateContext` is reset. They can be used to create instances that should be always available and may not require artist's manual creation (e.g. `workfile`). Should not create duplicated instance and validate existence before creates a new. Method `remove_instances` is implemented to do nothing.
## Host
Host implementation should be main entrance for creators how their logic should work. In most of cases must store data somewhere ideally to workfile if host has workfile and it's possible.
Host implementation must have available these functions to be able handle creation changes.
### List all created instances (`list_instances`)
List of all instances for current context (from workfile). Each item is dictionary with all data that are stored. Creators must implement their creation so host will be able to find the instance with this function.
### Remove instances (`remove_instances`)
Remove instance from context (from workfile). This must remove all metadata about instance so instance is not retrieved with `list_instances`. This is default host implementation of instance removement. Creator can do more cleanup before this function is called and can stop calling of this function completely (e.g. when Creator removed node where are also stored metadata).
### Update instances (`update_instances`)
Instance data has changed and update of changes should be saved so next call of `list_instances` will return modified values.
Host implementation must have available global context metadata handler functions. One to get current context data and second to update them. Currently are to context data stored only context publish plugin attribute values.
### Get global context data (`get_context_data`)
There are data that are not specific for any instance but are specific for whole context (e.g. Context plugins values).
@ -77,5 +74,5 @@ There are data that are not specific for any instance but are specific for whole
### Update global context data (`update_context_data`)
Update global context data.
### Get context title (`get_context_title`)
This is optional but is recommended. String returned from this function will be shown in UI.
### Optional title of context
It is recommended to implement `get_context_title` function. String returned from this function will be shown in UI as context in which artist is.