From df7924b85b3af5c3b2ce17a4bccdb91ed3529071 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 22 Aug 2024 17:01:57 +0200 Subject: [PATCH 001/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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 1de8e300c78850a59d028104139c8bd93e834a96 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 11 Feb 2025 20:07:16 +0200 Subject: [PATCH 007/115] 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 008/115] 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 009/115] 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 010/115] 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 011/115] 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 012/115] 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 013/115] 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 014/115] 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 015/115] 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 016/115] 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 017/115] 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 018/115] 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 019/115] 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 020/115] `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 021/115] 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 1ec9106f0124d6d69ed7a90429d791b5ad46267d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 4 Mar 2025 16:06:07 +0100 Subject: [PATCH 022/115] Support `list[str]` for leaf entries in `core/project_folder_structure` settings to define folder names instead of requiring dicts with empty values --- client/ayon_core/pipeline/project_folders.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 902b969457..df4353c503 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -82,6 +82,14 @@ def create_project_folders(project_name, basic_paths=None): def _list_path_items(folder_structure): output = [] + + # Allow leaf folders of the `project_folder_structure` to use a list of + # strings instead of a dictionary of keys with empty values. + if isinstance(folder_structure, list): + assert all(isinstance(item, str) for item in folder_structure) + return [folder_structure] + + # Process key, value as key for folder names and value its subfolders for key, value in folder_structure.items(): if not value: output.append(key) From b9adc29be190e9c2ecada1f64278669c53cf38ed Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 4 Mar 2025 16:06:45 +0100 Subject: [PATCH 023/115] Add some type hints --- client/ayon_core/pipeline/project_folders.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index df4353c503..a6f903701f 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -1,6 +1,7 @@ import os import re import json +from typing import Dict, Any, List, Union from ayon_core.settings import get_project_settings from ayon_core.lib import Logger @@ -9,7 +10,7 @@ from .anatomy import Anatomy from .template_data import get_project_template_data -def concatenate_splitted_paths(split_paths, anatomy): +def concatenate_splitted_paths(split_paths, anatomy: Anatomy): log = Logger.get_logger("concatenate_splitted_paths") pattern_array = re.compile(r"\[.*\]") output = [] @@ -47,7 +48,7 @@ def concatenate_splitted_paths(split_paths, anatomy): return output -def fill_paths(path_list, anatomy): +def fill_paths(path_list: List[str], anatomy: Anatomy): format_data = get_project_template_data(project_name=anatomy.project_name) format_data["root"] = anatomy.roots filled_paths = [] @@ -59,7 +60,7 @@ def fill_paths(path_list, anatomy): return filled_paths -def create_project_folders(project_name, basic_paths=None): +def create_project_folders(project_name: str, basic_paths=None): log = Logger.get_logger("create_project_folders") anatomy = Anatomy(project_name) if basic_paths is None: @@ -80,7 +81,8 @@ def create_project_folders(project_name, basic_paths=None): os.makedirs(path) -def _list_path_items(folder_structure): +def _list_path_items( + folder_structure: Union[Dict[str, Any], List[str]]): output = [] # Allow leaf folders of the `project_folder_structure` to use a list of @@ -107,7 +109,7 @@ def _list_path_items(folder_structure): return output -def get_project_basic_paths(project_name): +def get_project_basic_paths(project_name: str): project_settings = get_project_settings(project_name) folder_structure = ( project_settings["core"]["project_folder_structure"] From 7baf208c61f992a30de62f29a3aef484207ac542 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 14:51:12 +0100 Subject: [PATCH 024/115] Fix `List[str]` entries --- client/ayon_core/pipeline/project_folders.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index a6f903701f..37197495f9 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -89,7 +89,7 @@ def _list_path_items( # strings instead of a dictionary of keys with empty values. if isinstance(folder_structure, list): assert all(isinstance(item, str) for item in folder_structure) - return [folder_structure] + return [[path] for path in folder_structure] # Process key, value as key for folder names and value its subfolders for key, value in folder_structure.items(): @@ -119,4 +119,6 @@ def get_project_basic_paths(project_name: str): if isinstance(folder_structure, str): folder_structure = json.loads(folder_structure) - return _list_path_items(folder_structure) + result = _list_path_items(folder_structure) + print(result) + return [] From 1b4f6bea20b2c6e7abac4eb5b57b9377ae78db90 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 15:05:50 +0100 Subject: [PATCH 025/115] Revert debug code --- client/ayon_core/pipeline/project_folders.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 37197495f9..570442ff0c 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -119,6 +119,4 @@ def get_project_basic_paths(project_name: str): if isinstance(folder_structure, str): folder_structure = json.loads(folder_structure) - result = _list_path_items(folder_structure) - print(result) - return [] + return _list_path_items(folder_structure) From 5bb09d525657f7c03f034fb0dc060f87d9450a4e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:39:19 +0100 Subject: [PATCH 026/115] 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 027/115] 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 028/115] 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 029/115] 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 030/115] 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 031/115] 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 032/115] 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 033/115] 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 034/115] 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 035/115] 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 ab682da2b83b1e405ed776d6b18ca8234e83d302 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 11:51:29 +0100 Subject: [PATCH 036/115] Raise ValueError instead of using assertion --- client/ayon_core/pipeline/project_folders.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 570442ff0c..7386382d56 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -88,7 +88,9 @@ def _list_path_items( # Allow leaf folders of the `project_folder_structure` to use a list of # strings instead of a dictionary of keys with empty values. if isinstance(folder_structure, list): - assert all(isinstance(item, str) for item in folder_structure) + if not all(isinstance(item, str) for item in folder_structure): + raise ValueError( + f"List items must all be strings. Got: {folder_structure}") return [[path] for path in folder_structure] # Process key, value as key for folder names and value its subfolders From eb17eebb99460963ef4888c039975ede350596b5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 12:28:49 +0100 Subject: [PATCH 037/115] Update client/ayon_core/pipeline/project_folders.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/project_folders.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 7386382d56..1e4d807c49 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -1,3 +1,4 @@ +from __future__ import annotations import os import re import json From 8008ab02b543b2d42a0dfbb91bd6afca315d28cf Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 12:29:45 +0100 Subject: [PATCH 038/115] Use future annotations style --- client/ayon_core/pipeline/project_folders.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 1e4d807c49..def2af9ba1 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -2,7 +2,7 @@ from __future__ import annotations import os import re import json -from typing import Dict, Any, List, Union +from typing import Any, Union from ayon_core.settings import get_project_settings from ayon_core.lib import Logger @@ -49,7 +49,7 @@ def concatenate_splitted_paths(split_paths, anatomy: Anatomy): return output -def fill_paths(path_list: List[str], anatomy: Anatomy): +def fill_paths(path_list: list[str], anatomy: Anatomy): format_data = get_project_template_data(project_name=anatomy.project_name) format_data["root"] = anatomy.roots filled_paths = [] @@ -83,7 +83,7 @@ def create_project_folders(project_name: str, basic_paths=None): def _list_path_items( - folder_structure: Union[Dict[str, Any], List[str]]): + folder_structure: Union[dict[str, Any], list[str]]): output = [] # Allow leaf folders of the `project_folder_structure` to use a list of From 99e5e326f8a6627366815d21fc9f5c23d0650392 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 16:07:03 +0100 Subject: [PATCH 039/115] 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 040/115] 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 041/115] 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 042/115] 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 043/115] 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 044/115] 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 bb625ffef448e63be53e1e0ba9c364b6c2b40dda Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 17:39:16 +0100 Subject: [PATCH 045/115] LoaderPlugin takes no arguments on initialization --- client/ayon_core/pipeline/load/plugins.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 1fb906fd65..b601914acd 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -13,15 +13,7 @@ from .utils import get_representation_path_from_context class LoaderPlugin(list): - """Load representation into host application - - Arguments: - context (dict): avalon-core:context-1.0 - - .. versionadded:: 4.0 - This class was introduced - - """ + """Load representation into host application""" product_types = set() representations = set() From 00214c1defcd1ed6556eb278f338234e6f5ba672 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 21:53:13 +0100 Subject: [PATCH 046/115] Remove `python_2_comp.py` (we do not support Py2 for quite some time now) --- client/ayon_core/lib/python_2_comp.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 client/ayon_core/lib/python_2_comp.py diff --git a/client/ayon_core/lib/python_2_comp.py b/client/ayon_core/lib/python_2_comp.py deleted file mode 100644 index 900db59062..0000000000 --- a/client/ayon_core/lib/python_2_comp.py +++ /dev/null @@ -1,17 +0,0 @@ -# Deprecated file -# - the file container 'WeakMethod' implementation for Python 2 which is not -# needed anymore. -import warnings -import weakref - - -WeakMethod = weakref.WeakMethod - -warnings.warn( - ( - "'ayon_core.lib.python_2_comp' is deprecated." - "Please use 'weakref.WeakMethod'." - ), - DeprecationWarning, - stacklevel=2 -) From 12f6d76043986b8965bd6fc8d6402299b9bf52ea Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 22:01:21 +0100 Subject: [PATCH 047/115] 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 9d68669b6a99b450ad7e7860f453e1b15f4e8cde Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:40:36 +0100 Subject: [PATCH 048/115] do not trigger publish attributes collection after each add bulk, but when it finishes --- client/ayon_core/pipeline/create/context.py | 90 ++++++++++----------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 24557234f4..26b04ed3ed 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -839,7 +839,7 @@ class CreateContext: publish_attributes.update(output) for plugin in self.plugins_with_defs: - attr_defs = plugin.get_attr_defs_for_context (self) + attr_defs = plugin.get_attr_defs_for_context(self) if not attr_defs: continue self._publish_attributes.set_publish_plugin_attr_defs( @@ -1259,50 +1259,6 @@ class CreateContext: with self._bulk_context("add", sender) as bulk_info: yield bulk_info - # Set publish attributes before bulk context is exited - for instance in bulk_info.get_data(): - publish_attributes = instance.publish_attributes - # Prepare publish plugin attributes and set it on instance - for plugin in self.plugins_with_defs: - try: - if is_func_signature_supported( - plugin.convert_attribute_values, self, instance - ): - plugin.convert_attribute_values(self, instance) - - elif plugin.__instanceEnabled__: - output = plugin.convert_attribute_values( - publish_attributes - ) - if output: - publish_attributes.update(output) - - except Exception: - self.log.error( - "Failed to convert attribute values of" - f" plugin '{plugin.__name__}'", - exc_info=True - ) - - for plugin in self.plugins_with_defs: - attr_defs = None - try: - attr_defs = plugin.get_attr_defs_for_instance( - self, instance - ) - except Exception: - self.log.error( - "Failed to get attribute definitions" - f" from plugin '{plugin.__name__}'.", - exc_info=True - ) - - if not attr_defs: - continue - instance.set_publish_plugin_attr_defs( - plugin.__name__, attr_defs - ) - @contextmanager def bulk_instances_collection(self, sender=None): """DEPRECATED use 'bulk_add_instances' instead.""" @@ -2251,6 +2207,50 @@ class CreateContext: if not instances_to_validate: return + # Set publish attributes before bulk callbacks are triggered + for instance in instances_to_validate: + publish_attributes = instance.publish_attributes + # Prepare publish plugin attributes and set it on instance + for plugin in self.plugins_with_defs: + try: + if is_func_signature_supported( + plugin.convert_attribute_values, self, instance + ): + plugin.convert_attribute_values(self, instance) + + elif plugin.__instanceEnabled__: + output = plugin.convert_attribute_values( + publish_attributes + ) + if output: + publish_attributes.update(output) + + except Exception: + self.log.error( + "Failed to convert attribute values of" + f" plugin '{plugin.__name__}'", + exc_info=True + ) + + for plugin in self.plugins_with_defs: + attr_defs = None + try: + attr_defs = plugin.get_attr_defs_for_instance( + self, instance + ) + except Exception: + self.log.error( + "Failed to get attribute definitions" + f" from plugin '{plugin.__name__}'.", + exc_info=True + ) + + if not attr_defs: + continue + instance.set_publish_plugin_attr_defs( + plugin.__name__, attr_defs + ) + # Cache folder and task entities for all instances at once self.get_instances_context_info(instances_to_validate) From 60c9229f41a604243a71c4ffe65bf6e5b0b7e686 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:47:08 +0100 Subject: [PATCH 049/115] use differnt varible name for aov frames --- client/ayon_core/pipeline/farm/pyblish_functions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 0261a0c2b5..c6f3ae7115 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -808,14 +808,14 @@ def _create_instances_for_aov( frames_to_render is not None and isinstance(collected_files, (list, tuple)) # not single file ): - frames_to_render = convert_frames_str_to_list(frames_to_render) + aov_frames_to_render = convert_frames_str_to_list(frames_to_render) collections, _ = clique.assemble(collected_files) collected_files = _get_real_files_to_render( - collections[0], frames_to_render) + collections[0], aov_frames_to_render) else: frame_start = int(skeleton.get("frameStartHandle")) frame_end = int(skeleton.get("frameEndHandle")) - frames_to_render = list(range(frame_start, frame_end + 1)) + aov_frames_to_render = list(range(frame_start, frame_end + 1)) dynamic_data = { "aov": aov, @@ -937,8 +937,8 @@ def _create_instances_for_aov( "name": ext, "ext": ext, "files": collected_files, - "frameStart": frames_to_render[0], - "frameEnd": frames_to_render[-1], + "frameStart": aov_frames_to_render[0], + "frameEnd": aov_frames_to_render[-1], # If expectedFile are absolute, we need only filenames "stagingDir": staging_dir, "fps": new_instance.get("fps"), From 4a7f79a7bad5b533eb9112ab3ea0ada70a243566 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 13 Mar 2025 16:06:47 +0000 Subject: [PATCH 050/115] [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 332001aef7..7717a7a215 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.4+dev" +__version__ = "1.1.5" diff --git a/package.py b/package.py index 2c9072d443..69e4b85ef2 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.4+dev" +version = "1.1.5" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 0bbe0f90e1..eca50f349d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.4+dev" +version = "1.1.5" description = "" authors = ["Ynput Team "] readme = "README.md" From 7c4fb2d2e2f317a3f63c475fee6d4018e5ced990 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 13 Mar 2025 16:07:28 +0000 Subject: [PATCH 051/115] [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 7717a7a215..de5c199428 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" +__version__ = "1.1.5+dev" diff --git a/package.py b/package.py index 69e4b85ef2..250b77fe52 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.5" +version = "1.1.5+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index eca50f349d..94badd2f1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.5" +version = "1.1.5+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From b72a1e1d28440670feec11477581041dae880041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 17 Mar 2025 14:19:18 +0100 Subject: [PATCH 052/115] :bug: fix publish plugin discovery and some typos and unused imports along the way --- client/ayon_core/pipeline/publish/lib.py | 65 ++++++++++++++----- .../tools/publisher/models/publish.py | 2 +- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index cc5f67c74b..d0f90b003d 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -1,10 +1,15 @@ +"""Library functions for publishing.""" +from __future__ import annotations import os import sys +import importlib import inspect import copy +from pathlib import Path import warnings import xml.etree.ElementTree -from typing import Optional, Union, List +from typing import TYPE_CHECKING, Optional, Union, List + import ayon_api import pyblish.util @@ -13,7 +18,6 @@ import pyblish.api from ayon_core.lib import ( Logger, - import_filepath, filter_profiles, ) from ayon_core.settings import get_project_settings @@ -25,6 +29,9 @@ from .constants import ( DEFAULT_HERO_PUBLISH_TEMPLATE, ) +if TYPE_CHECKING: + from types import ModuleType + def get_template_name_profiles( project_name, project_settings=None, logger=None @@ -163,7 +170,7 @@ class HelpContent: def load_help_content_from_filepath(filepath): """Load help content from xml file. - Xml file may containt errors and warnings. + Xml file may contain errors and warnings. """ errors = {} warnings = {} @@ -208,8 +215,38 @@ def load_help_content_from_plugin(plugin): return load_help_content_from_filepath(filepath) -def publish_plugins_discover(paths=None): - """Find and return available pyblish plug-ins +def _import_module(module_name: str, path: Path) -> ModuleType: + """Import module from path. + + Args: + module_name (str): Name of module. + path (Path): Path to module file. + + Returns: + ModuleType: Imported module. + + Raises: + ImportError: When module cannot be imported. + + """ + spec = importlib.util.spec_from_file_location(module_name, path) + if spec is None: + msg = f"Cannot import path '{path}' as a Python module" + raise ImportError(msg) + + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + if spec.loader is None: + msg = f"Cannot import path '{path}' as a Python module" + raise ImportError(msg) + spec.loader.exec_module(module) + + return module + + +def publish_plugins_discover( + paths: Optional[list[str]] = None) -> DiscoverResult: + """Find and return available pyblish plug-ins. Overridden function from `pyblish` module to be able to collect crashed files and reason of their crash. @@ -252,17 +289,13 @@ def publish_plugins_discover(paths=None): continue try: - module = import_filepath(abspath, mod_name) + module = _import_module(mod_name, Path(abspath)) - # Store reference to original module, to avoid - # garbage collection from collecting it's global - # imports, such as `import os`. - sys.modules[abspath] = module - - except Exception as err: + except Exception as err: # noqa: BLE001 + # we need broad exception to catch all possible errors. result.crashed_file_paths[abspath] = sys.exc_info() - log.debug("Skipped: \"%s\" (%s)", mod_name, err) + log.debug('Skipped: "%s" (%s)', mod_name, err) continue for plugin in pyblish.plugin.plugins_from_module(module): @@ -282,7 +315,7 @@ def publish_plugins_discover(paths=None): plugin_names.append(plugin.__name__) plugin.__module__ = module.__file__ - key = "{0}.{1}".format(plugin.__module__, plugin.__name__) + key = f"{plugin.__module__}.{plugin.__name__}" plugins[key] = plugin # Include plug-ins from registration. @@ -427,7 +460,7 @@ def filter_pyblish_plugins(plugins): log = Logger.get_logger("filter_pyblish_plugins") # TODO: Don't use host from 'pyblish.api' but from defined host by us. - # - kept becau on farm is probably used host 'shell' which propably + # - kept because on farm is probably used host 'shell' which probably # affect how settings are applied there host_name = pyblish.api.current_host() project_name = os.environ.get("AYON_PROJECT_NAME") @@ -529,7 +562,7 @@ def filter_instances_for_context_plugin(plugin, context): Args: plugin (pyblish.api.Plugin): Plugin with filters. - context (pyblish.api.Context): Pyblish context with insances. + context (pyblish.api.Context): Pyblish context with instances. Returns: Iterator[pyblish.lib.Instance]: Iteration of valid instances. diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index 97a956b18f..b17b450846 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -279,7 +279,7 @@ class PublishReportMaker: "name": plugin.__name__, "label": label, "order": plugin.order, - "filepath": inspect.getfile(plugin), + "filepath": inspect.getfile(plugin.__class__), "docstring": docstring, "plugin_type": plugin_type, "families": list(plugin.families), From 61a5246b1b9d814f8952f7de2fc84a871ed535af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 17 Mar 2025 15:18:34 +0100 Subject: [PATCH 053/115] :recycle: reorganize the fix --- client/ayon_core/lib/python_module_tools.py | 3 +- client/ayon_core/pipeline/publish/lib.py | 47 +++---------------- .../tools/publisher/models/publish.py | 2 +- 3 files changed, 9 insertions(+), 43 deletions(-) diff --git a/client/ayon_core/lib/python_module_tools.py b/client/ayon_core/lib/python_module_tools.py index d146e069a9..71daeac42a 100644 --- a/client/ayon_core/lib/python_module_tools.py +++ b/client/ayon_core/lib/python_module_tools.py @@ -28,6 +28,7 @@ def import_filepath(filepath, module_name=None): module_loader = importlib.machinery.SourceFileLoader( module_name, filepath ) + sys.modules[module_name] = module module_loader.exec_module(module) return module @@ -193,7 +194,7 @@ def is_func_signature_supported(func, *args, **kwargs): Notes: This does NOT check if the function would work with passed arguments only if they can be passed in. If function have *args, **kwargs - in paramaters, this will always return 'True'. + in parameters, this will always return 'True'. Example: >>> def my_function(my_number): diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index d0f90b003d..0602711524 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -2,14 +2,11 @@ from __future__ import annotations import os import sys -import importlib import inspect import copy -from pathlib import Path import warnings import xml.etree.ElementTree -from typing import TYPE_CHECKING, Optional, Union, List - +from typing import Optional, Union, List import ayon_api import pyblish.util @@ -17,6 +14,7 @@ import pyblish.plugin import pyblish.api from ayon_core.lib import ( + import_filepath, Logger, filter_profiles, ) @@ -29,9 +27,6 @@ from .constants import ( DEFAULT_HERO_PUBLISH_TEMPLATE, ) -if TYPE_CHECKING: - from types import ModuleType - def get_template_name_profiles( project_name, project_settings=None, logger=None @@ -215,35 +210,6 @@ def load_help_content_from_plugin(plugin): return load_help_content_from_filepath(filepath) -def _import_module(module_name: str, path: Path) -> ModuleType: - """Import module from path. - - Args: - module_name (str): Name of module. - path (Path): Path to module file. - - Returns: - ModuleType: Imported module. - - Raises: - ImportError: When module cannot be imported. - - """ - spec = importlib.util.spec_from_file_location(module_name, path) - if spec is None: - msg = f"Cannot import path '{path}' as a Python module" - raise ImportError(msg) - - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - if spec.loader is None: - msg = f"Cannot import path '{path}' as a Python module" - raise ImportError(msg) - spec.loader.exec_module(module) - - return module - - def publish_plugins_discover( paths: Optional[list[str]] = None) -> DiscoverResult: """Find and return available pyblish plug-ins. @@ -289,7 +255,7 @@ def publish_plugins_discover( continue try: - module = _import_module(mod_name, Path(abspath)) + module = import_filepath(abspath, mod_name) except Exception as err: # noqa: BLE001 # we need broad exception to catch all possible errors. @@ -313,9 +279,8 @@ def publish_plugins_discover( continue plugin_names.append(plugin.__name__) - - plugin.__module__ = module.__file__ - key = f"{plugin.__module__}.{plugin.__name__}" + plugin.__file__ = module.__file__ + key = f"{module.__file__}.{plugin.__name__}" plugins[key] = plugin # Include plug-ins from registration. @@ -394,7 +359,7 @@ def get_plugin_settings(plugin, project_settings, log, category=None): # Settings category determined from path # - usually path is './/plugins/publish/' # - category can be host name of addon name ('maya', 'deadline', ...) - filepath = os.path.normpath(inspect.getsourcefile(plugin)) + filepath = os.path.normpath(inspect.getfile(plugin.__class__)) split_path = filepath.rsplit(os.path.sep, 5) if len(split_path) < 4: diff --git a/client/ayon_core/tools/publisher/models/publish.py b/client/ayon_core/tools/publisher/models/publish.py index b17b450846..97a956b18f 100644 --- a/client/ayon_core/tools/publisher/models/publish.py +++ b/client/ayon_core/tools/publisher/models/publish.py @@ -279,7 +279,7 @@ class PublishReportMaker: "name": plugin.__name__, "label": label, "order": plugin.order, - "filepath": inspect.getfile(plugin.__class__), + "filepath": inspect.getfile(plugin), "docstring": docstring, "plugin_type": plugin_type, "families": list(plugin.families), From fb64de8d3276b94af27a6224089831981f76c25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 17 Mar 2025 15:31:39 +0100 Subject: [PATCH 054/115] :recycle: make changes more backward safe --- client/ayon_core/lib/python_module_tools.py | 20 +++++++++++++++++--- client/ayon_core/pipeline/publish/lib.py | 3 ++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/lib/python_module_tools.py b/client/ayon_core/lib/python_module_tools.py index 71daeac42a..7ff8758a90 100644 --- a/client/ayon_core/lib/python_module_tools.py +++ b/client/ayon_core/lib/python_module_tools.py @@ -1,6 +1,8 @@ +"""Tools for working with python modules and classes.""" import os import sys import types +from typing import Optional import importlib import inspect import logging @@ -8,13 +10,22 @@ import logging log = logging.getLogger(__name__) -def import_filepath(filepath, module_name=None): +def import_filepath( + filepath: str, + module_name: Optional[str]=None, + sys_module_name: Optional[str]=None) -> types.ModuleType: """Import python file as python module. Args: filepath (str): Path to python file. module_name (str): Name of loaded module. Only for Python 3. By default is filled with filename of filepath. + sys_module_name (str): Name of module in `sys.modules` where to store + loaded module. By default is None so module is not added to + `sys.modules`. + + Todo (antirotor): We should add the module to the sys.modules always but + we need to be careful about it and test it properly. """ if module_name is None: @@ -28,7 +39,9 @@ def import_filepath(filepath, module_name=None): module_loader = importlib.machinery.SourceFileLoader( module_name, filepath ) - sys.modules[module_name] = module + # only add to sys.modules if requested + if sys_module_name: + sys.modules[sys_module_name] = module module_loader.exec_module(module) return module @@ -127,7 +140,8 @@ def classes_from_module(superclass, module): return classes -def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None): +def import_module_from_dirpath( + dirpath, folder_name, dst_module_name=None): """Import passed directory as a python module. Imported module can be assigned as a child attribute of already loaded diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 0602711524..048109e3ea 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -255,7 +255,8 @@ def publish_plugins_discover( continue try: - module = import_filepath(abspath, mod_name) + module = import_filepath( + abspath, mod_name, sys_module_name=mod_name) except Exception as err: # noqa: BLE001 # we need broad exception to catch all possible errors. From d31374043464ef4bb92bd3c4f04b41ddaf5a90f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:22:17 +0100 Subject: [PATCH 055/115] Update client/ayon_core/lib/python_module_tools.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/lib/python_module_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/python_module_tools.py b/client/ayon_core/lib/python_module_tools.py index 7ff8758a90..a6dc8031c7 100644 --- a/client/ayon_core/lib/python_module_tools.py +++ b/client/ayon_core/lib/python_module_tools.py @@ -12,8 +12,8 @@ log = logging.getLogger(__name__) def import_filepath( filepath: str, - module_name: Optional[str]=None, - sys_module_name: Optional[str]=None) -> types.ModuleType: + module_name: Optional[str] = None, + sys_module_name: Optional[str] = None) -> types.ModuleType: """Import python file as python module. Args: From 44e32c5bfe852c8605b7707a56e53b525714dd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:22:24 +0100 Subject: [PATCH 056/115] Update client/ayon_core/pipeline/publish/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/publish/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 048109e3ea..49ecab2221 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -360,7 +360,7 @@ def get_plugin_settings(plugin, project_settings, log, category=None): # Settings category determined from path # - usually path is './/plugins/publish/' # - category can be host name of addon name ('maya', 'deadline', ...) - filepath = os.path.normpath(inspect.getfile(plugin.__class__)) + filepath = os.path.normpath(inspect.getfile(plugin)) split_path = filepath.rsplit(os.path.sep, 5) if len(split_path) < 4: From aef050f390937a6fe6497869e3aec2aa98654363 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:23:29 +0100 Subject: [PATCH 057/115] added function 'get_addons_resources_dir' --- client/ayon_core/lib/__init__.py | 2 ++ client/ayon_core/lib/local_settings.py | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index 03ed574081..92c3966e77 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -9,6 +9,7 @@ from .local_settings import ( AYONSettingsRegistry, get_launcher_local_dir, get_launcher_storage_dir, + get_addons_resources_dir, get_local_site_id, get_ayon_username, ) @@ -142,6 +143,7 @@ __all__ = [ "AYONSettingsRegistry", "get_launcher_local_dir", "get_launcher_storage_dir", + "get_addons_resources_dir", "get_local_site_id", "get_ayon_username", diff --git a/client/ayon_core/lib/local_settings.py b/client/ayon_core/lib/local_settings.py index eff0068f00..d994145d4b 100644 --- a/client/ayon_core/lib/local_settings.py +++ b/client/ayon_core/lib/local_settings.py @@ -96,6 +96,30 @@ def get_launcher_local_dir(*subdirs: str) -> str: return os.path.join(storage_dir, *subdirs) +def get_addons_resources_dir(addon_name: str, *args) -> str: + """Get directory for storing resources for addons. + + Some addons might need to store ad-hoc resources that are not part of + addon client package (e.g. because of size). Studio might define + dedicated directory to store them with 'AYON_ADDONS_RESOURCES_DIR' + environment variable. By default, is used 'addons_resources' in + launcher storage (might be shared across platforms). + + Args: + addon_name (str): Addon name. + *args (str): Subfolders in resources directory. + + Returns: + str: Path to resources directory. + + """ + addons_resources_dir = os.getenv("AYON_ADDONS_RESOURCES_DIR") + if not addons_resources_dir: + addons_resources_dir = get_launcher_storage_dir("addons_resources") + + return os.path.join(addons_resources_dir, addon_name, *args) + + class AYONSecureRegistry: """Store information using keyring. From e56c71d3eaba32c35ae70371741be43433adaad2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:51:07 +0100 Subject: [PATCH 058/115] ceil height of 'ExpandingTextEdit' --- client/ayon_core/tools/utils/widgets.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 1074b6d4fb..f08e2fa5f2 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -1,4 +1,5 @@ import logging +import math from typing import Optional, List, Set, Any from qtpy import QtWidgets, QtCore, QtGui @@ -413,8 +414,8 @@ class ExpandingTextEdit(QtWidgets.QTextEdit): return margins.top() + document.size().height() + margins.bottom() def sizeHint(self): - width = super(ExpandingTextEdit, self).sizeHint().width() - return QtCore.QSize(width, self.heightForWidth(width)) + width = super().sizeHint().width() + return QtCore.QSize(width, math.ceil(self.heightForWidth(width))) class BaseClickableFrame(QtWidgets.QFrame): From fd4e5f58d48ee62757d6087802c9756e6a248f02 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:06:14 +0100 Subject: [PATCH 059/115] change where the ceil happens --- client/ayon_core/tools/utils/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index f08e2fa5f2..0cd6d68ab3 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -411,11 +411,13 @@ class ExpandingTextEdit(QtWidgets.QTextEdit): document = self.document().clone() document.setTextWidth(document_width) - return margins.top() + document.size().height() + margins.bottom() + return math.ceil( + margins.top() + document.size().height() + margins.bottom() + ) def sizeHint(self): width = super().sizeHint().width() - return QtCore.QSize(width, math.ceil(self.heightForWidth(width))) + return QtCore.QSize(width, self.heightForWidth(width)) class BaseClickableFrame(QtWidgets.QFrame): From a9f6b39de3e28478a92e5e3744d021ce20d3eadf Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Tue, 25 Mar 2025 18:12:36 +0100 Subject: [PATCH 060/115] 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 061/115] 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 062/115] 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 063/115] 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 064/115] 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 065/115] 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 066/115] [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 067/115] [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 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 068/115] 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 069/115] 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 070/115] 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 071/115] 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 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 072/115] 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 073/115] 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 074/115] 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 075/115] 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 076/115] 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 077/115] 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 078/115] 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 079/115] 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 080/115] 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 081/115] 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 082/115] 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 083/115] 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 084/115] 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 085/115] 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 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 086/115] :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 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 087/115] 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 088/115] 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 089/115] [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 090/115] [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 091/115] 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 092/115] :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 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 093/115] :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 094/115] 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 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 095/115] 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 096/115] 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 097/115] 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 098/115] 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 099/115] 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 100/115] 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 101/115] [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 102/115] [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 103/115] 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 104/115] 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 105/115] 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 106/115] 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 107/115] 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 108/115] 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 109/115] 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 8d253033332e55b46ca0bcbeca59da50d078cdd7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 16 Apr 2025 21:53:17 +0200 Subject: [PATCH 110/115] 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 111/115] 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 112/115] 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 113/115] 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 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 114/115] 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 115/115] 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])