From 4a7bc9a571e5aafa54368038edd0bbbff732af16 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 23 Feb 2024 15:27:03 +0100 Subject: [PATCH] 'prepare_template_data' supports subdict values --- client/ayon_core/lib/plugin_tools.py | 149 ++++++++++++++++++++------- 1 file changed, 113 insertions(+), 36 deletions(-) diff --git a/client/ayon_core/lib/plugin_tools.py b/client/ayon_core/lib/plugin_tools.py index d204fc2c8f..5ad4da88b9 100644 --- a/client/ayon_core/lib/plugin_tools.py +++ b/client/ayon_core/lib/plugin_tools.py @@ -1,54 +1,131 @@ # -*- coding: utf-8 -*- -"""Avalon/Pyblish plugin tools.""" +"""AYON plugin tools.""" import os import logging import re +import collections log = logging.getLogger(__name__) +CAPITALIZE_REGEX = re.compile(r"[a-zA-Z0-9]") + + +def _capitalize_value(value): + """Capitalize first char of value. + + Function finds first available character or number in passed string + and uppers the character. + + Example: + >>> _capitalize_value("host") + 'Host' + >>> _capitalize_value("01_shot") + '01_shot' + >>> _capitalize_value("_shot") + '_Shot' + + Args: + value (str): Value where to capitalize first character. + """ + + # - conditions are because of possible index errors + # - regex is to skip symbols that are not chars or numbers + # - e.g. "{key}" which starts with curly bracket + capitalized = "" + for idx in range(len(value or "")): + char = value[idx] + if not CAPITALIZE_REGEX.match(char): + capitalized += char + else: + capitalized += char.upper() + capitalized += value[idx + 1:] + break + return capitalized + + +def _separate_keys_and_value(data): + valid_items = [] + hierachy_queue = collections.deque() + hierachy_queue.append((data, [])) + while hierachy_queue: + item = hierachy_queue.popleft() + src_data, keys = item + if src_data is None: + continue + + if isinstance(src_data, (list, tuple, set)): + for idx, item in enumerate(src_data): + hierachy_queue.append((item, keys + [idx])) + continue + + if isinstance(src_data, dict): + for key, value in src_data.items(): + hierachy_queue.append((value, keys + [key])) + continue + + if keys: + valid_items.append((keys, src_data)) + return valid_items + def prepare_template_data(fill_pairs): + """Prepares formatted data for filling template. + + It produces multiple variants of keys (key, Key, KEY) to control + format of filled template. + + Example: + >>> src_data = { + ... "host": "maya", + ... } + >>> output = prepare_template_data(src_data) + >>> sorted(list(output.items())) # sort & list conversion for tests + [('HOST', 'MAYA'), ('Host', 'Maya'), ('host', 'maya')] + + Args: + fill_pairs (Union[dict[str, Any], Iterable[Tuple[str, Any]]]): The + value that are prepared for template. + + Returns: + dict[str, str]: Prepared values for template. """ - Prepares formatted data for filling template. - It produces multiple variants of keys (key, Key, KEY) to control - format of filled template. + valid_items = _separate_keys_and_value(fill_pairs) + output = {} + for item in valid_items: + keys, value = item + upper_value = value.upper() + capitalized_value = _capitalize_value(value) - Args: - fill_pairs (iterable) of tuples (key, value) - Returns: - (dict) - ('host', 'maya') > {'host':'maya', 'Host': 'Maya', 'HOST': 'MAYA'} - - """ - fill_data = {} - regex = re.compile(r"[a-zA-Z0-9]") - for key, value in dict(fill_pairs).items(): - # Handle cases when value is `None` (standalone publisher) - if value is None: + first_key = keys.pop(0) + if not keys: + output[first_key] = value + output[first_key.upper()] = upper_value + output[first_key.capitalize()] = capitalized_value continue - # Keep value as it is - fill_data[key] = value - # Both key and value are with upper case - fill_data[key.upper()] = value.upper() - # Capitalize only first char of value - # - conditions are because of possible index errors - # - regex is to skip symbols that are not chars or numbers - # - e.g. "{key}" which starts with curly bracket - capitalized = "" - for idx in range(len(value or "")): - char = value[idx] - if not regex.match(char): - capitalized += char + # Prepare 'normal', 'upper' and 'capitalized' variables + normal = output.setdefault(first_key, {}) + capitalized = output.setdefault(first_key.capitalize(), {}) + upper = output.setdefault(first_key.upper(), {}) + + keys_deque = collections.deque(keys) + while keys_deque: + key = keys_deque.popleft() + upper_key = key + if isinstance(key, str): + upper_key = key.upper() + + if not keys_deque: + # Fill value on last key + upper[upper_key] = upper_value + capitalized[key] = capitalized_value + normal[key] = value else: - capitalized += char.upper() - capitalized += value[idx + 1:] - break - - fill_data[key.capitalize()] = capitalized - - return fill_data + normal = normal.setdefault(key, {}) + capitalized = capitalized.setdefault(key, {}) + upper = upper.setdefault(upper_key, {}) + return output def source_hash(filepath, *args):