moved dict mutable entity to special file

This commit is contained in:
iLLiCiTiT 2021-02-05 17:11:49 +01:00
parent 8abe266995
commit cebb44e8fd
2 changed files with 368 additions and 358 deletions

View file

@ -0,0 +1,368 @@
import copy
from .lib import NOT_SET
from .constants import (
OverrideState,
METADATA_KEYS,
M_DYNAMIC_KEY_LABEL,
M_ENVIRONMENT_KEY
)
from . import ItemEntity
class DictMutableKeysEntity(ItemEntity):
schema_types = ["dict-modifiable"]
_miss_arg = object()
def __getitem__(self, key):
return self.children_by_key[key]
def __setitem__(self, key, value):
self.set_value_for_key(key, value)
def __iter__(self):
for key in self.keys():
yield key
def pop(self, key, default=_miss_arg):
if key not in self.children_by_key:
if default is self._miss_arg:
raise KeyError("Key \"{}\" not found.".format(key))
return default
child_obj = self.children_by_key.pop(key)
self.children.remove(child_obj)
self.on_value_change()
return child_obj
def get(self, key, default=None):
return self.children_by_key.get(key, default)
def keys(self):
return self.children_by_key.keys()
def values(self):
return self.children_by_key.values()
def items(self):
return self.children_by_key.items()
def clear(self):
for key in tuple(self.children_by_key.keys()):
self.pop(key)
def change_key(self, old_key, new_key):
if new_key == old_key:
return
self.children_by_key[new_key] = self.children_by_key.pop(old_key)
def change_child_key(self, child_entity, new_key):
old_key = None
for key, child in self.children_by_key.items():
if child is child_entity:
old_key = key
break
self.change_key(old_key, new_key)
def get_child_key(self, child_entity):
for key, child in self.children_by_key.items():
if child is child_entity:
return key
return None
def add_new_key(self, key):
new_child = self.create_schema_object(self.item_schema, self, True)
self.children.append(new_child)
self.children_by_key[key] = new_child
return new_child
def item_initalization(self):
self.default_metadata = {}
self.studio_override_metadata = {}
self.project_override_metadata = {}
# current_metadata are still when schema is loaded
self.current_metadata = {}
self.valid_value_types = (dict, )
self.value_on_not_set = {}
self.children = []
self.children_by_key = {}
self._current_value = NOT_SET
self.value_is_env_group = (
self.schema_data.get("value_is_env_group") or False
)
self.required_keys = self.schema_data.get("required_keys") or []
self.collapsible_key = self.schema_data.get("collapsable_key") or False
# GUI attributes
self.hightlight_content = (
self.schema_data.get("highlight_content") or False
)
self.collapsible = self.schema_data.get("collapsable", True)
self.collapsed = self.schema_data.get("collapsed", True)
object_type = self.schema_data["object_type"]
if not isinstance(object_type, dict):
# Backwards compatibility
object_type = {
"type": object_type
}
input_modifiers = self.schema_data.get("input_modifiers") or {}
if input_modifiers:
self.log.warning((
"Used deprecated key `input_modifiers` to define item."
" Rather use `object_type` as dictionary with modifiers."
))
object_type.update(input_modifiers)
self.item_schema = object_type
if self.value_is_env_group:
self.item_schema["env_group_key"] = ""
if not self.group_item:
self.is_group = True
def schema_validations(self):
super(DictMutableKeysEntity, self).schema_validations()
# TODO Ability to store labels should be defined with different key
if self.collapsible_key and not self.file_item:
raise ValueError((
"{}: Modifiable dictionary with collapsible keys is not under"
" file item so can't store metadata."
).format(self.path))
for child_obj in self.children:
child_obj.schema_validations()
def get_child_path(self, child_obj):
result_key = None
for key, _child_obj in self.children_by_key.items():
if _child_obj is child_obj:
result_key = key
break
if result_key is None:
raise ValueError("Didn't found child {}".format(child_obj))
return "/".join([self.path, result_key])
def set_value_for_key(self, key, value, batch=False):
# TODO Check for value type if is Settings entity?
child_obj = self.children_by_key.get(key)
if not child_obj:
child_obj = self.add_new_key(key)
child_obj.set_value(value)
if not batch:
self.on_value_change()
def on_change(self):
# TODO implement
pass
def on_child_change(self, child_obj):
# TODO implement
print("{} on_child_change not yet implemented".format(
self.__class__.__name__
))
def _metadata_for_current_state(self):
if (
self.override_state is OverrideState.PROJECT
and self.project_override_value is not NOT_SET
):
previous_metadata = self.project_override_value
elif self.studio_override_value is not NOT_SET:
previous_metadata = self.studio_override_metadata
else:
previous_metadata = self.default_metadata
return copy.deepcopy(previous_metadata)
def get_metadata_from_value(self, value, previous_metadata=None):
"""Get metada for entered value.
Method may modify entered value object in case that contain .
"""
metadata = {}
if not isinstance(value, dict):
return metadata
# Fill label metadata
# - first check if value contain them
if M_DYNAMIC_KEY_LABEL in value:
metadata[M_DYNAMIC_KEY_LABEL] = value.pop(M_DYNAMIC_KEY_LABEL)
# - check if metadata for current state contain metadata
elif M_DYNAMIC_KEY_LABEL in previous_metadata:
# Get previous metadata fo current state if were not entered
if previous_metadata is None:
previous_metadata = self._metadata_for_current_state()
# Create copy to not affect data passed with arguments
label_metadata = copy.deepcopy(
previous_metadata[M_DYNAMIC_KEY_LABEL]
)
for key in tuple(label_metadata.keys()):
if key not in value:
label_metadata.pop(key)
metadata[M_DYNAMIC_KEY_LABEL] = label_metadata
# Pop all other metadata keys from value
for key in METADATA_KEYS:
if key in value:
value.pop(key)
# Add environment metadata
if self.is_env_group:
metadata[M_ENVIRONMENT_KEY] = {
self.env_group_key: list(value.keys())
}
return metadata
def set_value(self, value):
for _key, _value in value.items():
self.set_value_for_key(_key, _value, True)
self.on_value_change()
def on_value_change(self):
raise NotImplementedError(self.__class__.__name__)
def set_override_state(self, state):
# TODO change metadata
self.override_state = state
if not self.has_default_value and state > OverrideState.DEFAULTS:
# Ignore if is dynamic item and use default in that case
if not self.is_dynamic_item and not self.is_in_dynamic_item:
raise DefaultsNotDefined(self)
using_overrides = True
if (
state is OverrideState.PROJECT
and self.project_override_value is not NOT_SET
):
value = self.project_override_value
metadata = self.project_override_metadata
elif self.studio_override_value is not NOT_SET:
value = self.studio_override_value
metadata = self.studio_override_metadata
else:
using_overrides = False
value = self.default_value
metadata = self.default_metadata
# TODO REQUIREMENT value must be stored to _current_value
# - current value must not be dynamic!!!
# - it is required to update metadata on the fly
if value is NOT_SET:
value = self.value_on_not_set
new_value = copy.deepcopy(value)
self._current_value = new_value
# It is important to pass `new_value`!!!
self.current_metadata = self.get_metadata_from_value(
new_value, metadata
)
# Simulate `clear` method without triggering value change
for key in tuple(self.children_by_key.keys()):
child_obj = self.children_by_key.pop(key)
self.children.remove(child_obj)
# Create new children
for _key, _value in self._current_value.items():
child_obj = self.add_new_key(_key)
child_obj.update_default_value(_value)
if using_overrides:
if state is OverrideState.STUDIO:
child_obj.update_studio_values(value)
else:
child_obj.update_project_values(value)
child_obj.set_override_state(state)
@property
def value(self):
return self._current_value
@property
def has_unsaved_changes(self):
pass
@property
def child_has_studio_override(self):
pass
@property
def child_is_modified(self):
pass
@property
def child_has_project_override(self):
if self.override_state is OverrideState.PROJECT:
# TODO implement
pass
return False
def settings_value(self):
if self.override_state is OverrideState.NOT_DEFINED:
return NOT_SET
if self.is_group:
if self.override_state is OverrideState.STUDIO:
if not self._has_studio_override:
return NOT_SET
elif self.override_state is OverrideState.PROJECT:
if not self._has_project_override:
return NOT_SET
output = copy.deepcopy(self._current_value)
output.update(copy.deepcopy(self.current_metadata))
return output
def _discard_changes(self, *args):
pass
def remove_overrides(self):
pass
def reset_to_pype_default(self):
pass
def set_as_overriden(self):
pass
def set_studio_default(self):
pass
def _prepare_value(self, value):
metadata = {}
if isinstance(value, dict):
for key in METADATA_KEYS:
if key in value:
metadata[key] = value.pop(key)
return value, metadata
def update_default_value(self, value):
self.has_default_value = value is not NOT_SET
value, metadata = self._prepare_value(value)
self.default_value = value
self.default_metadata = metadata
def update_studio_values(self, value):
value, metadata = self._prepare_value(value)
self.project_override_value = value
self.studio_override_metadata = metadata
def update_project_values(self, value):
value, metadata = self._prepare_value(value)
self.studio_override_value = value
self.project_override_metadata = metadata

View file

@ -214,364 +214,6 @@ class GUIEntity(ItemEntity):
self.require_key = False
class DictMutableKeysEntity(ItemEntity):
schema_types = ["dict-modifiable"]
_miss_arg = object()
def __getitem__(self, key):
return self.children_by_key[key]
def __setitem__(self, key, value):
self.set_value_for_key(key, value)
def __iter__(self):
for key in self.keys():
yield key
def pop(self, key, default=_miss_arg):
if key not in self.children_by_key:
if default is self._miss_arg:
raise KeyError("Key \"{}\" not found.".format(key))
return default
child_obj = self.children_by_key.pop(key)
self.children.remove(child_obj)
self.on_value_change()
return child_obj
def get(self, key, default=None):
return self.children_by_key.get(key, default)
def keys(self):
return self.children_by_key.keys()
def values(self):
return self.children_by_key.values()
def items(self):
return self.children_by_key.items()
def clear(self):
for key in tuple(self.children_by_key.keys()):
self.pop(key)
def change_key(self, old_key, new_key):
if new_key == old_key:
return
self.children_by_key[new_key] = self.children_by_key.pop(old_key)
def change_child_key(self, child_entity, new_key):
old_key = None
for key, child in self.children_by_key.items():
if child is child_entity:
old_key = key
break
self.change_key(old_key, new_key)
def get_child_key(self, child_entity):
for key, child in self.children_by_key.items():
if child is child_entity:
return key
return None
def add_new_key(self, key):
new_child = self.create_schema_object(self.item_schema, self, True)
self.children.append(new_child)
self.children_by_key[key] = new_child
return new_child
def item_initalization(self):
self.default_metadata = {}
self.studio_override_metadata = {}
self.project_override_metadata = {}
# current_metadata are still when schema is loaded
self.current_metadata = {}
self.valid_value_types = (dict, )
self.value_on_not_set = {}
self.children = []
self.children_by_key = {}
self._current_value = NOT_SET
self.value_is_env_group = (
self.schema_data.get("value_is_env_group") or False
)
self.required_keys = self.schema_data.get("required_keys") or []
self.collapsible_key = self.schema_data.get("collapsable_key") or False
# GUI attributes
self.hightlight_content = (
self.schema_data.get("highlight_content") or False
)
self.collapsible = self.schema_data.get("collapsable", True)
self.collapsed = self.schema_data.get("collapsed", True)
object_type = self.schema_data["object_type"]
if not isinstance(object_type, dict):
# Backwards compatibility
object_type = {
"type": object_type
}
input_modifiers = self.schema_data.get("input_modifiers") or {}
if input_modifiers:
self.log.warning((
"Used deprecated key `input_modifiers` to define item."
" Rather use `object_type` as dictionary with modifiers."
))
object_type.update(input_modifiers)
self.item_schema = object_type
if self.value_is_env_group:
self.item_schema["env_group_key"] = ""
if not self.group_item:
self.is_group = True
def schema_validations(self):
super(DictMutableKeysEntity, self).schema_validations()
# TODO Ability to store labels should be defined with different key
if self.collapsible_key and not self.file_item:
raise ValueError((
"{}: Modifiable dictionary with collapsible keys is not under"
" file item so can't store metadata."
).format(self.path))
for child_obj in self.children:
child_obj.schema_validations()
def get_child_path(self, child_obj):
result_key = None
for key, _child_obj in self.children_by_key.items():
if _child_obj is child_obj:
result_key = key
break
if result_key is None:
raise ValueError("Didn't found child {}".format(child_obj))
return "/".join([self.path, result_key])
def set_value_for_key(self, key, value, batch=False):
# TODO Check for value type if is Settings entity?
child_obj = self.children_by_key.get(key)
if not child_obj:
child_obj = self.add_new_key(key)
child_obj.set_value(value)
if not batch:
self.on_value_change()
def on_change(self):
# TODO implement
pass
def on_child_change(self, child_obj):
# TODO implement
print("{} on_child_change not yet implemented".format(
self.__class__.__name__
))
def _metadata_for_current_state(self):
if (
self.override_state is OverrideState.PROJECT
and self.project_override_value is not NOT_SET
):
previous_metadata = self.project_override_value
elif self.studio_override_value is not NOT_SET:
previous_metadata = self.studio_override_metadata
else:
previous_metadata = self.default_metadata
return copy.deepcopy(previous_metadata)
def get_metadata_from_value(self, value, previous_metadata=None):
"""Get metada for entered value.
Method may modify entered value object in case that contain .
"""
metadata = {}
if not isinstance(value, dict):
return metadata
# Fill label metadata
# - first check if value contain them
if M_DYNAMIC_KEY_LABEL in value:
metadata[M_DYNAMIC_KEY_LABEL] = value.pop(M_DYNAMIC_KEY_LABEL)
# - check if metadata for current state contain metadata
elif M_DYNAMIC_KEY_LABEL in previous_metadata:
# Get previous metadata fo current state if were not entered
if previous_metadata is None:
previous_metadata = self._metadata_for_current_state()
# Create copy to not affect data passed with arguments
label_metadata = copy.deepcopy(
previous_metadata[M_DYNAMIC_KEY_LABEL]
)
for key in tuple(label_metadata.keys()):
if key not in value:
label_metadata.pop(key)
metadata[M_DYNAMIC_KEY_LABEL] = label_metadata
# Pop all other metadata keys from value
for key in METADATA_KEYS:
if key in value:
value.pop(key)
# Add environment metadata
if self.is_env_group:
metadata[M_ENVIRONMENT_KEY] = {
self.env_group_key: list(value.keys())
}
return metadata
def set_value(self, value):
for _key, _value in value.items():
self.set_value_for_key(_key, _value, True)
self.on_value_change()
def on_value_change(self):
raise NotImplementedError(self.__class__.__name__)
def set_override_state(self, state):
# TODO change metadata
self.override_state = state
if not self.has_default_value and state > OverrideState.DEFAULTS:
# Ignore if is dynamic item and use default in that case
if not self.is_dynamic_item and not self.is_in_dynamic_item:
raise DefaultsNotDefined(self)
using_overrides = True
if (
state is OverrideState.PROJECT
and self.project_override_value is not NOT_SET
):
value = self.project_override_value
metadata = self.project_override_metadata
elif self.studio_override_value is not NOT_SET:
value = self.studio_override_value
metadata = self.studio_override_metadata
else:
using_overrides = False
value = self.default_value
metadata = self.default_metadata
# TODO REQUIREMENT value must be stored to _current_value
# - current value must not be dynamic!!!
# - it is required to update metadata on the fly
if value is NOT_SET:
value = self.value_on_not_set
new_value = copy.deepcopy(value)
self._current_value = new_value
# It is important to pass `new_value`!!!
self.current_metadata = self.get_metadata_from_value(
new_value, metadata
)
# Simulate `clear` method without triggering value change
for key in tuple(self.children_by_key.keys()):
child_obj = self.children_by_key.pop(key)
self.children.remove(child_obj)
# Create new children
for _key, _value in self._current_value.items():
child_obj = self.add_new_key(_key)
child_obj.update_default_value(_value)
if using_overrides:
if state is OverrideState.STUDIO:
child_obj.update_studio_values(value)
else:
child_obj.update_project_values(value)
child_obj.set_override_state(state)
@property
def value(self):
return self._current_value
@property
def has_unsaved_changes(self):
pass
@property
def child_has_studio_override(self):
pass
@property
def child_is_modified(self):
pass
@property
def child_has_project_override(self):
if self.override_state is OverrideState.PROJECT:
# TODO implement
pass
return False
def settings_value(self):
if self.override_state is OverrideState.NOT_DEFINED:
return NOT_SET
if self.is_group:
if self.override_state is OverrideState.STUDIO:
if not self._has_studio_override:
return NOT_SET
elif self.override_state is OverrideState.PROJECT:
if not self._has_project_override:
return NOT_SET
output = copy.deepcopy(self._current_value)
output.update(copy.deepcopy(self.current_metadata))
return output
def _discard_changes(self, *args):
pass
def remove_overrides(self):
pass
def reset_to_pype_default(self):
pass
def set_as_overriden(self):
pass
def set_studio_default(self):
pass
def _prepare_value(self, value):
metadata = {}
if isinstance(value, dict):
for key in METADATA_KEYS:
if key in value:
metadata[key] = value.pop(key)
return value, metadata
def update_default_value(self, value):
self.has_default_value = value is not NOT_SET
value, metadata = self._prepare_value(value)
self.default_value = value
self.default_metadata = metadata
def update_studio_values(self, value):
value, metadata = self._prepare_value(value)
self.project_override_value = value
self.studio_override_metadata = metadata
def update_project_values(self, value):
value, metadata = self._prepare_value(value)
self.studio_override_value = value
self.project_override_metadata = metadata
class PathEntity(ItemEntity):
schema_types = ["path-widget"]
platforms = ("windows", "darwin", "linux")