Merge branch 'develop' into enhancement/anatomy_template_enum

This commit is contained in:
Jakub Trllo 2025-04-29 16:40:41 +02:00 committed by GitHub
commit 8cfcd3627f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 268 additions and 196 deletions

View file

@ -35,6 +35,16 @@ body:
label: Version
description: What version are you running? Look to AYON Tray
options:
- 1.1.9
- 1.1.8
- 1.1.7
- 1.1.6
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.14
- 1.0.13
- 1.0.12

View file

@ -21,6 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v1
- uses: astral-sh/ruff-action@v3
with:
changed-files: "true"
version-file: "pyproject.toml"

View file

@ -37,7 +37,7 @@ def _handle_error(
if process_context.headless:
if detail:
print(detail)
print(f"{10*'*'}\n{message}\n{10*'*'}")
print(f"{10 * '*'}\n{message}\n{10 * '*'}")
return
current_dir = os.path.dirname(os.path.abspath(__file__))

View file

@ -24,7 +24,6 @@ from ayon_core.lib.env_tools import (
)
@click.group(invoke_without_command=True)
@click.pass_context
@click.option("--use-staging", is_flag=True,
@ -173,7 +172,6 @@ def contextselection(
main(output_path, project, folder, strict)
@main_cli.command(
context_settings=dict(
ignore_unknown_options=True,

View file

@ -22,12 +22,10 @@ import clique
if typing.TYPE_CHECKING:
from typing import Self, Tuple, Union, TypedDict, Pattern
class EnumItemDict(TypedDict):
label: str
value: Any
EnumItemsInputType = Union[
Dict[Any, str],
List[Tuple[Any, str]],
@ -35,7 +33,6 @@ if typing.TYPE_CHECKING:
List[EnumItemDict]
]
class FileDefItemDict(TypedDict):
directory: str
filenames: List[str]
@ -289,6 +286,7 @@ AttrDefType = TypeVar("AttrDefType", bound=AbstractAttrDef)
# UI attribute definitions won't hold value
# -----------------------------------------
class UIDef(AbstractAttrDef):
is_value_def = False

View file

@ -177,10 +177,12 @@ def initialize_ayon_connection(force=False):
return _new_get_last_versions(
con, *args, **kwargs
)
def _lv_by_pi_wrapper(*args, **kwargs):
return _new_get_last_version_by_product_id(
con, *args, **kwargs
)
def _lv_by_pn_wrapper(*args, **kwargs):
return _new_get_last_version_by_product_name(
con, *args, **kwargs

View file

@ -39,6 +39,7 @@ class Terminal:
"""
from ayon_core.lib import env_value_to_bool
log_no_colors = env_value_to_bool(
"AYON_LOG_NO_COLORS", default=None
)

View file

@ -162,7 +162,7 @@ def find_tool_in_custom_paths(paths, tool, validation_func=None):
# Handle cases when path is just an executable
# - it allows to use executable from PATH
# - basename must match 'tool' value (without extension)
extless_path, ext = os.path.splitext(path)
extless_path, _ext = os.path.splitext(path)
if extless_path == tool:
executable_path = find_executable(tool)
if executable_path and (
@ -181,7 +181,7 @@ def find_tool_in_custom_paths(paths, tool, validation_func=None):
# If path is a file validate it
if os.path.isfile(normalized):
basename, ext = os.path.splitext(os.path.basename(path))
basename, _ext = os.path.splitext(os.path.basename(path))
# Check if the filename has actually the sane bane as 'tool'
if basename == tool:
executable_path = find_executable(normalized)

View file

@ -872,7 +872,7 @@ class CreateContext:
"""
return self._event_hub.add_callback(INSTANCE_ADDED_TOPIC, callback)
def add_instances_removed_callback (self, callback):
def add_instances_removed_callback(self, callback):
"""Register callback for removed instances.
Event is triggered when instances are already removed from context.
@ -933,7 +933,7 @@ class CreateContext:
"""
self._event_hub.add_callback(VALUE_CHANGED_TOPIC, callback)
def add_pre_create_attr_defs_change_callback (self, callback):
def add_pre_create_attr_defs_change_callback(self, callback):
"""Register callback to listen pre-create attribute changes.
Create plugin can trigger refresh of pre-create attributes. Usage of
@ -961,7 +961,7 @@ class CreateContext:
PRE_CREATE_ATTR_DEFS_CHANGED_TOPIC, callback
)
def add_create_attr_defs_change_callback (self, callback):
def add_create_attr_defs_change_callback(self, callback):
"""Register callback to listen create attribute changes.
Create plugin changed attribute definitions of instance.
@ -986,7 +986,7 @@ class CreateContext:
"""
self._event_hub.add_callback(CREATE_ATTR_DEFS_CHANGED_TOPIC, callback)
def add_publish_attr_defs_change_callback (self, callback):
def add_publish_attr_defs_change_callback(self, callback):
"""Register callback to listen publish attribute changes.
Publish plugin changed attribute definitions of instance of context.

View file

@ -369,7 +369,7 @@ class PublishAttributes:
return copy.deepcopy(self._origin_data)
def attribute_value_changed(self, key, changes):
self._parent.publish_attribute_value_changed(key, changes)
self._parent.publish_attribute_value_changed(key, changes)
def set_publish_plugin_attr_defs(
self,

View file

@ -255,7 +255,7 @@ def deliver_sequence(
report_items[""].append(msg)
return report_items, 0
dir_path, file_name = os.path.split(str(src_path))
dir_path, _file_name = os.path.split(str(src_path))
context = repre["context"]
ext = context.get("ext", context.get("representation"))
@ -270,7 +270,7 @@ def deliver_sequence(
# context.representation could be .psd
ext = ext.replace("..", ".")
src_collections, remainder = clique.assemble(os.listdir(dir_path))
src_collections, _remainder = clique.assemble(os.listdir(dir_path))
src_collection = None
for col in src_collections:
if col.tail != ext:

View file

@ -1,4 +1,4 @@
from __future__ import annotations
from __future__ import annotations
import copy
import os
import re
@ -660,14 +660,6 @@ def _get_legacy_product_name_and_group(
warnings.warn("Using legacy product name for renders",
DeprecationWarning)
if not source_product_name.startswith(product_type):
resulting_group_name = '{}{}{}{}{}'.format(
product_type,
task_name[0].upper(), task_name[1:],
source_product_name[0].upper(), source_product_name[1:])
else:
resulting_group_name = source_product_name
# create product name `<product type><Task><Product name>`
if not source_product_name.startswith(product_type):
resulting_group_name = '{}{}{}{}{}'.format(
@ -1168,7 +1160,7 @@ def prepare_cache_representations(skeleton_data, exp_files, anatomy):
"""
representations = []
collections, remainders = clique.assemble(exp_files)
collections, _remainders = clique.assemble(exp_files)
log = Logger.get_logger("farm_publishing")

View file

@ -41,7 +41,7 @@ def validate(data, schema=None):
if not _CACHED:
_precache()
root, schema = data["schema"].rsplit(":", 1)
_root, schema = data["schema"].rsplit(":", 1)
if isinstance(schema, str):
schema = _cache[schema + ".json"]

View file

@ -209,7 +209,7 @@ def get_staging_dir_info(
staging_dir_config = get_staging_dir_config(
project_entity["name"],
task_type,
task_name ,
task_name,
product_type,
product_name,
host_name,

View file

@ -329,9 +329,9 @@ def get_last_workfile(
Returns:
str: Last or first workfile as filename of full path to filename.
"""
filename, version = get_last_workfile_with_version(
"""
filename, _version = get_last_workfile_with_version(
workdir, file_template, fill_data, extensions
)
if filename is None:

View file

@ -211,7 +211,7 @@ class DeleteOldVersions(load.ProductLoaderPlugin):
f"This will keep only the last {versions_to_keep} "
f"versions for the {num_contexts} selected product{s}."
)
informative_text="Warning: This will delete files from disk"
informative_text = "Warning: This will delete files from disk"
detailed_text = (
f"Keep only {versions_to_keep} versions for:\n{contexts_list}"
)

View file

@ -22,6 +22,7 @@ from ayon_core.tools.utils import show_message_dialog
OTIO = None
FRAME_SPLITTER = "__frame_splitter__"
def _import_otio():
global OTIO
if OTIO is None:

View file

@ -394,7 +394,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
if aov:
anatomy_data["aov"] = aov
def _fill_folder_data(self, instance, project_entity, anatomy_data):
# QUESTION: should we make sure that all folder data are popped if
# folder data cannot be found?

View file

@ -43,4 +43,3 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin):
if value:
self.log.debug(f"Setting job env: {key}: {value}")
env[key] = value

View file

@ -50,7 +50,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin):
"comments": instance.data.get("comments", []),
}
shot_data["attributes"] = {}
shot_data["attributes"] = {}
SHOT_ATTRS = (
"handleStart",
"handleEnd",

View file

@ -194,7 +194,6 @@ class CollectOtioSubsetResources(
repre = self._create_representation(
frame_start, frame_end, file=filename)
else:
_trim = False
dirname, filename = os.path.split(media_ref.target_url)
@ -209,7 +208,6 @@ class CollectOtioSubsetResources(
repre = self._create_representation(
frame_start, frame_end, file=filename, trim=_trim)
instance.data["originalDirname"] = self.staging_dir
# add representation to instance data
@ -221,7 +219,6 @@ class CollectOtioSubsetResources(
instance.data["representations"].append(repre)
self.log.debug(instance.data)
def _create_representation(self, start, end, **kwargs):

View file

@ -31,6 +31,9 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
# Keep "filesequence" for backwards compatibility of older jobs
targets = ["filesequence", "farm"]
label = "Collect rendered frames"
settings_category = "core"
remove_files = False
_context = None
@ -120,7 +123,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
self._fill_staging_dir(repre_data, anatomy)
representations.append(repre_data)
if not staging_dir_persistent:
if self.remove_files and not staging_dir_persistent:
add_repre_files_for_cleanup(instance, repre_data)
instance.data["representations"] = representations
@ -170,7 +173,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
os.environ.update(session_data)
staging_dir_persistent = self._process_path(data, anatomy)
if not staging_dir_persistent:
if self.remove_files and not staging_dir_persistent:
context.data["cleanupFullPaths"].append(path)
context.data["cleanupEmptyDirs"].append(
os.path.dirname(path)

View file

@ -280,7 +280,7 @@ class ExtractOIIOTranscode(publish.Extractor):
collection = collections[0]
frames = list(collection.indexes)
if collection.holes():
if collection.holes().indexes:
return files_to_convert
frame_str = "{}-{}#".format(frames[0], frames[-1])

View file

@ -1333,7 +1333,7 @@ class ExtractReview(pyblish.api.InstancePlugin):
bg_red, bg_green, bg_blue = overscan_color
else:
# Backwards compatibility
bg_red, bg_green, bg_blue, _ = overscan_color
bg_red, bg_green, bg_blue, _ = overscan_color
overscan_color_value = "#{0:0>2X}{1:0>2X}{2:0>2X}".format(
bg_red, bg_green, bg_blue

View file

@ -17,7 +17,7 @@ from ayon_core.lib import (
)
from ayon_core.lib.transcoding import convert_colorspace
from ayon_core.lib.transcoding import VIDEO_EXTENSIONS
from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS
class ExtractThumbnail(pyblish.api.InstancePlugin):
@ -163,9 +163,12 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
# Store new staging to cleanup paths
instance.context.data["cleanupFullPaths"].append(dst_staging)
thumbnail_created = False
oiio_supported = is_oiio_supported()
thumbnail_created = False
for repre in filtered_repres:
# Reset for each iteration to handle cases where multiple
# reviewable thumbnails are needed
repre_thumb_created = False
repre_files = repre["files"]
src_staging = os.path.normpath(repre["stagingDir"])
if not isinstance(repre_files, (list, tuple)):
@ -214,7 +217,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
)
# If the input can read by OIIO then use OIIO method for
# conversion otherwise use ffmpeg
thumbnail_created = self._create_thumbnail_oiio(
repre_thumb_created = self._create_thumbnail_oiio(
full_input_path,
full_output_path,
colorspace_data
@ -223,21 +226,22 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
# Try to use FFMPEG if OIIO is not supported or for cases when
# oiiotool isn't available or representation is not having
# colorspace data
if not thumbnail_created:
if not repre_thumb_created:
if oiio_supported:
self.log.debug(
"Converting with FFMPEG because input"
" can't be read by OIIO."
)
thumbnail_created = self._create_thumbnail_ffmpeg(
repre_thumb_created = self._create_thumbnail_ffmpeg(
full_input_path, full_output_path
)
# Skip representation and try next one if wasn't created
if not thumbnail_created:
if not repre_thumb_created:
continue
thumbnail_created = True
if len(explicit_repres) > 1:
repre_name = "thumbnail_{}".format(repre["outputName"])
else:
@ -332,7 +336,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
return need_thumb_repres
def _get_filtered_repres(self, instance):
filtered_repres = []
review_repres = []
other_repres = []
src_repres = instance.data.get("representations") or []
for repre in src_repres:
@ -344,17 +349,36 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
# to be published locally
continue
if "review" not in tags:
continue
if not repre.get("files"):
self.log.debug((
"Representation \"{}\" doesn't have files. Skipping"
).format(repre["name"]))
continue
filtered_repres.append(repre)
return filtered_repres
if "review" in tags:
review_repres.append(repre)
elif self._is_valid_images_repre(repre):
other_repres.append(repre)
return review_repres + other_repres
def _is_valid_images_repre(self, repre):
"""Check if representation contains valid image files
Args:
repre (dict): representation
Returns:
bool: whether the representation has the valid image content
"""
# Get first file's extension
first_file = repre["files"]
if isinstance(first_file, (list, tuple)):
first_file = first_file[0]
ext = os.path.splitext(first_file)[1].lower()
return ext in IMAGE_EXTENSIONS or ext in VIDEO_EXTENSIONS
def _create_thumbnail_oiio(
self,
@ -450,7 +474,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
# output arguments from presets
jpeg_items.extend(ffmpeg_args.get("output") or [])
# we just want one frame from movie files
jpeg_items.extend(["-vframes", "1"])
jpeg_items.extend(["-frames:v", "1"])
if resolution_arg:
jpeg_items.extend(resolution_arg)
@ -498,7 +522,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
"-i", video_file_path,
"-analyzeduration", max_int,
"-probesize", max_int,
"-vframes", "1"
"-frames:v", "1"
]
# add output file path

View file

@ -170,7 +170,7 @@ class ExtractThumbnailFromSource(pyblish.api.InstancePlugin):
"-analyzeduration", max_int,
"-probesize", max_int,
"-i", src_path,
"-vframes", "1",
"-frames:v", "1",
dst_path
)

View file

@ -619,8 +619,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
# used for all represe
# from temp to final
original_directory = (
instance.data.get("originalDirname") or instance_stagingdir)
instance.data.get("originalDirname") or stagingdir)
_rootless = self.get_rootless_path(anatomy, original_directory)
if _rootless == original_directory:
raise KnownPublishError((
@ -684,7 +683,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
elif is_sequence_representation:
# Collection of files (sequence)
src_collections, remainders = clique.assemble(files)
src_collections, _remainders = clique.assemble(files)
src_collection = src_collections[0]
destination_indexes = list(src_collection.indexes)

View file

@ -7,7 +7,7 @@ class IntegrateResourcesPath(pyblish.api.InstancePlugin):
label = "Integrate Resources Path"
order = pyblish.api.IntegratorOrder - 0.05
families = ["clip", "projectfile", "plate"]
families = ["clip", "projectfile", "plate"]
def process(self, instance):
resources = instance.data.get("resources") or []

View file

@ -27,8 +27,10 @@ import collections
import pyblish.api
import ayon_api
from ayon_api import RequestTypes
from ayon_api.operations import OperationsSession
InstanceFilterResult = collections.namedtuple(
"InstanceFilterResult",
["instance", "thumbnail_path", "version_id"]
@ -161,6 +163,30 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin):
return None
return os.path.normpath(filled_path)
def _create_thumbnail(self, project_name: str, src_filepath: str) -> str:
"""Upload thumbnail to AYON and return its id.
This is temporary fix of 'create_thumbnail' function in ayon_api to
fix jpeg mime type.
"""
mime_type = None
with open(src_filepath, "rb") as stream:
if b"\xff\xd8\xff" == stream.read(3):
mime_type = "image/jpeg"
if mime_type is None:
return ayon_api.create_thumbnail(project_name, src_filepath)
response = ayon_api.upload_file(
f"projects/{project_name}/thumbnails",
src_filepath,
request_type=RequestTypes.post,
headers={"Content-Type": mime_type},
)
response.raise_for_status()
return response.json()["id"]
def _integrate_thumbnails(
self,
filtered_instance_items,
@ -179,7 +205,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin):
).format(instance_label))
continue
thumbnail_id = ayon_api.create_thumbnail(
thumbnail_id = self._create_thumbnail(
project_name, thumbnail_path
)

View file

@ -173,7 +173,6 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins):
if frame_end is not None:
options["frame_end"] = frame_end
options["label"] = align
self._add_burnin(text, align, options, DRAWTEXT)

View file

@ -175,7 +175,7 @@ class BaseObj:
self.log.warning("Invalid range '{}'".format(part))
continue
for idx in range(sub_parts[0], sub_parts[1]+1):
for idx in range(sub_parts[0], sub_parts[1] + 1):
indexes.append(idx)
return indexes
@ -353,7 +353,6 @@ class BaseObj:
self.items[item.id] = item
item.fill_data_format()
def reset(self):
for item in self.items.values():
item.reset()

View file

@ -282,7 +282,7 @@ class ItemTable(BaseItem):
value.draw(image, drawer)
def value_width(self):
row_heights, col_widths = self.size_values
_row_heights, col_widths = self.size_values
width = 0
for _width in col_widths:
width += _width
@ -292,7 +292,7 @@ class ItemTable(BaseItem):
return width
def value_height(self):
row_heights, col_widths = self.size_values
row_heights, _col_widths = self.size_values
height = 0
for _height in row_heights:
height += _height
@ -569,21 +569,21 @@ class TableField(BaseItem):
@property
def item_pos_x(self):
pos_x, pos_y, width, height = (
pos_x, _pos_y, _width, _height = (
self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx)
)
return pos_x
@property
def item_pos_y(self):
pos_x, pos_y, width, height = (
_pos_x, pos_y, _width, _height = (
self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx)
)
return pos_y
@property
def value_pos_x(self):
pos_x, pos_y, width, height = (
pos_x, _pos_y, width, _height = (
self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx)
)
alignment_hor = self.style["alignment-horizontal"].lower()
@ -605,7 +605,7 @@ class TableField(BaseItem):
@property
def value_pos_y(self):
pos_x, pos_y, width, height = (
_pos_x, pos_y, _width, height = (
self.parent.content_pos_info_by_cord(self.row_idx, self.col_idx)
)

View file

@ -27,9 +27,12 @@ class ThumbnailsModel:
entity_type,
entity_ids,
):
thumbnail_paths = set()
output = {
entity_id: None
for entity_id in entity_ids
}
if not project_name or not entity_type or not entity_ids:
return thumbnail_paths
return output
thumbnail_id_by_entity_id = {}
if entity_type == "folder":
@ -43,7 +46,7 @@ class ThumbnailsModel:
)
if not thumbnail_id_by_entity_id:
return thumbnail_paths
return output
entity_ids_by_thumbnail_id = collections.defaultdict(set)
for entity_id, thumbnail_id in thumbnail_id_by_entity_id.items():
@ -51,10 +54,6 @@ class ThumbnailsModel:
continue
entity_ids_by_thumbnail_id[thumbnail_id].add(entity_id)
output = {
entity_id: None
for entity_id in entity_ids
}
for thumbnail_id, entity_ids in entity_ids_by_thumbnail_id.items():
thumbnail_path = self._get_thumbnail_path(
project_name, entity_type, next(iter(entity_ids)), thumbnail_id

View file

@ -248,4 +248,3 @@ class EnhancedTabBar(QtWidgets.QTabBar):
else:
super().mouseReleaseEvent(event)

View file

@ -492,7 +492,7 @@ def show(parent=None):
try:
module.window.close()
del(module.window)
del module.window
except (AttributeError, RuntimeError):
pass

View file

@ -32,7 +32,7 @@ from qtpy import QtWidgets, QtCore, QtGui
import pyblish.api
from ayon_core import style
TAB = 4* "&nbsp;"
TAB = 4 * "&nbsp;"
HEADER_SIZE = "15px"
KEY_COLOR = QtGui.QColor("#ffffff")
@ -243,7 +243,7 @@ class DebugUI(QtWidgets.QDialog):
self._set_window_title(plugin=result["plugin"])
print(10*"<", result["plugin"].__name__, 10*">")
print(10 * "<", result["plugin"].__name__, 10 * ">")
plugin_order = result["plugin"].order
plugin_name = result["plugin"].__name__

View file

@ -519,9 +519,9 @@ class LoaderWindow(QtWidgets.QWidget):
thumbnail_paths.discard(None)
if thumbnail_paths:
self._thumbnails_widget.set_current_thumbnail_paths(
thumbnail_paths
)
self._thumbnails_widget.set_current_thumbnail_paths(
thumbnail_paths
)
else:
self._thumbnails_widget.set_current_thumbnails(None)

View file

@ -461,19 +461,19 @@ class CreateModel:
self._create_context.add_instances_added_callback(
self._cc_added_instance
)
self._create_context.add_instances_removed_callback (
self._create_context.add_instances_removed_callback(
self._cc_removed_instance
)
self._create_context.add_value_changed_callback(
self._cc_value_changed
)
self._create_context.add_pre_create_attr_defs_change_callback (
self._create_context.add_pre_create_attr_defs_change_callback(
self._cc_pre_create_attr_changed
)
self._create_context.add_create_attr_defs_change_callback (
self._create_context.add_create_attr_defs_change_callback(
self._cc_create_attr_changed
)
self._create_context.add_publish_attr_defs_change_callback (
self._create_context.add_publish_attr_defs_change_callback(
self._cc_publish_attr_changed
)

View file

@ -358,7 +358,7 @@ class PublishReportMaker:
exception = result.get("error")
if exception:
fname, line_no, func, exc = exception.traceback
fname, line_no, func, _ = exception.traceback
# Conversion of exception into string may crash
try:

View file

@ -1,12 +0,0 @@
import warnings
from .broker import StdOutBroker
warnings.warn(
(
"Import of 'StdOutBroker' from 'ayon_core.tools.stdout_broker.app'"
" is deprecated. Please use 'ayon_core.tools.stdout_broker' instead."
),
DeprecationWarning
)
__all__ = ("StdOutBroker", )

View file

@ -738,4 +738,3 @@ def main(force=False):
sys.exit(1)
main()

View file

@ -350,21 +350,21 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
if project_name is None:
return True
string_pattern = self.filterRegularExpression().pattern()
if string_pattern:
return string_pattern.lower() in project_name.lower()
# Current project keep always visible
default = super(ProjectSortFilterProxy, self).filterAcceptsRow(
source_row, source_parent
)
if not default:
return default
# Make sure current project is visible
if index.data(PROJECT_IS_CURRENT_ROLE):
return True
default = super().filterAcceptsRow(source_row, source_parent)
if not default:
return default
string_pattern = self.filterRegularExpression().pattern()
if (
string_pattern
and string_pattern.lower() not in project_name.lower()
):
return False
if (
self._filter_inactive
and not index.data(PROJECT_IS_ACTIVE_ROLE)

View file

@ -575,7 +575,7 @@ class TasksWidget(QtWidgets.QWidget):
if self._tasks_model.is_refreshing:
return
parent_id, task_id, task_name, _ = self._get_selected_item_ids()
_parent_id, task_id, task_name, _ = self._get_selected_item_ids()
self._controller.set_selected_task(task_id, task_name)
self.selection_changed.emit()

View file

@ -462,7 +462,7 @@ class WorkfileEntitiesModel:
anatomy = self._controller.project_anatomy
workdir, filename = os.path.split(filepath)
success, rootless_dir = anatomy.find_root_template_from_path(workdir)
_, rootless_dir = anatomy.find_root_template_from_path(workdir)
return "/".join([
os.path.normpath(rootless_dir).replace("\\", "/"),
filename

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring AYON addon 'core' version."""
__version__ = "1.1.7+dev"
__version__ = "1.1.9+dev"

View file

@ -1,6 +1,6 @@
name = "core"
title = "Core"
version = "1.1.7+dev"
version = "1.1.9+dev"
client_dir = "ayon_core"
@ -9,4 +9,6 @@ plugin_for = ["ayon_server"]
ayon_server_version = ">=1.7.6,<2.0.0"
ayon_launcher_version = ">=1.0.2"
ayon_required_addons = {}
ayon_compatible_addons = {}
ayon_compatible_addons = {
"harmony": ">0.4.0",
}

View file

@ -5,7 +5,7 @@
[tool.poetry]
name = "ayon-core"
version = "1.1.7+dev"
version = "1.1.9+dev"
description = ""
authors = ["Ynput Team <team@ynput.io>"]
readme = "README.md"
@ -20,7 +20,7 @@ pytest = "^8.0"
pytest-print = "^1.0"
ayon-python-api = "^1.0"
# linting dependencies
ruff = "^0.3.3"
ruff = "0.11.7"
pre-commit = "^3.6.2"
codespell = "^2.2.6"
semver = "^3.0.2"
@ -41,80 +41,6 @@ pymdown-extensions = "^10.14.3"
mike = "^2.1.3"
mkdocstrings-shell = "^1.0.2"
[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
"vendor",
"generated",
]
# Same as Black.
line-length = 79
indent-width = 4
# Assume Python 3.9
target-version = "py39"
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
select = ["E", "F", "W"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
exclude = [
"client/ayon_core/modules/click_wrap.py",
"client/ayon_core/scripts/slates/__init__.py"
]
[tool.ruff.lint.per-file-ignores]
"client/ayon_core/lib/__init__.py" = ["E402"]
[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
[tool.codespell]
# Ignore words that are not in the dictionary.
ignore-words-list = "ayon,ynput,parms,parm,hda,developpement"
@ -123,7 +49,7 @@ ignore-words-list = "ayon,ynput,parms,parm,hda,developpement"
# Remove with next codespell release (>2.2.6)
ignore-regex = ".*codespell:ignore.*"
skip = "./.*,./package/*,*/vendor/*,*/unreal/integration/*,*/aftereffects/api/extension/js/libs/*"
skip = "./.*,./package/*,*/client/ayon_core/vendor/*"
count = true
quiet-level = 3

86
ruff.toml Normal file
View file

@ -0,0 +1,86 @@
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
"vendor",
"generated",
]
# Same as Black.
line-length = 79
indent-width = 4
# Assume Python 3.9
target-version = "py39"
[lint]
preview = true
pydocstyle.convention = "google"
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
select = ["E", "F", "W"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
exclude = [
"client/ayon_core/scripts/slates/__init__.py"
]
[lint.per-file-ignores]
"client/ayon_core/lib/__init__.py" = ["E402"]
[format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false
# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"

View file

@ -938,6 +938,20 @@ class IntegrateHeroVersionModel(BaseSettingsModel):
"hero versions.")
class CollectRenderedFilesModel(BaseSettingsModel):
remove_files: bool = SettingsField(
False,
title="Remove rendered files",
description=(
"Remove rendered files and metadata json on publish.\n\n"
"Note that when enabled but the render is to a configured "
"persistent staging directory the files will not be removed. "
"However with this disabled the files will **not** be removed in "
"either case."
)
)
class CleanUpModel(BaseSettingsModel):
_isGroup = True
paterns: list[str] = SettingsField( # codespell:ignore paterns
@ -1050,6 +1064,10 @@ class PublishPuginsModel(BaseSettingsModel):
"published as a render/review product of its own."
)
)
CollectRenderedFiles: CollectRenderedFilesModel = SettingsField(
default_factory=CollectRenderedFilesModel,
title="Clean up farm rendered files"
)
CleanUp: CleanUpModel = SettingsField(
default_factory=CleanUpModel,
title="Clean Up"
@ -1437,6 +1455,9 @@ DEFAULT_PUBLISH_VALUES = {
"AttachReviewables": {
"enabled": True,
},
"CollectRenderedFiles": {
"remove_files": False
},
"CleanUp": {
"paterns": [], # codespell:ignore paterns
"remove_temp_renders": False

View file

@ -101,6 +101,7 @@ def test_image_sequence():
expected_data,
)
def test_media_retimed():
"""
EXR image sequence.

View file

@ -215,6 +215,7 @@ def test_short_movie_tail_gap_handles():
assert calls == expected
def test_multiple_review_clips_no_gap():
"""
Use multiple review clips (image sequence).
@ -298,6 +299,7 @@ def test_multiple_review_clips_no_gap():
assert calls == expected
def test_multiple_review_clips_with_gap():
"""
Use multiple review clips (image sequence) with gap.

View file

@ -257,7 +257,6 @@ def test_movie_timewarp():
)
def test_img_sequence_no_handles():
"""
Img sequence clip (no embedded timecode)
@ -334,6 +333,7 @@ def test_img_sequence_relative_source_range():
expected_data
)
def test_img_sequence_conform_to_23_976fps():
"""
Img sequence clip
@ -409,6 +409,7 @@ def test_img_sequence_reverse_speed_no_tc():
handle_end=0,
)
def test_img_sequence_reverse_speed_from_24_to_23_976fps():
"""
Img sequence clip