From 0dc3e17273ae5a4f8645f4cfdd3392fd720c9ce7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Feb 2025 02:46:30 +0100 Subject: [PATCH 01/30] 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 02/30] 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 03/30] 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 04/30] 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 5bb09d525657f7c03f034fb0dc060f87d9450a4e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 17:39:19 +0100 Subject: [PATCH 05/30] 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 06/30] 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 07/30] 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 08/30] 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 09/30] 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 10/30] 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 99e5e326f8a6627366815d21fc9f5c23d0650392 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 16:07:03 +0100 Subject: [PATCH 11/30] 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 12/30] 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 13/30] 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 14/30] 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 15/30] 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 16/30] 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 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 17/30] 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 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 18/30] :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 19/30] :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 20/30] :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 21/30] 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 22/30] 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 23/30] 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 24/30] 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 25/30] 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 26/30] 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 27/30] 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 28/30] 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 29/30] 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 2b53a1841ad5f936937e514b94a4318313d52e30 Mon Sep 17 00:00:00 2001 From: Philippe Leprince Date: Thu, 27 Mar 2025 16:39:29 +0100 Subject: [PATCH 30/30] 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]