diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index b9ecff4233..308494b4d8 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -9,9 +9,8 @@ import logging import threading import collections from uuid import uuid4 -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod -import six import appdirs import ayon_api from semver import VersionInfo @@ -499,8 +498,7 @@ def is_func_marked(func): return getattr(func, _MARKING_ATTR, False) -@six.add_metaclass(ABCMeta) -class AYONAddon(object): +class AYONAddon(ABC): """Base class of AYON addon. Attributes: diff --git a/client/ayon_core/addon/interfaces.py b/client/ayon_core/addon/interfaces.py index 86e0c6e060..b273e7839b 100644 --- a/client/ayon_core/addon/interfaces.py +++ b/client/ayon_core/addon/interfaces.py @@ -1,7 +1,5 @@ from abc import ABCMeta, abstractmethod -import six - from ayon_core import resources @@ -15,8 +13,7 @@ class _AYONInterfaceMeta(ABCMeta): return str(self) -@six.add_metaclass(_AYONInterfaceMeta) -class AYONInterface: +class AYONInterface(metaclass=_AYONInterfaceMeta): """Base class of Interface that can be used as Mixin with abstract parts. This is way how AYON addon can define that contains specific predefined diff --git a/client/ayon_core/host/dirmap.py b/client/ayon_core/host/dirmap.py index b90b414240..19841845e7 100644 --- a/client/ayon_core/host/dirmap.py +++ b/client/ayon_core/host/dirmap.py @@ -7,18 +7,15 @@ exists is used. """ import os -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod import platform -import six - from ayon_core.lib import Logger from ayon_core.addon import AddonsManager from ayon_core.settings import get_project_settings -@six.add_metaclass(ABCMeta) -class HostDirmap(object): +class HostDirmap(ABC): """Abstract class for running dirmap on a workfile in a host. Dirmap is used to translate paths inside of host workfile from one diff --git a/client/ayon_core/host/host.py b/client/ayon_core/host/host.py index 081aafdbe3..5a29de6cd7 100644 --- a/client/ayon_core/host/host.py +++ b/client/ayon_core/host/host.py @@ -1,15 +1,13 @@ import os import logging import contextlib -from abc import ABCMeta, abstractproperty -import six +from abc import ABC, abstractproperty # NOTE can't import 'typing' because of issues in Maya 2020 # - shiboken crashes on 'typing' module import -@six.add_metaclass(ABCMeta) -class HostBase(object): +class HostBase(ABC): """Base of host implementation class. Host is pipeline implementation of DCC application. This class should help diff --git a/client/ayon_core/host/interfaces.py b/client/ayon_core/host/interfaces.py index 7157ad6f7e..c077dfeae9 100644 --- a/client/ayon_core/host/interfaces.py +++ b/client/ayon_core/host/interfaces.py @@ -1,5 +1,4 @@ -from abc import ABCMeta, abstractmethod -import six +from abc import ABC, abstractmethod class MissingMethodsError(ValueError): @@ -106,8 +105,7 @@ class ILoadHost: return self.get_containers() -@six.add_metaclass(ABCMeta) -class IWorkfileHost: +class IWorkfileHost(ABC): """Implementation requirements to be able use workfile utils and tool.""" @staticmethod diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 0a9d38ab65..360d47ea17 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -6,7 +6,6 @@ import json import copy from abc import ABCMeta, abstractmethod, abstractproperty -import six import clique # Global variable which store attribute definitions by type @@ -91,8 +90,7 @@ class AbstractAttrDefMeta(ABCMeta): return obj -@six.add_metaclass(AbstractAttrDefMeta) -class AbstractAttrDef(object): +class AbstractAttrDef(metaclass=AbstractAttrDefMeta): """Abstraction of attribute definition. Each attribute definition must have implemented validation and @@ -349,7 +347,7 @@ class NumberDef(AbstractAttrDef): ) def convert_value(self, value): - if isinstance(value, six.string_types): + if isinstance(value, str): try: value = float(value) except Exception: @@ -396,12 +394,12 @@ class TextDef(AbstractAttrDef): if multiline is None: multiline = False - elif not isinstance(default, six.string_types): + elif not isinstance(default, str): raise TypeError(( - "'default' argument must be a {}, not '{}'" - ).format(six.string_types, type(default))) + f"'default' argument must be a str, not '{type(default)}'" + )) - if isinstance(regex, six.string_types): + if isinstance(regex, str): regex = re.compile(regex) self.multiline = multiline @@ -418,7 +416,7 @@ class TextDef(AbstractAttrDef): ) def convert_value(self, value): - if isinstance(value, six.string_types): + if isinstance(value, str): return value return self.default @@ -736,7 +734,7 @@ class FileDefItem(object): else: output.append(item) - elif isinstance(item, six.string_types): + elif isinstance(item, str): str_filepaths.append(item) else: raise TypeError( @@ -844,7 +842,7 @@ class FileDef(AbstractAttrDef): if isinstance(default, dict): FileDefItem.from_dict(default) - elif isinstance(default, six.string_types): + elif isinstance(default, str): default = FileDefItem.from_paths([default.strip()])[0] else: @@ -883,14 +881,14 @@ class FileDef(AbstractAttrDef): ) def convert_value(self, value): - if isinstance(value, six.string_types) or isinstance(value, dict): + if isinstance(value, (str, dict)): value = [value] if isinstance(value, (tuple, list, set)): string_paths = [] dict_items = [] for item in value: - if isinstance(item, six.string_types): + if isinstance(item, str): string_paths.append(item.strip()) elif isinstance(item, dict): try: diff --git a/client/ayon_core/lib/file_transaction.py b/client/ayon_core/lib/file_transaction.py index 81a3b386f6..47b10dd994 100644 --- a/client/ayon_core/lib/file_transaction.py +++ b/client/ayon_core/lib/file_transaction.py @@ -2,7 +2,6 @@ import os import logging import sys import errno -import six from ayon_core.lib import create_hard_link @@ -158,11 +157,13 @@ class FileTransaction(object): def rollback(self): errors = 0 + last_exc = None # Rollback any transferred files for path in self._transferred: try: os.remove(path) - except OSError: + except OSError as exc: + last_exc = exc errors += 1 self.log.error( "Failed to rollback created file: {}".format(path), @@ -172,7 +173,8 @@ class FileTransaction(object): for backup, original in self._backup_to_original.items(): try: os.rename(backup, original) - except OSError: + except OSError as exc: + last_exc = exc errors += 1 self.log.error( "Failed to restore original file: {} -> {}".format( @@ -183,7 +185,7 @@ class FileTransaction(object): self.log.error( "{} errors occurred during rollback.".format(errors), exc_info=True) - six.reraise(*sys.exc_info()) + raise last_exc @property def transferred(self): @@ -200,11 +202,9 @@ class FileTransaction(object): try: os.makedirs(dirname) except OSError as e: - if e.errno == errno.EEXIST: - pass - else: + if e.errno != errno.EEXIST: self.log.critical("An unexpected error occurred.") - six.reraise(*sys.exc_info()) + raise e def _same_paths(self, src, dst): # handles same paths but with C:/project vs c:/project diff --git a/client/ayon_core/lib/local_settings.py b/client/ayon_core/lib/local_settings.py index fd255c997f..54432265d9 100644 --- a/client/ayon_core/lib/local_settings.py +++ b/client/ayon_core/lib/local_settings.py @@ -4,7 +4,7 @@ import os import json import platform from datetime import datetime -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod # disable lru cache in Python 2 try: @@ -24,7 +24,6 @@ try: except ImportError: import ConfigParser as configparser -import six import appdirs import ayon_api @@ -133,8 +132,7 @@ class AYONSecureRegistry: keyring.delete_password(self._name, name) -@six.add_metaclass(ABCMeta) -class ASettingRegistry(): +class ASettingRegistry(ABC): """Abstract class defining structure of **SettingRegistry** class. It is implementing methods to store secure items into keyring, otherwise diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index a766dbd9c1..01a6985a25 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -2,8 +2,6 @@ import os import re import numbers -import six - KEY_PATTERN = re.compile(r"(\{.*?[^{0]*\})") KEY_PADDING_PATTERN = re.compile(r"([^:]+)\S+[><]\S+") SUB_DICT_PATTERN = re.compile(r"([^\[\]]+)") @@ -14,7 +12,7 @@ class TemplateUnsolved(Exception): """Exception for unsolved template when strict is set to True.""" msg = "Template \"{0}\" is unsolved.{1}{2}" - invalid_types_msg = " Keys with invalid DataType: `{0}`." + invalid_types_msg = " Keys with invalid data type: `{0}`." missing_keys_msg = " Missing keys: \"{0}\"." def __init__(self, template, missing_keys, invalid_types): @@ -43,7 +41,7 @@ class TemplateUnsolved(Exception): class StringTemplate(object): """String that can be formatted.""" def __init__(self, template): - if not isinstance(template, six.string_types): + if not isinstance(template, str): raise TypeError("<{}> argument must be a string, not {}.".format( self.__class__.__name__, str(type(template)) )) @@ -63,7 +61,7 @@ class StringTemplate(object): new_parts = [] for part in parts: - if not isinstance(part, six.string_types): + if not isinstance(part, str): new_parts.append(part) continue @@ -113,7 +111,7 @@ class StringTemplate(object): """ result = TemplatePartResult() for part in self._parts: - if isinstance(part, six.string_types): + if isinstance(part, str): result.add_output(part) else: part.format(data, result) @@ -176,7 +174,7 @@ class StringTemplate(object): value = "<>" elif ( len(parts) == 1 - and isinstance(parts[0], six.string_types) + and isinstance(parts[0], str) ): value = "<{}>".format(parts[0]) else: @@ -200,8 +198,9 @@ class StringTemplate(object): new_parts.extend(tmp_parts[idx]) return new_parts + class TemplateResult(str): - """Result of template format with most of information in. + """Result of template format with most of the information in. Args: used_values (dict): Dictionary of template filling data with @@ -299,7 +298,7 @@ class TemplatePartResult: self._optional = True def add_output(self, other): - if isinstance(other, six.string_types): + if isinstance(other, str): self._output += other elif isinstance(other, TemplatePartResult): @@ -457,7 +456,7 @@ class FormattingPart: return True for inh_class in type(value).mro(): - if inh_class in six.string_types: + if inh_class is str: return True return False @@ -568,7 +567,7 @@ class OptionalPart: def format(self, data, result): new_result = TemplatePartResult(True) for part in self._parts: - if isinstance(part, six.string_types): + if isinstance(part, str): new_result.add_output(part) else: part.format(data, new_result) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 0e6025ad3b..624f1c9588 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -3,9 +3,7 @@ import copy import collections from typing import TYPE_CHECKING, Optional -from abc import ABCMeta, abstractmethod - -import six +from abc import ABC, abstractmethod from ayon_core.settings import get_project_settings from ayon_core.lib import Logger @@ -38,8 +36,7 @@ class CreatorError(Exception): super(CreatorError, self).__init__(message) -@six.add_metaclass(ABCMeta) -class ProductConvertorPlugin(object): +class ProductConvertorPlugin(ABC): """Helper for conversion of instances created using legacy creators. Conversion from legacy creators would mean to lose legacy instances, @@ -152,8 +149,7 @@ class ProductConvertorPlugin(object): self._create_context.remove_convertor_item(self.identifier) -@six.add_metaclass(ABCMeta) -class BaseCreator: +class BaseCreator(ABC): """Plugin that create and modify instance data before publishing process. We should maybe find better name as creation is only one part of its logic diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 811a98ce4b..902b969457 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -2,8 +2,6 @@ import os import re import json -import six - from ayon_core.settings import get_project_settings from ayon_core.lib import Logger @@ -109,6 +107,6 @@ def get_project_basic_paths(project_name): if not folder_structure: return [] - if isinstance(folder_structure, six.string_types): + if isinstance(folder_structure, str): folder_structure = json.loads(folder_structure) return _list_path_items(folder_structure) diff --git a/client/ayon_core/pipeline/publish/abstract_collect_render.py b/client/ayon_core/pipeline/publish/abstract_collect_render.py index 17cab876b6..bd2d76c39c 100644 --- a/client/ayon_core/pipeline/publish/abstract_collect_render.py +++ b/client/ayon_core/pipeline/publish/abstract_collect_render.py @@ -7,8 +7,6 @@ TODO: use @dataclass when times come. from abc import abstractmethod import attr -import six - import pyblish.api from .publish_plugins import AbstractMetaContextPlugin @@ -122,8 +120,9 @@ class RenderInstance(object): raise ValueError("both tiles X a Y sizes are set to 1") -@six.add_metaclass(AbstractMetaContextPlugin) -class AbstractCollectRender(pyblish.api.ContextPlugin): +class AbstractCollectRender( + pyblish.api.ContextPlugin, metaclass=AbstractMetaContextPlugin +): """Gather all publishable render layers from renderSetup.""" order = pyblish.api.CollectorOrder + 0.01 diff --git a/client/ayon_core/pipeline/publish/abstract_expected_files.py b/client/ayon_core/pipeline/publish/abstract_expected_files.py index f9f3c17ef5..fffe723739 100644 --- a/client/ayon_core/pipeline/publish/abstract_expected_files.py +++ b/client/ayon_core/pipeline/publish/abstract_expected_files.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- """Abstract ExpectedFile class definition.""" -from abc import ABCMeta, abstractmethod -import six +from abc import ABC, abstractmethod -@six.add_metaclass(ABCMeta) -class ExpectedFiles: +class ExpectedFiles(ABC): """Class grouping functionality for all supported renderers. Attributes: diff --git a/client/ayon_core/pipeline/schema/__init__.py b/client/ayon_core/pipeline/schema/__init__.py index db98a6d080..d16755696d 100644 --- a/client/ayon_core/pipeline/schema/__init__.py +++ b/client/ayon_core/pipeline/schema/__init__.py @@ -17,7 +17,6 @@ import json import logging import jsonschema -import six log_ = logging.getLogger(__name__) @@ -44,7 +43,7 @@ def validate(data, schema=None): root, schema = data["schema"].rsplit(":", 1) - if isinstance(schema, six.string_types): + if isinstance(schema, str): schema = _cache[schema + ".json"] resolver = jsonschema.RefResolver( diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 467a24a52f..7b15dff049 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -15,9 +15,8 @@ import os import re import collections import copy -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod -import six from ayon_api import ( get_folders, get_folder_by_path, @@ -82,8 +81,7 @@ class TemplateLoadFailed(Exception): pass -@six.add_metaclass(ABCMeta) -class AbstractTemplateBuilder(object): +class AbstractTemplateBuilder(ABC): """Abstraction of Template Builder. Builder cares about context, shared data, cache, discovery of plugins @@ -941,8 +939,7 @@ class AbstractTemplateBuilder(object): ) -@six.add_metaclass(ABCMeta) -class PlaceholderPlugin(object): +class PlaceholderPlugin(ABC): """Plugin which care about handling of placeholder items logic. Plugin create and update placeholders in scene and populate them on diff --git a/client/ayon_core/plugins/load/delete_old_versions.py b/client/ayon_core/plugins/load/delete_old_versions.py index 62302e7123..f8c45baff6 100644 --- a/client/ayon_core/plugins/load/delete_old_versions.py +++ b/client/ayon_core/plugins/load/delete_old_versions.py @@ -1,6 +1,7 @@ import collections import os import uuid +from typing import List, Dict, Any import clique import ayon_api @@ -41,11 +42,13 @@ class DeleteOldVersions(load.ProductLoaderPlugin): ) ] + requires_confirmation = True + def delete_whole_dir_paths(self, dir_paths, delete=True): size = 0 for dir_path in dir_paths: - # Delete all files and fodlers in dir path + # Delete all files and folders in dir path for root, dirs, files in os.walk(dir_path, topdown=False): for name in files: file_path = os.path.join(root, name) @@ -192,6 +195,42 @@ class DeleteOldVersions(load.ProductLoaderPlugin): ) msgBox.exec_() + def _confirm_delete(self, + contexts: List[Dict[str, Any]], + versions_to_keep: int) -> bool: + """Prompt user for a deletion confirmation""" + + contexts_list = "\n".join(sorted( + "- {folder[name]} > {product[name]}".format_map(context) + for context in contexts + )) + num_contexts = len(contexts) + s = "s" if num_contexts > 1 else "" + text = ( + "Are you sure you want to delete versions?\n\n" + 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" + detailed_text = ( + f"Keep only {versions_to_keep} versions for:\n{contexts_list}" + ) + + messagebox = QtWidgets.QMessageBox() + messagebox.setIcon(QtWidgets.QMessageBox.Warning) + messagebox.setWindowTitle("Delete Old Versions") + messagebox.setText(text) + messagebox.setInformativeText(informative_text) + messagebox.setDetailedText(detailed_text) + messagebox.setStandardButtons( + QtWidgets.QMessageBox.Yes + | QtWidgets.QMessageBox.Cancel + ) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Cancel) + messagebox.setStyleSheet(style.load_stylesheet()) + messagebox.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) + return messagebox.exec_() == QtWidgets.QMessageBox.Yes + def get_data(self, context, versions_count): product_entity = context["product"] folder_entity = context["folder"] @@ -365,19 +404,29 @@ class DeleteOldVersions(load.ProductLoaderPlugin): return size def load(self, contexts, name=None, namespace=None, options=None): + + # Get user options + versions_to_keep = 2 + remove_publish_folder = False + if options: + versions_to_keep = options.get( + "versions_to_keep", versions_to_keep + ) + remove_publish_folder = options.get( + "remove_publish_folder", remove_publish_folder + ) + + # Because we do not want this run by accident we will add an extra + # user confirmation + if ( + self.requires_confirmation + and not self._confirm_delete(contexts, versions_to_keep) + ): + return + try: size = 0 for count, context in enumerate(contexts): - versions_to_keep = 2 - remove_publish_folder = False - if options: - versions_to_keep = options.get( - "versions_to_keep", versions_to_keep - ) - remove_publish_folder = options.get( - "remove_publish_folder", remove_publish_folder - ) - data = self.get_data(context, versions_to_keep) if not data: continue @@ -408,6 +457,8 @@ class CalculateOldVersions(DeleteOldVersions): ) ] + requires_confirmation = False + def main(self, project_name, data, remove_publish_folder): size = 0 diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 93774842ca..58a032a030 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -6,7 +6,6 @@ import platform import shutil import clique -import six import pyblish.api from ayon_core import resources, AYON_CORE_ROOT @@ -456,7 +455,7 @@ class ExtractBurnin(publish.Extractor): sys_name = platform.system().lower() font_filepath = font_filepath.get(sys_name) - if font_filepath and isinstance(font_filepath, six.string_types): + if font_filepath and isinstance(font_filepath, str): font_filepath = font_filepath.format(**os.environ) if not os.path.exists(font_filepath): font_filepath = None diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 1891c25521..c2793f98a2 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -4,9 +4,8 @@ import copy import json import shutil import subprocess -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod -import six import clique import speedcopy import pyblish.api @@ -1661,8 +1660,7 @@ class ExtractReview(pyblish.api.InstancePlugin): return vf_back -@six.add_metaclass(ABCMeta) -class _OverscanValue: +class _OverscanValue(ABC): def __repr__(self): return "<{}> {}".format(self.__class__.__name__, str(self)) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 2da33bfb19..a2cf910fa6 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -4,7 +4,6 @@ import sys import copy import clique -import six import pyblish.api from ayon_api import ( get_attributes_for_type, @@ -160,15 +159,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Raise DuplicateDestinationError as KnownPublishError # and rollback the transactions file_transactions.rollback() - six.reraise(KnownPublishError, - KnownPublishError(exc), - sys.exc_info()[2]) - except Exception: + raise KnownPublishError(exc).with_traceback(sys.exc_info()[2]) + + except Exception as exc: # clean destination # todo: preferably we'd also rollback *any* changes to the database file_transactions.rollback() self.log.critical("Error when registering", exc_info=True) - six.reraise(*sys.exc_info()) + raise exc # Finalizing can't rollback safely so no use for moving it to # the try, except. diff --git a/client/ayon_core/style/__init__.py b/client/ayon_core/style/__init__.py index 8d3089ef86..064f527f2b 100644 --- a/client/ayon_core/style/__init__.py +++ b/client/ayon_core/style/__init__.py @@ -2,7 +2,6 @@ import os import copy import json import collections -import six from ayon_core import resources @@ -75,7 +74,7 @@ def _convert_color_values_to_objects(value): output[_key] = _convert_color_values_to_objects(_value) return output - if not isinstance(value, six.string_types): + if not isinstance(value, str): raise TypeError(( "Unexpected type in colors data '{}'. Expected 'str' or 'dict'." ).format(str(type(value)))) diff --git a/client/ayon_core/tools/common_models/hierarchy.py b/client/ayon_core/tools/common_models/hierarchy.py index f92563db20..6bccb0f468 100644 --- a/client/ayon_core/tools/common_models/hierarchy.py +++ b/client/ayon_core/tools/common_models/hierarchy.py @@ -1,18 +1,16 @@ import time import collections import contextlib -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod import ayon_api -import six from ayon_core.lib import NestedCacheItem HIERARCHY_MODEL_SENDER = "hierarchy.model" -@six.add_metaclass(ABCMeta) -class AbstractHierarchyController: +class AbstractHierarchyController(ABC): @abstractmethod def emit_event(self, topic, data, source): pass diff --git a/client/ayon_core/tools/launcher/abstract.py b/client/ayon_core/tools/launcher/abstract.py index 921fe7bc5b..63ba4cd717 100644 --- a/client/ayon_core/tools/launcher/abstract.py +++ b/client/ayon_core/tools/launcher/abstract.py @@ -1,10 +1,7 @@ -from abc import ABCMeta, abstractmethod - -import six +from abc import ABC, abstractmethod -@six.add_metaclass(ABCMeta) -class AbstractLauncherCommon(object): +class AbstractLauncherCommon(ABC): @abstractmethod def register_event_callback(self, topic, callback): """Register event callback. diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index 3a1a23edd7..6a68af1eb5 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -1,5 +1,4 @@ -from abc import ABCMeta, abstractmethod -import six +from abc import ABC, abstractmethod from ayon_core.lib.attribute_definitions import ( AbstractAttrDef, @@ -347,8 +346,7 @@ class ActionItem: return cls(**data) -@six.add_metaclass(ABCMeta) -class _BaseLoaderController(object): +class _BaseLoaderController(ABC): """Base loader controller abstraction. Abstract base class that is required for both frontend and backed. diff --git a/client/ayon_core/tools/publisher/publish_report_viewer/window.py b/client/ayon_core/tools/publisher/publish_report_viewer/window.py index 3ee986e6f7..aedc3b9e31 100644 --- a/client/ayon_core/tools/publisher/publish_report_viewer/window.py +++ b/client/ayon_core/tools/publisher/publish_report_viewer/window.py @@ -1,6 +1,5 @@ import os import json -import six import uuid import appdirs @@ -387,7 +386,7 @@ class LoadedFilesModel(QtGui.QStandardItemModel): if not filepaths: return - if isinstance(filepaths, six.string_types): + if isinstance(filepaths, str): filepaths = [filepaths] filtered_paths = [] diff --git a/client/ayon_core/tools/workfiles/abstract.py b/client/ayon_core/tools/workfiles/abstract.py index e949915ab2..b78e987032 100644 --- a/client/ayon_core/tools/workfiles/abstract.py +++ b/client/ayon_core/tools/workfiles/abstract.py @@ -1,7 +1,6 @@ import os -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod -import six from ayon_core.style import get_default_entity_icon_color @@ -335,8 +334,7 @@ class WorkareaFilepathResult: self.filepath = filepath -@six.add_metaclass(ABCMeta) -class AbstractWorkfilesCommon(object): +class AbstractWorkfilesCommon(ABC): @abstractmethod def is_host_valid(self): """Host is valid for workfiles tool work.