From df7924b85b3af5c3b2ce17a4bccdb91ed3529071 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 22 Aug 2024 17:01:57 +0200 Subject: [PATCH 001/150] adding CIRCUIT host to publish plugin host attribute --- client/ayon_core/plugins/publish/collect_audio.py | 1 + client/ayon_core/plugins/publish/collect_otio_review.py | 3 +-- client/ayon_core/plugins/publish/extract_burnin.py | 3 ++- client/ayon_core/plugins/publish/extract_review.py | 3 ++- client/ayon_core/plugins/publish/extract_thumbnail.py | 3 ++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_audio.py b/client/ayon_core/plugins/publish/collect_audio.py index c1633e414e..57c69ef2b2 100644 --- a/client/ayon_core/plugins/publish/collect_audio.py +++ b/client/ayon_core/plugins/publish/collect_audio.py @@ -39,6 +39,7 @@ class CollectAudio(pyblish.api.ContextPlugin): "blender", "houdini", "max", + "circuit", ] audio_product_name = "audioMain" diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 69cf9199e7..7d496052dc 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -21,8 +21,7 @@ class CollectOtioReview(pyblish.api.InstancePlugin): label = "Collect OTIO Review" order = pyblish.api.CollectorOrder - 0.078 - families = ["clip"] - hosts = ["resolve", "hiero", "flame"] + families = ["editorial.otio.review"] def process(self, instance): # Not all hosts can import this module. diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 58a032a030..f223059694 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -52,7 +52,8 @@ class ExtractBurnin(publish.Extractor): "houdini", "max", "blender", - "unreal" + "unreal", + "circuit", ] optional = True diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index c2793f98a2..0066d340ee 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -91,7 +91,8 @@ class ExtractReview(pyblish.api.InstancePlugin): "webpublisher", "aftereffects", "flame", - "unreal" + "unreal", + "circuit", ] # Supported extensions diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index d1b6e4e0cc..397abb87db 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -36,7 +36,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): "traypublisher", "substancepainter", "nuke", - "aftereffects" + "aftereffects", + "circuit", ] enabled = False From db46e329229c80fa241b09753a67de4d0df26141 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 1 Oct 2024 17:29:41 +0200 Subject: [PATCH 002/150] Update families list to include "deliveryProcess". - Update the families list in a plugin to include "deliveryProcess". --- client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index 60c92aa8b1..f255b9f9dc 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -22,7 +22,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): order = pyblish.api.ExtractorOrder - 0.01 label = "Extract Hierarchy To AYON" - families = ["clip", "shot"] + families = ["clip", "shot", "deliveryProcess"] def process(self, context): if not context.data.get("hierarchyContext"): From b09266b1d645bc424ac7c3358e6eee6970667327 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Nov 2024 14:23:08 +0100 Subject: [PATCH 003/150] Update dependency version constraints Adjusted the version constraint for a library to ensure compatibility by restricting it to less than 2.4.0. --- client/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pyproject.toml b/client/pyproject.toml index a0be9605b6..edf7f57317 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -15,6 +15,6 @@ qtawesome = "0.7.3" aiohttp-middlewares = "^2.0.0" Click = "^8" OpenTimelineIO = "0.16.0" -opencolorio = "^2.3.2" +opencolorio = "^2.3.2,<2.4.0" Pillow = "9.5.0" websocket-client = ">=0.40.0,<2" From d7470073621e534693e88ce235b764586b63f00e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Jan 2025 10:33:56 +0100 Subject: [PATCH 004/150] adding back what had been change by accidental commit Restored the original families to "editorial.otio.review" and added back the hosts for compatibility. This fixes issues caused by a previous accidental commit. --- client/ayon_core/plugins/publish/collect_otio_review.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 37706aae13..4708b0a97c 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -21,7 +21,8 @@ class CollectOtioReview(pyblish.api.InstancePlugin): label = "Collect OTIO Review" order = pyblish.api.CollectorOrder - 0.078 - families = ["editorial.otio.review"] + families = ["clip"] + hosts = ["resolve", "hiero", "flame"] def process(self, instance): # Not all hosts can import this module. From 90e3296bea5058d7c29d32f15805f45c4f40a1f0 Mon Sep 17 00:00:00 2001 From: Jose Caraballo Date: Sat, 25 Jan 2025 18:02:48 +0100 Subject: [PATCH 005/150] Support uppercase extensions. --- client/ayon_core/plugins/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 7c38b0453b..87f45781ab 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -200,7 +200,7 @@ class ExtractReview(pyblish.api.InstancePlugin): if input_ext.startswith("."): input_ext = input_ext[1:] - if input_ext not in self.supported_exts: + if input_ext.lower() not in self.supported_exts: self.log.info( "Representation has unsupported extension \"{}\"".format( input_ext From b15ead3c84a6680e12025894f0dee918edcfad60 Mon Sep 17 00:00:00 2001 From: Jose Caraballo Date: Mon, 27 Jan 2025 15:28:18 +0100 Subject: [PATCH 006/150] Convert extension to lowercase. --- client/ayon_core/plugins/publish/extract_review.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 87f45781ab..a3db16c898 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -196,11 +196,11 @@ class ExtractReview(pyblish.api.InstancePlugin): ).format(repre_name)) continue - input_ext = repre["ext"] + input_ext = repre["ext"].lower() if input_ext.startswith("."): input_ext = input_ext[1:] - if input_ext.lower() not in self.supported_exts: + if input_ext not in self.supported_exts: self.log.info( "Representation has unsupported extension \"{}\"".format( input_ext From 95743f29a3e67baa6d39f1975fd001575c8abdfb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 30 Jan 2025 14:35:23 +0100 Subject: [PATCH 007/150] Add ACES 1.3 Studio OCIO config option - Introduced new OCIO config for ACES 1.3 Studio. - Updated paths with relevant labels and descriptions. - Ensured compatibility with OCIO v2 requirements. --- server/settings/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/settings/main.py b/server/settings/main.py index 249bab85fd..c2f5c63f42 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -71,6 +71,12 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ + { + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", + "label": "ACES 1.3 Studio (OCIO v2)", + "description": ( + "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") + }, { "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "label": "ACES 1.2", From 8ff258983a54a7f665a12460e05eecfaf7a4426c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 30 Jan 2025 15:01:11 +0100 Subject: [PATCH 008/150] Update OCIO config path for clarity Refined the OCIO built-in paths to improve readability. Added a comment to ignore line length warning for better code style adherence. --- server/settings/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/main.py b/server/settings/main.py index c2f5c63f42..261bd7fc04 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,7 +72,7 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") From 1de8e300c78850a59d028104139c8bd93e834a96 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 11 Feb 2025 20:07:16 +0200 Subject: [PATCH 009/150] fix a typo --- client/ayon_core/pipeline/context_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index b9ae906ab4..386c7ba737 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -251,7 +251,7 @@ def uninstall_host(): pyblish.api.deregister_discovery_filter(filter_pyblish_plugins) deregister_loader_plugin_path(LOAD_PATH) deregister_inventory_action_path(INVENTORY_PATH) - log.info("Global plug-ins unregistred") + log.info("Global plug-ins unregistered") deregister_host() From bba0df9c0eac4e92b2eb2852736bfd183603657c Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 11 Feb 2025 20:07:37 +0200 Subject: [PATCH 010/150] don't `version_up` twice --- client/ayon_core/pipeline/context_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 386c7ba737..a809261fa2 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -617,7 +617,7 @@ def version_up_current_workfile(): last_workfile_path = get_last_workfile( work_root, file_template, data, extensions, True ) - new_workfile_path = version_up(last_workfile_path) + new_workfile_path = last_workfile_path if os.path.exists(new_workfile_path): new_workfile_path = version_up(new_workfile_path) host.save_workfile(new_workfile_path) From 99f60e10125489c4863f1d905fef9c1c6644c10e Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 11 Feb 2025 20:28:03 +0200 Subject: [PATCH 011/150] replace `os.listdir` by `os.scandir` --- client/ayon_core/lib/path_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/path_tools.py b/client/ayon_core/lib/path_tools.py index 31baac168c..3826f1383e 100644 --- a/client/ayon_core/lib/path_tools.py +++ b/client/ayon_core/lib/path_tools.py @@ -136,8 +136,8 @@ def version_up(filepath): index += len(new_label) clash_basename = clash_basename[:index] - for file in os.listdir(dirname): - if file.endswith(ext) and file.startswith(clash_basename): + for file in os.scandir(dirname): + if file.name.endswith(ext) and file.name.startswith(clash_basename): log.info("Skipping existing version %s" % new_label) return version_up(new_filename) From 9791f591f579e3f7106f1616293f0e034f66cd8a Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 11 Feb 2025 20:30:26 +0200 Subject: [PATCH 012/150] use `f` instead of `file` --- client/ayon_core/lib/path_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/path_tools.py b/client/ayon_core/lib/path_tools.py index 3826f1383e..f3c35d998b 100644 --- a/client/ayon_core/lib/path_tools.py +++ b/client/ayon_core/lib/path_tools.py @@ -136,8 +136,8 @@ def version_up(filepath): index += len(new_label) clash_basename = clash_basename[:index] - for file in os.scandir(dirname): - if file.name.endswith(ext) and file.name.startswith(clash_basename): + for f in os.scandir(dirname): + if f.name.endswith(ext) and f.name.startswith(clash_basename): log.info("Skipping existing version %s" % new_label) return version_up(new_filename) From 0dc3e17273ae5a4f8645f4cfdd3392fd720c9ce7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Feb 2025 02:46:30 +0100 Subject: [PATCH 013/150] Implement draft for AttachReviewables integrator --- .../publish/integrate_attach_reviewable.py | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 client/ayon_core/plugins/publish/integrate_attach_reviewable.py diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py new file mode 100644 index 0000000000..0457e2ad1b --- /dev/null +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -0,0 +1,95 @@ +import copy +import pyblish.api +from typing import List + +from ayon_core.lib import EnumDef +from ayon_core.pipeline import OptionalPyblishPluginMixin + + +class AttachReviewables(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Attach reviewable to other instances""" + + families = ["render", "review"] + order = pyblish.api.IntegratorOrder - 0.499 + label = "Attach reviewables" + + def process(self, instance): + # TODO: Support farm. + # If instance is being submitted to the farm we should pass through + # the 'attached reviewables' metadata to the farm job + # TODO: Reviewable frame range and resolutions + # Because we are attaching the data to another instance, how do we + # correctly propagate the resolution + frame rate to the other + # instance? Do we even need to? + # TODO: If this were to attach 'renders' to another instance that would + # mean there wouldn't necessarily be a render publish separate as a + # result. Is that correct expected behavior? + attr_values = self.get_attr_values_from_data(instance.data) + attach_to = attr_values.get("attach", []) + if not attach_to: + self.log.debug( + "Reviewable is not set to attach to another instance.") + return + + attach_instances: List[pyblish.api.Instance] = [] + for attach_instance_id in attach_to: + # Find the `pyblish.api.Instance` matching the `CreatedInstance.id` + # in the `attach_to` list + attach_instance = next(( + _inst for _inst in instance.context + if _inst.data.get("instance_id") == attach_instance_id + ), None) + if not attach_instance: + continue + + # Skip inactive instances + if not attach_instance.data.get("active", True): + continue + + attach_instances.append(attach_instance) + + self.log.debug( + f"Attaching reviewable to other instances: {attach_instances}") + + # Copy the representations of this reviewable instance to the other + # instance + representations = instance.data.get("representations", []) + for attach_instance in attach_instances: + self.log.info(f"Attaching to {attach_instance.name}") + attach_instance.data.setdefault("representations", []).extend( + copy.deepcopy(representations) + ) + + # Delete representations on the reviewable instance itself + for repre in representations: + self.log.debug( + "Marking representation as deleted because it was " + f"attached to other instances instead: {repre}") + repre.setdefault("tags", []).append("delete") + + @classmethod + def get_attr_defs_for_instance(cls, create_context, instance): + # TODO: Check if instance is actually a 'reviewable' + # Filtering of instance, if needed, can be customized + if not cls.instance_matches_plugin_families(instance): + return [] + + items = [] + for other_instance in create_context.instances: + if other_instance == instance: + continue + items.append({ + "label": other_instance.label, + "value": str(other_instance.id) + }) + + return [ + EnumDef( + "attach", + label="Attach reviewable", + multiselection=True, + items=items, + tooltip="Attach this reviewable to another instance", + ) + ] \ No newline at end of file From 0659bead0d2d65e254792e0aafc8e462f95f81b3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Feb 2025 08:52:23 +0100 Subject: [PATCH 014/150] Cosmetics --- client/ayon_core/plugins/publish/integrate_attach_reviewable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 0457e2ad1b..f3e7f38493 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -92,4 +92,4 @@ class AttachReviewables(pyblish.api.InstancePlugin, items=items, tooltip="Attach this reviewable to another instance", ) - ] \ No newline at end of file + ] From 72fdcf9c660044e4ee0e8b991e60d55d4ca32647 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Feb 2025 08:54:55 +0100 Subject: [PATCH 015/150] Improve docstring --- .../plugins/publish/integrate_attach_reviewable.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index f3e7f38493..8142b4f947 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -8,7 +8,18 @@ from ayon_core.pipeline import OptionalPyblishPluginMixin class AttachReviewables(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): - """Attach reviewable to other instances""" + """Attach reviewable to other instances + + This pre-integrator plugin allows instances to be 'attached to' other + instances by moving all its representations over to the other instance. + Even though this technically could work for any representation the current + intent is to use for reviewables only, like e.g. `review` or `render` + product type. + + When the reviewable is attached to another instance, the instance itself + will not be published as a separate entity. Instead, the representations + will be copied/moved to the instances it is attached to. + """ families = ["render", "review"] order = pyblish.api.IntegratorOrder - 0.499 From bfd4a986fb51bf9437c537dc51e0bb1e1b0ef391 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Feb 2025 09:05:23 +0100 Subject: [PATCH 016/150] Reformat using ruff --- .../publish/integrate_attach_reviewable.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 8142b4f947..4c4bc3d987 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -6,8 +6,9 @@ from ayon_core.lib import EnumDef from ayon_core.pipeline import OptionalPyblishPluginMixin -class AttachReviewables(pyblish.api.InstancePlugin, - OptionalPyblishPluginMixin): +class AttachReviewables( + pyblish.api.InstancePlugin, OptionalPyblishPluginMixin +): """Attach reviewable to other instances This pre-integrator plugin allows instances to be 'attached to' other @@ -40,17 +41,22 @@ class AttachReviewables(pyblish.api.InstancePlugin, attach_to = attr_values.get("attach", []) if not attach_to: self.log.debug( - "Reviewable is not set to attach to another instance.") + "Reviewable is not set to attach to another instance." + ) return attach_instances: List[pyblish.api.Instance] = [] for attach_instance_id in attach_to: # Find the `pyblish.api.Instance` matching the `CreatedInstance.id` # in the `attach_to` list - attach_instance = next(( - _inst for _inst in instance.context - if _inst.data.get("instance_id") == attach_instance_id - ), None) + attach_instance = next( + ( + _inst + for _inst in instance.context + if _inst.data.get("instance_id") == attach_instance_id + ), + None, + ) if not attach_instance: continue @@ -61,7 +67,8 @@ class AttachReviewables(pyblish.api.InstancePlugin, attach_instances.append(attach_instance) self.log.debug( - f"Attaching reviewable to other instances: {attach_instances}") + f"Attaching reviewable to other instances: {attach_instances}" + ) # Copy the representations of this reviewable instance to the other # instance @@ -76,7 +83,8 @@ class AttachReviewables(pyblish.api.InstancePlugin, for repre in representations: self.log.debug( "Marking representation as deleted because it was " - f"attached to other instances instead: {repre}") + f"attached to other instances instead: {repre}" + ) repre.setdefault("tags", []).append("delete") @classmethod @@ -90,10 +98,12 @@ class AttachReviewables(pyblish.api.InstancePlugin, for other_instance in create_context.instances: if other_instance == instance: continue - items.append({ - "label": other_instance.label, - "value": str(other_instance.id) - }) + items.append( + { + "label": other_instance.label, + "value": str(other_instance.id), + } + ) return [ EnumDef( From da4ebf8eaead85285b7ae985db2788d162c82ae4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:42:31 +0100 Subject: [PATCH 017/150] use env variables to fill root values --- client/ayon_core/pipeline/anatomy/roots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/anatomy/roots.py b/client/ayon_core/pipeline/anatomy/roots.py index 2773559d49..71f842d9e0 100644 --- a/client/ayon_core/pipeline/anatomy/roots.py +++ b/client/ayon_core/pipeline/anatomy/roots.py @@ -25,7 +25,7 @@ class RootItem(FormatObject): self._log = None lowered_platform_keys = {} for key, value in root_raw_data.items(): - lowered_platform_keys[key.lower()] = value + lowered_platform_keys[key.lower()] = value.format(**os.environ) self.raw_data = lowered_platform_keys self.cleaned_data = self._clean_roots(lowered_platform_keys) self.name = name From 2615e650cb2715799af9458a5b9ef16ff8b22455 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:42:45 +0100 Subject: [PATCH 018/150] fill only platform root --- client/ayon_core/pipeline/anatomy/roots.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/anatomy/roots.py b/client/ayon_core/pipeline/anatomy/roots.py index 71f842d9e0..1c37e82f08 100644 --- a/client/ayon_core/pipeline/anatomy/roots.py +++ b/client/ayon_core/pipeline/anatomy/roots.py @@ -23,16 +23,19 @@ class RootItem(FormatObject): def __init__(self, parent, root_raw_data, name): super(RootItem, self).__init__() self._log = None - lowered_platform_keys = {} - for key, value in root_raw_data.items(): - lowered_platform_keys[key.lower()] = value.format(**os.environ) + lowered_platform_keys = { + key.lower(): value + for key, value in root_raw_data.items() + } self.raw_data = lowered_platform_keys self.cleaned_data = self._clean_roots(lowered_platform_keys) self.name = name self.parent = parent self.available_platforms = set(lowered_platform_keys.keys()) - self.value = lowered_platform_keys.get(platform.system().lower()) + self.value = lowered_platform_keys[platform.system().lower()].format( + **os.environ + ) self.clean_value = self._clean_root(self.value) def __format__(self, *args, **kwargs): @@ -105,10 +108,10 @@ class RootItem(FormatObject): def _clean_roots(self, raw_data): """Clean all values of raw root item values.""" - cleaned = {} - for key, value in raw_data.items(): - cleaned[key] = self._clean_root(value) - return cleaned + return { + key: self._clean_root(value) + for key, value in raw_data.items() + } def path_remapper(self, path, dst_platform=None, src_platform=None): """Remap path for specific platform. From 5a6501aa0861f1eecac9d057085f3953a7416eb0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:03:18 +0100 Subject: [PATCH 019/150] use 'format_map' --- client/ayon_core/pipeline/anatomy/roots.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/anatomy/roots.py b/client/ayon_core/pipeline/anatomy/roots.py index 1c37e82f08..128834dcbc 100644 --- a/client/ayon_core/pipeline/anatomy/roots.py +++ b/client/ayon_core/pipeline/anatomy/roots.py @@ -33,8 +33,10 @@ class RootItem(FormatObject): self.parent = parent self.available_platforms = set(lowered_platform_keys.keys()) - self.value = lowered_platform_keys[platform.system().lower()].format( - **os.environ + + current_platform = platform.system().lower() + self.value = lowered_platform_keys[current_platform].format_map( + os.environ ) self.clean_value = self._clean_root(self.value) From cee3219304697cc3852e167650b7c40e87912a95 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Sat, 15 Feb 2025 00:49:22 +0200 Subject: [PATCH 020/150] revert using `os.scandir` inside `version_up` --- client/ayon_core/lib/path_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/path_tools.py b/client/ayon_core/lib/path_tools.py index f3c35d998b..31baac168c 100644 --- a/client/ayon_core/lib/path_tools.py +++ b/client/ayon_core/lib/path_tools.py @@ -136,8 +136,8 @@ def version_up(filepath): index += len(new_label) clash_basename = clash_basename[:index] - for f in os.scandir(dirname): - if f.name.endswith(ext) and f.name.startswith(clash_basename): + for file in os.listdir(dirname): + if file.endswith(ext) and file.startswith(clash_basename): log.info("Skipping existing version %s" % new_label) return version_up(new_filename) From 65d7404d428229e8573169040cb5fec4b5f4e522 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Sat, 15 Feb 2025 01:03:34 +0200 Subject: [PATCH 021/150] add a note about `get_last_workfile` --- client/ayon_core/pipeline/context_tools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index a809261fa2..beb62755f5 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -617,6 +617,9 @@ def version_up_current_workfile(): last_workfile_path = get_last_workfile( work_root, file_template, data, extensions, True ) + # `get_last_workfile` will return the first expected file version + # if no files exist yet. In that case, if they do not exist we will + # want to save v001 new_workfile_path = last_workfile_path if os.path.exists(new_workfile_path): new_workfile_path = version_up(new_workfile_path) From 817e4dc3380ba66e93465cf28262f8f96eb07a1f Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Sat, 15 Feb 2025 01:08:52 +0200 Subject: [PATCH 022/150] `version_up_current_workfile`: Raise error if parent folder of the workfile doesn't exist. --- client/ayon_core/pipeline/context_tools.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index beb62755f5..a2732d2c55 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -623,4 +623,12 @@ def version_up_current_workfile(): new_workfile_path = last_workfile_path if os.path.exists(new_workfile_path): new_workfile_path = version_up(new_workfile_path) + + # Raise an error if the parent folder doesn't exist as `host.save_workfile` + # is not supposed/able to create missing folders. + parent_folder = os.path.dirname(new_workfile_path) + if not os.path.exists(parent_folder): + raise AssertionError( + f"Folder {parent_folder} does not exist yet.") + host.save_workfile(new_workfile_path) From 26acd0efad991f8296c142f682dc3f11b450d6be Mon Sep 17 00:00:00 2001 From: Mustafa Jafar Date: Thu, 27 Feb 2025 15:24:16 +0200 Subject: [PATCH 023/150] update log message Co-authored-by: Roy Nieterau --- client/ayon_core/pipeline/context_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index a2732d2c55..3951a4d6e9 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -629,6 +629,6 @@ def version_up_current_workfile(): parent_folder = os.path.dirname(new_workfile_path) if not os.path.exists(parent_folder): raise AssertionError( - f"Folder {parent_folder} does not exist yet.") + f"Work area directory '{parent_folder}' does not exist yet.") host.save_workfile(new_workfile_path) From 5bb09d525657f7c03f034fb0dc060f87d9450a4e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:39:19 +0100 Subject: [PATCH 024/150] Fix check whether instance was found (it may be falsey if empty instance list) --- client/ayon_core/plugins/publish/integrate_attach_reviewable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 4c4bc3d987..92ff1c6cfd 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -57,7 +57,7 @@ class AttachReviewables( ), None, ) - if not attach_instance: + if attach_instance is None: continue # Skip inactive instances From d887a21bf9d6906f202cb86d14d22718268a591c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:39:41 +0100 Subject: [PATCH 025/150] Disallow attaching reviewables to reviewables for now --- .../ayon_core/plugins/publish/integrate_attach_reviewable.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 92ff1c6cfd..1127b45ba2 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -98,6 +98,11 @@ class AttachReviewables( for other_instance in create_context.instances: if other_instance == instance: continue + + # Do not allow attaching to other reviewable instances + if other_instance.data["productType"] in cls.families: + continue + items.append( { "label": other_instance.label, From cb2c1b0329e100208db0b3249c5170808e4d9137 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:40:07 +0100 Subject: [PATCH 026/150] Set `integrate` to false on the representation to avoid warnings logged by the Integrator --- .../ayon_core/plugins/publish/integrate_attach_reviewable.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 1127b45ba2..dc8b87fa99 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -87,6 +87,10 @@ class AttachReviewables( ) repre.setdefault("tags", []).append("delete") + # Stop integrator from trying to integrate this instance + if attach_to: + instance.data["integrate"] = False + @classmethod def get_attr_defs_for_instance(cls, create_context, instance): # TODO: Check if instance is actually a 'reviewable' From e388b42ad8f3b4055afd0a3ba9ca9c701cd4917d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:40:28 +0100 Subject: [PATCH 027/150] Turn log to info because it makes it clearer what is going on --- client/ayon_core/plugins/publish/integrate_attach_reviewable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index dc8b87fa99..41a510a2d3 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -66,7 +66,7 @@ class AttachReviewables( attach_instances.append(attach_instance) - self.log.debug( + self.log.info( f"Attaching reviewable to other instances: {attach_instances}" ) From 2a3f9b743b4efb89870a7ea18e7e0042a84792d4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:55:22 +0100 Subject: [PATCH 028/150] Log instance names instead of the objects --- .../ayon_core/plugins/publish/integrate_attach_reviewable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 41a510a2d3..63a7699cb0 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -66,8 +66,11 @@ class AttachReviewables( attach_instances.append(attach_instance) + instances_names = ", ".join( + instance.name for instance in attach_instances + ) self.log.info( - f"Attaching reviewable to other instances: {attach_instances}" + f"Attaching reviewable to other instances: {instances_names}" ) # Copy the representations of this reviewable instance to the other From 597b91c1356ee718df603317c08686ce34e65b07 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:57:51 +0100 Subject: [PATCH 029/150] For now disallow attaching to 'farm' instances --- .../plugins/publish/integrate_attach_reviewable.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 63a7699cb0..74a8cd3ee9 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -64,6 +64,14 @@ class AttachReviewables( if not attach_instance.data.get("active", True): continue + # For now do not support attaching to 'farm' instances until we + # can pass the 'attaching' on to the farm jobs. + if attach_instance.data.get("farm"): + self.log.warning( + "Attaching to farm instances is not supported yet." + ) + continue + attach_instances.append(attach_instance) instances_names = ", ".join( From 1f571e65c168eb0f481ce7704638082b46fafaa1 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 7 Mar 2025 14:17:13 +0200 Subject: [PATCH 030/150] implement `MissingWorkdirError` --- client/ayon_core/pipeline/context_tools.py | 5 +++-- client/ayon_core/pipeline/workfile/__init__.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 3951a4d6e9..2f002ae2c8 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -27,7 +27,8 @@ from .workfile import ( get_workdir, get_custom_workfile_template_by_string_context, get_workfile_template_key_from_context, - get_last_workfile + get_last_workfile, + MissingWorkdirError, ) from . import ( register_loader_plugin_path, @@ -628,7 +629,7 @@ def version_up_current_workfile(): # is not supposed/able to create missing folders. parent_folder = os.path.dirname(new_workfile_path) if not os.path.exists(parent_folder): - raise AssertionError( + raise MissingWorkdirError( f"Work area directory '{parent_folder}' does not exist yet.") host.save_workfile(new_workfile_path) diff --git a/client/ayon_core/pipeline/workfile/__init__.py b/client/ayon_core/pipeline/workfile/__init__.py index 05f939024c..aa7e150bca 100644 --- a/client/ayon_core/pipeline/workfile/__init__.py +++ b/client/ayon_core/pipeline/workfile/__init__.py @@ -16,6 +16,7 @@ from .path_resolving import ( from .utils import ( should_use_last_workfile_on_launch, should_open_workfiles_tool_on_launch, + MissingWorkdirError, ) from .build_workfile import BuildWorkfile @@ -46,6 +47,7 @@ __all__ = ( "should_use_last_workfile_on_launch", "should_open_workfiles_tool_on_launch", + "MissingWorkdirError", "BuildWorkfile", From 8f45e20c2ac5d7d3a1ed58f3bda62faf9023d499 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 7 Mar 2025 14:17:37 +0200 Subject: [PATCH 031/150] use `MissingWorkdirError` --- client/ayon_core/pipeline/workfile/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/pipeline/workfile/utils.py b/client/ayon_core/pipeline/workfile/utils.py index 53de3269b2..1a5ecdff4a 100644 --- a/client/ayon_core/pipeline/workfile/utils.py +++ b/client/ayon_core/pipeline/workfile/utils.py @@ -2,6 +2,10 @@ from ayon_core.lib import filter_profiles from ayon_core.settings import get_project_settings +class MissingWorkdirError(Exception): + pass + + def should_use_last_workfile_on_launch( project_name, host_name, From bfb24435ab24beaa66e810113088c3c833b1c2bf Mon Sep 17 00:00:00 2001 From: Mustafa Jafar Date: Sun, 9 Mar 2025 01:19:30 +0200 Subject: [PATCH 032/150] Update log message Co-authored-by: Roy Nieterau --- client/ayon_core/pipeline/context_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 2f002ae2c8..66556bbb35 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -630,6 +630,6 @@ def version_up_current_workfile(): parent_folder = os.path.dirname(new_workfile_path) if not os.path.exists(parent_folder): raise MissingWorkdirError( - f"Work area directory '{parent_folder}' does not exist yet.") + f"Work area directory '{parent_folder}' does not exist.") host.save_workfile(new_workfile_path) From c05c5045246aedfa20d662bee9e87a0ea814291d Mon Sep 17 00:00:00 2001 From: Mustafa Jafar Date: Sun, 9 Mar 2025 01:20:41 +0200 Subject: [PATCH 033/150] add doc string to `MissingWorkdirError` Co-authored-by: Roy Nieterau --- client/ayon_core/pipeline/workfile/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/workfile/utils.py b/client/ayon_core/pipeline/workfile/utils.py index 1a5ecdff4a..25be061dec 100644 --- a/client/ayon_core/pipeline/workfile/utils.py +++ b/client/ayon_core/pipeline/workfile/utils.py @@ -3,6 +3,7 @@ from ayon_core.settings import get_project_settings class MissingWorkdirError(Exception): + """Raised when accessing a work directory not found on disk.""" pass From 99e5e326f8a6627366815d21fc9f5c23d0650392 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 16:07:03 +0100 Subject: [PATCH 034/150] Add "Artist Note" input field to Save Workfile prompt --- client/ayon_core/tools/workfiles/abstract.py | 1 + client/ayon_core/tools/workfiles/control.py | 19 +++++++++++++------ .../tools/workfiles/widgets/files_widget.py | 4 +++- .../tools/workfiles/widgets/save_as_dialog.py | 11 +++++++++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/workfiles/abstract.py b/client/ayon_core/tools/workfiles/abstract.py index b78e987032..d9f3247305 100644 --- a/client/ayon_core/tools/workfiles/abstract.py +++ b/client/ayon_core/tools/workfiles/abstract.py @@ -1016,6 +1016,7 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon): workdir, filename, template_key, + artist_note, ): """Save current state of workfile to workarea. diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index ca97015eea..6634899bdc 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -554,6 +554,7 @@ class BaseWorkfileController( workdir, filename, template_key, + artist_note, ): self._emit_event("save_as.started") @@ -565,6 +566,7 @@ class BaseWorkfileController( workdir, filename, template_key, + artist_note=artist_note, ) except Exception: failed = True @@ -701,11 +703,12 @@ class BaseWorkfileController( def _save_as_workfile( self, - folder_id, - task_id, - workdir, - filename, - template_key, + folder_id: str, + task_id: str, + workdir: str, + filename: str, + template_key: str, + artist_note, src_filepath=None, ): # Trigger before save event @@ -748,7 +751,11 @@ class BaseWorkfileController( self._host_save_workfile(dst_filepath) # Make sure workfile info exists - self.save_workfile_info(folder_id, task_name, dst_filepath, None) + if not artist_note: + artist_note = None + self.save_workfile_info( + folder_id, task_name, dst_filepath, note=artist_note + ) # Create extra folders create_workdir_extra_folders( diff --git a/client/ayon_core/tools/workfiles/widgets/files_widget.py b/client/ayon_core/tools/workfiles/widgets/files_widget.py index dbe5966c31..2ff6298f3c 100644 --- a/client/ayon_core/tools/workfiles/widgets/files_widget.py +++ b/client/ayon_core/tools/workfiles/widgets/files_widget.py @@ -213,7 +213,8 @@ class FilesWidget(QtWidgets.QWidget): self._controller.duplicate_workfile( filepath, result["workdir"], - result["filename"] + result["filename"], + artist_note=result["artist_note"] ) def _on_workarea_browse_clicked(self): @@ -261,6 +262,7 @@ class FilesWidget(QtWidgets.QWidget): result["workdir"], result["filename"], result["template_key"], + artist_note=result["artist_note"] ) def _on_workarea_path_changed(self, event): diff --git a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py index 77dac1198a..6c2e340726 100644 --- a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py +++ b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py @@ -143,6 +143,11 @@ class SaveAsDialog(QtWidgets.QDialog): version_layout.addWidget(version_input) version_layout.addWidget(last_version_check) + # Artist note widget + artist_note_input = QtWidgets.QPlainTextEdit(inputs_widget) + artist_note_input.setPlaceholderText( + "Provide a note about this workfile.") + # Preview widget preview_widget = QtWidgets.QLabel("Preview filename", inputs_widget) preview_widget.setWordWrap(True) @@ -161,6 +166,7 @@ class SaveAsDialog(QtWidgets.QDialog): subversion_label = QtWidgets.QLabel("Subversion:", inputs_widget) extension_label = QtWidgets.QLabel("Extension:", inputs_widget) preview_label = QtWidgets.QLabel("Preview:", inputs_widget) + artist_note_label = QtWidgets.QLabel("Artist Note:", inputs_widget) # Build inputs inputs_layout = QtWidgets.QGridLayout(inputs_widget) @@ -172,6 +178,8 @@ class SaveAsDialog(QtWidgets.QDialog): inputs_layout.addWidget(extension_combobox, 2, 1) inputs_layout.addWidget(preview_label, 3, 0) inputs_layout.addWidget(preview_widget, 3, 1) + inputs_layout.addWidget(artist_note_label, 4, 0) + inputs_layout.addWidget(artist_note_input, 4, 1) # Build layout main_layout = QtWidgets.QVBoxLayout(self) @@ -206,11 +214,13 @@ class SaveAsDialog(QtWidgets.QDialog): self._extension_combobox = extension_combobox self._subversion_input = subversion_input self._preview_widget = preview_widget + self._artist_note_input = artist_note_input self._version_label = version_label self._subversion_label = subversion_label self._extension_label = extension_label self._preview_label = preview_label + self._artist_note_label = artist_note_label # Post init setup @@ -322,6 +332,7 @@ class SaveAsDialog(QtWidgets.QDialog): "folder_id": self._folder_id, "task_id": self._task_id, "template_key": self._template_key, + "artist_note": self._artist_note_input.toPlainText(), } self.close() From 4ce2ea74432dd6803dc4a1e39b918ea14a1145b7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 16:13:38 +0100 Subject: [PATCH 035/150] Fix duplicate workfile from published --- client/ayon_core/tools/workfiles/abstract.py | 3 ++- client/ayon_core/tools/workfiles/control.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/workfiles/abstract.py b/client/ayon_core/tools/workfiles/abstract.py index d9f3247305..53116c1029 100644 --- a/client/ayon_core/tools/workfiles/abstract.py +++ b/client/ayon_core/tools/workfiles/abstract.py @@ -1060,7 +1060,7 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon): pass @abstractmethod - def duplicate_workfile(self, src_filepath, workdir, filename): + def duplicate_workfile(self, src_filepath, workdir, filename, artist_note): """Duplicate workfile. Workfiles is not opened when done. @@ -1069,6 +1069,7 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon): src_filepath (str): Source workfile path. workdir (str): Destination workdir. filename (str): Destination filename. + artist_note (str): Artist note. """ pass diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index 6634899bdc..8d5b22fc32 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -610,7 +610,7 @@ class BaseWorkfileController( {"failed": failed}, ) - def duplicate_workfile(self, src_filepath, workdir, filename): + def duplicate_workfile(self, src_filepath, workdir, filename, artist_note): self._emit_event("workfile_duplicate.started") failed = False From 297756403455b841e886309d8991fed8f213abef Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 16:16:49 +0100 Subject: [PATCH 036/150] Fix Copy to different context from published context --- client/ayon_core/tools/workfiles/abstract.py | 2 ++ client/ayon_core/tools/workfiles/control.py | 2 ++ client/ayon_core/tools/workfiles/widgets/files_widget.py | 1 + 3 files changed, 5 insertions(+) diff --git a/client/ayon_core/tools/workfiles/abstract.py b/client/ayon_core/tools/workfiles/abstract.py index 53116c1029..152ca33d99 100644 --- a/client/ayon_core/tools/workfiles/abstract.py +++ b/client/ayon_core/tools/workfiles/abstract.py @@ -1041,6 +1041,7 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon): workdir, filename, template_key, + artist_note, ): """Action to copy published workfile representation to workarea. @@ -1055,6 +1056,7 @@ class AbstractWorkfilesFrontend(AbstractWorkfilesCommon): workdir (str): Workarea directory. filename (str): Workarea filename. template_key (str): Template key. + artist_note (str): Artist note. """ pass diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index 8d5b22fc32..33c3b3f6e1 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -586,6 +586,7 @@ class BaseWorkfileController( workdir, filename, template_key, + artist_note, ): self._emit_event("copy_representation.started") @@ -597,6 +598,7 @@ class BaseWorkfileController( workdir, filename, template_key, + artist_note, src_filepath=representation_filepath ) except Exception: diff --git a/client/ayon_core/tools/workfiles/widgets/files_widget.py b/client/ayon_core/tools/workfiles/widgets/files_widget.py index 2ff6298f3c..f0b74f4289 100644 --- a/client/ayon_core/tools/workfiles/widgets/files_widget.py +++ b/client/ayon_core/tools/workfiles/widgets/files_widget.py @@ -315,6 +315,7 @@ class FilesWidget(QtWidgets.QWidget): result["workdir"], result["filename"], result["template_key"], + artist_note=result["artist_note"] ) def _on_save_as_request(self): From a1b0a24e548d3ebc90b38f00c14d5897e59013a3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 11 Mar 2025 11:37:27 +0100 Subject: [PATCH 037/150] Update client/ayon_core/tools/workfiles/control.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/tools/workfiles/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/workfiles/control.py b/client/ayon_core/tools/workfiles/control.py index 33c3b3f6e1..3a7459da0c 100644 --- a/client/ayon_core/tools/workfiles/control.py +++ b/client/ayon_core/tools/workfiles/control.py @@ -710,7 +710,7 @@ class BaseWorkfileController( workdir: str, filename: str, template_key: str, - artist_note, + artist_note: str, src_filepath=None, ): # Trigger before save event From 87124eee5360a2dc48a725ba918c9a455d714e54 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 11 Mar 2025 11:38:05 +0100 Subject: [PATCH 038/150] Update client/ayon_core/tools/workfiles/widgets/save_as_dialog.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/tools/workfiles/widgets/save_as_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py index 6c2e340726..79a94a1d78 100644 --- a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py +++ b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py @@ -144,7 +144,7 @@ class SaveAsDialog(QtWidgets.QDialog): version_layout.addWidget(last_version_check) # Artist note widget - artist_note_input = QtWidgets.QPlainTextEdit(inputs_widget) + artist_note_input = PlaceholderPlainTextEdit(inputs_widget) artist_note_input.setPlaceholderText( "Provide a note about this workfile.") From 02265616749035873ceb472f11c14ef64fb462f3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 11 Mar 2025 11:39:09 +0100 Subject: [PATCH 039/150] Add import --- client/ayon_core/tools/workfiles/widgets/save_as_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py index 79a94a1d78..f50c3e1368 100644 --- a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py +++ b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py @@ -1,6 +1,6 @@ from qtpy import QtWidgets, QtCore -from ayon_core.tools.utils import PlaceholderLineEdit +from ayon_core.tools.utils import PlaceholderLineEdit, PlaceholderPlainTextEdit class SubversionLineEdit(QtWidgets.QWidget): From d0e591067156b381c3ced96ed6703b013aeced7d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 17:47:21 +0100 Subject: [PATCH 040/150] Remove long deprecated `LoaderPlugin.fname` property Make sure to remove all usages of `self.fname` from any `LoaderPlugin` in your addons --- client/ayon_core/pipeline/load/plugins.py | 13 ------------- client/ayon_core/pipeline/load/utils.py | 6 ------ 2 files changed, 19 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 1fb906fd65..6075916369 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -229,19 +229,6 @@ class LoaderPlugin(list): """ return cls.options or [] - @property - def fname(self): - """Backwards compatibility with deprecation warning""" - - self.log.warning(( - "DEPRECATION WARNING: Source - Loader plugin {}." - " The 'fname' property on the Loader plugin will be removed in" - " future versions of OpenPype. Planned version to drop the support" - " is 3.16.6 or 3.17.0." - ).format(self.__class__.__name__)) - if hasattr(self, "_fname"): - return self._fname - @classmethod def get_representation_name_aliases(cls, representation_name: str): """Return representation names to which switching is allowed from diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index de8e1676e7..b130161190 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -316,12 +316,6 @@ def load_with_repre_context( ) loader = Loader() - - # Backwards compatibility: Originally the loader's __init__ required the - # representation context to set `fname` attribute to the filename to load - # Deprecated - to be removed in OpenPype 3.16.6 or 3.17.0. - loader._fname = get_representation_path_from_context(repre_context) - return loader.load(repre_context, name, namespace, options) From ffc76e639a5d516140950da3c9a2c3a9d305c773 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 21:50:06 +0100 Subject: [PATCH 041/150] Remove deprecated `convert_for_ffmpeg` --- client/ayon_core/lib/__init__.py | 2 - client/ayon_core/lib/transcoding.py | 135 +--------------------------- 2 files changed, 2 insertions(+), 135 deletions(-) diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index 03ed574081..0c64b88d11 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -97,7 +97,6 @@ from .profiles_filtering import ( from .transcoding import ( get_transcode_temp_directory, should_convert_for_ffmpeg, - convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_ffprobe_data, get_ffprobe_streams, @@ -196,7 +195,6 @@ __all__ = [ "get_transcode_temp_directory", "should_convert_for_ffmpeg", - "convert_for_ffmpeg", "convert_input_paths_for_ffmpeg", "get_ffprobe_data", "get_ffprobe_streams", diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 1fda014bd8..c4030b3f97 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -526,137 +526,6 @@ def should_convert_for_ffmpeg(src_filepath): return False -# Deprecated since 2022 4 20 -# - Reason - Doesn't convert sequences right way: Can't handle gaps, reuse -# first frame for all frames and changes filenames when input -# is sequence. -# - use 'convert_input_paths_for_ffmpeg' instead -def convert_for_ffmpeg( - first_input_path, - output_dir, - input_frame_start=None, - input_frame_end=None, - logger=None -): - """Convert source file to format supported in ffmpeg. - - Currently can convert only exrs. - - Args: - first_input_path (str): Path to first file of a sequence or a single - file path for non-sequential input. - output_dir (str): Path to directory where output will be rendered. - Must not be same as input's directory. - input_frame_start (int): Frame start of input. - input_frame_end (int): Frame end of input. - logger (logging.Logger): Logger used for logging. - - Raises: - ValueError: If input filepath has extension not supported by function. - Currently is supported only ".exr" extension. - """ - if logger is None: - logger = logging.getLogger(__name__) - - logger.warning(( - "DEPRECATED: 'ayon_core.lib.transcoding.convert_for_ffmpeg' is" - " deprecated function of conversion for FFMpeg. Please replace usage" - " with 'ayon_core.lib.transcoding.convert_input_paths_for_ffmpeg'" - )) - - ext = os.path.splitext(first_input_path)[1].lower() - if ext != ".exr": - raise ValueError(( - "Function 'convert_for_ffmpeg' currently support only" - " \".exr\" extension. Got \"{}\"." - ).format(ext)) - - is_sequence = False - if input_frame_start is not None and input_frame_end is not None: - is_sequence = int(input_frame_end) != int(input_frame_start) - - input_info = get_oiio_info_for_input(first_input_path, logger=logger) - - # Change compression only if source compression is "dwaa" or "dwab" - # - they're not supported in ffmpeg - compression = input_info["attribs"].get("compression") - if compression in ("dwaa", "dwab"): - compression = "none" - - # Prepare subprocess arguments - oiio_cmd = get_oiio_tool_args( - "oiiotool", - # Don't add any additional attributes - "--nosoftwareattrib", - ) - # Add input compression if available - if compression: - oiio_cmd.extend(["--compression", compression]) - - # Collect channels to export - input_arg, channels_arg = get_oiio_input_and_channel_args(input_info) - - oiio_cmd.extend([ - input_arg, first_input_path, - # Tell oiiotool which channels should be put to top stack (and output) - "--ch", channels_arg, - # Use first subimage - "--subimage", "0" - ]) - - # Add frame definitions to arguments - if is_sequence: - oiio_cmd.extend([ - "--frames", "{}-{}".format(input_frame_start, input_frame_end) - ]) - - for attr_name, attr_value in input_info["attribs"].items(): - if not isinstance(attr_value, str): - continue - - # Remove attributes that have string value longer than allowed length - # for ffmpeg or when contain prohibited symbols - erase_reason = "Missing reason" - erase_attribute = False - if len(attr_value) > MAX_FFMPEG_STRING_LEN: - erase_reason = "has too long value ({} chars).".format( - len(attr_value) - ) - erase_attribute = True - - if not erase_attribute: - for char in NOT_ALLOWED_FFMPEG_CHARS: - if char in attr_value: - erase_attribute = True - erase_reason = ( - "contains unsupported character \"{}\"." - ).format(char) - break - - if erase_attribute: - # Set attribute to empty string - logger.info(( - "Removed attribute \"{}\" from metadata because {}." - ).format(attr_name, erase_reason)) - oiio_cmd.extend(["--eraseattrib", attr_name]) - - # Add last argument - path to output - if is_sequence: - ext = os.path.splitext(first_input_path)[1] - base_filename = "tmp.%{:0>2}d{}".format( - len(str(input_frame_end)), ext - ) - else: - base_filename = os.path.basename(first_input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) - - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) - - def convert_input_paths_for_ffmpeg( input_paths, output_dir, @@ -664,7 +533,7 @@ def convert_input_paths_for_ffmpeg( ): """Convert source file to format supported in ffmpeg. - Currently can convert only exrs. The input filepaths should be files + Can currently convert only EXRs. The input filepaths should be files with same type. Information about input is loaded only from first found file. @@ -692,7 +561,7 @@ def convert_input_paths_for_ffmpeg( if ext != ".exr": raise ValueError(( - "Function 'convert_for_ffmpeg' currently support only" + "Function 'convert_input_paths_for_ffmpeg' currently only supports" " \".exr\" extension. Got \"{}\"." ).format(ext)) From 12f6d76043986b8965bd6fc8d6402299b9bf52ea Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 22:01:21 +0100 Subject: [PATCH 042/150] Remove deprecated `StdOutBroker` import fallback --- client/ayon_core/tools/stdout_broker/app.py | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 client/ayon_core/tools/stdout_broker/app.py diff --git a/client/ayon_core/tools/stdout_broker/app.py b/client/ayon_core/tools/stdout_broker/app.py deleted file mode 100644 index ae73db1bb9..0000000000 --- a/client/ayon_core/tools/stdout_broker/app.py +++ /dev/null @@ -1,12 +0,0 @@ -import warnings -from .broker import StdOutBroker - -warnings.warn( - ( - "Import of 'StdOutBroker' from 'ayon_core.tools.stdout_broker.app'" - " is deprecated. Please use 'ayon_core.tools.stdout_broker' instead." - ), - DeprecationWarning -) - -__all__ = ("StdOutBroker", ) From 5540e7923a5f3dc2cc6179deccc783e9c33888c0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Mar 2025 10:59:38 +0100 Subject: [PATCH 043/150] Update client/ayon_core/lib/transcoding.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/lib/transcoding.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index c4030b3f97..3e77f39a8f 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -561,9 +561,9 @@ def convert_input_paths_for_ffmpeg( if ext != ".exr": raise ValueError(( - "Function 'convert_input_paths_for_ffmpeg' currently only supports" - " \".exr\" extension. Got \"{}\"." - ).format(ext)) + "Function 'convert_input_paths_for_ffmpeg' currently supports" + f" only \".exr\" extension. Got \"{ext}\"." + )) input_info = get_oiio_info_for_input(first_input_path, logger=logger) From 8424ad39078b3610894b0e53142a9b01ea6c5811 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Mar 2025 11:00:59 +0100 Subject: [PATCH 044/150] Remove redundant brackets --- client/ayon_core/lib/transcoding.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 3e77f39a8f..8c84e1c4dc 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -560,10 +560,10 @@ def convert_input_paths_for_ffmpeg( ext = os.path.splitext(first_input_path)[1].lower() if ext != ".exr": - raise ValueError(( + raise ValueError( "Function 'convert_input_paths_for_ffmpeg' currently supports" f" only \".exr\" extension. Got \"{ext}\"." - )) + ) input_info = get_oiio_info_for_input(first_input_path, logger=logger) From a9f6b39de3e28478a92e5e3744d021ce20d3eadf Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Tue, 25 Mar 2025 18:12:36 +0100 Subject: [PATCH 045/150] automated mkdocs setup Signed-off-by: Philippe Leprince --- .gitignore | 3 + docs/css/custom.css | 12 + docs/img/ay-symbol-blackw-full.png | Bin 0 -> 1632 bytes docs/img/favicon.ico | Bin 0 -> 490 bytes docs/index.md | 1 + mkdocs.yml | 67 +++ mkdocs_hooks.py | 132 +++++ poetry.lock | 758 ++++++++++++++++++++++++++++- pyproject.toml | 12 +- 9 files changed, 982 insertions(+), 3 deletions(-) create mode 100644 docs/css/custom.css create mode 100644 docs/img/ay-symbol-blackw-full.png create mode 100644 docs/img/favicon.ico create mode 100644 docs/index.md create mode 100644 mkdocs.yml create mode 100644 mkdocs_hooks.py diff --git a/.gitignore b/.gitignore index 41389755f1..4e56d77392 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,6 @@ dump.sql mypy.ini .github_changelog_generator + +# ignore mkdocs build +site/ diff --git a/docs/css/custom.css b/docs/css/custom.css new file mode 100644 index 0000000000..28461e6eca --- /dev/null +++ b/docs/css/custom.css @@ -0,0 +1,12 @@ +[data-md-color-scheme="slate"] { + /* simple slate overrides */ + --md-primary-fg-color: hsl(155, 49%, 50%); + --md-accent-fg-color: rgb(93, 200, 156); + --md-typeset-a-color: hsl(155, 49%, 45%) !important; +} +[data-md-color-scheme="default"] { + /* simple default overrides */ + --md-primary-fg-color: hsl(155, 49%, 50%); + --md-accent-fg-color: rgb(93, 200, 156); + --md-typeset-a-color: hsl(155, 49%, 45%) !important; +} diff --git a/docs/img/ay-symbol-blackw-full.png b/docs/img/ay-symbol-blackw-full.png new file mode 100644 index 0000000000000000000000000000000000000000..5edda784cc7a2512bc050aeb5779465cd04c2155 GIT binary patch literal 1632 zcmV-m2A}zfP)|OZpvMT@p z1?fpdK~#9!?VZ_j<2npP2TK@H|Np@^HmE0U%ie zwrho$g@vvQV82#~@&=G|0QSm38essrE`Y6a5b_{_ZveJIfb%f1-vrz!2L}SM1p+`L z4Isw}xJeE|9xN~f!14B_gUlen^`rmtz(@{K+wf~FAPWFI8GpG03nNh40^eR}`_(b* z8-J+-%jO_-@LyF0ur&Y|<1cjpw@$#fOYNM4)DJ)@0%R6|jS=Wiz|C_I=Nf>U7Qk%< zl-9scPo z3Cx5R%E5-s@I$Tu^jl%mX^2B`{K^P4o`yIC$FJK8TTeqAa^qLc!H&}qht%*@TVdC6 zm_umziU@R{2Rn`r7e4;k^z0a1r3ja`U?!+D8k+^NV@nR5yR0fu&YGJ75|)#BI*rkVftrp z6mf5x7MS!FxXP0O7!d_s{e`9_;HZ|^8!I@a1xi9IQ~aY_A&$RusYu0{2`JkVg_?~hiJ*b~gvXT_eUXnFz`lYEZ(}AMJP9^0sPLpoKur{sd`4_Z zO>9cgbt3kxX89CYOcKq(-XDldwJ^)ADT6)$KPJj&9W?#NEl@Q9AEF>tF(b0I#%e9_ z*|&NQ`Ol+Uq1bPvrZndN4n_bb9D5piqzbeXP+Pc20qBW+1JH?p0RZ|CAPWYN>jLO1 z0w5a(G*1bP0MLv8XES2I3D}W?h5%?n0LYR74O0Tlrc|ASkc|m^15k|sXLDk&EwHH- z8U&yU0U(P8RJ20OCWUTWU~Ma$0l@j7br6`6fO4DQ@;OL$4WM;eppF402uw;qB?Ag_ z5N#WPt2_g08E{(!WZeK-WD=lezzqUq-vC-;5};?mRSr&^1u7bF(F!Nd0yPc56_!CX z4S1P@!`=faeIzT?f-O;OXkv+;uQr1D@sJ=(^ko2Q?*wQY@Q{P# z%>Yu~3Q{-V7XswZ0MgwH)HmP<0_f3z?~NdPG=Nlh!xu2%TMeL315ook$O;CKVgxK< zK-N4Du!I4icp_*C12_x_1HynXAPk5$7!N}6DC~YRj4TOu-1xDtOi^ z34H@Fc7|eaFm{LId_WwLi9=FxOfF7}>Nucjz{MK_?BRU@UgEWBy;o)+-Wt1mZ_M(W zqebt|0+LMwsQmr;@f6HAfVk?1WB}2U0f;TXCrO-&VOBDW z7Or(873EIlIT6LwKxI2eo;o=1xOh(J^n}6w)#aY^{rCKb3;!u~VZ}*z0vS*W?WMRo z@~L)%?PUw?ZUL>QT|d}AYNP9!TivPy4YWJ<7X}bou&uG$CfJ;B#)Litao(Fnd9See}c`$7L48j z?CMlAO`U|4=H+hSCoCayPh<3}Ah-rupk8Q%tCxU%DVhvMPf_-612?Pd9uoEZ<3Rxa g4bPjr^y3ws0MrSDl)+%k9smFU07*qoM6N<$f>Hk4NdN!< literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000000..612c7a5e0d --- /dev/null +++ b/docs/index.md @@ -0,0 +1 @@ +--8<-- "README.md" diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..ededa9ae5c --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,67 @@ +site_name: ayon-core + +nav: + - Home: index.md + - License: license.md + +theme: + name: material + palette: + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/toggle-switch-off-outline + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/toggle-switch + name: Switch to dark mode + logo: img/ay-symbol-blackw-full.png + favicon: img/favicon.ico + features: + - navigation.sections + - navigation.path + - navigation.prune + +extra: + version: + provider: mike + +extra_css: [css/custom.css] + +markdown_extensions: + - mdx_gh_links + - pymdownx.snippets + +plugins: + - search + - offline + - mkdocs-autoapi: + autoapi_dir: ./ + autoapi_add_nav_entry: Reference + autoapi_ignore: + - .* + - docs/**/* + - tests/**/* + - tools/**/* + - .*/**/* + - ./*.py + - mkdocstrings: + handlers: + python: + paths: + - . + - client + - server + - minify: + minify_html: true + minify_js: true + minify_css: true + htmlmin_opts: + remove_comments: true + cache_safe: true + - mike + +hooks: + - mkdocs_hooks.py diff --git a/mkdocs_hooks.py b/mkdocs_hooks.py new file mode 100644 index 0000000000..fb424b236a --- /dev/null +++ b/mkdocs_hooks.py @@ -0,0 +1,132 @@ +import os +import sys +from pathlib import Path +from shutil import rmtree +import json + +TMP_FILE = "./missing_init_files.json" + + +def add_missing_init_files(*roots, msg=""): + """ + This function takes in one or more root directories as arguments and scans + them for Python files without an `__init__.py` file. It generates a JSON + file named `missing_init_files.json` containing the paths of these files. + + Args: + *roots: Variable number of root directories to scan. + + Returns: + None + """ + nfiles = [] + + for root in roots: + if not os.path.exists(root): + continue + for dirpath, dirs, files in os.walk(root): + if "__init__.py" in files: + continue + else: + Path(f"{dirpath}/__init__.py").touch() + nfiles.append(f"{dirpath}/__init__.py") + sys.stdout.write( + "\r\x1b[K" + f"{msg}: created {len(nfiles)} " + "temp '__init__.py' files" + ) + sys.stdout.flush() + + with open(TMP_FILE, "w") as f: + json.dump(nfiles, f) + + sys.stdout.write("\n") + sys.stdout.flush() + + +def remove_missing_init_files(msg=""): + """ + This function removes temporary `__init__.py` files created in the + `add_missing_init_files()` function. It reads the paths of these files from + a JSON file named `missing_init_files.json`. + + Args: + None + + Returns: + None + """ + with open(TMP_FILE, "r") as f: + nfiles = json.load(f) + + for file in nfiles: + Path(file).unlink() + sys.stdout.write( + "\r\x1b[K" + f"{msg}: removed {len(nfiles)} temp '__init__.py' files" + ) + sys.stdout.flush() + + os.remove(TMP_FILE) + + sys.stdout.write("\n") + sys.stdout.flush() + + +def remove_pychache_dirs(msg=""): + """ + This function walks the current directory and removes all existing '__pycache__' + directories. + + Args: + msg: An optional message to display during the removal process. + + Returns: + None + """ + nremoved = 0 + + for dirpath, dirs, files in os.walk("."): + if "__pycache__" in dirs: + pydir = Path(f"{dirpath}/__pycache__") + rmtree(pydir) + nremoved += 1 + sys.stdout.write( + "\r\x1b[K" + f"{msg}: removed {nremoved} '__pycache__' directories" + ) + sys.stdout.flush() + + if not nremoved: + sys.stdout.write(f"{msg}: no __pycache__ dirs found") + + sys.stdout.write("\n") + sys.stdout.flush() + + +# mkdocs hooks ----------------------------------------------------------------- + + +def on_startup(command, dirty): + remove_pychache_dirs(msg="HOOK - on_startup") + + +def on_pre_build(config): + """ + This function is called before the MkDocs build process begins. It adds + temporary `__init__.py` files to directories that do not contain one, to + make sure mkdocs doesn't ignore them. + """ + add_missing_init_files( + "client", + "server", + "services", + "tests", + msg="HOOK - on_pre_build", + ) + + +def on_post_build(config): + """ + This function is called after the MkDocs build process ends. It removes + temporary `__init__.py` files that were added in the `on_pre_build()` + function. + """ + remove_missing_init_files(msg="HOOK - on_post_build") diff --git a/poetry.lock b/poetry.lock index 2d040a5f91..40a6887567 100644 --- a/poetry.lock +++ b/poetry.lock @@ -49,6 +49,40 @@ appdirs = ">=1,<2" requests = ">=2.27.1" Unidecode = ">=1.3.0" +[[package]] +name = "babel" +version = "2.17.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] + +[[package]] +name = "backrefs" +version = "5.8" +description = "A wrapper around re and regex that adds additional back references." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"}, + {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"}, + {file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"}, + {file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"}, + {file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"}, + {file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"}, +] + +[package.extras] +extras = ["regex"] + [[package]] name = "certifi" version = "2025.1.31" @@ -175,6 +209,21 @@ files = [ {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "clique" version = "2.0.0" @@ -217,12 +266,22 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "csscompressor" +version = "0.9.5" +description = "A python port of YUI CSS Compressor" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "csscompressor-0.9.5.tar.gz", hash = "sha256:afa22badbcf3120a4f392e4d22f9fff485c044a1feda4a950ecc5eba9dd31a05"}, +] + [[package]] name = "distlib" version = "0.3.9" @@ -267,6 +326,50 @@ docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3) testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "griffe" +version = "1.6.2" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "griffe-1.6.2-py3-none-any.whl", hash = "sha256:6399f7e663150e4278a312a8e8a14d2f3d7bd86e2ef2f8056a1058e38579c2ee"}, + {file = "griffe-1.6.2.tar.gz", hash = "sha256:3a46fa7bd83280909b63c12b9a975732a927dd97809efe5b7972290b606c5d91"}, +] + +[package.dependencies] +colorama = ">=0.4" + +[[package]] +name = "htmlmin2" +version = "0.1.13" +description = "An HTML Minifier" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "htmlmin2-0.1.13-py3-none-any.whl", hash = "sha256:75609f2a42e64f7ce57dbff28a39890363bde9e7e5885db633317efbdf8c79a2"}, +] + [[package]] name = "identify" version = "2.6.7" @@ -297,6 +400,53 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "importlib-metadata" +version = "8.6.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, + {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"}, + {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -309,6 +459,389 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsmin" +version = "3.0.1" +description = "JavaScript minifier." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "jsmin-3.0.1.tar.gz", hash = "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc"}, +] + +[[package]] +name = "markdown" +version = "3.7" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markdown-checklist" +version = "0.4.4" +description = "Python Markdown extension for task lists with checkboxes" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "markdown-checklist-0.4.4.tar.gz", hash = "sha256:69c93850798b1e01cdc6fcd4a80592d941f669f6451bbf69c71a4ffd1142f849"}, +] + +[package.dependencies] +markdown = "*" + +[package.extras] +coverage = ["coverage", "figleaf"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + +[[package]] +name = "mdx-gh-links" +version = "0.4" +description = "An extension to Python-Markdown which adds support for shorthand links to GitHub users, repositories, issues and commits." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "mdx_gh_links-0.4-py3-none-any.whl", hash = "sha256:9057bca1fa5280bf1fcbf354381e46c9261cc32c2d5c0407801f8a910be5f099"}, + {file = "mdx_gh_links-0.4.tar.gz", hash = "sha256:41d5aac2ab201425aa0a19373c4095b79e5e015fdacfe83c398199fe55ca3686"}, +] + +[package.dependencies] +markdown = ">=3.0.0" + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mike" +version = "2.1.3" +description = "Manage multiple versions of your MkDocs-powered documentation" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, + {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, +] + +[package.dependencies] +importlib-metadata = "*" +importlib-resources = "*" +jinja2 = ">=2.7" +mkdocs = ">=1.0" +pyparsing = ">=3.0" +pyyaml = ">=5.1" +pyyaml-env-tag = "*" +verspec = "*" + +[package.extras] +dev = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] +test = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] + +[[package]] +name = "mkdocs" +version = "1.6.1" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} +jinja2 = ">=2.11.1" +markdown = ">=3.3.6" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" +packaging = ">=20.5" +pathspec = ">=0.11.1" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-autoapi" +version = "0.4.0" +description = "MkDocs plugin providing automatic API reference generation" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mkdocs_autoapi-0.4.0-py3-none-any.whl", hash = "sha256:ce2b5e8b4d1df37bd1273a6bce1dded152107e2280cedbfa085cea10edf8531b"}, + {file = "mkdocs_autoapi-0.4.0.tar.gz", hash = "sha256:409c2da8eb297ef51381b066744ac1cdf846220bcc779bdba680fbc5a080df1e"}, +] + +[package.dependencies] +mkdocs = ">=1.4.0" +mkdocstrings = ">=0.19.0" + +[package.extras] +python = ["mkdocstrings[python] (>=0.19.0)"] +python-legacy = ["mkdocstrings[python-legacy] (>=0.19.0)"] +vba = ["mkdocstrings-vba (>=0.0.10)"] + +[[package]] +name = "mkdocs-autorefs" +version = "1.4.1" +description = "Automatically link across pages in MkDocs." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mkdocs_autorefs-1.4.1-py3-none-any.whl", hash = "sha256:9793c5ac06a6ebbe52ec0f8439256e66187badf4b5334b5fde0b128ec134df4f"}, + {file = "mkdocs_autorefs-1.4.1.tar.gz", hash = "sha256:4b5b6235a4becb2b10425c2fa191737e415b37aa3418919db33e5d774c9db079"}, +] + +[package.dependencies] +Markdown = ">=3.3" +markupsafe = ">=2.0.1" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" + +[[package]] +name = "mkdocs-material" +version = "9.6.9" +description = "Documentation that simply works" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs_material-9.6.9-py3-none-any.whl", hash = "sha256:6e61b7fb623ce2aa4622056592b155a9eea56ff3487d0835075360be45a4c8d1"}, + {file = "mkdocs_material-9.6.9.tar.gz", hash = "sha256:a4872139715a1f27b2aa3f3dc31a9794b7bbf36333c0ba4607cf04786c94f89c"}, +] + +[package.dependencies] +babel = ">=2.10,<3.0" +backrefs = ">=5.7.post1,<6.0" +colorama = ">=0.4,<1.0" +jinja2 = ">=3.0,<4.0" +markdown = ">=3.2,<4.0" +mkdocs = ">=1.6,<2.0" +mkdocs-material-extensions = ">=1.3,<2.0" +paginate = ">=0.5,<1.0" +pygments = ">=2.16,<3.0" +pymdown-extensions = ">=10.2,<11.0" +requests = ">=2.26,<3.0" + +[package.extras] +git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] +recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocs-minify-plugin" +version = "0.8.0" +description = "An MkDocs plugin to minify HTML, JS or CSS files prior to being written to disk" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mkdocs-minify-plugin-0.8.0.tar.gz", hash = "sha256:bc11b78b8120d79e817308e2b11539d790d21445eb63df831e393f76e52e753d"}, + {file = "mkdocs_minify_plugin-0.8.0-py3-none-any.whl", hash = "sha256:5fba1a3f7bd9a2142c9954a6559a57e946587b21f133165ece30ea145c66aee6"}, +] + +[package.dependencies] +csscompressor = ">=0.9.5" +htmlmin2 = ">=0.1.13" +jsmin = ">=3.0.1" +mkdocs = ">=1.4.1" + +[[package]] +name = "mkdocstrings" +version = "0.29.0" +description = "Automatic documentation from sources, for MkDocs." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mkdocstrings-0.29.0-py3-none-any.whl", hash = "sha256:8ea98358d2006f60befa940fdebbbc88a26b37ecbcded10be726ba359284f73d"}, + {file = "mkdocstrings-0.29.0.tar.gz", hash = "sha256:3657be1384543ce0ee82112c3e521bbf48e41303aa0c229b9ffcccba057d922e"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +Jinja2 = ">=2.11.1" +Markdown = ">=3.6" +MarkupSafe = ">=1.1" +mkdocs = ">=1.6" +mkdocs-autorefs = ">=1.4" +pymdown-extensions = ">=6.3" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} + +[package.extras] +crystal = ["mkdocstrings-crystal (>=0.3.4)"] +python = ["mkdocstrings-python (>=1.16.2)"] +python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] + +[[package]] +name = "mkdocstrings-python" +version = "1.16.8" +description = "A Python handler for mkdocstrings." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mkdocstrings_python-1.16.8-py3-none-any.whl", hash = "sha256:211b7aaf776cd45578ecb531e5ad0d3a35a8be9101a6bfa10de38a69af9d8fd8"}, + {file = "mkdocstrings_python-1.16.8.tar.gz", hash = "sha256:9453ccae69be103810c1cf6435ce71c8f714ae37fef4d87d16aa92a7c800fe1d"}, +] + +[package.dependencies] +griffe = ">=1.6.2" +mkdocs-autorefs = ">=1.4" +mkdocstrings = ">=0.28.3" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + [[package]] name = "mock" version = "5.1.0" @@ -400,6 +933,34 @@ files = [ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] +[[package]] +name = "paginate" +version = "0.5.7" +description = "Divides large result sets into pages for easier browsing" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + [[package]] name = "platformdirs" version = "4.3.6" @@ -464,6 +1025,55 @@ files = [ {file = "pyblish_base-1.8.12-py2.py3-none-any.whl", hash = "sha256:2cbe956bfbd4175a2d7d22b344cd345800f4d4437153434ab658fc12646a11e8"}, ] +[[package]] +name = "pygments" +version = "2.19.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pymdown-extensions" +version = "10.14.3" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9"}, + {file = "pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b"}, +] + +[package.dependencies] +markdown = ">=3.6" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.19.1)"] + +[[package]] +name = "pyparsing" +version = "3.2.3" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, + {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pytest" version = "8.3.4" @@ -505,6 +1115,21 @@ pytest = ">=8.3.2" [package.extras] test = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "pytest-mock (>=3.14)"] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + [[package]] name = "pyyaml" version = "6.0.2" @@ -568,6 +1193,21 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[package.dependencies] +pyyaml = "*" + [[package]] name = "requests" version = "2.32.3" @@ -629,6 +1269,18 @@ files = [ {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, ] +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + [[package]] name = "tomli" version = "2.2.1" @@ -671,6 +1323,30 @@ files = [ {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] +[[package]] +name = "tomlkit" +version = "0.13.2" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, + {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + [[package]] name = "unidecode" version = "1.3.8" @@ -701,6 +1377,21 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "verspec" +version = "0.1.0" +description = "Flexible version handling" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, + {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, +] + +[package.extras] +test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] + [[package]] name = "virtualenv" version = "20.29.2" @@ -722,7 +1413,70 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +[[package]] +name = "watchdog" +version = "6.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, + {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, + {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, + {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, + {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "zipp" +version = "3.21.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [metadata] lock-version = "2.1" python-versions = ">=3.9.1,<3.10" -content-hash = "0a399d239c49db714c1166c20286fdd5cd62faf12e45ab85833c4d6ea7a04a2a" +content-hash = "58c2656b970622b91e31e92602f4a80d65ac07c8a11d61b6a06c3cac298616bd" diff --git a/pyproject.toml b/pyproject.toml index 94badd2f1a..45b91782c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.5+dev" +version = "1.1.5" description = "" authors = ["Ynput Team "] readme = "README.md" @@ -29,6 +29,16 @@ attrs = "^25.0.0" pyblish-base = "^1.8.7" clique = "^2.0.0" opentimelineio = "^0.17.0" +tomlkit = "^0.13.2" +requests = "^2.32.3" +mkdocs-material = "^9.6.7" +mkdocs-autoapi = "^0.4.0" +mkdocstrings-python = "^1.16.2" +mkdocs-minify-plugin = "^0.8.0" +markdown-checklist = "^0.4.4" +mdx-gh-links = "^0.4" +pymdown-extensions = "^10.14.3" +mike = "^2.1.3" [tool.ruff] From 04e91389e17a7636c9c1aaeb61bc9554bbb257b9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Mar 2025 22:55:11 +0100 Subject: [PATCH 046/150] Make launcher project filter field case insensitive --- client/ayon_core/tools/utils/projects_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/utils/projects_widget.py b/client/ayon_core/tools/utils/projects_widget.py index fd361493ab..88d8a6c9f5 100644 --- a/client/ayon_core/tools/utils/projects_widget.py +++ b/client/ayon_core/tools/utils/projects_widget.py @@ -286,6 +286,7 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): self._sort_by_type = True # Disable case sensitivity self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) def _type_sort(self, l_index, r_index): if not self._sort_by_type: From 8c919be06865c06fa5b2baa80134a266c9e6f3fd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 26 Mar 2025 12:10:59 +0100 Subject: [PATCH 047/150] Update client/ayon_core/tools/workfiles/widgets/save_as_dialog.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/tools/workfiles/widgets/save_as_dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py index f50c3e1368..bddff816fe 100644 --- a/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py +++ b/client/ayon_core/tools/workfiles/widgets/save_as_dialog.py @@ -178,8 +178,8 @@ class SaveAsDialog(QtWidgets.QDialog): inputs_layout.addWidget(extension_combobox, 2, 1) inputs_layout.addWidget(preview_label, 3, 0) inputs_layout.addWidget(preview_widget, 3, 1) - inputs_layout.addWidget(artist_note_label, 4, 0) - inputs_layout.addWidget(artist_note_input, 4, 1) + inputs_layout.addWidget(artist_note_label, 4, 0, 1, 2) + inputs_layout.addWidget(artist_note_input, 5, 0, 1, 2) # Build layout main_layout = QtWidgets.QVBoxLayout(self) From ce9970636f71601835459af5f1990b5d69fcca03 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 26 Mar 2025 17:32:15 +0100 Subject: [PATCH 048/150] Allow enabling/disabling Attach Reviewables feature --- .../publish/integrate_attach_reviewable.py | 2 ++ server/settings/publish_plugins.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py index 74a8cd3ee9..b98d8d28fe 100644 --- a/client/ayon_core/plugins/publish/integrate_attach_reviewable.py +++ b/client/ayon_core/plugins/publish/integrate_attach_reviewable.py @@ -26,6 +26,8 @@ class AttachReviewables( order = pyblish.api.IntegratorOrder - 0.499 label = "Attach reviewables" + settings_category = "core" + def process(self, instance): # TODO: Support farm. # If instance is being submitted to the farm we should pass through diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index c9c66e65d9..39a9c028f9 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -12,6 +12,10 @@ from ayon_server.settings import ( from ayon_server.types import ColorRGBA_uint8 +class EnabledModel(BaseSettingsModel): + enabled: bool = SettingsField(True) + + class ValidateBaseModel(BaseSettingsModel): _isGroup = True enabled: bool = SettingsField(True) @@ -1026,6 +1030,17 @@ class PublishPuginsModel(BaseSettingsModel): default_factory=IntegrateHeroVersionModel, title="Integrate Hero Version" ) + AttachReviewables: EnabledModel = SettingsField( + default_factory=EnabledModel, + title="Attach Reviewables", + description=( + "When enabled, expose an 'Attach Reviewables' attribute on review" + " and render instances in the publisher to allow including the" + " media to be attached to another instance.\n\n" + "If a reviewable is attached to another instance it will not be " + "published as a render/review product of its own." + ) + ) CleanUp: CleanUpModel = SettingsField( default_factory=CleanUpModel, title="Clean Up" @@ -1410,6 +1425,9 @@ DEFAULT_PUBLISH_VALUES = { ], "use_hardlinks": False }, + "AttachReviewables": { + "enabled": True, + }, "CleanUp": { "paterns": [], # codespell:ignore paterns "remove_temp_renders": False From 4283d0b4534a93d1a79c8c86350eb348aef0904e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 27 Mar 2025 12:59:15 +0100 Subject: [PATCH 049/150] Allow disabling removal of rendered files on farm renders With this disabled the `metadata.json` and work area renders will remain on disk after a publish even if the folder is not a persistent staging dir --- .../plugins/publish/collect_rendered_files.py | 7 +++++-- server/settings/publish_plugins.py | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index deecf7ba24..c69dddd6ec 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -31,6 +31,9 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): # Keep "filesequence" for backwards compatibility of older jobs targets = ["filesequence", "farm"] label = "Collect rendered frames" + settings_category = "core" + + remove_files = True _context = None @@ -120,7 +123,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): self._fill_staging_dir(repre_data, anatomy) representations.append(repre_data) - if not staging_dir_persistent: + if self.remove_files and not staging_dir_persistent: add_repre_files_for_cleanup(instance, repre_data) instance.data["representations"] = representations @@ -170,7 +173,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): os.environ.update(session_data) staging_dir_persistent = self._process_path(data, anatomy) - if not staging_dir_persistent: + if self.remove_files and not staging_dir_persistent: context.data["cleanupFullPaths"].append(path) context.data["cleanupEmptyDirs"].append( os.path.dirname(path) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index c9c66e65d9..029eab5fc4 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -925,6 +925,20 @@ class IntegrateHeroVersionModel(BaseSettingsModel): "hero versions.") +class CollectRenderedFilesModel(BaseSettingsModel): + remove_files: bool = SettingsField( + True, + title="Remove rendered files", + description=( + "Remove rendered files and metadata json on publish.\n\n" + "Note that when enabled but the render is to a configured " + "persistent staging directory the files will not be removed. " + "However with this disabled the files will **not** be removed in " + "either case." + ) + ) + + class CleanUpModel(BaseSettingsModel): _isGroup = True paterns: list[str] = SettingsField( # codespell:ignore paterns @@ -1026,6 +1040,10 @@ class PublishPuginsModel(BaseSettingsModel): default_factory=IntegrateHeroVersionModel, title="Integrate Hero Version" ) + CollectRenderedFiles: CollectRenderedFilesModel = SettingsField( + default_factory=CollectRenderedFilesModel, + title="Clean up farm rendered files" + ) CleanUp: CleanUpModel = SettingsField( default_factory=CleanUpModel, title="Clean Up" @@ -1410,6 +1428,9 @@ DEFAULT_PUBLISH_VALUES = { ], "use_hardlinks": False }, + "CollectRenderedFiles": { + "remove_files": True + }, "CleanUp": { "paterns": [], # codespell:ignore paterns "remove_temp_renders": False From 2b53a1841ad5f936937e514b94a4318313d52e30 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Thu, 27 Mar 2025 16:39:29 +0100 Subject: [PATCH 050/150] some fixes Signed-off-by: Philippe Leprince --- docs/license.md | 1 + mkdocs.yml | 10 +++- mkdocs_hooks.py | 143 ++++++++++++++++++++++++++++++++++-------------- poetry.lock | 35 +++++++++++- pyproject.toml | 1 + 5 files changed, 144 insertions(+), 46 deletions(-) create mode 100644 docs/license.md diff --git a/docs/license.md b/docs/license.md new file mode 100644 index 0000000000..f409d45232 --- /dev/null +++ b/docs/license.md @@ -0,0 +1 @@ +--8<-- "LICENSE" diff --git a/mkdocs.yml b/mkdocs.yml index ededa9ae5c..8e4c2663bc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,5 @@ site_name: ayon-core +repo_url: https://github.com/ynput/ayon-core nav: - Home: index.md @@ -45,15 +46,18 @@ plugins: - docs/**/* - tests/**/* - tools/**/* + - stubs/**/* # mocha fix + - ./**/pythonrc.py # houdini fix - .*/**/* - ./*.py - mkdocstrings: handlers: python: paths: - - . - - client - - server + - ./ + - client/* + - server/* + - services/* - minify: minify_html: true minify_js: true diff --git a/mkdocs_hooks.py b/mkdocs_hooks.py index fb424b236a..1faa1954f9 100644 --- a/mkdocs_hooks.py +++ b/mkdocs_hooks.py @@ -1,10 +1,71 @@ import os -import sys from pathlib import Path from shutil import rmtree import json +import glob +import logging TMP_FILE = "./missing_init_files.json" +NFILES = [] + +# ----------------------------------------------------------------------------- + + +class ColorFormatter(logging.Formatter): + grey = "\x1b[38;20m" + green = "\x1b[32;20m" + yellow = "\x1b[33;20m" + red = "\x1b[31;20m" + bold_red = "\x1b[31;1m" + reset = "\x1b[0m" + fmt = ( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s " # noqa + "(%(filename)s:%(lineno)d)" + ) + + FORMATS = { + logging.DEBUG: grey + fmt + reset, + logging.INFO: green + fmt + reset, + logging.WARNING: yellow + fmt + reset, + logging.ERROR: red + fmt + reset, + logging.CRITICAL: bold_red + fmt + reset, + } + + def format(self, record): + log_fmt = self.FORMATS.get(record.levelno) + formatter = logging.Formatter(log_fmt) + return formatter.format(record) + + +ch = logging.StreamHandler() +ch.setFormatter(ColorFormatter()) + +logging.basicConfig( + level=logging.INFO, + handlers=[ch], +) + + +# ----------------------------------------------------------------------------- + + +def create_init_file(dirpath, msg): + global NFILES + ini_file = f"{dirpath}/__init__.py" + Path(ini_file).touch() + NFILES.append(ini_file) + logging.info(f"{msg}: created '{ini_file}'") + + +def create_parent_init_files(dirpath: str, rootpath: str, msg: str): + parent_path = dirpath + while parent_path != rootpath: + parent_path = os.path.dirname(parent_path) + parent_init = os.path.join(parent_path, "__init__.py") + if not os.path.exists(parent_init): + create_init_file(parent_path, msg) + else: + break def add_missing_init_files(*roots, msg=""): @@ -19,28 +80,26 @@ def add_missing_init_files(*roots, msg=""): Returns: None """ - nfiles = [] for root in roots: if not os.path.exists(root): continue - for dirpath, dirs, files in os.walk(root): + rootpath = os.path.abspath(root) + for dirpath, dirs, files in os.walk(rootpath): if "__init__.py" in files: continue - else: - Path(f"{dirpath}/__init__.py").touch() - nfiles.append(f"{dirpath}/__init__.py") - sys.stdout.write( - "\r\x1b[K" + f"{msg}: created {len(nfiles)} " - "temp '__init__.py' files" - ) - sys.stdout.flush() + + if "." in dirpath: + continue + + if not glob.glob(os.path.join(dirpath, "*.py")): + continue + + create_init_file(dirpath, msg) + create_parent_init_files(dirpath, rootpath, msg) with open(TMP_FILE, "w") as f: - json.dump(nfiles, f) - - sys.stdout.write("\n") - sys.stdout.flush() + json.dump(NFILES, f) def remove_missing_init_files(msg=""): @@ -55,26 +114,26 @@ def remove_missing_init_files(msg=""): Returns: None """ - with open(TMP_FILE, "r") as f: - nfiles = json.load(f) + global NFILES + nfiles = [] + if os.path.exists(TMP_FILE): + with open(TMP_FILE, "r") as f: + nfiles = json.load(f) + else: + nfiles = NFILES for file in nfiles: Path(file).unlink() - sys.stdout.write( - "\r\x1b[K" + f"{msg}: removed {len(nfiles)} temp '__init__.py' files" - ) - sys.stdout.flush() + logging.info(f"{msg}: removed {file}") os.remove(TMP_FILE) - - sys.stdout.write("\n") - sys.stdout.flush() + NFILES = [] def remove_pychache_dirs(msg=""): """ - This function walks the current directory and removes all existing '__pycache__' - directories. + This function walks the current directory and removes all existing + '__pycache__' directories. Args: msg: An optional message to display during the removal process. @@ -89,19 +148,13 @@ def remove_pychache_dirs(msg=""): pydir = Path(f"{dirpath}/__pycache__") rmtree(pydir) nremoved += 1 - sys.stdout.write( - "\r\x1b[K" + f"{msg}: removed {nremoved} '__pycache__' directories" - ) - sys.stdout.flush() + logging.info(f"{msg}: removed '{pydir}'") if not nremoved: - sys.stdout.write(f"{msg}: no __pycache__ dirs found") - - sys.stdout.write("\n") - sys.stdout.flush() + logging.info(f"{msg}: no __pycache__ dirs found") -# mkdocs hooks ----------------------------------------------------------------- +# mkdocs hooks ---------------------------------------------------------------- def on_startup(command, dirty): @@ -114,13 +167,19 @@ def on_pre_build(config): temporary `__init__.py` files to directories that do not contain one, to make sure mkdocs doesn't ignore them. """ - add_missing_init_files( - "client", - "server", - "services", - "tests", - msg="HOOK - on_pre_build", - ) + try: + add_missing_init_files( + "client", + "server", + "services", + msg="HOOK - on_pre_build", + ) + except BaseException as e: + logging.error(e) + remove_missing_init_files( + msg="HOOK - on_post_build: cleaning up on error !" + ) + raise def on_post_build(config): diff --git a/poetry.lock b/poetry.lock index 40a6887567..96e1dc0f4c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -842,6 +842,22 @@ mkdocs-autorefs = ">=1.4" mkdocstrings = ">=0.28.3" typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} +[[package]] +name = "mkdocstrings-shell" +version = "1.0.3" +description = "A shell scripts/libraries handler for mkdocstrings." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mkdocstrings_shell-1.0.3-py3-none-any.whl", hash = "sha256:b23ebe43d06c9c19a541548f34d42ee4e4324ae06423eba8a9136e295c67f345"}, + {file = "mkdocstrings_shell-1.0.3.tar.gz", hash = "sha256:3bdea6a1e794a5d0e15d461f33b92e0b9f3b9a1e2c33671d9a2b7d83c761096a"}, +] + +[package.dependencies] +mkdocstrings = ">=0.28.3" +shellman = ">=1.0.2" + [[package]] name = "mock" version = "5.1.0" @@ -1269,6 +1285,23 @@ files = [ {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, ] +[[package]] +name = "shellman" +version = "1.0.2" +description = "Write documentation in comments and render it with templates." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "shellman-1.0.2-py3-none-any.whl", hash = "sha256:f8c960fd2d3785e195f86fcd8f110a8d51a950e759d82c14a5af0bd71b918b3c"}, + {file = "shellman-1.0.2.tar.gz", hash = "sha256:48cba79d6415c0d013ad4dfd2205ed81b0e468795d1886dcda943ac78eaffd38"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +jinja2 = ">=3" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + [[package]] name = "six" version = "1.17.0" @@ -1479,4 +1512,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9.1,<3.10" -content-hash = "58c2656b970622b91e31e92602f4a80d65ac07c8a11d61b6a06c3cac298616bd" +content-hash = "24b6215b9c20a4f64f844d3deb121618aef510b1c5ee54242e50305db6c0c4f4" diff --git a/pyproject.toml b/pyproject.toml index 45b91782c3..8c7ea9aec2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ markdown-checklist = "^0.4.4" mdx-gh-links = "^0.4" pymdown-extensions = "^10.14.3" mike = "^2.1.3" +mkdocstrings-shell = "^1.0.2" [tool.ruff] From 92bd17cfd9d4d37e54cb589caa41e43b3f190fdc Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 31 Mar 2025 09:08:41 +0000 Subject: [PATCH 051/150] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index de5c199428..07da797efa 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.5+dev" +__version__ = "1.1.6" diff --git a/package.py b/package.py index 250b77fe52..e649a787f8 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.5+dev" +version = "1.1.6" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 8c7ea9aec2..2627dd02f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.5" +version = "1.1.6" description = "" authors = ["Ynput Team "] readme = "README.md" From 19dacdc4fe65c00f2a1ae7d02e940ff020ad4f21 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 31 Mar 2025 09:09:18 +0000 Subject: [PATCH 052/150] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 07da797efa..d021a03e7e 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.6" +__version__ = "1.1.6+dev" diff --git a/package.py b/package.py index e649a787f8..9af45719a7 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.6" +version = "1.1.6+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 2627dd02f7..3a76b25c0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.6" +version = "1.1.6+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From d20942892f0f6b00c5b5abdf867351b25c2f247b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 31 Mar 2025 14:45:31 +0200 Subject: [PATCH 053/150] Used new enum for template names from Anatomy --- server/settings/publish_plugins.py | 13 +++++++++++-- server/settings/tools.py | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 39a9c028f9..c32d8d360c 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -7,6 +7,7 @@ from ayon_server.settings import ( normalize_name, ensure_unique_names, task_types_enum, + anatomy_template_items_enum ) from ayon_server.types import ColorRGBA_uint8 @@ -889,7 +890,11 @@ class IntegrateANTemplateNameProfileModel(BaseSettingsModel): default_factory=list, title="Task names" ) - template_name: str = SettingsField("", title="Template name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="publish") + ) class IntegrateHeroTemplateNameProfileModel(BaseSettingsModel): @@ -910,7 +915,11 @@ class IntegrateHeroTemplateNameProfileModel(BaseSettingsModel): default_factory=list, title="Task names" ) - template_name: str = SettingsField("", title="Template name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="publish") + ) class IntegrateHeroVersionModel(BaseSettingsModel): diff --git a/server/settings/tools.py b/server/settings/tools.py index 32c72e7a98..d1e6cb50eb 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -5,6 +5,7 @@ from ayon_server.settings import ( normalize_name, ensure_unique_names, task_types_enum, + anatomy_template_items_enum ) @@ -283,7 +284,11 @@ class PublishTemplateNameProfile(BaseSettingsModel): task_names: list[str] = SettingsField( default_factory=list, title="Task names" ) - template_name: str = SettingsField("", title="Template name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="publish") + ) class CustomStagingDirProfileModel(BaseSettingsModel): @@ -306,7 +311,11 @@ class CustomStagingDirProfileModel(BaseSettingsModel): custom_staging_dir_persistent: bool = SettingsField( False, title="Custom Staging Folder Persistent" ) - template_name: str = SettingsField("", title="Template Name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum + ) class PublishToolModel(BaseSettingsModel): From ff8d4f5ddaa0c974a4a9a96553778f2b18139c85 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 1 Apr 2025 11:54:56 +0200 Subject: [PATCH 054/150] Added missed argument --- server/settings/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index d1e6cb50eb..28ceeedbe4 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -314,7 +314,7 @@ class CustomStagingDirProfileModel(BaseSettingsModel): template_name: str = SettingsField( "", title="Template name", - enum_resolver=anatomy_template_items_enum + enum_resolver=anatomy_template_items_enum(category="publish") ) From 7dad20d9c98c71c37db2bbdba22b32249c0001e4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 1 Apr 2025 12:07:10 +0200 Subject: [PATCH 055/150] Bump up required server version --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 9af45719a7..51851c0f5f 100644 --- a/package.py +++ b/package.py @@ -6,7 +6,7 @@ client_dir = "ayon_core" plugin_for = ["ayon_server"] -ayon_server_version = ">=1.0.3,<2.0.0" +ayon_server_version = ">=1.7.5,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = {} From d91e6d07e2bca1d1426e0c63e1f3b1e8d18bde8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:08:49 +0200 Subject: [PATCH 056/150] added basic implementation of filtering by ids --- .../ayon_core/tools/utils/folders_widget.py | 39 +++++++++++++++++-- client/ayon_core/tools/utils/tasks_widget.py | 36 ++++++++++++++++- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/utils/folders_widget.py b/client/ayon_core/tools/utils/folders_widget.py index b3b5cebb0e..2e2c5b5d77 100644 --- a/client/ayon_core/tools/utils/folders_widget.py +++ b/client/ayon_core/tools/utils/folders_widget.py @@ -1,4 +1,5 @@ import collections +from typing import Optional from qtpy import QtWidgets, QtGui, QtCore @@ -33,7 +34,7 @@ class FoldersQtModel(QtGui.QStandardItemModel): refreshed = QtCore.Signal() def __init__(self, controller): - super(FoldersQtModel, self).__init__() + super().__init__() self._controller = controller self._items_by_id = {} @@ -334,6 +335,29 @@ class FoldersQtModel(QtGui.QStandardItemModel): self.refreshed.emit() +class FoldersProxyModel(RecursiveSortFilterProxyModel): + def __init__(self): + super().__init__() + + self._folder_ids_filter = None + + def set_folder_ids_filter(self, folder_ids: Optional[list[str]]): + if self._folder_ids_filter == folder_ids: + return + self._folder_ids_filter = folder_ids + self.invalidateFilter() + + def filterAcceptsRow(self, row, parent_index): + if self._folder_ids_filter is not None: + if not self._folder_ids_filter: + return False + source_index = self.sourceModel().index(row, 0, parent_index) + folder_id = source_index.data(FOLDER_ID_ROLE) + if folder_id not in self._folder_ids_filter: + return False + return super().filterAcceptsRow(row, parent_index) + + class FoldersWidget(QtWidgets.QWidget): """Folders widget. @@ -369,13 +393,13 @@ class FoldersWidget(QtWidgets.QWidget): refreshed = QtCore.Signal() def __init__(self, controller, parent, handle_expected_selection=False): - super(FoldersWidget, self).__init__(parent) + super().__init__(parent) folders_view = TreeView(self) folders_view.setHeaderHidden(True) folders_model = FoldersQtModel(controller) - folders_proxy_model = RecursiveSortFilterProxyModel() + folders_proxy_model = FoldersProxyModel() folders_proxy_model.setSourceModel(folders_model) folders_proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) @@ -446,6 +470,15 @@ class FoldersWidget(QtWidgets.QWidget): if name: self._folders_view.expandAll() + def set_folder_ids_filter(self, folder_ids: Optional[list[str]]): + """Set filter of folder ids. + + Args: + folder_ids (list[str]): The list of folder ids. + + """ + self._folders_proxy_model.set_folder_ids_filter(folder_ids) + def refresh(self): """Refresh folders model. diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 30846e6cda..00960b6ba6 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -1,3 +1,5 @@ +from typing import Optional + from qtpy import QtWidgets, QtGui, QtCore from ayon_core.style import ( @@ -343,6 +345,29 @@ class TasksQtModel(QtGui.QStandardItemModel): return self._has_content +class TasksProxyModel(QtCore.QSortFilterProxyModel): + def __init__(self): + super().__init__() + + self._task_ids_filter: Optional[set[str]] = None + + def set_task_ids_filter(self, task_ids: Optional[set[str]]): + if self._task_ids_filter == task_ids: + return + self._task_ids_filter = task_ids + self.invalidateFilter() + + def filterAcceptsRow(self, row, parent_index): + if self._task_ids_filter is not None: + if not self._task_ids_filter: + return False + source_index = self.sourceModel().index(row, 0, parent_index) + task_id = source_index.data(ITEM_ID_ROLE) + if task_id not in self._task_ids_filter: + return False + return super().filterAcceptsRow(row, parent_index) + + class TasksWidget(QtWidgets.QWidget): """Tasks widget. @@ -364,7 +389,7 @@ class TasksWidget(QtWidgets.QWidget): tasks_view.setIndentation(0) tasks_model = TasksQtModel(controller) - tasks_proxy_model = QtCore.QSortFilterProxyModel() + tasks_proxy_model = TasksProxyModel() tasks_proxy_model.setSourceModel(tasks_model) tasks_proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) @@ -490,6 +515,15 @@ class TasksWidget(QtWidgets.QWidget): ) return True + def set_task_ids_filter(self, task_ids: Optional[list[str]]): + """Set filter of folder ids. + + Args: + task_ids (list[str]): The list of folder ids. + + """ + self._tasks_proxy_model.set_task_ids_filter(task_ids) + def _on_tasks_refresh_finished(self, event): """Tasks were refreshed in controller. From 37bf4a28c7fda385b86a44f2efb8330f5d6f5a5d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:09:42 +0200 Subject: [PATCH 057/150] added method to get task and folder ids for assignees --- .../tools/common_models/hierarchy.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/client/ayon_core/tools/common_models/hierarchy.py b/client/ayon_core/tools/common_models/hierarchy.py index edff8471b0..891eb80960 100644 --- a/client/ayon_core/tools/common_models/hierarchy.py +++ b/client/ayon_core/tools/common_models/hierarchy.py @@ -227,6 +227,9 @@ class HierarchyModel(object): self._tasks_by_id = NestedCacheItem( levels=2, default_factory=dict, lifetime=self.lifetime) + self._entity_ids_by_assignee = NestedCacheItem( + levels=2, default_factory=dict, lifetime=self.lifetime) + self._folders_refreshing = set() self._tasks_refreshing = set() self._controller = controller @@ -238,6 +241,8 @@ class HierarchyModel(object): self._task_items.reset() self._tasks_by_id.reset() + self._entity_ids_by_assignee.reset() + def refresh_project(self, project_name): """Force to refresh folder items for a project. @@ -461,6 +466,54 @@ class HierarchyModel(object): output = self.get_task_entities(project_name, {task_id}) return output[task_id] + def get_entity_ids_for_assignees( + self, project_name: str, assignees: list[str] + ): + folder_ids = set() + task_ids = set() + output = { + "folder_ids": folder_ids, + "task_ids": task_ids, + } + assignees = set(assignees) + for assignee in tuple(assignees): + cache = self._entity_ids_by_assignee[project_name][assignee] + if cache.is_valid: + assignees.discard(assignee) + assignee_data = cache.get_data() + folder_ids.update(assignee_data["folder_ids"]) + task_ids.update(assignee_data["task_ids"]) + + if not assignees: + return output + + tasks = ayon_api.get_tasks( + project_name, + assignees_all=assignees, + fields={"id", "folderId", "assignees"}, + ) + tasks_assignee = {} + for task in tasks: + folder_ids.add(task["folderId"]) + task_ids.add(task["id"]) + for assignee in task["assignees"]: + tasks_assignee.setdefault(assignee, []).append(task) + + for assignee, tasks in tasks_assignee.items(): + cache = self._entity_ids_by_assignee[project_name][assignee] + assignee_folder_ids = set() + assignee_task_ids = set() + assignee_data = { + "folder_ids": assignee_folder_ids, + "task_ids": assignee_task_ids, + } + for task in tasks: + assignee_folder_ids.add(task["folderId"]) + assignee_task_ids.add(task["id"]) + cache.update_data(assignee_data) + + return output + @contextlib.contextmanager def _folder_refresh_event_manager(self, project_name, sender): self._folders_refreshing.add(project_name) From d7b5e06077dd9ec99bf5e1e636338f33fc7eed40 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:10:06 +0200 Subject: [PATCH 058/150] basic implementation of setting filters in launcher --- client/ayon_core/tools/launcher/abstract.py | 42 ++++++++++++------- client/ayon_core/tools/launcher/control.py | 20 ++++++++- .../tools/launcher/ui/hierarchy_page.py | 34 +++++++++++++-- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/tools/launcher/abstract.py b/client/ayon_core/tools/launcher/abstract.py index 63ba4cd717..ea0842f24d 100644 --- a/client/ayon_core/tools/launcher/abstract.py +++ b/client/ayon_core/tools/launcher/abstract.py @@ -160,8 +160,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: list[FolderItem]: Minimum possible information needed for visualisation of folder hierarchy. - """ + """ pass @abstractmethod @@ -180,8 +180,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: list[TaskItem]: Minimum possible information needed for visualisation of tasks. - """ + """ pass @abstractmethod @@ -190,8 +190,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: Union[str, None]: Selected project name. - """ + """ pass @abstractmethod @@ -200,8 +200,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: Union[str, None]: Selected folder id. - """ + """ pass @abstractmethod @@ -210,8 +210,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: Union[str, None]: Selected task id. - """ + """ pass @abstractmethod @@ -220,8 +220,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: Union[str, None]: Selected task name. - """ + """ pass @abstractmethod @@ -238,8 +238,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: dict[str, Union[str, None]]: Selected context. - """ + """ pass @abstractmethod @@ -249,8 +249,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Args: project_name (Union[str, None]): Project nameor None if no project is selected. - """ + """ pass @abstractmethod @@ -260,8 +260,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Args: folder_id (Union[str, None]): Folder id or None if no folder is selected. - """ + """ pass @abstractmethod @@ -273,8 +273,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): is selected. task_name (Union[str, None]): Task name or None if no task is selected. - """ + """ pass # Actions @@ -290,8 +290,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Returns: list[ActionItem]: List of action items that should be shown for given context. - """ + """ pass @abstractmethod @@ -303,8 +303,8 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): folder_id (Union[str, None]): Folder id. task_id (Union[str, None]): Task id. action_id (str): Action identifier. - """ + """ pass @abstractmethod @@ -317,10 +317,10 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): project_name (Union[str, None]): Project name. folder_id (Union[str, None]): Folder id. task_id (Union[str, None]): Task id. - action_id (Iterable[str]): Action identifiers. + action_ids (Iterable[str]): Action identifiers. enabled (bool): New value of force not open workfile. - """ + """ pass @abstractmethod @@ -340,5 +340,17 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon): Triggers 'controller.refresh.actions.started' event at the beginning and 'controller.refresh.actions.finished' at the end. """ - + pass + + @abstractmethod + def get_my_tasks_entity_ids(self, project_name: str): + """Get entity ids for my tasks. + + Args: + project_name (str): Project name. + + Returns: + dict[str, Union[list[str]]]: Folder and task ids. + + """ pass diff --git a/client/ayon_core/tools/launcher/control.py b/client/ayon_core/tools/launcher/control.py index d60e4747e3..45cb2b7945 100644 --- a/client/ayon_core/tools/launcher/control.py +++ b/client/ayon_core/tools/launcher/control.py @@ -1,4 +1,4 @@ -from ayon_core.lib import Logger +from ayon_core.lib import Logger, get_ayon_username from ayon_core.lib.events import QueuedEventSystem from ayon_core.settings import get_project_settings from ayon_core.tools.common_models import ProjectsModel, HierarchyModel @@ -6,6 +6,8 @@ from ayon_core.tools.common_models import ProjectsModel, HierarchyModel from .abstract import AbstractLauncherFrontEnd, AbstractLauncherBackend from .models import LauncherSelectionModel, ActionsModel +NOT_SET = object() + class BaseLauncherController( AbstractLauncherFrontEnd, AbstractLauncherBackend @@ -15,6 +17,8 @@ class BaseLauncherController( self._event_system = None self._log = None + self._username = NOT_SET + self._selection_model = LauncherSelectionModel(self) self._projects_model = ProjectsModel(self) self._hierarchy_model = HierarchyModel(self) @@ -168,5 +172,19 @@ class BaseLauncherController( self._emit_event("controller.refresh.actions.finished") + def get_my_tasks_entity_ids(self, project_name: str): + username = self._get_my_username() + assignees = [] + if username: + assignees.append(username) + return self._hierarchy_model.get_entity_ids_for_assignees( + project_name, assignees + ) + + def _get_my_username(self): + if self._username is NOT_SET: + self._username = get_ayon_username() + return self._username + def _emit_event(self, topic, data=None): self.emit_event(topic, data, "controller") diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index ad48e8ac77..78ca5aaa1e 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -10,12 +10,13 @@ from ayon_core.tools.utils import ( ProjectsCombobox, FoldersWidget, TasksWidget, + NiceCheckbox, ) class HierarchyPage(QtWidgets.QWidget): def __init__(self, controller, parent): - super(HierarchyPage, self).__init__(parent) + super().__init__(parent) # Header header_widget = QtWidgets.QWidget(self) @@ -46,14 +47,23 @@ class HierarchyPage(QtWidgets.QWidget): # - Folders widget with filter folders_wrapper = QtWidgets.QWidget(content_body) - folders_filter_text = PlaceholderLineEdit(folders_wrapper) + filters_widget = QtWidgets.QWidget(folders_wrapper) + + folders_filter_text = PlaceholderLineEdit(filters_widget) folders_filter_text.setPlaceholderText("Filter folders...") + my_tasks_checkbox = NiceCheckbox(filters_widget) + + filters_layout = QtWidgets.QHBoxLayout(filters_widget) + filters_layout.setContentsMargins(0, 0, 0, 0) + filters_layout.addWidget(folders_filter_text, 1) + filters_layout.addWidget(my_tasks_checkbox, 0) + folders_widget = FoldersWidget(controller, folders_wrapper) folders_wrapper_layout = QtWidgets.QVBoxLayout(folders_wrapper) folders_wrapper_layout.setContentsMargins(0, 0, 0, 0) - folders_wrapper_layout.addWidget(folders_filter_text, 0) + folders_wrapper_layout.addWidget(filters_widget, 0) folders_wrapper_layout.addWidget(folders_widget, 1) # - Tasks widget @@ -72,6 +82,9 @@ class HierarchyPage(QtWidgets.QWidget): btn_back.clicked.connect(self._on_back_clicked) refresh_btn.clicked.connect(self._on_refresh_clicked) folders_filter_text.textChanged.connect(self._on_filter_text_changed) + my_tasks_checkbox.stateChanged.connect( + self._on_my_tasks_checkbox_state_changed + ) self._is_visible = False self._controller = controller @@ -81,6 +94,8 @@ class HierarchyPage(QtWidgets.QWidget): self._folders_widget = folders_widget self._tasks_widget = tasks_widget + self._project_name = None + # Post init projects_combobox.set_listen_to_selection_change(self._is_visible) @@ -91,6 +106,7 @@ class HierarchyPage(QtWidgets.QWidget): self._projects_combobox.set_listen_to_selection_change(visible) if visible and project_name: self._projects_combobox.set_selection(project_name) + self._project_name = project_name def refresh(self): self._folders_widget.refresh() @@ -104,3 +120,15 @@ class HierarchyPage(QtWidgets.QWidget): def _on_filter_text_changed(self, text): self._folders_widget.set_name_filter(text) + + def _on_my_tasks_checkbox_state_changed(self, state): + folder_ids = None + task_ids = None + if state == QtCore.Qt.Checked: + entity_ids = self._controller.get_my_tasks_entity_ids( + self._project_name + ) + folder_ids = entity_ids["folder_ids"] + task_ids = entity_ids["task_ids"] + self._folders_widget.set_folder_ids_filter(folder_ids) + self._tasks_widget.set_task_ids_filter(task_ids) \ No newline at end of file From e7bae0f2606b6d9c2c37994fed3770090fa1f31d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:10:15 +0200 Subject: [PATCH 059/150] use py3 super calls --- client/ayon_core/tools/launcher/ui/window.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/launcher/ui/window.py b/client/ayon_core/tools/launcher/ui/window.py index 2d52a73c38..aa336108ed 100644 --- a/client/ayon_core/tools/launcher/ui/window.py +++ b/client/ayon_core/tools/launcher/ui/window.py @@ -17,7 +17,7 @@ class LauncherWindow(QtWidgets.QWidget): page_side_anim_interval = 250 def __init__(self, controller=None, parent=None): - super(LauncherWindow, self).__init__(parent) + super().__init__(parent) if controller is None: controller = BaseLauncherController() @@ -153,14 +153,14 @@ class LauncherWindow(QtWidgets.QWidget): self.resize(520, 740) def showEvent(self, event): - super(LauncherWindow, self).showEvent(event) + super().showEvent(event) self._window_is_active = True if not self._actions_refresh_timer.isActive(): self._actions_refresh_timer.start() self._controller.refresh() def closeEvent(self, event): - super(LauncherWindow, self).closeEvent(event) + super().closeEvent(event) self._window_is_active = False self._actions_refresh_timer.stop() @@ -176,7 +176,7 @@ class LauncherWindow(QtWidgets.QWidget): self._on_actions_refresh_timeout() self._actions_refresh_timer.start() - super(LauncherWindow, self).changeEvent(event) + super().changeEvent(event) def _on_actions_refresh_timeout(self): # Stop timer if widget is not visible From 93ea50cac145de2e568c6f17480fe487e62ec10c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 1 Apr 2025 12:15:22 +0200 Subject: [PATCH 060/150] Update correct category --- server/settings/publish_plugins.py | 2 +- server/settings/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index c32d8d360c..028cf9fffa 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -918,7 +918,7 @@ class IntegrateHeroTemplateNameProfileModel(BaseSettingsModel): template_name: str = SettingsField( "", title="Template name", - enum_resolver=anatomy_template_items_enum(category="publish") + enum_resolver=anatomy_template_items_enum(category="hero") ) diff --git a/server/settings/tools.py b/server/settings/tools.py index 28ceeedbe4..b003ef2244 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -314,7 +314,7 @@ class CustomStagingDirProfileModel(BaseSettingsModel): template_name: str = SettingsField( "", title="Template name", - enum_resolver=anatomy_template_items_enum(category="publish") + enum_resolver=anatomy_template_items_enum(category="staging") ) From 9887b069ff1bebe2b69c757aaf4ed1df77c243d4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:44:57 +0200 Subject: [PATCH 061/150] add a warning to roots --- client/ayon_core/pipeline/anatomy/roots.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/pipeline/anatomy/roots.py b/client/ayon_core/pipeline/anatomy/roots.py index 128834dcbc..c7b9ff8ebc 100644 --- a/client/ayon_core/pipeline/anatomy/roots.py +++ b/client/ayon_core/pipeline/anatomy/roots.py @@ -35,6 +35,9 @@ class RootItem(FormatObject): self.available_platforms = set(lowered_platform_keys.keys()) current_platform = platform.system().lower() + # WARNING: Using environment variables in roots is not considered + # as production safe. Some features may not work as expected, for + # example USD resolver or site sync. self.value = lowered_platform_keys[current_platform].format_map( os.environ ) From 345840bb1c28cd34f433ec1465a74ed12a886560 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:06:37 +0200 Subject: [PATCH 062/150] added exception for missing environment variable --- client/ayon_core/pipeline/anatomy/__init__.py | 2 ++ .../ayon_core/pipeline/anatomy/exceptions.py | 5 +++++ client/ayon_core/pipeline/anatomy/roots.py | 22 ++++++++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/anatomy/__init__.py b/client/ayon_core/pipeline/anatomy/__init__.py index 336d09ccaa..7000f51495 100644 --- a/client/ayon_core/pipeline/anatomy/__init__.py +++ b/client/ayon_core/pipeline/anatomy/__init__.py @@ -1,5 +1,6 @@ from .exceptions import ( ProjectNotSet, + RootMissingEnv, RootCombinationError, TemplateMissingKey, AnatomyTemplateUnsolved, @@ -9,6 +10,7 @@ from .anatomy import Anatomy __all__ = ( "ProjectNotSet", + "RootMissingEnv", "RootCombinationError", "TemplateMissingKey", "AnatomyTemplateUnsolved", diff --git a/client/ayon_core/pipeline/anatomy/exceptions.py b/client/ayon_core/pipeline/anatomy/exceptions.py index 39f116baf0..24df0e3046 100644 --- a/client/ayon_core/pipeline/anatomy/exceptions.py +++ b/client/ayon_core/pipeline/anatomy/exceptions.py @@ -5,6 +5,11 @@ class ProjectNotSet(Exception): """Exception raised when is created Anatomy without project name.""" +class RootMissingEnv(KeyError): + """Raised when root requires environment variables which is not filled.""" + pass + + class RootCombinationError(Exception): """This exception is raised when templates has combined root types.""" diff --git a/client/ayon_core/pipeline/anatomy/roots.py b/client/ayon_core/pipeline/anatomy/roots.py index c7b9ff8ebc..bd09a9fe51 100644 --- a/client/ayon_core/pipeline/anatomy/roots.py +++ b/client/ayon_core/pipeline/anatomy/roots.py @@ -2,9 +2,11 @@ import os import platform import numbers -from ayon_core.lib import Logger +from ayon_core.lib import Logger, StringTemplate from ayon_core.lib.path_templates import FormatObject +from .exceptions import RootMissingEnv + class RootItem(FormatObject): """Represents one item or roots. @@ -21,7 +23,7 @@ class RootItem(FormatObject): multi root setup otherwise None value is expected. """ def __init__(self, parent, root_raw_data, name): - super(RootItem, self).__init__() + super().__init__() self._log = None lowered_platform_keys = { key.lower(): value @@ -38,9 +40,19 @@ class RootItem(FormatObject): # WARNING: Using environment variables in roots is not considered # as production safe. Some features may not work as expected, for # example USD resolver or site sync. - self.value = lowered_platform_keys[current_platform].format_map( - os.environ - ) + try: + self.value = lowered_platform_keys[current_platform].format_map( + os.environ + ) + except KeyError: + result = StringTemplate(self.value).format(os.environ.copy()) + is_are = "is" if len(result.missing_keys) == 1 else "are" + missing_keys = ", ".join(result.missing_keys) + raise RootMissingEnv( + f"Root \"{name}\" requires environment variable/s" + f" {missing_keys} which {is_are} not available." + ) + self.clean_value = self._clean_root(self.value) def __format__(self, *args, **kwargs): From 9986e7def97e620dc27b6eb69417f1a5ab1e5483 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:06:48 +0200 Subject: [PATCH 063/150] capture exception in pre launch hook --- client/ayon_core/hooks/pre_global_host_data.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hooks/pre_global_host_data.py b/client/ayon_core/hooks/pre_global_host_data.py index 12da6f12f8..23f725901c 100644 --- a/client/ayon_core/hooks/pre_global_host_data.py +++ b/client/ayon_core/hooks/pre_global_host_data.py @@ -1,12 +1,15 @@ from ayon_api import get_project, get_folder_by_path, get_task_by_name +from ayon_core.pipeline import Anatomy +from ayon_core.pipeline.anatomy import RootMissingEnv + from ayon_applications import PreLaunchHook +from ayon_applications.exceptions import ApplicationLaunchFailed from ayon_applications.utils import ( EnvironmentPrepData, prepare_app_environments, prepare_context_environments ) -from ayon_core.pipeline import Anatomy class GlobalHostDataHook(PreLaunchHook): @@ -67,9 +70,12 @@ class GlobalHostDataHook(PreLaunchHook): self.data["project_entity"] = project_entity # Anatomy - self.data["anatomy"] = Anatomy( - project_name, project_entity=project_entity - ) + try: + self.data["anatomy"] = Anatomy( + project_name, project_entity=project_entity + ) + except RootMissingEnv as exc: + raise ApplicationLaunchFailed(str(exc)) folder_path = self.data.get("folder_path") if not folder_path: From 0f974c135225b5fce01ade11f6c945dda943c9d8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:15:31 +0200 Subject: [PATCH 064/150] fix state change handling --- client/ayon_core/tools/launcher/ui/hierarchy_page.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index 78ca5aaa1e..c3255b2d47 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -5,13 +5,12 @@ from ayon_core.tools.utils import ( PlaceholderLineEdit, SquareButton, RefreshButton, -) -from ayon_core.tools.utils import ( ProjectsCombobox, FoldersWidget, TasksWidget, NiceCheckbox, ) +from ayon_core.tools.utils.lib import checkstate_int_to_enum class HierarchyPage(QtWidgets.QWidget): @@ -52,11 +51,14 @@ class HierarchyPage(QtWidgets.QWidget): folders_filter_text = PlaceholderLineEdit(filters_widget) folders_filter_text.setPlaceholderText("Filter folders...") + my_tasks_label = QtWidgets.QLabel("My tasks", self) my_tasks_checkbox = NiceCheckbox(filters_widget) + my_tasks_checkbox.setChecked(False) filters_layout = QtWidgets.QHBoxLayout(filters_widget) filters_layout.setContentsMargins(0, 0, 0, 0) filters_layout.addWidget(folders_filter_text, 1) + filters_layout.addWidget(my_tasks_label, 0) filters_layout.addWidget(my_tasks_checkbox, 0) folders_widget = FoldersWidget(controller, folders_wrapper) @@ -91,6 +93,7 @@ class HierarchyPage(QtWidgets.QWidget): self._btn_back = btn_back self._projects_combobox = projects_combobox + self._my_tasks_checkbox = my_tasks_checkbox self._folders_widget = folders_widget self._tasks_widget = tasks_widget @@ -111,6 +114,9 @@ class HierarchyPage(QtWidgets.QWidget): def refresh(self): self._folders_widget.refresh() self._tasks_widget.refresh() + self._on_my_tasks_checkbox_state_changed( + self._on_my_tasks_checkbox.checkState() + ) def _on_back_clicked(self): self._controller.set_selected_project(None) @@ -124,6 +130,7 @@ class HierarchyPage(QtWidgets.QWidget): def _on_my_tasks_checkbox_state_changed(self, state): folder_ids = None task_ids = None + state = checkstate_int_to_enum(state) if state == QtCore.Qt.Checked: entity_ids = self._controller.get_my_tasks_entity_ids( self._project_name From c4c609432b0579e6d6528ba9217bdffa878a3849 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:15:45 +0200 Subject: [PATCH 065/150] skip filtering if items that are not tasks --- client/ayon_core/tools/utils/tasks_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 00960b6ba6..6037c47448 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -363,7 +363,7 @@ class TasksProxyModel(QtCore.QSortFilterProxyModel): return False source_index = self.sourceModel().index(row, 0, parent_index) task_id = source_index.data(ITEM_ID_ROLE) - if task_id not in self._task_ids_filter: + if task_id is not None and task_id not in self._task_ids_filter: return False return super().filterAcceptsRow(row, parent_index) From ba31114aa38d093056fe9e454630c667fd05d354 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:55:42 +0200 Subject: [PATCH 066/150] move the checkbox next to project combo --- .../tools/launcher/ui/hierarchy_page.py | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index c3255b2d47..fbe1630474 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -26,12 +26,18 @@ class HierarchyPage(QtWidgets.QWidget): projects_combobox = ProjectsCombobox(controller, header_widget) + my_tasks_label = QtWidgets.QLabel("My tasks", header_widget) + my_tasks_checkbox = NiceCheckbox(header_widget) + my_tasks_checkbox.setChecked(False) + refresh_btn = RefreshButton(header_widget) header_layout = QtWidgets.QHBoxLayout(header_widget) header_layout.setContentsMargins(0, 0, 0, 0) header_layout.addWidget(btn_back, 0) header_layout.addWidget(projects_combobox, 1) + header_layout.addWidget(my_tasks_label, 0) + header_layout.addWidget(my_tasks_checkbox, 0) header_layout.addWidget(refresh_btn, 0) # Body - Folders + Tasks selection @@ -46,26 +52,14 @@ class HierarchyPage(QtWidgets.QWidget): # - Folders widget with filter folders_wrapper = QtWidgets.QWidget(content_body) - filters_widget = QtWidgets.QWidget(folders_wrapper) - - folders_filter_text = PlaceholderLineEdit(filters_widget) + folders_filter_text = PlaceholderLineEdit(folders_wrapper) folders_filter_text.setPlaceholderText("Filter folders...") - my_tasks_label = QtWidgets.QLabel("My tasks", self) - my_tasks_checkbox = NiceCheckbox(filters_widget) - my_tasks_checkbox.setChecked(False) - - filters_layout = QtWidgets.QHBoxLayout(filters_widget) - filters_layout.setContentsMargins(0, 0, 0, 0) - filters_layout.addWidget(folders_filter_text, 1) - filters_layout.addWidget(my_tasks_label, 0) - filters_layout.addWidget(my_tasks_checkbox, 0) - folders_widget = FoldersWidget(controller, folders_wrapper) folders_wrapper_layout = QtWidgets.QVBoxLayout(folders_wrapper) folders_wrapper_layout.setContentsMargins(0, 0, 0, 0) - folders_wrapper_layout.addWidget(filters_widget, 0) + folders_wrapper_layout.addWidget(folders_filter_text, 0) folders_wrapper_layout.addWidget(folders_widget, 1) # - Tasks widget From 9eb652bd953c6b6e5d38459fa34c2b872edaabda Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:56:52 +0200 Subject: [PATCH 067/150] add missing line --- client/ayon_core/tools/launcher/ui/hierarchy_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index fbe1630474..f8eab20ddc 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -132,4 +132,4 @@ class HierarchyPage(QtWidgets.QWidget): folder_ids = entity_ids["folder_ids"] task_ids = entity_ids["task_ids"] self._folders_widget.set_folder_ids_filter(folder_ids) - self._tasks_widget.set_task_ids_filter(task_ids) \ No newline at end of file + self._tasks_widget.set_task_ids_filter(task_ids) From 60848df43044af39ceecd9574439088ae3500900 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:36:38 +0200 Subject: [PATCH 068/150] added '_update' and '_pop' helper methods --- .../ayon_core/pipeline/create/structures.py | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index 17bb85b720..097b5a553c 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -160,27 +160,14 @@ class AttributeValues: return self._attr_defs_by_key.get(key, default) def update(self, value): - changes = {} - for _key, _value in dict(value).items(): - if _key in self._data and self._data.get(_key) == _value: - continue - self._data[_key] = _value - changes[_key] = _value - + changes = self._update(value) if changes: self._parent.attribute_value_changed(self._key, changes) def pop(self, key, default=None): - has_key = key in self._data - value = self._data.pop(key, default) - # Remove attribute definition if is 'UnknownDef' - # - gives option to get rid of unknown values - attr_def = self._attr_defs_by_key.get(key) - if isinstance(attr_def, UnknownDef): - self._attr_defs_by_key.pop(key) - self._attr_defs.remove(attr_def) - elif has_key: - self._parent.attribute_value_changed(self._key, {key: None}) + value, changes = self._pop(key, default) + if changes: + self._parent.attribute_value_changed(self._key, changes) return value def reset_values(self): @@ -228,6 +215,29 @@ class AttributeValues: return serialize_attr_defs(self._attr_defs) + def _update(self, value): + changes = {} + for key, value in dict(value).items(): + if key in self._data and self._data.get(key) == value: + continue + self._data[key] = value + changes[key] = value + return changes + + def _pop(self, key, default): + has_key = key in self._data + value = self._data.pop(key, default) + # Remove attribute definition if is 'UnknownDef' + # - gives option to get rid of unknown values + attr_def = self._attr_defs_by_key.get(key) + changes = {} + if isinstance(attr_def, UnknownDef): + self._attr_defs_by_key.pop(key) + self._attr_defs.remove(attr_def) + elif has_key: + changes[key] = None + return value, changes + class CreatorAttributeValues(AttributeValues): """Creator specific attribute values of an instance.""" From 67db8e7632ec175c8155faf773884d654759eca4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:36:55 +0200 Subject: [PATCH 069/150] implemented 'set_value' on AttributeValues --- client/ayon_core/pipeline/create/structures.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index 097b5a553c..17f717dd3e 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -170,6 +170,16 @@ class AttributeValues: self._parent.attribute_value_changed(self._key, changes) return value + def set_value(self, value): + pop_keys = set(value.keys()) - set(self._data.keys()) + changes = self._update(value) + for key in pop_keys: + _, key_changes = self._pop(key, None) + changes.update(key_changes) + + if changes: + self._parent.attribute_value_changed(self._key, changes) + def reset_values(self): self._data = {} From 6116a2767770b76c32cefbc6bcf39de059bbeee1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:37:43 +0200 Subject: [PATCH 070/150] implemented setitem for PublishAttributes --- client/ayon_core/pipeline/create/structures.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index 17f717dd3e..f2a8a43d3f 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -290,6 +290,20 @@ class PublishAttributes: def __getitem__(self, key): return self._data[key] + def __setitem__(self, key, value): + """Set value for plugin. + + Args: + key (str): Plugin name. + value (dict[str, Any]): Value to set. + + """ + current_value = self._data.get(key) + if isinstance(current_value, PublishAttributeValues): + current_value.set_value(value) + else: + self._data[key] = value + def __contains__(self, key): return key in self._data From 0bad1078ab68ac92560572c4395fb1c7873ed1f3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:37:57 +0200 Subject: [PATCH 071/150] implemented delitem too --- client/ayon_core/pipeline/create/structures.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index f2a8a43d3f..6b45a5c610 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -304,6 +304,9 @@ class PublishAttributes: else: self._data[key] = value + def __delitem__(self, key): + self.pop(key) + def __contains__(self, key): return key in self._data From 81eff5ae6fb7658eab69579204fc7ff00ec5966c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:47:46 +0200 Subject: [PATCH 072/150] fix attribute name --- client/ayon_core/tools/launcher/ui/hierarchy_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index f8eab20ddc..835a963323 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -109,7 +109,7 @@ class HierarchyPage(QtWidgets.QWidget): self._folders_widget.refresh() self._tasks_widget.refresh() self._on_my_tasks_checkbox_state_changed( - self._on_my_tasks_checkbox.checkState() + self._my_tasks_checkbox.checkState() ) def _on_back_clicked(self): From 009348bca9bc89c93433568b99a575efe6182751 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:01:07 +0200 Subject: [PATCH 073/150] move widgets around --- .../tools/launcher/ui/hierarchy_page.py | 38 +++++++++++-------- .../ayon_core/tools/utils/folders_widget.py | 6 +++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index 835a963323..7c34989947 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -26,18 +26,12 @@ class HierarchyPage(QtWidgets.QWidget): projects_combobox = ProjectsCombobox(controller, header_widget) - my_tasks_label = QtWidgets.QLabel("My tasks", header_widget) - my_tasks_checkbox = NiceCheckbox(header_widget) - my_tasks_checkbox.setChecked(False) - refresh_btn = RefreshButton(header_widget) header_layout = QtWidgets.QHBoxLayout(header_widget) header_layout.setContentsMargins(0, 0, 0, 0) header_layout.addWidget(btn_back, 0) header_layout.addWidget(projects_combobox, 1) - header_layout.addWidget(my_tasks_label, 0) - header_layout.addWidget(my_tasks_checkbox, 0) header_layout.addWidget(refresh_btn, 0) # Body - Folders + Tasks selection @@ -49,23 +43,36 @@ class HierarchyPage(QtWidgets.QWidget): ) content_body.setOrientation(QtCore.Qt.Horizontal) - # - Folders widget with filter - folders_wrapper = QtWidgets.QWidget(content_body) + # - filters + filters_widget = QtWidgets.QWidget(self) - folders_filter_text = PlaceholderLineEdit(folders_wrapper) + folders_filter_text = PlaceholderLineEdit(filters_widget) folders_filter_text.setPlaceholderText("Filter folders...") - folders_widget = FoldersWidget(controller, folders_wrapper) + my_tasks_tooltip = ( + "Filter folders and task to only those you are assigned to." + ) + my_tasks_label = QtWidgets.QLabel("My tasks", filters_widget) + my_tasks_label.setToolTip(my_tasks_tooltip) - folders_wrapper_layout = QtWidgets.QVBoxLayout(folders_wrapper) - folders_wrapper_layout.setContentsMargins(0, 0, 0, 0) - folders_wrapper_layout.addWidget(folders_filter_text, 0) - folders_wrapper_layout.addWidget(folders_widget, 1) + my_tasks_checkbox = NiceCheckbox(filters_widget) + my_tasks_checkbox.setChecked(False) + my_tasks_checkbox.setToolTip(my_tasks_tooltip) + + filters_layout = QtWidgets.QHBoxLayout(filters_widget) + filters_layout.setContentsMargins(0, 0, 0, 0) + filters_layout.addWidget(folders_filter_text, 1) + filters_layout.addWidget(my_tasks_label, 0) + filters_layout.addWidget(my_tasks_checkbox, 0) + + # - Folders widget + folders_widget = FoldersWidget(controller, content_body) + folders_widget.set_header_visible(True) # - Tasks widget tasks_widget = TasksWidget(controller, content_body) - content_body.addWidget(folders_wrapper) + content_body.addWidget(folders_widget) content_body.addWidget(tasks_widget) content_body.setStretchFactor(0, 100) content_body.setStretchFactor(1, 65) @@ -73,6 +80,7 @@ class HierarchyPage(QtWidgets.QWidget): main_layout = QtWidgets.QVBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(header_widget, 0) + main_layout.addWidget(filters_widget, 0) main_layout.addWidget(content_body, 1) btn_back.clicked.connect(self._on_back_clicked) diff --git a/client/ayon_core/tools/utils/folders_widget.py b/client/ayon_core/tools/utils/folders_widget.py index 2e2c5b5d77..7e3f2865e7 100644 --- a/client/ayon_core/tools/utils/folders_widget.py +++ b/client/ayon_core/tools/utils/folders_widget.py @@ -36,6 +36,9 @@ class FoldersQtModel(QtGui.QStandardItemModel): def __init__(self, controller): super().__init__() + self.setColumnCount(1) + self.setHeaderData(0, QtCore.Qt.Horizontal, "Folders") + self._controller = controller self._items_by_id = {} self._parent_id_by_id = {} @@ -479,6 +482,9 @@ class FoldersWidget(QtWidgets.QWidget): """ self._folders_proxy_model.set_folder_ids_filter(folder_ids) + def set_header_visible(self, visible: bool): + self._folders_view.setHeaderHidden(not visible) + def refresh(self): """Refresh folders model. From 9ebd2bbc640af6fc4feded1dd9416f54947c45a4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:20:53 +0200 Subject: [PATCH 074/150] fix typehints for older pythons --- client/ayon_core/tools/utils/folders_widget.py | 1 + client/ayon_core/tools/utils/tasks_widget.py | 1 + 2 files changed, 2 insertions(+) diff --git a/client/ayon_core/tools/utils/folders_widget.py b/client/ayon_core/tools/utils/folders_widget.py index 7e3f2865e7..7b71dd087c 100644 --- a/client/ayon_core/tools/utils/folders_widget.py +++ b/client/ayon_core/tools/utils/folders_widget.py @@ -1,3 +1,4 @@ +from __future__ import annotations import collections from typing import Optional diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 6037c47448..9118611c23 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -1,3 +1,4 @@ +from __future__ import annotations from typing import Optional from qtpy import QtWidgets, QtGui, QtCore From d8c442e7f56a511c22376dadd828cda715ae19c4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Apr 2025 15:00:44 +0200 Subject: [PATCH 075/150] Update OCIO config paths and compatible addons - Added a new compatible addon with version requirement. - Updated existing OCIO config paths for ACES 1.3 and added support for ACES 2.0. - Adjusted labels and descriptions for clarity on OCIO versions. --- package.py | 4 +++- server/settings/main.py | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/package.py b/package.py index af3342f3f2..bb8278151d 100644 --- a/package.py +++ b/package.py @@ -9,4 +9,6 @@ plugin_for = ["ayon_server"] ayon_server_version = ">=1.0.3,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} -ayon_compatible_addons = {} +ayon_compatible_addons = { + "ayon_ocio": ">=1.2.0", +} diff --git a/server/settings/main.py b/server/settings/main.py index 261bd7fc04..520c533aab 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,18 +72,30 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") }, { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 + "label": "ACES 1.3 Studio (OCIO v2.1)", + "description": ( + "Aces 1.3 Studio OCIO config file. Requires OCIO v2.1.") + }, + { + "value": "{BUILTIN_OCIO_ROOT}/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "label": "ACES 2.0 Studio (OCIO v2.4)", + "description": ( + "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") + }, + { + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", "label": "Nuke default", }, ] From ee213afce408dfb90e1b3265f16cdb19e59cfd66 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Apr 2025 15:02:19 +0200 Subject: [PATCH 076/150] Update OCIO config paths and add comments - Added line length comments to OCIO config paths - No functional changes, just improved code clarity --- server/settings/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 520c533aab..a7d82ec363 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -90,12 +90,12 @@ def _ocio_built_in_paths(): "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", # noqa: E501 "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", # noqa: E501 "label": "Nuke default", }, ] From 89a494b43f5d1a3c27a95f88d13458462381ebb5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 3 Apr 2025 11:28:52 +0200 Subject: [PATCH 077/150] Added profile targeting Hero --- server/settings/tools.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index b003ef2244..6b07910454 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -291,6 +291,29 @@ class PublishTemplateNameProfile(BaseSettingsModel): ) +class HeroTemplateNameProfile(BaseSettingsModel): + _layout = "expanded" + product_types: list[str] = SettingsField( + default_factory=list, + title="Product types" + ) + # TODO this should use hosts enum + hosts: list[str] = SettingsField(default_factory=list, title="Hosts") + task_types: list[str] = SettingsField( + default_factory=list, + title="Task types", + enum_resolver=task_types_enum + ) + task_names: list[str] = SettingsField( + default_factory=list, title="Task names" + ) + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="hero") + ) + + class CustomStagingDirProfileModel(BaseSettingsModel): active: bool = SettingsField(True, title="Is active") hosts: list[str] = SettingsField(default_factory=list, title="Host names") @@ -323,7 +346,7 @@ class PublishToolModel(BaseSettingsModel): default_factory=list, title="Template name profiles" ) - hero_template_name_profiles: list[PublishTemplateNameProfile] = ( + hero_template_name_profiles: list[HeroTemplateNameProfile] = ( SettingsField( default_factory=list, title="Hero template name profiles" From b8a90c43368f60ef8cd414b005d3af47dff56abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Thu, 3 Apr 2025 16:40:22 +0200 Subject: [PATCH 078/150] :recycle: add future annotation because type hints are using `list`, it breaks on Python 3.7 --- client/ayon_core/tools/loader/ui/product_types_combo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/loader/ui/product_types_combo.py b/client/ayon_core/tools/loader/ui/product_types_combo.py index 91fa52b0e9..525f1cae1b 100644 --- a/client/ayon_core/tools/loader/ui/product_types_combo.py +++ b/client/ayon_core/tools/loader/ui/product_types_combo.py @@ -1,3 +1,4 @@ +from __future__ import annotations from qtpy import QtGui, QtCore from ._multicombobox import ( From 2cd0b0ddbfdc8086156a2dee50aada811a3233c8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 4 Apr 2025 14:47:19 +0200 Subject: [PATCH 079/150] Update OCIO config paths for cleaner structure - Removed redundant directory from ACES 1.2 path - Simplified Nuke default config path --- server/settings/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index a7d82ec363..21612ee362 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -90,12 +90,12 @@ def _ocio_built_in_paths(): "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", # noqa: E501 "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", # noqa: E501 "label": "Nuke default", }, ] From a810be650620e8181849cfd81334ad3c64164c58 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:59:07 +0200 Subject: [PATCH 080/150] handle popped plugin values --- client/ayon_core/pipeline/create/context.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 26b04ed3ed..6ac6685647 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -2303,10 +2303,16 @@ class CreateContext: for plugin_name, plugin_value in item_changes.pop( "publish_attributes" ).items(): + if plugin_value is None: + current_publish[plugin_name] = None + continue plugin_changes = current_publish.setdefault( plugin_name, {} ) - plugin_changes.update(plugin_value) + if plugin_changes is None: + current_publish[plugin_name] = plugin_value + else: + plugin_changes.update(plugin_value) item_values.update(item_changes) From abfb0aedee18debe9b4ec9f6e1053cdde7e1a7d9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:18:49 +0200 Subject: [PATCH 081/150] changed how thumbnails are received --- client/ayon_core/pipeline/thumbnails.py | 19 +++++- .../tools/common_models/thumbnails.py | 61 +++++++++++++++++-- client/ayon_core/tools/loader/abstract.py | 12 +++- client/ayon_core/tools/loader/control.py | 11 +++- client/ayon_core/tools/loader/ui/window.py | 45 ++++++-------- 5 files changed, 109 insertions(+), 39 deletions(-) diff --git a/client/ayon_core/pipeline/thumbnails.py b/client/ayon_core/pipeline/thumbnails.py index 401d95f273..658372888f 100644 --- a/client/ayon_core/pipeline/thumbnails.py +++ b/client/ayon_core/pipeline/thumbnails.py @@ -226,11 +226,26 @@ class _CacheItems: thumbnails_cache = ThumbnailsCache() -def get_thumbnail_path(project_name, thumbnail_id): +def get_thumbnail_path( + project_name: str, + entity_type: str, + entity_id: str, + thumbnail_id: str +): """Get path to thumbnail image. + Thumbnail is cached by thumbnail id but is received using entity type and + entity id. + + Notes: + Function 'get_thumbnail_by_id' can't be used because does not work + for artists. The endpoint can't validate artist permissions. + Args: project_name (str): Project where thumbnail belongs to. + entity_type (str): Entity type "folder", "task", "version" + and "workfile". + entity_id (str): Entity id. thumbnail_id (Union[str, None]): Thumbnail id. Returns: @@ -251,7 +266,7 @@ def get_thumbnail_path(project_name, thumbnail_id): # 'get_thumbnail_by_id' did not return output of # 'ServerAPI' method. con = ayon_api.get_server_api_connection() - result = con.get_thumbnail_by_id(project_name, thumbnail_id) + result = con.get_thumbnail(project_name, entity_type, entity_id) if result is not None and result.is_valid: return _CacheItems.thumbnails_cache.store_thumbnail( diff --git a/client/ayon_core/tools/common_models/thumbnails.py b/client/ayon_core/tools/common_models/thumbnails.py index 2fa1e36e5c..5111d0be28 100644 --- a/client/ayon_core/tools/common_models/thumbnails.py +++ b/client/ayon_core/tools/common_models/thumbnails.py @@ -21,8 +21,50 @@ class ThumbnailsModel: self._folders_cache.reset() self._versions_cache.reset() - def get_thumbnail_path(self, project_name, thumbnail_id): - return self._get_thumbnail_path(project_name, thumbnail_id) + def get_thumbnail_paths( + self, + project_name, + entity_type, + entity_ids, + ): + thumbnail_paths = set() + if not project_name or not entity_type or not entity_ids: + return thumbnail_paths + + thumbnail_id_by_entity_id = {} + if entity_type == "folder": + thumbnail_id_by_entity_id = self.get_folder_thumbnail_ids( + project_name, entity_ids + ) + + elif entity_type == "version": + thumbnail_id_by_entity_id = self.get_version_thumbnail_ids( + project_name, entity_ids + ) + + if not thumbnail_id_by_entity_id: + return thumbnail_paths + + entity_ids_by_thumbnail_id = collections.defaultdict(set) + for entity_id, thumbnail_id in thumbnail_id_by_entity_id.items(): + if not thumbnail_id: + continue + entity_ids_by_thumbnail_id[thumbnail_id].add(entity_id) + + output = { + entity_id: None + for entity_id in entity_ids + } + for thumbnail_id, entity_ids in entity_ids_by_thumbnail_id.items(): + thumbnail_path = self._get_thumbnail_path( + project_name, entity_type, next(iter(entity_ids)), thumbnail_id + ) + if not thumbnail_path: + continue + for entity_id in entity_ids: + output[entity_id] = thumbnail_path + + return output def get_folder_thumbnail_ids(self, project_name, folder_ids): project_cache = self._folders_cache[project_name] @@ -56,7 +98,13 @@ class ThumbnailsModel: output[version_id] = cache.get_data() return output - def _get_thumbnail_path(self, project_name, thumbnail_id): + def _get_thumbnail_path( + self, + project_name, + entity_type, + entity_id, + thumbnail_id + ): if not thumbnail_id: return None @@ -64,7 +112,12 @@ class ThumbnailsModel: if thumbnail_id in project_cache: return project_cache[thumbnail_id] - filepath = get_thumbnail_path(project_name, thumbnail_id) + filepath = get_thumbnail_path( + project_name, + entity_type, + entity_id, + thumbnail_id + ) project_cache[thumbnail_id] = filepath return filepath diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index 26b476de1f..d0d7cd430b 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -733,7 +733,12 @@ class FrontendLoaderController(_BaseLoaderController): pass @abstractmethod - def get_thumbnail_path(self, project_name, thumbnail_id): + def get_thumbnail_paths( + self, + project_name, + entity_type, + entity_ids + ): """Get thumbnail path for thumbnail id. This method should get a path to a thumbnail based on thumbnail id. @@ -742,10 +747,11 @@ class FrontendLoaderController(_BaseLoaderController): Args: project_name (str): Project name. - thumbnail_id (str): Thumbnail id. + entity_type (str): Entity type. + entity_ids (set[str]): Entity ids. Returns: - Union[str, None]: Thumbnail path or None if not found. + dict[str, Union[str, None]]: Thumbnail path by entity id. """ pass diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 7959a63edb..b3a80b34d4 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -259,9 +259,14 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): project_name, version_ids ) - def get_thumbnail_path(self, project_name, thumbnail_id): - return self._thumbnails_model.get_thumbnail_path( - project_name, thumbnail_id + def get_thumbnail_paths( + self, + project_name, + entity_type, + entity_ids, + ): + return self._thumbnails_model.get_thumbnail_paths( + project_name, entity_type, entity_ids ) def change_products_group(self, project_name, product_ids, group_name): diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index b846484c39..3d2e15c630 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -501,38 +501,29 @@ class LoaderWindow(QtWidgets.QWidget): self._update_thumbnails() def _update_thumbnails(self): + # TODO make this threaded and show loading animation while running project_name = self._selected_project_name - thumbnail_ids = set() + entity_type = None + entity_ids = set() if self._selected_version_ids: - thumbnail_id_by_entity_id = ( - self._controller.get_version_thumbnail_ids( - project_name, - self._selected_version_ids - ) - ) - thumbnail_ids = set(thumbnail_id_by_entity_id.values()) + entity_ids = set(self._selected_version_ids) + entity_type = "version" elif self._selected_folder_ids: - thumbnail_id_by_entity_id = ( - self._controller.get_folder_thumbnail_ids( - project_name, - self._selected_folder_ids - ) - ) - thumbnail_ids = set(thumbnail_id_by_entity_id.values()) + entity_ids = set(self._selected_folder_ids) + entity_type = "folder" - thumbnail_ids.discard(None) - - if not thumbnail_ids: - self._thumbnails_widget.set_current_thumbnails(None) - return - - thumbnail_paths = set() - for thumbnail_id in thumbnail_ids: - thumbnail_path = self._controller.get_thumbnail_path( - project_name, thumbnail_id) - thumbnail_paths.add(thumbnail_path) + thumbnail_path_by_entity_id = self._controller.get_thumbnail_paths( + project_name, entity_type, entity_ids + ) + thumbnail_paths = set(thumbnail_path_by_entity_id.values()) thumbnail_paths.discard(None) - self._thumbnails_widget.set_current_thumbnail_paths(thumbnail_paths) + + if thumbnail_paths: + self._thumbnails_widget.set_current_thumbnail_paths( + thumbnail_paths + ) + else: + self._thumbnails_widget.set_current_thumbnails(None) def _on_projects_refresh(self): self._refresh_handler.set_project_refreshed() From 76734fbbd2ee402876bfd3e3e6a1de7ec0bfcb1e Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 7 Apr 2025 20:55:32 +0000 Subject: [PATCH 082/150] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index d021a03e7e..b3c68e5390 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.6+dev" +__version__ = "1.1.7" diff --git a/package.py b/package.py index 9af45719a7..121b909486 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.6+dev" +version = "1.1.7" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 3a76b25c0d..5445048777 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.6+dev" +version = "1.1.7" description = "" authors = ["Ynput Team "] readme = "README.md" From abbc9ffc378dbb2c8553a3d8217a3b51e17ca699 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 7 Apr 2025 20:56:09 +0000 Subject: [PATCH 083/150] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index b3c68e5390..962ec487a7 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.7" +__version__ = "1.1.7+dev" diff --git a/package.py b/package.py index 121b909486..fe870315cf 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.7" +version = "1.1.7+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 5445048777..86df6535aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.7" +version = "1.1.7+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 6f1981ca968a9c0a5c52b970c1ec6eaa62bc2cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:54:25 +0200 Subject: [PATCH 084/150] Delete poetry.lock Poetry lock shouldn't be tracked file to avoid conflics, it should be always recreated from dependencies defined in `pyproject.toml` --- poetry.lock | 1515 --------------------------------------------------- 1 file changed, 1515 deletions(-) delete mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 96e1dc0f4c..0000000000 --- a/poetry.lock +++ /dev/null @@ -1,1515 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - -[[package]] -name = "attrs" -version = "25.1.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, - {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "ayon-python-api" -version = "1.0.12" -description = "AYON Python API" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "ayon-python-api-1.0.12.tar.gz", hash = "sha256:8e4c03436df8afdda4c6ad4efce436068771995bb0153a90e003364afa0e7f55"}, - {file = "ayon_python_api-1.0.12-py3-none-any.whl", hash = "sha256:65f61c2595dd6deb26fed5e3fda7baef887f475fa4b21df12513646ddccf4a7d"}, -] - -[package.dependencies] -appdirs = ">=1,<2" -requests = ">=2.27.1" -Unidecode = ">=1.3.0" - -[[package]] -name = "babel" -version = "2.17.0" -description = "Internationalization utilities" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, - {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, -] - -[package.extras] -dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] - -[[package]] -name = "backrefs" -version = "5.8" -description = "A wrapper around re and regex that adds additional back references." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"}, - {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"}, - {file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"}, - {file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"}, - {file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"}, - {file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"}, -] - -[package.extras] -extras = ["regex"] - -[[package]] -name = "certifi" -version = "2025.1.31" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, -] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "clique" -version = "2.0.0" -description = "Manage collections with common numerical component" -optional = false -python-versions = ">=3.0, <4.0" -groups = ["dev"] -files = [ - {file = "clique-2.0.0-py2.py3-none-any.whl", hash = "sha256:45e2a4c6078382e0b217e5e369494279cf03846d95ee601f93290bed5214c22e"}, - {file = "clique-2.0.0.tar.gz", hash = "sha256:6e1115dbf21b1726f4b3db9e9567a662d6bdf72487c4a0a1f8cb7f10cf4f4754"}, -] - -[package.extras] -dev = ["lowdown (>=0.2.0,<1)", "pytest (>=2.3.5,<5)", "pytest-cov (>=2,<3)", "pytest-runner (>=2.7,<3)", "sphinx (>=2,<4)", "sphinx-rtd-theme (>=0.1.6,<1)"] -doc = ["lowdown (>=0.2.0,<1)", "sphinx (>=2,<4)", "sphinx-rtd-theme (>=0.1.6,<1)"] -test = ["pytest (>=2.3.5,<5)", "pytest-cov (>=2,<3)", "pytest-runner (>=2.7,<3)"] - -[[package]] -name = "codespell" -version = "2.4.1" -description = "Fix common misspellings in text files" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425"}, - {file = "codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5"}, -] - -[package.extras] -dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] -hard-encoding-detection = ["chardet"] -toml = ["tomli ; python_version < \"3.11\""] -types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "csscompressor" -version = "0.9.5" -description = "A python port of YUI CSS Compressor" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "csscompressor-0.9.5.tar.gz", hash = "sha256:afa22badbcf3120a4f392e4d22f9fff485c044a1feda4a950ecc5eba9dd31a05"}, -] - -[[package]] -name = "distlib" -version = "0.3.9" -description = "Distribution utilities" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, - {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "filelock" -version = "3.17.0" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, - {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] - -[[package]] -name = "ghp-import" -version = "2.1.0" -description = "Copy your docs directly to the gh-pages branch." -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, - {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, -] - -[package.dependencies] -python-dateutil = ">=2.8.1" - -[package.extras] -dev = ["flake8", "markdown", "twine", "wheel"] - -[[package]] -name = "griffe" -version = "1.6.2" -description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "griffe-1.6.2-py3-none-any.whl", hash = "sha256:6399f7e663150e4278a312a8e8a14d2f3d7bd86e2ef2f8056a1058e38579c2ee"}, - {file = "griffe-1.6.2.tar.gz", hash = "sha256:3a46fa7bd83280909b63c12b9a975732a927dd97809efe5b7972290b606c5d91"}, -] - -[package.dependencies] -colorama = ">=0.4" - -[[package]] -name = "htmlmin2" -version = "0.1.13" -description = "An HTML Minifier" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "htmlmin2-0.1.13-py3-none-any.whl", hash = "sha256:75609f2a42e64f7ce57dbff28a39890363bde9e7e5885db633317efbdf8c79a2"}, -] - -[[package]] -name = "identify" -version = "2.6.7" -description = "File identification library for Python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "identify-2.6.7-py2.py3-none-any.whl", hash = "sha256:155931cb617a401807b09ecec6635d6c692d180090a1cedca8ef7d58ba5b6aa0"}, - {file = "identify-2.6.7.tar.gz", hash = "sha256:3fa266b42eba321ee0b2bb0936a6a6b9e36a1351cbb69055b3082f4193035684"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "importlib-metadata" -version = "8.6.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -description = "Read resources from Python packages" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"}, - {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] -type = ["pytest-mypy"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, - {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsmin" -version = "3.0.1" -description = "JavaScript minifier." -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "jsmin-3.0.1.tar.gz", hash = "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc"}, -] - -[[package]] -name = "markdown" -version = "3.7" -description = "Python implementation of John Gruber's Markdown." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, - {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "markdown-checklist" -version = "0.4.4" -description = "Python Markdown extension for task lists with checkboxes" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "markdown-checklist-0.4.4.tar.gz", hash = "sha256:69c93850798b1e01cdc6fcd4a80592d941f669f6451bbf69c71a4ffd1142f849"}, -] - -[package.dependencies] -markdown = "*" - -[package.extras] -coverage = ["coverage", "figleaf"] -testing = ["pytest"] - -[[package]] -name = "markupsafe" -version = "3.0.2" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, -] - -[[package]] -name = "mdx-gh-links" -version = "0.4" -description = "An extension to Python-Markdown which adds support for shorthand links to GitHub users, repositories, issues and commits." -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "mdx_gh_links-0.4-py3-none-any.whl", hash = "sha256:9057bca1fa5280bf1fcbf354381e46c9261cc32c2d5c0407801f8a910be5f099"}, - {file = "mdx_gh_links-0.4.tar.gz", hash = "sha256:41d5aac2ab201425aa0a19373c4095b79e5e015fdacfe83c398199fe55ca3686"}, -] - -[package.dependencies] -markdown = ">=3.0.0" - -[[package]] -name = "mergedeep" -version = "1.3.4" -description = "A deep merge function for 🐍." -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, -] - -[[package]] -name = "mike" -version = "2.1.3" -description = "Manage multiple versions of your MkDocs-powered documentation" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, - {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, -] - -[package.dependencies] -importlib-metadata = "*" -importlib-resources = "*" -jinja2 = ">=2.7" -mkdocs = ">=1.0" -pyparsing = ">=3.0" -pyyaml = ">=5.1" -pyyaml-env-tag = "*" -verspec = "*" - -[package.extras] -dev = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] -test = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] - -[[package]] -name = "mkdocs" -version = "1.6.1" -description = "Project documentation with Markdown." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, - {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} -ghp-import = ">=1.0" -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} -jinja2 = ">=2.11.1" -markdown = ">=3.3.6" -markupsafe = ">=2.0.1" -mergedeep = ">=1.3.4" -mkdocs-get-deps = ">=0.2.0" -packaging = ">=20.5" -pathspec = ">=0.11.1" -pyyaml = ">=5.1" -pyyaml-env-tag = ">=0.1" -watchdog = ">=2.0" - -[package.extras] -i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] - -[[package]] -name = "mkdocs-autoapi" -version = "0.4.0" -description = "MkDocs plugin providing automatic API reference generation" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mkdocs_autoapi-0.4.0-py3-none-any.whl", hash = "sha256:ce2b5e8b4d1df37bd1273a6bce1dded152107e2280cedbfa085cea10edf8531b"}, - {file = "mkdocs_autoapi-0.4.0.tar.gz", hash = "sha256:409c2da8eb297ef51381b066744ac1cdf846220bcc779bdba680fbc5a080df1e"}, -] - -[package.dependencies] -mkdocs = ">=1.4.0" -mkdocstrings = ">=0.19.0" - -[package.extras] -python = ["mkdocstrings[python] (>=0.19.0)"] -python-legacy = ["mkdocstrings[python-legacy] (>=0.19.0)"] -vba = ["mkdocstrings-vba (>=0.0.10)"] - -[[package]] -name = "mkdocs-autorefs" -version = "1.4.1" -description = "Automatically link across pages in MkDocs." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mkdocs_autorefs-1.4.1-py3-none-any.whl", hash = "sha256:9793c5ac06a6ebbe52ec0f8439256e66187badf4b5334b5fde0b128ec134df4f"}, - {file = "mkdocs_autorefs-1.4.1.tar.gz", hash = "sha256:4b5b6235a4becb2b10425c2fa191737e415b37aa3418919db33e5d774c9db079"}, -] - -[package.dependencies] -Markdown = ">=3.3" -markupsafe = ">=2.0.1" -mkdocs = ">=1.1" - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.0" -description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, - {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} -mergedeep = ">=1.3.4" -platformdirs = ">=2.2.0" -pyyaml = ">=5.1" - -[[package]] -name = "mkdocs-material" -version = "9.6.9" -description = "Documentation that simply works" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mkdocs_material-9.6.9-py3-none-any.whl", hash = "sha256:6e61b7fb623ce2aa4622056592b155a9eea56ff3487d0835075360be45a4c8d1"}, - {file = "mkdocs_material-9.6.9.tar.gz", hash = "sha256:a4872139715a1f27b2aa3f3dc31a9794b7bbf36333c0ba4607cf04786c94f89c"}, -] - -[package.dependencies] -babel = ">=2.10,<3.0" -backrefs = ">=5.7.post1,<6.0" -colorama = ">=0.4,<1.0" -jinja2 = ">=3.0,<4.0" -markdown = ">=3.2,<4.0" -mkdocs = ">=1.6,<2.0" -mkdocs-material-extensions = ">=1.3,<2.0" -paginate = ">=0.5,<1.0" -pygments = ">=2.16,<3.0" -pymdown-extensions = ">=10.2,<11.0" -requests = ">=2.26,<3.0" - -[package.extras] -git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] -imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] -recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -description = "Extension pack for Python Markdown and MkDocs Material." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, - {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, -] - -[[package]] -name = "mkdocs-minify-plugin" -version = "0.8.0" -description = "An MkDocs plugin to minify HTML, JS or CSS files prior to being written to disk" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mkdocs-minify-plugin-0.8.0.tar.gz", hash = "sha256:bc11b78b8120d79e817308e2b11539d790d21445eb63df831e393f76e52e753d"}, - {file = "mkdocs_minify_plugin-0.8.0-py3-none-any.whl", hash = "sha256:5fba1a3f7bd9a2142c9954a6559a57e946587b21f133165ece30ea145c66aee6"}, -] - -[package.dependencies] -csscompressor = ">=0.9.5" -htmlmin2 = ">=0.1.13" -jsmin = ">=3.0.1" -mkdocs = ">=1.4.1" - -[[package]] -name = "mkdocstrings" -version = "0.29.0" -description = "Automatic documentation from sources, for MkDocs." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mkdocstrings-0.29.0-py3-none-any.whl", hash = "sha256:8ea98358d2006f60befa940fdebbbc88a26b37ecbcded10be726ba359284f73d"}, - {file = "mkdocstrings-0.29.0.tar.gz", hash = "sha256:3657be1384543ce0ee82112c3e521bbf48e41303aa0c229b9ffcccba057d922e"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} -Jinja2 = ">=2.11.1" -Markdown = ">=3.6" -MarkupSafe = ">=1.1" -mkdocs = ">=1.6" -mkdocs-autorefs = ">=1.4" -pymdown-extensions = ">=6.3" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} - -[package.extras] -crystal = ["mkdocstrings-crystal (>=0.3.4)"] -python = ["mkdocstrings-python (>=1.16.2)"] -python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] - -[[package]] -name = "mkdocstrings-python" -version = "1.16.8" -description = "A Python handler for mkdocstrings." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mkdocstrings_python-1.16.8-py3-none-any.whl", hash = "sha256:211b7aaf776cd45578ecb531e5ad0d3a35a8be9101a6bfa10de38a69af9d8fd8"}, - {file = "mkdocstrings_python-1.16.8.tar.gz", hash = "sha256:9453ccae69be103810c1cf6435ce71c8f714ae37fef4d87d16aa92a7c800fe1d"}, -] - -[package.dependencies] -griffe = ">=1.6.2" -mkdocs-autorefs = ">=1.4" -mkdocstrings = ">=0.28.3" -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "mkdocstrings-shell" -version = "1.0.3" -description = "A shell scripts/libraries handler for mkdocstrings." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mkdocstrings_shell-1.0.3-py3-none-any.whl", hash = "sha256:b23ebe43d06c9c19a541548f34d42ee4e4324ae06423eba8a9136e295c67f345"}, - {file = "mkdocstrings_shell-1.0.3.tar.gz", hash = "sha256:3bdea6a1e794a5d0e15d461f33b92e0b9f3b9a1e2c33671d9a2b7d83c761096a"}, -] - -[package.dependencies] -mkdocstrings = ">=0.28.3" -shellman = ">=1.0.2" - -[[package]] -name = "mock" -version = "5.1.0" -description = "Rolling backport of unittest.mock for all Pythons" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mock-5.1.0-py3-none-any.whl", hash = "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744"}, - {file = "mock-5.1.0.tar.gz", hash = "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d"}, -] - -[package.extras] -build = ["blurb", "twine", "wheel"] -docs = ["sphinx"] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "opentimelineio" -version = "0.17.0" -description = "Editorial interchange format and API" -optional = false -python-versions = "!=3.9.0,>=3.7" -groups = ["dev"] -files = [ - {file = "OpenTimelineIO-0.17.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:2dd31a570cabfd6227c1b1dd0cc038da10787492c26c55de058326e21fe8a313"}, - {file = "OpenTimelineIO-0.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a1da5d4803d1ba5e846b181a9e0f4a392c76b9acc5e08947772bc086f2ebfc0"}, - {file = "OpenTimelineIO-0.17.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3527977aec8202789a42d60e1e0dc11b4154f585ef72921760445f43e7967a00"}, - {file = "OpenTimelineIO-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3aafb4c50455832ed2627c2cac654b896473a5c1f8348ddc07c10be5cfbd59"}, - {file = "OpenTimelineIO-0.17.0-cp310-cp310-win32.whl", hash = "sha256:fee45af9f6330773893cd0858e92f8256bb5bde4229b44a76f03e59a9fb1b1b6"}, - {file = "OpenTimelineIO-0.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:d51887619689c21d67cc4b11b1088f99ae44094513315e7a144be00f1393bfa8"}, - {file = "OpenTimelineIO-0.17.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:cbf05c3e8c0187969f79e91f7495d1f0dc3609557874d8e601ba2e072c70ddb1"}, - {file = "OpenTimelineIO-0.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d3430c3f4e88c5365d7b6afbee920b0815b62ecf141abe44cd739c9eedc04284"}, - {file = "OpenTimelineIO-0.17.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1912345227b0bd1654c7153863eadbcee60362aa46340678e576e5d2aa3106a"}, - {file = "OpenTimelineIO-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51e06eb11a868d970c1534e39faf916228d5163bf3598076d408d8f393ab0bd4"}, - {file = "OpenTimelineIO-0.17.0-cp311-cp311-win32.whl", hash = "sha256:5c3a3f4780b25a8c1a80d788becba691d12b629069ad8783d0db21027639276f"}, - {file = "OpenTimelineIO-0.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c8726b33af30ba42928972192311ea0f986edbbd5f74651bada182d4fe805c"}, - {file = "OpenTimelineIO-0.17.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:9a9af4105a088c0ab131780e49db268db7e37871aac33db842de6b2b16f14e39"}, - {file = "OpenTimelineIO-0.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e653ad1dd3b85f5c312a742dc24b61b330964aa391dc5bc072fe8b9c85adff1"}, - {file = "OpenTimelineIO-0.17.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a77823c27a1b93c6b87682372c3734ac5fddc10bfe53875e657d43c60fb885"}, - {file = "OpenTimelineIO-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4f4efcf3ddd81b62c4feb49a0bcc309b50ffeb6a8c48ab173d169a029006f4d"}, - {file = "OpenTimelineIO-0.17.0-cp312-cp312-win32.whl", hash = "sha256:9872ab74a20bb2bb3a50af04e80fe9238998d67d6be4e30e45aebe25d3eefac6"}, - {file = "OpenTimelineIO-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:c83b78be3312d3152d7e07ab32b0086fe220acc2a5b035b70ad69a787c0ece62"}, - {file = "OpenTimelineIO-0.17.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:0e671a6f2a1f772445bb326c7640dc977cfc3db589fe108a783a0311939cfac8"}, - {file = "OpenTimelineIO-0.17.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b931a3189b4ce064f06f15a89fe08ef4de01f7dcf0abc441fe2e02ef2a3311bb"}, - {file = "OpenTimelineIO-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923cb54d806c981cf1e91916c3e57fba5664c22f37763dd012bad5a5a7bd4db4"}, - {file = "OpenTimelineIO-0.17.0-cp37-cp37m-win32.whl", hash = "sha256:8e16598c5084dcb21df3d83978b0e5f72300af9edd4cdcb85e3b0ba5da0df4e8"}, - {file = "OpenTimelineIO-0.17.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7eed5033494888fb3f802af50e60559e279b2f398802748872903c2f54efd2c9"}, - {file = "OpenTimelineIO-0.17.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:118baa22b9227da5003bee653601a68686ae2823682dcd7d13c88178c63081c3"}, - {file = "OpenTimelineIO-0.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:43389eacdee2169de454e1c79ecfea82f54a9e73b67151427a9b621349a22b7f"}, - {file = "OpenTimelineIO-0.17.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17659b1e6aa42ed617a942f7a2bfc6ecc375d0464ec127ce9edf896278ecaee9"}, - {file = "OpenTimelineIO-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36d5ea8cfbebf3c9013cc680eef5be48bffb515aafa9dc31e99bf66052a4ca3d"}, - {file = "OpenTimelineIO-0.17.0-cp38-cp38-win32.whl", hash = "sha256:cc67c74eb4b73bc0f7d135d3ff3dbbd86b2d451a9b142690a8d1631ad79c46f2"}, - {file = "OpenTimelineIO-0.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:69b39079bee6fa4aff34c6ad6544df394bc7388483fa5ce958ecd16e243a53ad"}, - {file = "OpenTimelineIO-0.17.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a33554894dea17c22feec0201991e705c2c90a679ba2a012a0c558a7130df711"}, - {file = "OpenTimelineIO-0.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6b1ad3b3155370245b851b2f7b60006b2ebbb5bb76dd0fdc49bb4dce73fa7d96"}, - {file = "OpenTimelineIO-0.17.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:030454a9c0e9e82e5a153119f9afb8f3f4e64a3b27f80ac0dcde44b029fd3f3f"}, - {file = "OpenTimelineIO-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce64376a28919533bd4f744ff8885118abefa73f78fd408f95fa7a9489855b6"}, - {file = "OpenTimelineIO-0.17.0-cp39-cp39-win32.whl", hash = "sha256:fa8cdceb25f9003c3c0b5b32baef2c764949d88b867161ddc6f44f48f6bbfa4a"}, - {file = "OpenTimelineIO-0.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:fbcf8a000cd688633c8dc5d22e91912013c67c674329eba603358e3b54da32bf"}, - {file = "opentimelineio-0.17.0.tar.gz", hash = "sha256:10ef324e710457e9977387cd9ef91eb24a9837bfb370aec3330f9c0f146cea85"}, -] - -[package.extras] -dev = ["check-manifest", "coverage (>=4.5)", "flake8 (>=3.5)", "urllib3 (>=1.24.3)"] -view = ["PySide2 (>=5.11,<6.0) ; platform_machine == \"x86_64\"", "PySide6 (>=6.2,<7.0) ; platform_machine == \"aarch64\""] - -[[package]] -name = "packaging" -version = "24.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, -] - -[[package]] -name = "paginate" -version = "0.5.7" -description = "Divides large result sets into pages for easier browsing" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, - {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, -] - -[package.extras] -dev = ["pytest", "tox"] -lint = ["black"] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.6" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "3.8.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, - {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pyblish-base" -version = "1.8.12" -description = "Plug-in driven automation framework for content" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "pyblish-base-1.8.12.tar.gz", hash = "sha256:ebc184eb038864380555227a8b58055dd24ece7e6ef7f16d33416c718512871b"}, - {file = "pyblish_base-1.8.12-py2.py3-none-any.whl", hash = "sha256:2cbe956bfbd4175a2d7d22b344cd345800f4d4437153434ab658fc12646a11e8"}, -] - -[[package]] -name = "pygments" -version = "2.19.1" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pymdown-extensions" -version = "10.14.3" -description = "Extension pack for Python Markdown." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9"}, - {file = "pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b"}, -] - -[package.dependencies] -markdown = ">=3.6" -pyyaml = "*" - -[package.extras] -extra = ["pygments (>=2.19.1)"] - -[[package]] -name = "pyparsing" -version = "3.2.3" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, - {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pytest" -version = "8.3.4" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.5,<2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-print" -version = "1.0.2" -description = "pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout)" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pytest_print-1.0.2-py3-none-any.whl", hash = "sha256:3ae7891085dddc3cd697bd6956787240107fe76d6b5cdcfcd782e33ca6543de9"}, - {file = "pytest_print-1.0.2.tar.gz", hash = "sha256:2780350a7bbe7117f99c5d708dc7b0431beceda021b1fd3f11200670d7f33679"}, -] - -[package.dependencies] -pytest = ">=8.3.2" - -[package.extras] -test = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "pytest-mock (>=3.14)"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["dev"] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "pyyaml-env-tag" -version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, -] - -[package.dependencies] -pyyaml = "*" - -[[package]] -name = "requests" -version = "2.32.3" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "ruff" -version = "0.3.7" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, - {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, - {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, - {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, - {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, - {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, - {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, - {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, -] - -[[package]] -name = "semver" -version = "3.0.4" -description = "Python helper for Semantic Versioning (https://semver.org)" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}, - {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, -] - -[[package]] -name = "shellman" -version = "1.0.2" -description = "Write documentation in comments and render it with templates." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "shellman-1.0.2-py3-none-any.whl", hash = "sha256:f8c960fd2d3785e195f86fcd8f110a8d51a950e759d82c14a5af0bd71b918b3c"}, - {file = "shellman-1.0.2.tar.gz", hash = "sha256:48cba79d6415c0d013ad4dfd2205ed81b0e468795d1886dcda943ac78eaffd38"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} -jinja2 = ">=3" -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["dev"] -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] - -[[package]] -name = "tomlkit" -version = "0.13.2" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, - {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "unidecode" -version = "1.3.8" -description = "ASCII transliterations of Unicode text" -optional = false -python-versions = ">=3.5" -groups = ["dev"] -files = [ - {file = "Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39"}, - {file = "Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4"}, -] - -[[package]] -name = "urllib3" -version = "2.3.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "verspec" -version = "0.1.0" -description = "Flexible version handling" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, - {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, -] - -[package.extras] -test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] - -[[package]] -name = "virtualenv" -version = "20.29.2" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a"}, - {file = "virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] - -[[package]] -name = "watchdog" -version = "6.0.0" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, - {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, - {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, - {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, - {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, - {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, - {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, - {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, - {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, - {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, - {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "zipp" -version = "3.21.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.9.1,<3.10" -content-hash = "24b6215b9c20a4f64f844d3deb121618aef510b1c5ee54242e50305db6c0c4f4" From acf9bb56eb82b2a4c3ea2f8aded45262af9187c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:42:44 +0200 Subject: [PATCH 085/150] :hammer: add poetry.lock to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4e56d77392..72c4204dc0 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ dump.sql # Poetry ######## .poetry/ +poetry.lock .python-version .editorconfig .pre-commit-config.yaml From edc0aa5867684eceed3da3a809567ab60f3ecc17 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 9 Apr 2025 15:56:39 +0200 Subject: [PATCH 086/150] Remove unnecessary E501 comments --- server/settings/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 21612ee362..f49866dc95 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -90,12 +90,12 @@ def _ocio_built_in_paths(): "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", "label": "Nuke default", }, ] From 63a29cd9ec68f5e4f9c45c5944becae41be96661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:15:20 +0200 Subject: [PATCH 087/150] :arrow_up:: update ruff action and take the ruff version from pyproject --- .github/workflows/pr_linting.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_linting.yml b/.github/workflows/pr_linting.yml index 896d5b7f4d..d41596fb4a 100644 --- a/.github/workflows/pr_linting.yml +++ b/.github/workflows/pr_linting.yml @@ -21,6 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/ruff-action@v1 + - uses: astral-sh/ruff-action@v3 with: changed-files: "true" + version-file: "pyproject.toml" From 3dc1eb49b1b909cc0aa2a8a97807187d2216e377 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:49:42 +0200 Subject: [PATCH 088/150] don't use deprecated commands --- client/ayon_core/plugins/publish/extract_thumbnail.py | 4 ++-- .../plugins/publish/extract_thumbnail_from_source.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 7062a0a591..cb26765b63 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -450,7 +450,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # output arguments from presets jpeg_items.extend(ffmpeg_args.get("output") or []) # we just want one frame from movie files - jpeg_items.extend(["-vframes", "1"]) + jpeg_items.extend(["-frames:v", "1"]) if resolution_arg: jpeg_items.extend(resolution_arg) @@ -498,7 +498,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): "-i", video_file_path, "-analyzeduration", max_int, "-probesize", max_int, - "-vframes", "1" + "-frames:v", "1" ] # add output file path diff --git a/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py b/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py index 7751d73335..59a62b1d7b 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail_from_source.py @@ -170,7 +170,7 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin): "-analyzeduration", max_int, "-probesize", max_int, "-i", src_path, - "-vframes", "1", + "-frames:v", "1", dst_path ) From 51f86e5c5743f3252bbc60745f44f806ff386457 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Apr 2025 11:20:55 +0200 Subject: [PATCH 089/150] Bump server dependency version --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 9f01049fb9..d9edc36cdd 100644 --- a/package.py +++ b/package.py @@ -6,7 +6,7 @@ client_dir = "ayon_core" plugin_for = ["ayon_server"] -ayon_server_version = ">=1.7.5,<2.0.0" +ayon_server_version = ">=1.7.6,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = {} From 7ec8494b1a92d2024516b3829b857ff00326c79c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:34:11 +0200 Subject: [PATCH 090/150] fix thumbnail upload --- .../plugins/publish/integrate_thumbnail.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate_thumbnail.py b/client/ayon_core/plugins/publish/integrate_thumbnail.py index ca32e60cc2..067c3470e8 100644 --- a/client/ayon_core/plugins/publish/integrate_thumbnail.py +++ b/client/ayon_core/plugins/publish/integrate_thumbnail.py @@ -27,8 +27,10 @@ import collections import pyblish.api import ayon_api +from ayon_api import RequestTypes from ayon_api.operations import OperationsSession + InstanceFilterResult = collections.namedtuple( "InstanceFilterResult", ["instance", "thumbnail_path", "version_id"] @@ -161,6 +163,30 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): return None return os.path.normpath(filled_path) + def _create_thumbnail(self, project_name: str, src_filepath: str) -> str: + """Upload thumbnail to AYON and return its id. + + This is temporary fix of 'create_thumbnail' function in ayon_api to + fix jpeg mime type. + + """ + mime_type = None + with open(src_filepath, "rb") as stream: + if b"\xff\xd8\xff" == stream.read(3): + mime_type = "image/jpeg" + + if mime_type is None: + return ayon_api.create_thumbnail(project_name, src_filepath) + + response = ayon_api.upload_file( + f"projects/{project_name}/thumbnails", + src_filepath, + request_type=RequestTypes.post, + headers={"Content-Type": mime_type}, + ) + response.raise_for_status() + return response.json()["id"] + def _integrate_thumbnails( self, filtered_instance_items, @@ -179,7 +205,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin): ).format(instance_label)) continue - thumbnail_id = ayon_api.create_thumbnail( + thumbnail_id = self._create_thumbnail( project_name, thumbnail_path ) From 402e98b4436fac66cc50a1bf519a073d26e33cc4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 11 Apr 2025 15:55:05 +0200 Subject: [PATCH 091/150] Fixes issue with original directory lookup Fixes a bug where the instance staging directory was being incorrectly used when looking up the original directory for publish in place workflows, instead of the staging directory from representation. Adds debug logging to inspect instance data during integration. --- client/ayon_core/plugins/publish/integrate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index ae043a10a9..6b903b55be 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -2,6 +2,7 @@ import os import logging import sys import copy +from pprint import pformat import clique import pyblish.api @@ -612,6 +613,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): is_udim = bool(repre.get("udim")) + self.log.debug(pformat(instance.data)) # handle publish in place if "{originalDirname}" in template: # store as originalDirname only original value without project root @@ -619,8 +621,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # used for all represe # from temp to final original_directory = ( - instance.data.get("originalDirname") or instance_stagingdir) - + instance.data.get("originalDirname") or stagingdir) _rootless = self.get_rootless_path(anatomy, original_directory) if _rootless == original_directory: raise KnownPublishError(( From 047b77e501629310859f2293f1815242a265d75f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Sat, 12 Apr 2025 01:06:29 +0200 Subject: [PATCH 092/150] return the same output all the time --- client/ayon_core/tools/common_models/thumbnails.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/common_models/thumbnails.py b/client/ayon_core/tools/common_models/thumbnails.py index 5111d0be28..d25c6a1ecd 100644 --- a/client/ayon_core/tools/common_models/thumbnails.py +++ b/client/ayon_core/tools/common_models/thumbnails.py @@ -27,9 +27,12 @@ class ThumbnailsModel: entity_type, entity_ids, ): - thumbnail_paths = set() + output = { + entity_id: None + for entity_id in entity_ids + } if not project_name or not entity_type or not entity_ids: - return thumbnail_paths + return output thumbnail_id_by_entity_id = {} if entity_type == "folder": @@ -43,7 +46,7 @@ class ThumbnailsModel: ) if not thumbnail_id_by_entity_id: - return thumbnail_paths + return output entity_ids_by_thumbnail_id = collections.defaultdict(set) for entity_id, thumbnail_id in thumbnail_id_by_entity_id.items(): @@ -51,10 +54,6 @@ class ThumbnailsModel: continue entity_ids_by_thumbnail_id[thumbnail_id].add(entity_id) - output = { - entity_id: None - for entity_id in entity_ids - } for thumbnail_id, entity_ids in entity_ids_by_thumbnail_id.items(): thumbnail_path = self._get_thumbnail_path( project_name, entity_type, next(iter(entity_ids)), thumbnail_id From 46f7fa4cc13fc986ede2895a268a3ead424c1051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 14 Apr 2025 14:00:11 +0200 Subject: [PATCH 093/150] Update client/ayon_core/plugins/publish/integrate.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/integrate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 6b903b55be..82f6412e38 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -613,7 +613,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): is_udim = bool(repre.get("udim")) - self.log.debug(pformat(instance.data)) # handle publish in place if "{originalDirname}" in template: # store as originalDirname only original value without project root From 97bfd0459dd88d1d3a23e1ca98bb520aeffd8cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 14 Apr 2025 14:00:18 +0200 Subject: [PATCH 094/150] Update client/ayon_core/plugins/publish/integrate.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/integrate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 82f6412e38..8e57980ba6 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -2,7 +2,6 @@ import os import logging import sys import copy -from pprint import pformat import clique import pyblish.api From f770a35d542c5b8d83e3f0b9ff79fddbaea206e1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 14 Apr 2025 15:42:12 +0200 Subject: [PATCH 095/150] Fixes thumbnail creation logic Moves the `thumbnail_created` flag initialization inside the loop. This ensures that the flag is reset for each representation, preventing it from being incorrectly skipped if a previous representation failed to create a thumbnail. --- client/ayon_core/plugins/publish/extract_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index cb26765b63..edf50c33b0 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -163,9 +163,9 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # Store new staging to cleanup paths instance.context.data["cleanupFullPaths"].append(dst_staging) - thumbnail_created = False oiio_supported = is_oiio_supported() for repre in filtered_repres: + thumbnail_created = False repre_files = repre["files"] src_staging = os.path.normpath(repre["stagingDir"]) if not isinstance(repre_files, (list, tuple)): From 122a4a9f091972f2a3fc96a5d77642e70280b588 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 14 Apr 2025 14:31:37 +0000 Subject: [PATCH 096/150] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 962ec487a7..b8dcbad2b9 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.7+dev" +__version__ = "1.1.8" diff --git a/package.py b/package.py index fe870315cf..06935f201c 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.7+dev" +version = "1.1.8" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 86df6535aa..472926f2a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.7+dev" +version = "1.1.8" description = "" authors = ["Ynput Team "] readme = "README.md" From 400774d438112962cc90c36fc6a217860df24d52 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 14 Apr 2025 14:32:11 +0000 Subject: [PATCH 097/150] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index b8dcbad2b9..01e431577e 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.8" +__version__ = "1.1.8+dev" diff --git a/package.py b/package.py index 06935f201c..a4ffe1a20d 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.8" +version = "1.1.8+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 472926f2a1..3da97e6b2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.8" +version = "1.1.8+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From b5961dcb92ec4b7fe85916061adc828476c40b15 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:52:25 +0200 Subject: [PATCH 098/150] enable preview in ruff linting --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 3da97e6b2a..89f1d2a2cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,7 @@ indent-width = 4 target-version = "py39" [tool.ruff.lint] +preview = true # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. select = ["E", "F", "W"] ignore = [] From b403fccf0524f38450694ce065aedb3b3c172bad Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:54:45 +0200 Subject: [PATCH 099/150] fix new line chars --- client/ayon_core/cli.py | 2 -- client/ayon_core/lib/attribute_definitions.py | 4 +--- client/ayon_core/lib/ayon_connection.py | 2 ++ client/ayon_core/lib/terminal.py | 1 + client/ayon_core/plugins/load/export_otio.py | 1 + .../plugins/publish/collect_anatomy_instance_data.py | 1 - .../ayon_core/plugins/publish/collect_farm_env_variables.py | 1 - .../plugins/publish/collect_otio_subset_resources.py | 3 --- client/ayon_core/scripts/otio_burnin.py | 1 - client/ayon_core/scripts/slates/slate_base/base.py | 1 - client/ayon_core/tools/console_interpreter/ui/widgets.py | 1 - client/ayon_core/tools/tray/lib.py | 1 - .../pipeline/editorial/test_collect_otio_frame_ranges.py | 1 + .../ayon_core/pipeline/editorial/test_extract_otio_review.py | 2 ++ .../pipeline/editorial/test_media_range_with_retimes.py | 3 ++- 15 files changed, 10 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 6f89a6d17d..322c294cfb 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -24,7 +24,6 @@ from ayon_core.lib.env_tools import ( ) - @click.group(invoke_without_command=True) @click.pass_context @click.option("--use-staging", is_flag=True, @@ -173,7 +172,6 @@ def contextselection( main(output_path, project, folder, strict) - @main_cli.command( context_settings=dict( ignore_unknown_options=True, diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 6b334aa16a..cb74fea0f1 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -22,12 +22,10 @@ import clique if typing.TYPE_CHECKING: from typing import Self, Tuple, Union, TypedDict, Pattern - class EnumItemDict(TypedDict): label: str value: Any - EnumItemsInputType = Union[ Dict[Any, str], List[Tuple[Any, str]], @@ -35,7 +33,6 @@ if typing.TYPE_CHECKING: List[EnumItemDict] ] - class FileDefItemDict(TypedDict): directory: str filenames: List[str] @@ -289,6 +286,7 @@ AttrDefType = TypeVar("AttrDefType", bound=AbstractAttrDef) # UI attribute definitions won't hold value # ----------------------------------------- + class UIDef(AbstractAttrDef): is_value_def = False diff --git a/client/ayon_core/lib/ayon_connection.py b/client/ayon_core/lib/ayon_connection.py index 1132d77aaa..32aa5ad629 100644 --- a/client/ayon_core/lib/ayon_connection.py +++ b/client/ayon_core/lib/ayon_connection.py @@ -177,10 +177,12 @@ def initialize_ayon_connection(force=False): return _new_get_last_versions( con, *args, **kwargs ) + def _lv_by_pi_wrapper(*args, **kwargs): return _new_get_last_version_by_product_id( con, *args, **kwargs ) + def _lv_by_pn_wrapper(*args, **kwargs): return _new_get_last_version_by_product_name( con, *args, **kwargs diff --git a/client/ayon_core/lib/terminal.py b/client/ayon_core/lib/terminal.py index 10fcc79a27..ea23feeb95 100644 --- a/client/ayon_core/lib/terminal.py +++ b/client/ayon_core/lib/terminal.py @@ -39,6 +39,7 @@ class Terminal: """ from ayon_core.lib import env_value_to_bool + log_no_colors = env_value_to_bool( "AYON_LOG_NO_COLORS", default=None ) diff --git a/client/ayon_core/plugins/load/export_otio.py b/client/ayon_core/plugins/load/export_otio.py index e7a844aed3..8094490246 100644 --- a/client/ayon_core/plugins/load/export_otio.py +++ b/client/ayon_core/plugins/load/export_otio.py @@ -22,6 +22,7 @@ from ayon_core.tools.utils import show_message_dialog OTIO = None FRAME_SPLITTER = "__frame_splitter__" + def _import_otio(): global OTIO if OTIO is None: diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 677ebb04a2..2fcf562dd0 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -394,7 +394,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if aov: anatomy_data["aov"] = aov - def _fill_folder_data(self, instance, project_entity, anatomy_data): # QUESTION: should we make sure that all folder data are popped if # folder data cannot be found? diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index 2782ea86ac..39c421381d 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -43,4 +43,3 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): if value: self.log.debug(f"Setting job env: {key}: {value}") env[key] = value - diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index f1fa6a817d..275b8a7f55 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -194,7 +194,6 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename) - else: _trim = False dirname, filename = os.path.split(media_ref.target_url) @@ -209,7 +208,6 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) - instance.data["originalDirname"] = self.staging_dir # add representation to instance data @@ -221,7 +219,6 @@ class CollectOtioSubsetResources( instance.data["representations"].append(repre) - self.log.debug(instance.data) def _create_representation(self, start, end, **kwargs): diff --git a/client/ayon_core/scripts/otio_burnin.py b/client/ayon_core/scripts/otio_burnin.py index cb72606222..77eeecaff6 100644 --- a/client/ayon_core/scripts/otio_burnin.py +++ b/client/ayon_core/scripts/otio_burnin.py @@ -173,7 +173,6 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): if frame_end is not None: options["frame_end"] = frame_end - options["label"] = align self._add_burnin(text, align, options, DRAWTEXT) diff --git a/client/ayon_core/scripts/slates/slate_base/base.py b/client/ayon_core/scripts/slates/slate_base/base.py index e1648c916a..91793d511d 100644 --- a/client/ayon_core/scripts/slates/slate_base/base.py +++ b/client/ayon_core/scripts/slates/slate_base/base.py @@ -353,7 +353,6 @@ class BaseObj: self.items[item.id] = item item.fill_data_format() - def reset(self): for item in self.items.values(): item.reset() diff --git a/client/ayon_core/tools/console_interpreter/ui/widgets.py b/client/ayon_core/tools/console_interpreter/ui/widgets.py index 2b9361666e..3dc55b081c 100644 --- a/client/ayon_core/tools/console_interpreter/ui/widgets.py +++ b/client/ayon_core/tools/console_interpreter/ui/widgets.py @@ -248,4 +248,3 @@ class EnhancedTabBar(QtWidgets.QTabBar): else: super().mouseReleaseEvent(event) - diff --git a/client/ayon_core/tools/tray/lib.py b/client/ayon_core/tools/tray/lib.py index 13ee1eea5c..deb49b9711 100644 --- a/client/ayon_core/tools/tray/lib.py +++ b/client/ayon_core/tools/tray/lib.py @@ -738,4 +738,3 @@ def main(force=False): sys.exit(1) main() - diff --git a/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py b/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py index 20f0c05804..2f67ee244c 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py +++ b/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py @@ -101,6 +101,7 @@ def test_image_sequence(): expected_data, ) + def test_media_retimed(): """ EXR image sequence. diff --git a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py index 8ad2e44b06..45191a2c53 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py +++ b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py @@ -215,6 +215,7 @@ def test_short_movie_tail_gap_handles(): assert calls == expected + def test_multiple_review_clips_no_gap(): """ Use multiple review clips (image sequence). @@ -298,6 +299,7 @@ def test_multiple_review_clips_no_gap(): assert calls == expected + def test_multiple_review_clips_with_gap(): """ Use multiple review clips (image sequence) with gap. diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 112d00b3e4..b475d629bb 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -257,7 +257,6 @@ def test_movie_timewarp(): ) - def test_img_sequence_no_handles(): """ Img sequence clip (no embedded timecode) @@ -334,6 +333,7 @@ def test_img_sequence_relative_source_range(): expected_data ) + def test_img_sequence_conform_to_23_976fps(): """ Img sequence clip @@ -409,6 +409,7 @@ def test_img_sequence_reverse_speed_no_tc(): handle_end=0, ) + def test_img_sequence_reverse_speed_from_24_to_23_976fps(): """ Img sequence clip From 486be39faaa6711cf4b61d965fef24a50a34c329 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:55:48 +0200 Subject: [PATCH 100/150] fix whitespaces --- client/ayon_core/addon/utils.py | 2 +- client/ayon_core/pipeline/create/context.py | 8 ++++---- client/ayon_core/pipeline/create/structures.py | 2 +- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- client/ayon_core/pipeline/staging_dir.py | 2 +- client/ayon_core/plugins/load/delete_old_versions.py | 2 +- client/ayon_core/plugins/publish/collect_hierarchy.py | 2 +- client/ayon_core/plugins/publish/extract_review.py | 2 +- .../ayon_core/plugins/publish/integrate_resources_path.py | 2 +- client/ayon_core/scripts/slates/slate_base/base.py | 2 +- client/ayon_core/tools/creator/window.py | 2 +- .../tools/experimental_tools/pyblish_debug_stepper.py | 4 ++-- client/ayon_core/tools/loader/ui/window.py | 6 +++--- client/ayon_core/tools/publisher/models/create.py | 8 ++++---- 14 files changed, 23 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/addon/utils.py b/client/ayon_core/addon/utils.py index f983e37d3c..bb365f42e1 100644 --- a/client/ayon_core/addon/utils.py +++ b/client/ayon_core/addon/utils.py @@ -37,7 +37,7 @@ def _handle_error( if process_context.headless: if detail: print(detail) - print(f"{10*'*'}\n{message}\n{10*'*'}") + print(f"{10 * '*'}\n{message}\n{10 * '*'}") return current_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 6ac6685647..f0d9fa8927 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -872,7 +872,7 @@ class CreateContext: """ return self._event_hub.add_callback(INSTANCE_ADDED_TOPIC, callback) - def add_instances_removed_callback (self, callback): + def add_instances_removed_callback(self, callback): """Register callback for removed instances. Event is triggered when instances are already removed from context. @@ -933,7 +933,7 @@ class CreateContext: """ self._event_hub.add_callback(VALUE_CHANGED_TOPIC, callback) - def add_pre_create_attr_defs_change_callback (self, callback): + def add_pre_create_attr_defs_change_callback(self, callback): """Register callback to listen pre-create attribute changes. Create plugin can trigger refresh of pre-create attributes. Usage of @@ -961,7 +961,7 @@ class CreateContext: PRE_CREATE_ATTR_DEFS_CHANGED_TOPIC, callback ) - def add_create_attr_defs_change_callback (self, callback): + def add_create_attr_defs_change_callback(self, callback): """Register callback to listen create attribute changes. Create plugin changed attribute definitions of instance. @@ -986,7 +986,7 @@ class CreateContext: """ self._event_hub.add_callback(CREATE_ATTR_DEFS_CHANGED_TOPIC, callback) - def add_publish_attr_defs_change_callback (self, callback): + def add_publish_attr_defs_change_callback(self, callback): """Register callback to listen publish attribute changes. Publish plugin changed attribute definitions of instance of context. diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index 6b45a5c610..d7ba6b9c24 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -369,7 +369,7 @@ class PublishAttributes: return copy.deepcopy(self._origin_data) def attribute_value_changed(self, key, changes): - self._parent.publish_attribute_value_changed(key, changes) + self._parent.publish_attribute_value_changed(key, changes) def set_publish_plugin_attr_defs( self, diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c6f3ae7115..77a2f1d4c0 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -1,4 +1,4 @@ -from __future__ import annotations +from __future__ import annotations import copy import os import re diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 1cb2979415..a172c177fd 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -209,7 +209,7 @@ def get_staging_dir_info( staging_dir_config = get_staging_dir_config( project_entity["name"], task_type, - task_name , + task_name, product_type, product_name, host_name, diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index f8c45baff6..3a42ccba7e 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -211,7 +211,7 @@ class DeleteOldVersions(load.ProductLoaderPlugin): f"This will keep only the last {versions_to_keep} " f"versions for the {num_contexts} selected product{s}." ) - informative_text="Warning: This will delete files from disk" + informative_text = "Warning: This will delete files from disk" detailed_text = ( f"Keep only {versions_to_keep} versions for:\n{contexts_list}" ) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 266c2e1458..56b48c37f6 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -50,7 +50,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "comments": instance.data.get("comments", []), } - shot_data["attributes"] = {} + shot_data["attributes"] = {} SHOT_ATTRS = ( "handleStart", "handleEnd", diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index df87abba91..a15886451b 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -1333,7 +1333,7 @@ class ExtractReview(pyblish.api.InstancePlugin): bg_red, bg_green, bg_blue = overscan_color else: # Backwards compatibility - bg_red, bg_green, bg_blue, _ = overscan_color + bg_red, bg_green, bg_blue, _ = overscan_color overscan_color_value = "#{0:0>2X}{1:0>2X}{2:0>2X}".format( bg_red, bg_green, bg_blue diff --git a/client/ayon_core/plugins/publish/integrate_resources_path.py b/client/ayon_core/plugins/publish/integrate_resources_path.py index 56dc0e5ef7..b518f7f6f1 100644 --- a/client/ayon_core/plugins/publish/integrate_resources_path.py +++ b/client/ayon_core/plugins/publish/integrate_resources_path.py @@ -7,7 +7,7 @@ class IntegrateResourcesPath(pyblish.api.InstancePlugin): label = "Integrate Resources Path" order = pyblish.api.IntegratorOrder - 0.05 - families = ["clip", "projectfile", "plate"] + families = ["clip", "projectfile", "plate"] def process(self, instance): resources = instance.data.get("resources") or [] diff --git a/client/ayon_core/scripts/slates/slate_base/base.py b/client/ayon_core/scripts/slates/slate_base/base.py index 91793d511d..a4427bbb86 100644 --- a/client/ayon_core/scripts/slates/slate_base/base.py +++ b/client/ayon_core/scripts/slates/slate_base/base.py @@ -175,7 +175,7 @@ class BaseObj: self.log.warning("Invalid range '{}'".format(part)) continue - for idx in range(sub_parts[0], sub_parts[1]+1): + for idx in range(sub_parts[0], sub_parts[1] + 1): indexes.append(idx) return indexes diff --git a/client/ayon_core/tools/creator/window.py b/client/ayon_core/tools/creator/window.py index 5bdc6da9b6..5d1c0a272a 100644 --- a/client/ayon_core/tools/creator/window.py +++ b/client/ayon_core/tools/creator/window.py @@ -492,7 +492,7 @@ def show(parent=None): try: module.window.close() - del(module.window) + del module.window except (AttributeError, RuntimeError): pass diff --git a/client/ayon_core/tools/experimental_tools/pyblish_debug_stepper.py b/client/ayon_core/tools/experimental_tools/pyblish_debug_stepper.py index 33de4bf036..a485c682a1 100644 --- a/client/ayon_core/tools/experimental_tools/pyblish_debug_stepper.py +++ b/client/ayon_core/tools/experimental_tools/pyblish_debug_stepper.py @@ -32,7 +32,7 @@ from qtpy import QtWidgets, QtCore, QtGui import pyblish.api from ayon_core import style -TAB = 4* " " +TAB = 4 * " " HEADER_SIZE = "15px" KEY_COLOR = QtGui.QColor("#ffffff") @@ -243,7 +243,7 @@ class DebugUI(QtWidgets.QDialog): self._set_window_title(plugin=result["plugin"]) - print(10*"<", result["plugin"].__name__, 10*">") + print(10 * "<", result["plugin"].__name__, 10 * ">") plugin_order = result["plugin"].order plugin_name = result["plugin"].__name__ diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index 3d2e15c630..b70f5554c7 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -519,9 +519,9 @@ class LoaderWindow(QtWidgets.QWidget): thumbnail_paths.discard(None) if thumbnail_paths: - self._thumbnails_widget.set_current_thumbnail_paths( - thumbnail_paths - ) + self._thumbnails_widget.set_current_thumbnail_paths( + thumbnail_paths + ) else: self._thumbnails_widget.set_current_thumbnails(None) diff --git a/client/ayon_core/tools/publisher/models/create.py b/client/ayon_core/tools/publisher/models/create.py index 9644af43e0..900168eaef 100644 --- a/client/ayon_core/tools/publisher/models/create.py +++ b/client/ayon_core/tools/publisher/models/create.py @@ -461,19 +461,19 @@ class CreateModel: self._create_context.add_instances_added_callback( self._cc_added_instance ) - self._create_context.add_instances_removed_callback ( + self._create_context.add_instances_removed_callback( self._cc_removed_instance ) self._create_context.add_value_changed_callback( self._cc_value_changed ) - self._create_context.add_pre_create_attr_defs_change_callback ( + self._create_context.add_pre_create_attr_defs_change_callback( self._cc_pre_create_attr_changed ) - self._create_context.add_create_attr_defs_change_callback ( + self._create_context.add_create_attr_defs_change_callback( self._cc_create_attr_changed ) - self._create_context.add_publish_attr_defs_change_callback ( + self._create_context.add_publish_attr_defs_change_callback( self._cc_publish_attr_changed ) From b0ac87b7b12006dc8b3fffe441b91a884cc65ce5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:56:21 +0200 Subject: [PATCH 101/150] handle unused variables --- client/ayon_core/lib/vendor_bin_utils.py | 4 ++-- client/ayon_core/pipeline/delivery.py | 4 ++-- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- client/ayon_core/pipeline/schema/__init__.py | 2 +- client/ayon_core/pipeline/workfile/path_resolving.py | 4 ++-- client/ayon_core/plugins/publish/integrate.py | 2 +- client/ayon_core/scripts/slates/slate_base/items.py | 12 ++++++------ client/ayon_core/tools/publisher/models/publish.py | 2 +- client/ayon_core/tools/utils/tasks_widget.py | 2 +- client/ayon_core/tools/workfiles/models/workfiles.py | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/client/ayon_core/lib/vendor_bin_utils.py b/client/ayon_core/lib/vendor_bin_utils.py index 41654476c2..412a9292cc 100644 --- a/client/ayon_core/lib/vendor_bin_utils.py +++ b/client/ayon_core/lib/vendor_bin_utils.py @@ -162,7 +162,7 @@ def find_tool_in_custom_paths(paths, tool, validation_func=None): # Handle cases when path is just an executable # - it allows to use executable from PATH # - basename must match 'tool' value (without extension) - extless_path, ext = os.path.splitext(path) + extless_path, _ext = os.path.splitext(path) if extless_path == tool: executable_path = find_executable(tool) if executable_path and ( @@ -181,7 +181,7 @@ def find_tool_in_custom_paths(paths, tool, validation_func=None): # If path is a file validate it if os.path.isfile(normalized): - basename, ext = os.path.splitext(os.path.basename(path)) + basename, _ext = os.path.splitext(os.path.basename(path)) # Check if the filename has actually the sane bane as 'tool' if basename == tool: executable_path = find_executable(normalized) diff --git a/client/ayon_core/pipeline/delivery.py b/client/ayon_core/pipeline/delivery.py index 55c840f3a5..e686b739ae 100644 --- a/client/ayon_core/pipeline/delivery.py +++ b/client/ayon_core/pipeline/delivery.py @@ -255,7 +255,7 @@ def deliver_sequence( report_items[""].append(msg) return report_items, 0 - dir_path, file_name = os.path.split(str(src_path)) + dir_path, _file_name = os.path.split(str(src_path)) context = repre["context"] ext = context.get("ext", context.get("representation")) @@ -270,7 +270,7 @@ def deliver_sequence( # context.representation could be .psd ext = ext.replace("..", ".") - src_collections, remainder = clique.assemble(os.listdir(dir_path)) + src_collections, _remainder = clique.assemble(os.listdir(dir_path)) src_collection = None for col in src_collections: if col.tail != ext: diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 77a2f1d4c0..812e9b0321 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -1168,7 +1168,7 @@ def prepare_cache_representations(skeleton_data, exp_files, anatomy): """ representations = [] - collections, remainders = clique.assemble(exp_files) + collections, _remainders = clique.assemble(exp_files) log = Logger.get_logger("farm_publishing") diff --git a/client/ayon_core/pipeline/schema/__init__.py b/client/ayon_core/pipeline/schema/__init__.py index d16755696d..5e4e8a668d 100644 --- a/client/ayon_core/pipeline/schema/__init__.py +++ b/client/ayon_core/pipeline/schema/__init__.py @@ -41,7 +41,7 @@ def validate(data, schema=None): if not _CACHED: _precache() - root, schema = data["schema"].rsplit(":", 1) + _root, schema = data["schema"].rsplit(":", 1) if isinstance(schema, str): schema = _cache[schema + ".json"] diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index 61c6e5b876..9b2fe25199 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -329,9 +329,9 @@ def get_last_workfile( Returns: str: Last or first workfile as filename of full path to filename. - """ - filename, version = get_last_workfile_with_version( + """ + filename, _version = get_last_workfile_with_version( workdir, file_template, fill_data, extensions ) if filename is None: diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 8e57980ba6..f1e066018c 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -683,7 +683,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): elif is_sequence_representation: # Collection of files (sequence) - src_collections, remainders = clique.assemble(files) + src_collections, _remainders = clique.assemble(files) src_collection = src_collections[0] destination_indexes = list(src_collection.indexes) diff --git a/client/ayon_core/scripts/slates/slate_base/items.py b/client/ayon_core/scripts/slates/slate_base/items.py index ec3358ed5e..eb7859a6e1 100644 --- a/client/ayon_core/scripts/slates/slate_base/items.py +++ b/client/ayon_core/scripts/slates/slate_base/items.py @@ -282,7 +282,7 @@ class ItemTable(BaseItem): value.draw(image, drawer) def value_width(self): - row_heights, col_widths = self.size_values + _row_heights, col_widths = self.size_values width = 0 for _width in col_widths: width += _width @@ -292,7 +292,7 @@ class ItemTable(BaseItem): return width def value_height(self): - row_heights, col_widths = self.size_values + row_heights, _col_widths = self.size_values height = 0 for _height in row_heights: height += _height @@ -569,21 +569,21 @@ class TableField(BaseItem): @property def item_pos_x(self): - pos_x, pos_y, width, height = ( + pos_x, _pos_y, _width, _height = ( self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx) ) return pos_x @property def item_pos_y(self): - pos_x, pos_y, width, height = ( + _pos_x, pos_y, _width, _height = ( self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx) ) return pos_y @property def value_pos_x(self): - pos_x, pos_y, width, height = ( + pos_x, _pos_y, width, _height = ( self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx) ) alignment_hor = self.style["alignment-horizontal"].lower() @@ -605,7 +605,7 @@ class TableField(BaseItem): @property def value_pos_y(self): - pos_x, pos_y, width, height = ( + _pos_x, pos_y, _width, height = ( self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx) ) diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index 97a956b18f..97070d106f 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -358,7 +358,7 @@ class PublishReportMaker: exception = result.get("error") if exception: - fname, line_no, func, exc = exception.traceback + fname, line_no, func, _ = exception.traceback # Conversion of exception into string may crash try: diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 9118611c23..744eb6060a 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -575,7 +575,7 @@ class TasksWidget(QtWidgets.QWidget): if self._tasks_model.is_refreshing: return - parent_id, task_id, task_name, _ = self._get_selected_item_ids() + _parent_id, task_id, task_name, _ = self._get_selected_item_ids() self._controller.set_selected_task(task_id, task_name) self.selection_changed.emit() diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py index c621a44937..cc034571f3 100644 --- a/client/ayon_core/tools/workfiles/models/workfiles.py +++ b/client/ayon_core/tools/workfiles/models/workfiles.py @@ -462,7 +462,7 @@ class WorkfileEntitiesModel: anatomy = self._controller.project_anatomy workdir, filename = os.path.split(filepath) - success, rootless_dir = anatomy.find_root_template_from_path(workdir) + _, rootless_dir = anatomy.find_root_template_from_path(workdir) return "/".join([ os.path.normpath(rootless_dir).replace("\\", "/"), filename From dc6bb42013b83c68328205bae226d048db6febb5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:56:33 +0200 Subject: [PATCH 102/150] define docstyle convention --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 89f1d2a2cf..4c4272cc30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,7 @@ target-version = "py39" [tool.ruff.lint] preview = true +pydocstyle.convention = "google" # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. select = ["E", "F", "W"] ignore = [] From d5f599b8110c95e61db23cc5f59645b51b221f6b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 15 Apr 2025 14:45:36 +0200 Subject: [PATCH 103/150] Fixes thumbnail creation logic for multiple reps Resets the `repre_thumb_created` flag for each representation to ensure that thumbnails are correctly generated for all reviewable representations when multiple are present. This prevents the logic from skipping subsequent representations after the first one successfully creates a thumbnail. --- .../plugins/publish/extract_thumbnail.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index edf50c33b0..18393022ed 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -164,8 +164,11 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): instance.context.data["cleanupFullPaths"].append(dst_staging) oiio_supported = is_oiio_supported() + repre_thumb_created = False for repre in filtered_repres: - thumbnail_created = False + # Reset for each iteration to handle cases where multiple + # reviewable thumbnails are needed + repre_thumb_created = False repre_files = repre["files"] src_staging = os.path.normpath(repre["stagingDir"]) if not isinstance(repre_files, (list, tuple)): @@ -214,7 +217,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): ) # If the input can read by OIIO then use OIIO method for # conversion otherwise use ffmpeg - thumbnail_created = self._create_thumbnail_oiio( + repre_thumb_created = self._create_thumbnail_oiio( full_input_path, full_output_path, colorspace_data @@ -223,19 +226,19 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # Try to use FFMPEG if OIIO is not supported or for cases when # oiiotool isn't available or representation is not having # colorspace data - if not thumbnail_created: + if not repre_thumb_created: if oiio_supported: self.log.debug( "Converting with FFMPEG because input" " can't be read by OIIO." ) - thumbnail_created = self._create_thumbnail_ffmpeg( + repre_thumb_created = self._create_thumbnail_ffmpeg( full_input_path, full_output_path ) # Skip representation and try next one if wasn't created - if not thumbnail_created: + if not repre_thumb_created: continue if len(explicit_repres) > 1: @@ -291,7 +294,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # There is no need to create more then one thumbnail break - if not thumbnail_created: + if not repre_thumb_created: self.log.warning("Thumbnail has not been created.") def _is_review_instance(self, instance): From 4f9f7724accd32b762f1b9d145b9aec4b2c27413 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:35:41 +0200 Subject: [PATCH 104/150] fix original intnetion of thumbnail created logging --- client/ayon_core/plugins/publish/extract_thumbnail.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 18393022ed..b72862ea22 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -164,7 +164,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): instance.context.data["cleanupFullPaths"].append(dst_staging) oiio_supported = is_oiio_supported() - repre_thumb_created = False + thumbnail_created = False for repre in filtered_repres: # Reset for each iteration to handle cases where multiple # reviewable thumbnails are needed @@ -241,6 +241,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): if not repre_thumb_created: continue + thumbnail_created = True if len(explicit_repres) > 1: repre_name = "thumbnail_{}".format(repre["outputName"]) else: @@ -294,7 +295,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # There is no need to create more then one thumbnail break - if not repre_thumb_created: + if not thumbnail_created: self.log.warning("Thumbnail has not been created.") def _is_review_instance(self, instance): From d0999af4efaf6abcee42b4165f7919e6253b97b0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:14:55 +0200 Subject: [PATCH 105/150] added aces subfolders --- server/settings/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index f49866dc95..97434d0b93 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,19 +72,19 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") }, { - "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2.1)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.1.") }, { - "value": "{BUILTIN_OCIO_ROOT}/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 "label": "ACES 2.0 Studio (OCIO v2.4)", "description": ( "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") From 8d253033332e55b46ca0bcbeca59da50d078cdd7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 16 Apr 2025 21:53:17 +0200 Subject: [PATCH 106/150] Revert defaults so removal of rendered files is disabled by default --- client/ayon_core/plugins/publish/collect_rendered_files.py | 2 +- server/settings/publish_plugins.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index c69dddd6ec..5c68af888f 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -33,7 +33,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): label = "Collect rendered frames" settings_category = "core" - remove_files = True + remove_files = False _context = None diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index e6a146a9d8..5f5891e4f4 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -931,7 +931,7 @@ class IntegrateHeroVersionModel(BaseSettingsModel): class CollectRenderedFilesModel(BaseSettingsModel): remove_files: bool = SettingsField( - True, + False, title="Remove rendered files", description=( "Remove rendered files and metadata json on publish.\n\n" @@ -1447,7 +1447,7 @@ DEFAULT_PUBLISH_VALUES = { "enabled": True, }, "CollectRenderedFiles": { - "remove_files": True + "remove_files": False }, "CleanUp": { "paterns": [], # codespell:ignore paterns From 644765cb85b5c786a3a7a59af4a00a623587eb38 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 16 Apr 2025 22:03:02 +0200 Subject: [PATCH 107/150] Remove redundant duplicate lines of code --- client/ayon_core/pipeline/farm/pyblish_functions.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c6f3ae7115..e10bcc9bb1 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -660,14 +660,6 @@ def _get_legacy_product_name_and_group( warnings.warn("Using legacy product name for renders", DeprecationWarning) - if not source_product_name.startswith(product_type): - resulting_group_name = '{}{}{}{}{}'.format( - product_type, - task_name[0].upper(), task_name[1:], - source_product_name[0].upper(), source_product_name[1:]) - else: - resulting_group_name = source_product_name - # create product name `` if not source_product_name.startswith(product_type): resulting_group_name = '{}{}{}{}{}'.format( From a39462497423327691675d0735ef9ac3ecd6e5f5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:28:51 +0200 Subject: [PATCH 108/150] don't skip other validations for string match --- client/ayon_core/tools/utils/projects_widget.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/utils/projects_widget.py b/client/ayon_core/tools/utils/projects_widget.py index 88d8a6c9f5..b88b2f410a 100644 --- a/client/ayon_core/tools/utils/projects_widget.py +++ b/client/ayon_core/tools/utils/projects_widget.py @@ -351,13 +351,14 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): return True string_pattern = self.filterRegularExpression().pattern() - if string_pattern: - return string_pattern.lower() in project_name.lower() + if ( + string_pattern + and string_pattern.lower() not in project_name.lower() + ): + return False # Current project keep always visible - default = super(ProjectSortFilterProxy, self).filterAcceptsRow( - source_row, source_parent - ) + default = super().filterAcceptsRow(source_row, source_parent) if not default: return default From 922d19137c0e7df497fc7be53fcf7472da8fd7b5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:19:48 +0200 Subject: [PATCH 109/150] change order of filters --- client/ayon_core/tools/utils/projects_widget.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/utils/projects_widget.py b/client/ayon_core/tools/utils/projects_widget.py index b88b2f410a..c340be2f83 100644 --- a/client/ayon_core/tools/utils/projects_widget.py +++ b/client/ayon_core/tools/utils/projects_widget.py @@ -350,6 +350,14 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): if project_name is None: return True + # Make sure current project is visible + if index.data(PROJECT_IS_CURRENT_ROLE): + return True + + default = super().filterAcceptsRow(source_row, source_parent) + if not default: + return default + string_pattern = self.filterRegularExpression().pattern() if ( string_pattern @@ -357,15 +365,6 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): ): return False - # Current project keep always visible - default = super().filterAcceptsRow(source_row, source_parent) - if not default: - return default - - # Make sure current project is visible - if index.data(PROJECT_IS_CURRENT_ROLE): - return True - if ( self._filter_inactive and not index.data(PROJECT_IS_ACTIVE_ROLE) From 71dc3650ace89b6ee77583ad43dd05690be32fa9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Apr 2025 10:25:51 +0200 Subject: [PATCH 110/150] Adds explicit resolution override to publisher Adds a plugin that allows users to explicitly override the resolution settings (width, height, pixel aspect) of instances during the publishing process. This provides a way to ensure consistency and accuracy in resolution values across different tasks and product types. The plugin is configurable through the AYON settings, allowing administrators to define the available resolution options and the product types for which the override is enabled. --- .../publish/collect_explicit_resolution.py | 104 ++++++++++++++++++ server/settings/publish_plugins.py | 92 +++++++++++++++- 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 client/ayon_core/plugins/publish/collect_explicit_resolution.py diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py new file mode 100644 index 0000000000..3ff08cbd34 --- /dev/null +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -0,0 +1,104 @@ +import pyblish.api +from ayon_core.lib import EnumDef +from ayon_core.pipeline import colorspace +from ayon_core.pipeline import publish +from ayon_core.pipeline.publish import PublishError + + +class CollectExplicitResolution( + pyblish.api.InstancePlugin, + publish.AYONPyblishPluginMixin +): + """Collect explicit user defined resolution attributes for instances""" + + label = "Choose Explicit Resolution" + order = pyblish.api.CollectorOrder + 0.49 + settings_category = "core" + + enabled = False + + default_resolution_item = (None, "Don't override") + # Settings + product_types = [] + options = [] + + # caching resoluton items + resolution_items = None + + def process(self, instance): + """Process the instance and collect explicit resolution attributes""" + + # Get the values from the instance data + values = self.get_attr_values_from_data(instance.data) + resolution_value = values.get("explicit_resolution", None) + if resolution_value is None: + return + + # Get the width, height and pixel_aspect from the resolution value + resolution_data = self._get_resolution_values(resolution_value) + + # Set the values to the instance data + instance.data.update(resolution_data) + + def _get_resolution_values(self, resolution_value): + """ + Returns width, height and pixel_aspect from the resolution value + + Arguments: + resolution_value (str): resolution value + + Returns: + dict: dictionary with width, height and pixel_aspect + """ + resolution_items = self._get_resolution_items() + item_values = None + # check if resolution_value is in cached items + if resolution_value in resolution_items: + item_values = resolution_items[resolution_value] + + if item_values: + # if the item is in the cache, get the values from it + return { + "resolutionWidth": item_values["width"], + "resolutionHeight": item_values["height"], + "pixelAspect": item_values["pixel_aspect"] + } + else: + raise PublishError( + f"Invalid resolution value: {resolution_value}") + + @classmethod + def _get_resolution_items(cls): + if cls.resolution_items is None: + resolution_items = {} + for item in cls.options: + item_text = f"{item['width']}x{item['height']}x{item['pixel_aspect']}" + resolution_items[item_text] = item + + cls.resolution_items = resolution_items + + return cls.resolution_items + + @classmethod + def get_attr_defs_for_instance( + cls, create_context, instance + ): + if instance.product_type not in cls.product_types: + return [] + + # Get the resolution items + resolution_items = cls._get_resolution_items() + + items = [cls.default_resolution_item] + # Add all cached resolution items to the dropdown options + for item_text in resolution_items: + items.append((item_text, item_text)) + + return [ + EnumDef( + "explicit_resolution", + items, + default="Don't override", + label="Override Resolution" + ) + ] diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 5f5891e4f4..7ad6c9c506 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1,4 +1,6 @@ +from collections.abc import Iterable from pydantic import validator +from typing import Any from ayon_server.settings import ( BaseSettingsModel, @@ -8,7 +10,7 @@ from ayon_server.settings import ( ensure_unique_names, task_types_enum, ) - +from ayon_server.exceptions import BadRequestException from ayon_server.types import ColorRGBA_uint8 @@ -157,6 +159,77 @@ class CollectUSDLayerContributionsModel(BaseSettingsModel): return value +class ResolutionOptionsModel(BaseSettingsModel): + _layout = "compact" + width: int = SettingsField( + 1920, + ge=0, + le=100000, + title="Width", + description=( + "Width resolution number value"), + placeholder="Width" + ) + height: int = SettingsField( + 1080, + title="Height", + ge=0, + le=100000, + description=( + "Height resolution number value"), + placeholder="Height" + ) + pixel_aspect: float = SettingsField( + 1.0, + title="Pixel aspect", + ge=0.0, + le=100000.0, + description=( + "Pixel Aspect resolution decimal number value"), + placeholder="Pixel aspect" + ) + + +def ensure_unique_resolution_option( + objects: Iterable[Any], field_name: str | None = None) -> None: # noqa: C901 + """Ensure a list of objects have unique option attributes. + + This function checks if the list of objects has unique 'width', + 'height' and 'pixel_aspect' properties. + """ + options = [] + for obj in objects: + item_test_text = f"{obj.width}x{obj.height}x{obj.pixel_aspect}" + if item_test_text not in options: + options.append(item_test_text) + else: + raise BadRequestException( + f"Duplicate option '{item_test_text}'") + + +class CollectExplicitResolutionModel(BaseSettingsModel): + enabled: bool = SettingsField(True, title="Enabled") + product_types: list[str] = SettingsField( + default_factory=list, + title="Product types", + description=( + "Only activate the attribute for following product types." + ) + ) + options: list[ResolutionOptionsModel] = SettingsField( + default_factory=list, + title="Resolution options", + description=( + "Options to be provided in publisher attribute" + ) + ) + + @validator("options") + def validate_unique_options(cls, value): + ensure_unique_resolution_option(value) + return value + + class AyonEntityURIModel(BaseSettingsModel): use_ayon_entity_uri: bool = SettingsField( title="Use AYON Entity URI", @@ -988,6 +1061,10 @@ class PublishPuginsModel(BaseSettingsModel): title="Collect USD Layer Contributions", ) ) + CollectExplicitResolution: CollectExplicitResolutionModel = SettingsField( + default_factory=CollectExplicitResolutionModel, + title="Collect Explicit Resolution" + ) ValidateEditorialAssetName: ValidateBaseModel = SettingsField( default_factory=ValidateBaseModel, title="Validate Editorial Asset Name" @@ -1162,6 +1239,19 @@ DEFAULT_PUBLISH_VALUES = { }, ] }, + "CollectExplicitResolution": { + "enabled": True, + "product_types": [ + "shot" + ], + "options": [ + { + "width": 2048, + "height": 1080, + "aspect_ratio": 1.5, + } + ] + }, "ValidateEditorialAssetName": { "enabled": True, "optional": False, From 4141ce30b403e40dddda5eb288c3a79ef3c4f45d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:39:14 +0200 Subject: [PATCH 111/150] add compatible harmony addon version to package.py --- package.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.py b/package.py index a4ffe1a20d..0358c2f4cd 100644 --- a/package.py +++ b/package.py @@ -9,4 +9,6 @@ plugin_for = ["ayon_server"] ayon_server_version = ">=1.0.3,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} -ayon_compatible_addons = {} +ayon_compatible_addons = { + "harmony": ">0.4.0", +} From 80e0fa6b17052cfdafc7635ae53cf2d5d1d4dec6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 23 Apr 2025 20:05:59 +0200 Subject: [PATCH 112/150] Correctly check whether sequence has any frames in the returned holes collection. As per [`clique.Collection.holes` documentation](https://clique.readthedocs.io/en/stable/api_reference/collection.html#clique.collection.Collection.holes): > Return Collection of missing indexes. --- client/ayon_core/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 1f2c2a89af..6cf30857a4 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -280,7 +280,7 @@ class ExtractOIIOTranscode(publish.Extractor): collection = collections[0] frames = list(collection.indexes) - if collection.holes(): + if collection.holes().indexes: return files_to_convert frame_str = "{}-{}#".format(frames[0], frames[-1]) From 468ab32b9a8c21e2add24bbdce7aad64ffe9ae50 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Apr 2025 11:44:43 +0200 Subject: [PATCH 113/150] Remove unused import and add trailing commas --- .../plugins/publish/collect_explicit_resolution.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 3ff08cbd34..1aa7147627 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -1,13 +1,12 @@ import pyblish.api from ayon_core.lib import EnumDef -from ayon_core.pipeline import colorspace from ayon_core.pipeline import publish from ayon_core.pipeline.publish import PublishError class CollectExplicitResolution( pyblish.api.InstancePlugin, - publish.AYONPyblishPluginMixin + publish.AYONPyblishPluginMixin, ): """Collect explicit user defined resolution attributes for instances""" @@ -61,7 +60,7 @@ class CollectExplicitResolution( return { "resolutionWidth": item_values["width"], "resolutionHeight": item_values["height"], - "pixelAspect": item_values["pixel_aspect"] + "pixelAspect": item_values["pixel_aspect"], } else: raise PublishError( @@ -72,7 +71,8 @@ class CollectExplicitResolution( if cls.resolution_items is None: resolution_items = {} for item in cls.options: - item_text = f"{item['width']}x{item['height']}x{item['pixel_aspect']}" + item_text = ( + f"{item['width']}x{item['height']}x{item['pixel_aspect']}") resolution_items[item_text] = item cls.resolution_items = resolution_items @@ -81,7 +81,7 @@ class CollectExplicitResolution( @classmethod def get_attr_defs_for_instance( - cls, create_context, instance + cls, create_context, instance, ): if instance.product_type not in cls.product_types: return [] @@ -99,6 +99,6 @@ class CollectExplicitResolution( "explicit_resolution", items, default="Don't override", - label="Override Resolution" - ) + label="Override Resolution", + ), ] From f47c0b4027eccc0ce17962c3298a21f5ce3745d0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:21:40 +0800 Subject: [PATCH 114/150] If there is no valid review representation for thumbnail creation, make sure the representation is with the image content so that it can create thumbnail --- .../plugins/publish/extract_thumbnail.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index b72862ea22..adcd7be846 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -17,7 +17,7 @@ from ayon_core.lib import ( ) from ayon_core.lib.transcoding import convert_colorspace -from ayon_core.lib.transcoding import VIDEO_EXTENSIONS +from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS class ExtractThumbnail(pyblish.api.InstancePlugin): @@ -349,7 +349,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): continue if "review" not in tags: - continue + if not self._is_valid_media_repre(repre): + continue if not repre.get("files"): self.log.debug(( @@ -360,6 +361,21 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): filtered_repres.append(repre) return filtered_repres + def _is_valid_media_repre(self, repre): + """Check if representation contains valid media files""" + files = repre.get("files") + if not files: + return False + + # Get first file's extension + if isinstance(files, (list, tuple)): + first_file = files[0] + else: + first_file = files + + ext = os.path.splitext(first_file)[1].lower() + return ext in IMAGE_EXTENSIONS + def _create_thumbnail_oiio( self, src_path, From 59e986dde23fb53cac335ca454b7405d9579ec1e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:23:27 +0800 Subject: [PATCH 115/150] add debug message into self._is_valid_media_repre --- client/ayon_core/plugins/publish/extract_thumbnail.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index adcd7be846..47710de5f6 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -365,6 +365,9 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): """Check if representation contains valid media files""" files = repre.get("files") if not files: + self.log.debug(( + "Representation \"{}\" doesn't have files. Skipping" + ).format(repre["name"])) return False # Get first file's extension From 637b157fd387c5d7943c2f57ec3508aaa79a3823 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:24:28 +0800 Subject: [PATCH 116/150] rename self._is_valid_media_repre to self._is_valid_image_repre --- client/ayon_core/plugins/publish/extract_thumbnail.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 47710de5f6..b48e6a69b7 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -349,7 +349,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): continue if "review" not in tags: - if not self._is_valid_media_repre(repre): + if not self._is_valid_images_repre(repre): continue if not repre.get("files"): @@ -361,8 +361,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): filtered_repres.append(repre) return filtered_repres - def _is_valid_media_repre(self, repre): - """Check if representation contains valid media files""" + def _is_valid_images_repre(self, repre): + """Check if representation contains valid image files""" files = repre.get("files") if not files: self.log.debug(( From 625e782d7e2065bde600f3cd6c99387b411a3d10 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:32:17 +0800 Subject: [PATCH 117/150] improve the docstring --- client/ayon_core/plugins/publish/extract_thumbnail.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index b48e6a69b7..6e0d899f30 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -362,7 +362,14 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): return filtered_repres def _is_valid_images_repre(self, repre): - """Check if representation contains valid image files""" + """Check if representation contains valid image files + + Args: + repre (dict): representation + + Returns: + bool: whether the representation has the valid image content + """ files = repre.get("files") if not files: self.log.debug(( From c0db02f7b519eb36ab249f60d5c122053cca862d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 17:14:14 +0800 Subject: [PATCH 118/150] improve the check on the valid representations for thumbnail creation --- .../plugins/publish/extract_thumbnail.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 6e0d899f30..cb941a53a9 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -336,7 +336,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): return need_thumb_repres def _get_filtered_repres(self, instance): - filtered_repres = [] + review_repres = [] + other_repres = [] src_repres = instance.data.get("representations") or [] for repre in src_repres: @@ -348,18 +349,22 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # to be published locally continue - if "review" not in tags: - if not self._is_valid_images_repre(repre): - continue - if not repre.get("files"): self.log.debug(( "Representation \"{}\" doesn't have files. Skipping" ).format(repre["name"])) continue - filtered_repres.append(repre) - return filtered_repres + has_review_tag = "review" in tags + if not has_review_tag and not self._is_valid_images_repre(repre): + continue + + if has_review_tag: + review_repres.append(repre) + else: + other_repres.append(repre) + + return review_repres + other_repres def _is_valid_images_repre(self, repre): """Check if representation contains valid image files @@ -372,9 +377,6 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): """ files = repre.get("files") if not files: - self.log.debug(( - "Representation \"{}\" doesn't have files. Skipping" - ).format(repre["name"])) return False # Get first file's extension @@ -384,7 +386,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): first_file = files ext = os.path.splitext(first_file)[1].lower() - return ext in IMAGE_EXTENSIONS + + return ext in IMAGE_EXTENSIONS or ext in VIDEO_EXTENSIONS def _create_thumbnail_oiio( self, From 4bb0e14106cb65e5937225695ef336c5e87a3a1d Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 29 Apr 2025 19:10:01 +0800 Subject: [PATCH 119/150] Update client/ayon_core/plugins/publish/extract_thumbnail.py Co-authored-by: Roy Nieterau --- client/ayon_core/plugins/publish/extract_thumbnail.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index cb941a53a9..9592264ee2 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -356,12 +356,9 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): continue has_review_tag = "review" in tags - if not has_review_tag and not self._is_valid_images_repre(repre): - continue - if has_review_tag: review_repres.append(repre) - else: + elif self._is_valid_images_repre(repre): other_repres.append(repre) return review_repres + other_repres From 212a918b3151b9d69ec0eafc69f911b61561e259 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:25:49 +0200 Subject: [PATCH 120/150] added base ruff config file --- ruff.toml | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 ruff.toml diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000000..153a5c7d88 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,74 @@ +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as Black. +line-length = 79 +indent-width = 4 + +[lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = ["E", "F", "W"] +ignore = [] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = false + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" From 644f79bf0d67341f05474240840fe129faa459eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:29:17 +0200 Subject: [PATCH 121/150] added specific ignores --- ruff.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ruff.toml b/ruff.toml index 153a5c7d88..e94728c0fc 100644 --- a/ruff.toml +++ b/ruff.toml @@ -26,6 +26,8 @@ exclude = [ "node_modules", "site-packages", "venv", + "vendor", + "generated", ] # Same as Black. @@ -46,6 +48,14 @@ unfixable = [] # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +exclude = [ + "client/ayon_core/modules/click_wrap.py", + "client/ayon_core/scripts/slates/__init__.py" +] + +[lint.per-file-ignores] +"client/ayon_core/lib/__init__.py" = ["E402"] + [format] # Like Black, use double quotes for strings. quote-style = "double" From 79dbdb38e2c682a126d0c7f75aad00f6ecabef02 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:29:36 +0200 Subject: [PATCH 122/150] added linting details --- ruff.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruff.toml b/ruff.toml index e94728c0fc..49dd7a8c17 100644 --- a/ruff.toml +++ b/ruff.toml @@ -35,9 +35,9 @@ line-length = 79 indent-width = 4 [lint] +preview = true +pydocstyle.convention = "google" # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. -# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or -# McCabe complexity (`C901`) by default. select = ["E", "F", "W"] ignore = [] From 9f7f1ed314d6e2419b587053229e2bb870812765 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:29:43 +0200 Subject: [PATCH 123/150] added target python --- ruff.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ruff.toml b/ruff.toml index 49dd7a8c17..701055a65a 100644 --- a/ruff.toml +++ b/ruff.toml @@ -34,6 +34,9 @@ exclude = [ line-length = 79 indent-width = 4 +# Assume Python 3.9 +target-version = "py39" + [lint] preview = true pydocstyle.convention = "google" From 87514ba2e30a13e30d2bccfe6db6bd5962a87b8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:30:02 +0200 Subject: [PATCH 124/150] remove ruff config from pyproject.toml --- pyproject.toml | 76 -------------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4c4272cc30..309b0f91a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,82 +41,6 @@ pymdown-extensions = "^10.14.3" mike = "^2.1.3" mkdocstrings-shell = "^1.0.2" - -[tool.ruff] -# Exclude a variety of commonly ignored directories. -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".ipynb_checkpoints", - ".mypy_cache", - ".nox", - ".pants.d", - ".pyenv", - ".pytest_cache", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - ".vscode", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "site-packages", - "venv", - "vendor", - "generated", -] - -# Same as Black. -line-length = 79 -indent-width = 4 - -# Assume Python 3.9 -target-version = "py39" - -[tool.ruff.lint] -preview = true -pydocstyle.convention = "google" -# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. -select = ["E", "F", "W"] -ignore = [] - -# Allow fix for all enabled rules (when `--fix`) is provided. -fixable = ["ALL"] -unfixable = [] - -# Allow unused variables when underscore-prefixed. -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" - -exclude = [ - "client/ayon_core/modules/click_wrap.py", - "client/ayon_core/scripts/slates/__init__.py" -] - -[tool.ruff.lint.per-file-ignores] -"client/ayon_core/lib/__init__.py" = ["E402"] - -[tool.ruff.format] -# Like Black, use double quotes for strings. -quote-style = "double" - -# Like Black, indent with spaces, rather than tabs. -indent-style = "space" - -# Like Black, respect magic trailing commas. -skip-magic-trailing-comma = false - -# Like Black, automatically detect the appropriate line ending. -line-ending = "auto" - [tool.codespell] # Ignore words that are not in the dictionary. ignore-words-list = "ayon,ynput,parms,parm,hda,developpement" From 2cb2a71e51e37614a12fc0c23378b61d57eee716 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:31:07 +0200 Subject: [PATCH 125/150] remove outdated paths --- pyproject.toml | 2 +- ruff.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 309b0f91a1..51f6048d8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ ignore-words-list = "ayon,ynput,parms,parm,hda,developpement" # Remove with next codespell release (>2.2.6) ignore-regex = ".*codespell:ignore.*" -skip = "./.*,./package/*,*/vendor/*,*/unreal/integration/*,*/aftereffects/api/extension/js/libs/*" +skip = "./.*,./package/*,*/client/ayon_core/vendor/*" count = true quiet-level = 3 diff --git a/ruff.toml b/ruff.toml index 701055a65a..f9b073e818 100644 --- a/ruff.toml +++ b/ruff.toml @@ -52,7 +52,6 @@ unfixable = [] dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" exclude = [ - "client/ayon_core/modules/click_wrap.py", "client/ayon_core/scripts/slates/__init__.py" ] From 591bf7c57be83a34677e4df720a42dd02d6b6017 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 14:01:01 +0200 Subject: [PATCH 126/150] bump ruff version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 51f6048d8a..3d85c30eda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ pytest = "^8.0" pytest-print = "^1.0" ayon-python-api = "^1.0" # linting dependencies -ruff = "^0.3.3" +ruff = "0.11.7" pre-commit = "^3.6.2" codespell = "^2.2.6" semver = "^3.0.2" From c32cdba660ae1d30bfb22831cb3d63dfec325d82 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:40:50 +0800 Subject: [PATCH 127/150] Update client/ayon_core/plugins/publish/extract_thumbnail.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/extract_thumbnail.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 9592264ee2..f67c571c90 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -372,15 +372,10 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): Returns: bool: whether the representation has the valid image content """ - files = repre.get("files") - if not files: - return False - # Get first file's extension - if isinstance(files, (list, tuple)): - first_file = files[0] - else: - first_file = files + first_file = repre["files"] + if isinstance(first_file, (list, tuple)): + first_file = first_file[0] ext = os.path.splitext(first_file)[1].lower() From 6d517d5e87074d7b4d4295c96e9bb405b36306b2 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:41:01 +0800 Subject: [PATCH 128/150] Update client/ayon_core/plugins/publish/extract_thumbnail.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/extract_thumbnail.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index f67c571c90..3a428c46a7 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -355,8 +355,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): ).format(repre["name"])) continue - has_review_tag = "review" in tags - if has_review_tag: + if "review" in tags: review_repres.append(repre) elif self._is_valid_images_repre(repre): other_repres.append(repre) From 0f977b92840eda1a55e7489d651232c3dbe512f5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 29 Apr 2025 15:52:48 +0200 Subject: [PATCH 129/150] Add addon version compatibility --- package.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.py b/package.py index 0358c2f4cd..d1e2fac5ca 100644 --- a/package.py +++ b/package.py @@ -11,4 +11,6 @@ ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = { "harmony": ">0.4.0", + "fusion": ">=0.3.3", + "openrv": ">=1.0.2" } From 5d8e3e37c1f32ad5e314588dc2210b13b3d57eb1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 29 Apr 2025 15:53:00 +0200 Subject: [PATCH 130/150] Cosmetics --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index d1e2fac5ca..9fbb6a7000 100644 --- a/package.py +++ b/package.py @@ -12,5 +12,5 @@ ayon_required_addons = {} ayon_compatible_addons = { "harmony": ">0.4.0", "fusion": ">=0.3.3", - "openrv": ">=1.0.2" + "openrv": ">=1.0.2", } From 20435c18bcb770f0fe03a1144181a917d09670ac Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 29 Apr 2025 13:56:28 +0000 Subject: [PATCH 131/150] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 01e431577e..6bc1e253c4 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.8+dev" +__version__ = "1.1.9" diff --git a/package.py b/package.py index 0358c2f4cd..111cfd08d0 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.8+dev" +version = "1.1.9" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 3d85c30eda..dd85bba802 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.8+dev" +version = "1.1.9" description = "" authors = ["Ynput Team "] readme = "README.md" From 839550d0cf5d89cbe6e2dc3fbf4306d8b383b628 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Apr 2025 13:57:15 +0000 Subject: [PATCH 132/150] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c0ab04abef..f602156aea 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,16 @@ body: label: Version description: What version are you running? Look to AYON Tray options: + - 1.1.9 + - 1.1.8 + - 1.1.7 + - 1.1.6 + - 1.1.5 + - 1.1.4 + - 1.1.3 + - 1.1.2 + - 1.1.1 + - 1.1.0 - 1.0.14 - 1.0.13 - 1.0.12 From abe038ee16d657d59e1bcb03b3c5e324e7fcff1c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 16:13:02 +0200 Subject: [PATCH 133/150] bump version to 1.1.9+dev --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 6bc1e253c4..ed56f67bb4 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.9" +__version__ = "1.1.9+dev" diff --git a/package.py b/package.py index 111cfd08d0..d28ebd059f 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.9" +version = "1.1.9+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index dd85bba802..5a89fbf7e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.9" +version = "1.1.9+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From b85e6b72cbad55190a0a539ca0ef9fd3d5de5b4a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 17:05:13 +0200 Subject: [PATCH 134/150] run update bug report after release trigger --- .github/workflows/update_bug_report.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update_bug_report.yml b/.github/workflows/update_bug_report.yml index 1e5da414bb..98a8454e4b 100644 --- a/.github/workflows/update_bug_report.yml +++ b/.github/workflows/update_bug_report.yml @@ -1,10 +1,11 @@ name: 🐞 Update Bug Report on: + workflow_run: + workflows: ["🚀 Release Trigger"] + types: + - completed workflow_dispatch: - release: - # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release - types: [published] jobs: update-bug-report: From 52d0cc8748707fb694c333b8f78693e0928d5759 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 30 Apr 2025 17:00:09 +0200 Subject: [PATCH 135/150] :sparkles: add hook to filter paths --- .../hooks/pre_remove_launcher_paths.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 client/ayon_core/hooks/pre_remove_launcher_paths.py diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py new file mode 100644 index 0000000000..a96978b368 --- /dev/null +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -0,0 +1,36 @@ +""""Pre launch hook to remove launcher paths from the system.""" +import os +from ayon_applications import PreLaunchHook, LaunchTypes + + +class PreRemoveLauncherPaths(PreLaunchHook): + """Remove launcher paths from the system. + + This hook is used to remove launcher paths from the system before launching + an application. It is used to ensure that the application is launched with + the correct environment variables. Especially for Windows, where + paths in `PATH` are used to load DLLs. This is important to avoid + conflicts with other applications that may have the same DLLs in their + paths. + """ + + order = 1 + + platforms = {"linux", "windows", "darwin"} + launch_types = {LaunchTypes.local} + + def execute(self): + # Remove launcher paths from the system + paths = [] + try: + ayon_root = self.launch_context.env["AYON_ROOT"] + except KeyError: + self.log.warning("AYON_ROOT not found in environment variables.") + return + + paths.extend( + path + for path in self.launch_context.env.get("PATH", "").split(os.pathsep) + if not path.startswith(ayon_root) + ) + self.launch_context.env["PATH"] = os.pathsep.join(paths) From cd7c4a37783b3c53b8e934a8524a18941c9ac0c0 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 30 Apr 2025 17:20:44 +0200 Subject: [PATCH 136/150] :recycle: make checks safer --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index a96978b368..800df0c214 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -23,14 +23,14 @@ class PreRemoveLauncherPaths(PreLaunchHook): # Remove launcher paths from the system paths = [] try: - ayon_root = self.launch_context.env["AYON_ROOT"] + ayon_root = os.path.normpath(self.launch_context.env["AYON_ROOT"]) except KeyError: self.log.warning("AYON_ROOT not found in environment variables.") return paths.extend( path - for path in self.launch_context.env.get("PATH", "").split(os.pathsep) - if not path.startswith(ayon_root) + for path in self.launch_context.env.get("PATH").split(os.pathsep) + if not os.path.normpath(path).startswith(ayon_root) ) self.launch_context.env["PATH"] = os.pathsep.join(paths) From 1f48b1568d0a988e749e42436ac545547756d726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:02:55 +0200 Subject: [PATCH 137/150] Update client/ayon_core/hooks/pre_remove_launcher_paths.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index 800df0c214..ee6ebd8950 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -30,7 +30,7 @@ class PreRemoveLauncherPaths(PreLaunchHook): paths.extend( path - for path in self.launch_context.env.get("PATH").split(os.pathsep) + for path in self.launch_context.env.get("PATH", "").split(os.pathsep) if not os.path.normpath(path).startswith(ayon_root) ) self.launch_context.env["PATH"] = os.pathsep.join(paths) From edabad6c13ec98e05a3b8c3f4d0e1b32b462165c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:07:30 +0200 Subject: [PATCH 138/150] Update client/ayon_core/hooks/pre_remove_launcher_paths.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index ee6ebd8950..4e3835d08b 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -22,11 +22,7 @@ class PreRemoveLauncherPaths(PreLaunchHook): def execute(self): # Remove launcher paths from the system paths = [] - try: - ayon_root = os.path.normpath(self.launch_context.env["AYON_ROOT"]) - except KeyError: - self.log.warning("AYON_ROOT not found in environment variables.") - return + ayon_root = os.path.normpath(os.environ["AYON_ROOT"]) paths.extend( path From 624dfcccadb4595d90f5c92127a30fbca99be897 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 2 May 2025 14:46:23 +0200 Subject: [PATCH 139/150] :recycle: some refactoring --- .../hooks/pre_remove_launcher_paths.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index 4e3835d08b..96ee997501 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -1,6 +1,10 @@ """"Pre launch hook to remove launcher paths from the system.""" +from __future__ import annotations + import os -from ayon_applications import PreLaunchHook, LaunchTypes +from typing import ClassVar + +from ayon_applications import PreLaunchHook class PreRemoveLauncherPaths(PreLaunchHook): @@ -15,18 +19,17 @@ class PreRemoveLauncherPaths(PreLaunchHook): """ order = 1 + launch_types: ClassVar[set] = set() - platforms = {"linux", "windows", "darwin"} - launch_types = {LaunchTypes.local} - - def execute(self): + def execute(self) -> None: + """Execute the hook.""" # Remove launcher paths from the system - paths = [] ayon_root = os.path.normpath(os.environ["AYON_ROOT"]) - paths.extend( + paths = [ path - for path in self.launch_context.env.get("PATH", "").split(os.pathsep) + for path in self.launch_context.env.get( + "PATH", "").split(os.pathsep) if not os.path.normpath(path).startswith(ayon_root) - ) + ] self.launch_context.env["PATH"] = os.pathsep.join(paths) From 917e32cb13923dfdc77d4bbdd1b041ee8492f3f4 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 2 May 2025 14:47:46 +0200 Subject: [PATCH 140/150] :recycle: remove unnecessary type hinting --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index 96ee997501..df27e512d0 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -1,8 +1,5 @@ """"Pre launch hook to remove launcher paths from the system.""" -from __future__ import annotations - import os -from typing import ClassVar from ayon_applications import PreLaunchHook @@ -17,9 +14,7 @@ class PreRemoveLauncherPaths(PreLaunchHook): conflicts with other applications that may have the same DLLs in their paths. """ - order = 1 - launch_types: ClassVar[set] = set() def execute(self) -> None: """Execute the hook.""" From 0063465626bb81f4a8cf9579576d406b473e189b Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 2 May 2025 12:51:59 +0000 Subject: [PATCH 141/150] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index ed56f67bb4..af87dbce6c 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.9+dev" +__version__ = "1.2.0" diff --git a/package.py b/package.py index 6229131f63..d6c58f223a 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.9+dev" +version = "1.2.0" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 5a89fbf7e3..2ce6d971dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.9+dev" +version = "1.2.0" description = "" authors = ["Ynput Team "] readme = "README.md" From 75110078618beab1f636bf26c24dcfd9b0393c75 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 2 May 2025 12:52:33 +0000 Subject: [PATCH 142/150] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index af87dbce6c..4fd7bde336 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.2.0" +__version__ = "1.2.0+dev" diff --git a/package.py b/package.py index d6c58f223a..1695cc7808 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.2.0" +version = "1.2.0+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 2ce6d971dd..c7e2bb5000 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.2.0" +version = "1.2.0+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 3bd3c2f6f5bf85c95f172b1b97e2e2a685b09fcf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 May 2025 12:53:24 +0000 Subject: [PATCH 143/150] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f602156aea..c1e18faf55 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to AYON Tray options: + - 1.2.0 - 1.1.9 - 1.1.8 - 1.1.7 From 47f8dcdce6871e96e295d0ab6d00957877360666 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 May 2025 12:05:01 +0200 Subject: [PATCH 144/150] Updates OCIO config paths Updates the built-in OCIO config paths to correct versioning and descriptions, ensuring accurate configuration options. --- server/settings/main.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 97434d0b93..a582763b4b 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,10 +72,10 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 - "label": "ACES 1.3 Studio (OCIO v2)", + "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "label": "ACES 2.0 Studio (OCIO v2.4)", "description": ( - "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") + "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 @@ -84,10 +84,10 @@ def _ocio_built_in_paths(): "Aces 1.3 Studio OCIO config file. Requires OCIO v2.1.") }, { - "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 - "label": "ACES 2.0 Studio (OCIO v2.4)", + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 + "label": "ACES 1.3 Studio (OCIO v2)", "description": ( - "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") + "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") }, { "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", From 93b59710b25303d105960e3ef370086f167c1b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 8 May 2025 17:27:52 +0200 Subject: [PATCH 145/150] Apply suggestions from code review Co-authored-by: Robin De Lillo Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../publish/collect_explicit_resolution.py | 18 +++++++++--------- server/settings/publish_plugins.py | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 1aa7147627..1a388d5487 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -50,21 +50,21 @@ class CollectExplicitResolution( dict: dictionary with width, height and pixel_aspect """ resolution_items = self._get_resolution_items() - item_values = None - # check if resolution_value is in cached items - if resolution_value in resolution_items: - item_values = resolution_items[resolution_value] + # ensure resolution_value is part of expected items + item_values = resolution_items.get(resolution_value) + # if the item is in the cache, get the values from it if item_values: - # if the item is in the cache, get the values from it return { "resolutionWidth": item_values["width"], "resolutionHeight": item_values["height"], "pixelAspect": item_values["pixel_aspect"], } - else: - raise PublishError( - f"Invalid resolution value: {resolution_value}") + + raise PublishError( + f"Invalid resolution value: {resolution_value} " + f"expected choices: {resolution_items}" + ) @classmethod def _get_resolution_items(cls): @@ -72,7 +72,7 @@ class CollectExplicitResolution( resolution_items = {} for item in cls.options: item_text = ( - f"{item['width']}x{item['height']}x{item['pixel_aspect']}") + f"{item['width']}x{item['height']} ({item['pixel_aspect']})") resolution_items[item_text] = item cls.resolution_items = resolution_items diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 7ad6c9c506..cce312c2f8 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -191,21 +191,21 @@ class ResolutionOptionsModel(BaseSettingsModel): def ensure_unique_resolution_option( - objects: Iterable[Any], field_name: str | None = None) -> None: # noqa: C901 + objects: list[Any], field_name: str | None = None) -> None: # noqa: C901 """Ensure a list of objects have unique option attributes. This function checks if the list of objects has unique 'width', 'height' and 'pixel_aspect' properties. """ - options = [] + options = set() for obj in objects: item_test_text = f"{obj.width}x{obj.height}x{obj.pixel_aspect}" - if item_test_text not in options: - options.append(item_test_text) - else: + if item_test_text in options: raise BadRequestException( f"Duplicate option '{item_test_text}'") + options.add(item_test_text) + class CollectExplicitResolutionModel(BaseSettingsModel): enabled: bool = SettingsField(True, title="Enabled") @@ -218,14 +218,14 @@ class CollectExplicitResolutionModel(BaseSettingsModel): ) options: list[ResolutionOptionsModel] = SettingsField( default_factory=list, - title="Resolution options", + title="Resolution choices", description=( - "Options to be provided in publisher attribute" + "Available resolution choices to be displayed in the publishers attribute." ) ) @validator("options") - def validate_unique_options(cls, value): + def validate_unique_resolution_options(cls, value): ensure_unique_resolution_option(value) return value From 753960ca9ace6aafe067e85a0e7384f868a999a6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 8 May 2025 17:33:08 +0200 Subject: [PATCH 146/150] Refactors explicit resolution collection Changes the collector order to ensure correct execution. Renames the "Override Resolution" label to "Force product resolution" for clarity. Removes default resolution values from server settings. The explicit resolution is intended to be defined on the instance level, not as a global default. --- .../plugins/publish/collect_explicit_resolution.py | 4 ++-- server/settings/publish_plugins.py | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 1a388d5487..7d70271846 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -11,7 +11,7 @@ class CollectExplicitResolution( """Collect explicit user defined resolution attributes for instances""" label = "Choose Explicit Resolution" - order = pyblish.api.CollectorOrder + 0.49 + order = pyblish.api.CollectorOrder - 0.091 settings_category = "core" enabled = False @@ -99,6 +99,6 @@ class CollectExplicitResolution( "explicit_resolution", items, default="Don't override", - label="Override Resolution", + label="Force product resolution", ), ] diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index cce312c2f8..4b75fb46b6 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1244,13 +1244,7 @@ DEFAULT_PUBLISH_VALUES = { "product_types": [ "shot" ], - "options": [ - { - "width": 2048, - "height": 1080, - "aspect_ratio": 1.5, - } - ] + "options": [] }, "ValidateEditorialAssetName": { "enabled": True, From 824dc0cc81d30618afc0afead8c584c99be8f53a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 9 May 2025 10:50:20 +0200 Subject: [PATCH 147/150] Improves readability of resolution display Updates the format of resolution items displayed in the publisher's attribute for better readability. Removes an unused import from server settings. --- .../ayon_core/plugins/publish/collect_explicit_resolution.py | 4 +++- server/settings/publish_plugins.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 7d70271846..3ea3d42102 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -72,7 +72,9 @@ class CollectExplicitResolution( resolution_items = {} for item in cls.options: item_text = ( - f"{item['width']}x{item['height']} ({item['pixel_aspect']})") + f"{item['width']}x{item['height']} " + f"({item['pixel_aspect']})" + ) resolution_items[item_text] = item cls.resolution_items = resolution_items diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 56c8a929b1..0d8489d8ff 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from pydantic import validator from typing import Any @@ -221,7 +220,8 @@ class CollectExplicitResolutionModel(BaseSettingsModel): default_factory=list, title="Resolution choices", description=( - "Available resolution choices to be displayed in the publishers attribute." + "Available resolution choices to be displayed in " + "the publishers attribute." ) ) From 92aa7e1ccb89103da0e7addfa5405b77fd4258b1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 9 May 2025 11:53:15 +0200 Subject: [PATCH 148/150] Updates ayon_ocio addon version Updates the minimum compatible version of the 'ayon_ocio' addon. This ensures compatibility with the latest features and fixes in the addon. --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 0dad0b9792..601d703857 100644 --- a/package.py +++ b/package.py @@ -10,7 +10,7 @@ ayon_server_version = ">=1.7.6,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = { - "ayon_ocio": ">=1.2.0", + "ayon_ocio": ">=1.2.1", "harmony": ">0.4.0", "fusion": ">=0.3.3", "openrv": ">=1.0.2", From e4c5b0d0a53d2382199f4cbc92ed3785a8bb8a12 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 9 May 2025 11:59:52 +0200 Subject: [PATCH 149/150] Fixes typo in default OCIO config path Corrects a typo in the built-in OCIO config path. It appends the missing ".ocio" extension to the ACES 2.0 Studio config path, ensuring that the OCIO configuration is correctly recognized and loaded. --- server/settings/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/main.py b/server/settings/main.py index a582763b4b..dd6af0a104 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,7 +72,7 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4.ocio", # noqa: E501 "label": "ACES 2.0 Studio (OCIO v2.4)", "description": ( "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") From c7faefa99489fe67b3014ebed5b9f8d6e6a0092e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 9 May 2025 14:22:00 +0200 Subject: [PATCH 150/150] reverse the replacements --- .../ayon_core/pipeline/create/product_name.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 0daec8a7ad..ecffa4a340 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -52,15 +52,15 @@ def get_product_name_template( # TODO remove formatting keys replacement template = ( matching_profile["template"] - .replace("{task[name]}", "{task}") - .replace("{Task[name]}", "{Task}") - .replace("{TASK[NAME]}", "{TASK}") - .replace("{product[type]}", "{family}") - .replace("{Product[type]}", "{Family}") - .replace("{PRODUCT[TYPE]}", "{FAMILY}") - .replace("{folder[name]}", "{asset}") - .replace("{Folder[name]}", "{Asset}") - .replace("{FOLDER[NAME]}", "{ASSET}") + .replace("{task}", "{task[name]}") + .replace("{Task}", "{Task[name]}") + .replace("{TASK}", "{TASK[NAME]}") + .replace("{family}", "{product[type]}") + .replace("{Family}", "{Product[type]}") + .replace("{FAMILY}", "{PRODUCT[TYPE]}") + .replace("{asset}", "{folder[name]}") + .replace("{Asset}", "{Folder[name]}") + .replace("{ASSET}", "{FOLDER[NAME]}") ) # Make sure template is set (matching may have empty string)