mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-27 14:22:37 +01:00
added basic docstrings to root entities
This commit is contained in:
parent
fdbb50c9f3
commit
a996532061
2 changed files with 176 additions and 38 deletions
|
|
@ -249,15 +249,17 @@ class BaseItemEntity(BaseEntity):
|
|||
|
||||
@abstractmethod
|
||||
def set_override_state(self, state):
|
||||
"""This method should set override state and refresh data.
|
||||
"""Set override state and trigger it on children.
|
||||
|
||||
Discard all changes in hierarchy and use values, metadata and
|
||||
all kind of states for defined state.
|
||||
Method discard all changes in hierarchy and use values, metadata
|
||||
and all kind of values for defined override state. May be used to
|
||||
apply updated values (default, studio overrides, project overrides).
|
||||
|
||||
Always should start on root entity and when triggered them must be
|
||||
called on all entities in hierarchy.
|
||||
Should start on root entity and when triggered then must be called on
|
||||
all entities in hierarchy.
|
||||
|
||||
TODO: add validation that parent's state is same as want to set.
|
||||
Args:
|
||||
state (OverrideState): State to which should be data changed.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -32,17 +32,26 @@ from pype.settings.lib import (
|
|||
|
||||
|
||||
class RootEntity(BaseItemEntity):
|
||||
"""Abstract class for root entities.
|
||||
|
||||
Root entity is top hierarchy entity without parent. Should care about
|
||||
saving and must have access to all entities in it's scope.
|
||||
"""
|
||||
schema_types = ["root"]
|
||||
|
||||
def __init__(self, schema_data, reset):
|
||||
super(RootEntity, self).__init__(schema_data)
|
||||
self.root_item = self
|
||||
self.item_initalization()
|
||||
if reset:
|
||||
self.reset()
|
||||
|
||||
@abstractmethod
|
||||
def reset(self):
|
||||
"""Reset values and entities to initial state.
|
||||
|
||||
Reload settings and entities should reset their changes or be
|
||||
recreated.
|
||||
"""
|
||||
pass
|
||||
|
||||
def __getitem__(self, key):
|
||||
|
|
@ -58,6 +67,11 @@ class RootEntity(BaseItemEntity):
|
|||
def get(self, key, default=None):
|
||||
return self.non_gui_children.get(key, default)
|
||||
|
||||
def set(self, value):
|
||||
"""Set value."""
|
||||
for _key, _value in value.items():
|
||||
self.non_gui_children[_key].set(_value)
|
||||
|
||||
def keys(self):
|
||||
return self.non_gui_children.keys()
|
||||
|
||||
|
|
@ -82,7 +96,7 @@ class RootEntity(BaseItemEntity):
|
|||
child_obj = self.create_schema_object(children_schema, self)
|
||||
self.children.append(child_obj)
|
||||
added_children.append(child_obj)
|
||||
if type(child_obj) in self._gui_types:
|
||||
if isinstance(child_obj, self._gui_types):
|
||||
continue
|
||||
|
||||
if child_obj.key in self.non_gui_children:
|
||||
|
|
@ -96,22 +110,36 @@ class RootEntity(BaseItemEntity):
|
|||
self.gui_layout.append(child_obj)
|
||||
|
||||
def item_initalization(self):
|
||||
# Store `self` to `root_item` for children entities
|
||||
self.root_item = self
|
||||
|
||||
self._loaded_types = None
|
||||
self._gui_types = None
|
||||
|
||||
# Children are stored by key as keys are immutable and are defined by
|
||||
# schema
|
||||
self.valid_value_types = (dict, )
|
||||
|
||||
self.children = []
|
||||
self.non_gui_children = {}
|
||||
self.gui_layout = []
|
||||
|
||||
self._add_children(self.schema_data)
|
||||
|
||||
for children in self.children:
|
||||
children.schema_validations()
|
||||
|
||||
def create_schema_object(self, schema_data, *args, **kwargs):
|
||||
"""Create entity by entered schema data.
|
||||
|
||||
Available entities are loaded on first run. Children entities can call
|
||||
this method.
|
||||
"""
|
||||
if self._loaded_types is None:
|
||||
# Load available entities
|
||||
from pype.settings import entities
|
||||
|
||||
# Define known abstract classes
|
||||
known_abstract_classes = (
|
||||
entities.BaseEntity,
|
||||
entities.BaseItemEntity,
|
||||
|
|
@ -120,26 +148,32 @@ class RootEntity(BaseItemEntity):
|
|||
)
|
||||
|
||||
self._loaded_types = {}
|
||||
self._gui_types = []
|
||||
_gui_types = []
|
||||
for attr in dir(entities):
|
||||
item = getattr(entities, attr)
|
||||
# Filter classes
|
||||
if not inspect.isclass(item):
|
||||
continue
|
||||
|
||||
# Skip classes that do not inherit from BaseEntity
|
||||
if not issubclass(item, entities.BaseEntity):
|
||||
continue
|
||||
|
||||
if inspect.isabstract(item):
|
||||
# Skip class that is abstract by design
|
||||
if item in known_abstract_classes:
|
||||
continue
|
||||
# Create an object to get crash and get traceback
|
||||
item()
|
||||
|
||||
# Backwards compatibility
|
||||
# Single entity may have multiple schema types
|
||||
for schema_type in item.schema_types:
|
||||
self._loaded_types[schema_type] = item
|
||||
|
||||
gui_type = getattr(item, "gui_type", False)
|
||||
if gui_type:
|
||||
self._gui_types.append(item)
|
||||
if item.gui_type:
|
||||
_gui_types.append(item)
|
||||
self._gui_types = tuple(_gui_types)
|
||||
|
||||
klass = self._loaded_types.get(schema_data["type"])
|
||||
if not klass:
|
||||
|
|
@ -148,35 +182,47 @@ class RootEntity(BaseItemEntity):
|
|||
return klass(schema_data, *args, **kwargs)
|
||||
|
||||
def set_override_state(self, state):
|
||||
"""Set override state and trigger it on children.
|
||||
|
||||
Method will discard all changes in hierarchy and use values, metadata
|
||||
and all kind of values for defined state.
|
||||
|
||||
Args:
|
||||
state (OverrideState): State to which should be data changed.
|
||||
"""
|
||||
self.override_state = state
|
||||
for child_obj in self.non_gui_children.values():
|
||||
child_obj.set_override_state(state)
|
||||
|
||||
def set(self, value):
|
||||
for _key, _value in value.items():
|
||||
self.non_gui_children[_key].set(_value)
|
||||
|
||||
def on_change(self):
|
||||
"""Trigger callbacks on change."""
|
||||
for callback in self.on_change_callbacks:
|
||||
callback()
|
||||
|
||||
def on_child_change(self, child_obj):
|
||||
def on_child_change(self, _child_entity):
|
||||
"""Whan any children has changed."""
|
||||
self.on_change()
|
||||
|
||||
def get_child_path(self, child_obj):
|
||||
for key, _child_obj in self.non_gui_children.items():
|
||||
if _child_obj is child_obj:
|
||||
def get_child_path(self, child_entity):
|
||||
"""Return path of children entity"""
|
||||
for key, _child_entity in self.non_gui_children.items():
|
||||
if _child_entity is child_entity:
|
||||
return key
|
||||
raise ValueError("Didn't found child {}".format(child_obj))
|
||||
raise ValueError("Didn't found child {}".format(child_entity))
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""Value for current override state without metadata."""
|
||||
output = {}
|
||||
for key, child_obj in self.non_gui_children.items():
|
||||
output[key] = child_obj.value
|
||||
return output
|
||||
|
||||
def settings_value(self):
|
||||
"""Value for current override state with metadata.
|
||||
|
||||
This is what should be stored on save method.
|
||||
"""
|
||||
if self.override_state is OverrideState.NOT_DEFINED:
|
||||
return NOT_SET
|
||||
|
||||
|
|
@ -201,6 +247,13 @@ class RootEntity(BaseItemEntity):
|
|||
|
||||
@property
|
||||
def child_has_studio_override(self):
|
||||
"""Any children has studio override.
|
||||
|
||||
Return's relevant data only if override state is STUDIO or PROJECT.
|
||||
|
||||
Returns:
|
||||
bool: True if any children has studio overrides.
|
||||
"""
|
||||
if self.override_state >= OverrideState.STUDIO:
|
||||
for child_obj in self.non_gui_children.values():
|
||||
if child_obj.child_has_studio_override:
|
||||
|
|
@ -209,6 +262,13 @@ class RootEntity(BaseItemEntity):
|
|||
|
||||
@property
|
||||
def child_has_project_override(self):
|
||||
"""Any children has project override.
|
||||
|
||||
Return's relevant data only if override state is PROJECT.
|
||||
|
||||
Returns:
|
||||
bool: True if any children has project overrides.
|
||||
"""
|
||||
if self.override_state >= OverrideState.PROJECT:
|
||||
for child_obj in self.non_gui_children.values():
|
||||
if child_obj.child_has_project_override:
|
||||
|
|
@ -217,63 +277,94 @@ class RootEntity(BaseItemEntity):
|
|||
|
||||
@property
|
||||
def has_unsaved_changes(self):
|
||||
"""Entity contain unsaved changes.
|
||||
|
||||
Root on it's own can't have any modifications so looks only on
|
||||
children.
|
||||
|
||||
Returns:
|
||||
bool: True if has unsaved changes.
|
||||
"""
|
||||
return self.child_is_modified
|
||||
|
||||
@property
|
||||
def child_is_modified(self):
|
||||
"""Any children has unsaved changed."""
|
||||
for child_obj in self.non_gui_children.values():
|
||||
if child_obj.has_unsaved_changes:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _discard_changes(self, on_change_trigger):
|
||||
"""Implementation of abstract method only trigger children callback."""
|
||||
for child_obj in self.non_gui_children.values():
|
||||
child_obj.discard_changes(on_change_trigger)
|
||||
|
||||
def add_to_studio_default(self):
|
||||
"""Implementation of abstract method only trigger children callback."""
|
||||
for child_obj in self.non_gui_children.values():
|
||||
child_obj.add_to_studio_default()
|
||||
|
||||
def _remove_from_studio_default(self, on_change_trigger):
|
||||
"""Implementation of abstract method only trigger children callback."""
|
||||
for child_obj in self.non_gui_children.values():
|
||||
child_obj.remove_from_studio_default(on_change_trigger)
|
||||
|
||||
def add_to_project_override(self):
|
||||
"""Implementation of abstract method only trigger children callback."""
|
||||
for child_obj in self.non_gui_children.values():
|
||||
child_obj.add_to_project_override()
|
||||
|
||||
def _remove_from_project_override(self):
|
||||
"""Implementation of abstract method only trigger children callback."""
|
||||
for child_obj in self.non_gui_children.values():
|
||||
child_obj.remove_from_project_override()
|
||||
|
||||
def save(self):
|
||||
"""Save values for current override state.
|
||||
|
||||
Values are stored with current values and modifications.
|
||||
"""
|
||||
if self.override_state is OverrideState.NOT_DEFINED:
|
||||
raise ValueError(
|
||||
"Can't save if override state is set to NOT_DEFINED"
|
||||
)
|
||||
|
||||
if self.override_state is OverrideState.DEFAULTS:
|
||||
self.save_default_values()
|
||||
self._save_default_values()
|
||||
|
||||
elif self.override_state is OverrideState.STUDIO:
|
||||
self.save_studio_values()
|
||||
self._save_studio_values()
|
||||
|
||||
elif self.override_state is OverrideState.PROJECT:
|
||||
self.save_project_values()
|
||||
self._save_project_values()
|
||||
|
||||
# Trigger reset to reload entities
|
||||
self.reset()
|
||||
|
||||
@abstractmethod
|
||||
def defaults_dir(self):
|
||||
"""Abstract method to return directory path to defaults.
|
||||
|
||||
Implementation of `_save_default_values` requires defaults dir where
|
||||
default data will be stored.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def validate_defaults_to_save(self, value):
|
||||
def _validate_defaults_to_save(self, value):
|
||||
"""Validate default values before save."""
|
||||
pass
|
||||
|
||||
def save_default_values(self):
|
||||
def _save_default_values(self):
|
||||
"""Save default values.
|
||||
|
||||
Do not call this method, always use `save`. Manually called method
|
||||
won't save current values as defaults if override state is not set to
|
||||
DEFAULTS.
|
||||
"""
|
||||
settings_value = self.settings_value()
|
||||
self.validate_defaults_to_save(settings_value)
|
||||
self._validate_defaults_to_save(settings_value)
|
||||
|
||||
defaults_dir = self.defaults_dir()
|
||||
for file_path, value in settings_value.items():
|
||||
|
|
@ -284,42 +375,63 @@ class RootEntity(BaseItemEntity):
|
|||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
print("Saving data to: ", subpath)
|
||||
self.log.debug("Saving data to: {}\n{}".format(subpath, value))
|
||||
with open(output_path, "w") as file_stream:
|
||||
json.dump(value, file_stream, indent=4)
|
||||
|
||||
@abstractmethod
|
||||
def save_studio_values(self):
|
||||
def _save_studio_values(self):
|
||||
"""Save studio override values."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_project_values(self):
|
||||
def _save_project_values(self):
|
||||
"""Save project override values."""
|
||||
pass
|
||||
|
||||
def is_in_defaults_state(self):
|
||||
"""Api callback to check if current state is DEFAULTS."""
|
||||
return self.override_state is OverrideState.DEFAULTS
|
||||
|
||||
def is_in_studio_state(self):
|
||||
"""Api callback to check if current state is STUDIO."""
|
||||
return self.override_state is OverrideState.STUDIO
|
||||
|
||||
def is_in_project_state(self):
|
||||
"""Api callback to check if current state is PROJECT."""
|
||||
return self.override_state is OverrideState.PROJECT
|
||||
|
||||
def set_defaults_state(self):
|
||||
"""Change override state to DEFAULTS."""
|
||||
self.set_override_state(OverrideState.DEFAULTS)
|
||||
|
||||
def set_studio_state(self):
|
||||
"""Change override state to STUDIO."""
|
||||
self.set_override_state(OverrideState.STUDIO)
|
||||
|
||||
def set_project_state(self):
|
||||
"""Change override state to PROJECT."""
|
||||
self.set_override_state(OverrideState.PROJECT)
|
||||
|
||||
|
||||
class SystemSettings(RootEntity):
|
||||
"""Root entity for system settings.
|
||||
|
||||
Allows to modify system settings via entity system and loaded schemas.
|
||||
|
||||
Args:
|
||||
set_studio_state (bool): Set studio values on initialization. By
|
||||
default is set to True.
|
||||
reset (bool): Reset values on initialization. By default is set to
|
||||
True.
|
||||
schema_data (dict): Pass schema data to entity. This is for development
|
||||
and debugging purposes.
|
||||
"""
|
||||
def __init__(
|
||||
self, set_studio_state=True, reset=True, schema_data=None
|
||||
):
|
||||
if schema_data is None:
|
||||
# Load system schemas
|
||||
schema_data = gui_schema("system_schema", "schema_main")
|
||||
|
||||
super(SystemSettings, self).__init__(schema_data, reset)
|
||||
|
|
@ -339,6 +451,14 @@ class SystemSettings(RootEntity):
|
|||
child_obj.update_studio_values(value)
|
||||
|
||||
def reset(self, new_state=None):
|
||||
"""Discard changes and reset entit's values.
|
||||
|
||||
Reload default values and studio override values and update entities.
|
||||
|
||||
Args:
|
||||
new_state (OverrideState): It is possible to change override state
|
||||
during reset. Current state is used if not defined.
|
||||
"""
|
||||
if new_state is None:
|
||||
new_state = self.override_state
|
||||
|
||||
|
|
@ -352,19 +472,29 @@ class SystemSettings(RootEntity):
|
|||
self.set_override_state(new_state)
|
||||
|
||||
def defaults_dir(self):
|
||||
"""Path to defaults directory.
|
||||
|
||||
Implementation of abstract method.
|
||||
"""
|
||||
return os.path.join(DEFAULTS_DIR, SYSTEM_SETTINGS_KEY)
|
||||
|
||||
def save_studio_values(self):
|
||||
def _save_studio_values(self):
|
||||
settings_value = self.settings_value()
|
||||
self.validate_duplicated_env_group(settings_value)
|
||||
print("Saving system settings: ", json.dumps(settings_value, indent=4))
|
||||
|
||||
self._validate_duplicated_env_group(settings_value)
|
||||
|
||||
self.log.debug("Saving system settings: {}".format(
|
||||
json.dumps(settings_value, indent=4)
|
||||
))
|
||||
save_studio_settings(settings_value)
|
||||
|
||||
def validate_defaults_to_save(self, value):
|
||||
self.validate_duplicated_env_group(value)
|
||||
def _validate_defaults_to_save(self, value):
|
||||
"""Valiations of default values before save."""
|
||||
self._validate_duplicated_env_group(value)
|
||||
|
||||
def _validate_duplicated_env_group(self, value, override_state=None):
|
||||
""" Validate duplicated environment groups.
|
||||
|
||||
def validate_duplicated_env_group(self, value, override_state=None):
|
||||
"""
|
||||
Raises:
|
||||
DuplicatedEnvGroups: When value contain duplicated env groups.
|
||||
"""
|
||||
|
|
@ -381,5 +511,11 @@ class SystemSettings(RootEntity):
|
|||
# Check if final_value contain duplicated environment groups
|
||||
find_environments(final_value)
|
||||
|
||||
def save_project_values(self):
|
||||
def _save_project_values(self):
|
||||
"""System settings can't have project overrides.
|
||||
|
||||
Raises:
|
||||
ValueError: Raise when called as entity can't use or store project
|
||||
overrides.
|
||||
"""
|
||||
raise ValueError("System settings can't save project overrides.")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue