diff --git a/client/ayon_core/addon/interfaces.py b/client/ayon_core/addon/interfaces.py index 303cdf3941..fa7acb6380 100644 --- a/client/ayon_core/addon/interfaces.py +++ b/client/ayon_core/addon/interfaces.py @@ -83,6 +83,10 @@ class IPluginPaths(AYONInterface): """Receive launcher actions paths. Give addons ability to add launcher actions paths. + + Returns: + list[str]: List of launcher action paths. + """ return self._get_plugin_paths_by_type("actions") @@ -98,6 +102,9 @@ class IPluginPaths(AYONInterface): Args: host_name (str): For which host are the plugins meant. + Returns: + list[str]: List of create plugin paths. + """ return self._get_plugin_paths_by_type("create") @@ -113,6 +120,9 @@ class IPluginPaths(AYONInterface): Args: host_name (str): For which host are the plugins meant. + Returns: + list[str]: List of load plugin paths. + """ return self._get_plugin_paths_by_type("load") @@ -128,6 +138,9 @@ class IPluginPaths(AYONInterface): Args: host_name (str): For which host are the plugins meant. + Returns: + list[str]: List of publish plugin paths. + """ return self._get_plugin_paths_by_type("publish") @@ -143,6 +156,9 @@ class IPluginPaths(AYONInterface): Args: host_name (str): For which host are the plugins meant. + Returns: + list[str]: List of inventory action plugin paths. + """ return self._get_plugin_paths_by_type("inventory") @@ -236,7 +252,12 @@ class ITrayAddon(AYONInterface): @staticmethod def admin_submenu(tray_menu: QtWidgets.QMenu) -> QtWidgets.QMenu: - """Get or create admin submenu.""" + """Get or create admin submenu. + + Returns: + QtWidgets.QMenu: Admin submenu. + + """ if ITrayAddon._admin_submenu is None: from qtpy import QtWidgets @@ -248,7 +269,16 @@ class ITrayAddon(AYONInterface): @staticmethod def add_action_to_admin_submenu( label: str, tray_menu: QtWidgets.QMenu) -> QtWidgets.QAction: - """Add action to admin submenu.""" + """Add action to admin submenu. + + Args: + label (str): Label of action. + tray_menu (QtWidgets.QMenu): Tray menu to add action to. + + Returns: + QtWidgets.QAction: Action added to admin submenu + + """ from qtpy import QtWidgets menu = ITrayAddon.admin_submenu(tray_menu) diff --git a/client/ayon_core/pipeline/traits/lifecycle.py b/client/ayon_core/pipeline/traits/lifecycle.py index be87a86cbc..2de90824e9 100644 --- a/client/ayon_core/pipeline/traits/lifecycle.py +++ b/client/ayon_core/pipeline/traits/lifecycle.py @@ -26,8 +26,10 @@ class Transient(TraitBase): Args: representation (Representation): Representation model. - Returns: - bool: True if representation is valid, False otherwise. + Raises: + TraitValidationError: If representation is marked as both + Persistent and Transient. + """ if representation.contains_trait(Persistent): msg = "Representation is marked as both Persistent and Transient." @@ -57,6 +59,10 @@ class Persistent(TraitBase): Args: representation (Representation): Representation model. + Raises: + TraitValidationError: If representation is marked + as both Persistent and Transient. + """ if representation.contains_trait(Transient): msg = "Representation is marked as both Persistent and Transient." diff --git a/tests/client/ayon_core/pipeline/traits/test_content_traits.py b/tests/client/ayon_core/pipeline/traits/test_content_traits.py index 78b905bab4..c1c8dcc25d 100644 --- a/tests/client/ayon_core/pipeline/traits/test_content_traits.py +++ b/tests/client/ayon_core/pipeline/traits/test_content_traits.py @@ -1,6 +1,7 @@ """Tests for the content traits.""" from __future__ import annotations +import re from pathlib import Path import pytest @@ -59,9 +60,9 @@ def test_bundles() -> None: sub_representation = Representation(name="test", traits=item) assert sub_representation.contains_trait(trait=Image) sub: MimeType = sub_representation.get_trait(trait=MimeType) - assert sub.mime_type in [ + assert sub.mime_type in { "image/jpeg", "image/tiff" - ] + } def test_file_locations_validation() -> None: @@ -94,7 +95,7 @@ def test_file_locations_validation() -> None: ) representation.add_trait(frameranged_trait) - # it should still validate fine + # it should still validate fine file_locations_trait.validate_trait(representation) # create empty file locations trait @@ -165,7 +166,7 @@ def test_get_file_location_from_frame() -> None: # test with custom regex sequence = Sequence( frame_padding=4, - frame_regex=r"boo_(?P(?P0*)\d+)\.exr") + frame_regex=re.compile("boo_(?P(?P0*)\d+)\.exr")) file_locations_list = [ FileLocation( file_path=Path(f"/path/to/boo_{frame}.exr"), diff --git a/tests/client/ayon_core/pipeline/traits/test_time_traits.py b/tests/client/ayon_core/pipeline/traits/test_time_traits.py index 100e4ed2b5..28ace89910 100644 --- a/tests/client/ayon_core/pipeline/traits/test_time_traits.py +++ b/tests/client/ayon_core/pipeline/traits/test_time_traits.py @@ -1,6 +1,7 @@ """Tests for the time related traits.""" from __future__ import annotations +import re from pathlib import Path import pytest @@ -183,6 +184,26 @@ def test_sequence_validations() -> None: with pytest.raises(TraitValidationError): representation.validate() + representation = Representation(name="test_7", traits=[ + FileLocations(file_paths=[ + FileLocation( + file_path=Path(f"/path/to/file.{frame}.exr"), + file_size=1024, + file_hash=None, + ) + for frame in range(996, 1050 + 1) # because range is zero based + ]), + Sequence( + frame_padding=4, + frame_regex=re.compile( + r"img\.(?P(?P0*)\d{4})\.png$")), + Handles( + frame_start_handle=5, + frame_end_handle=5, + inclusive=False + ) + ]) + representation.validate() def test_list_spec_to_frames() -> None: @@ -204,7 +225,7 @@ def test_list_spec_to_frames() -> None: assert Sequence.list_spec_to_frames("1") == [1] with pytest.raises( ValueError, - match="Invalid frame number in the list: .*"): + match=r"Invalid frame number in the list: .*"): Sequence.list_spec_to_frames("a") @@ -225,4 +246,3 @@ def test_sequence_get_frame_padding() -> None: assert Sequence.get_frame_padding( file_locations=representation.get_trait(FileLocations)) == 4 - diff --git a/tests/client/ayon_core/pipeline/traits/test_traits.py b/tests/client/ayon_core/pipeline/traits/test_traits.py index 42933056fa..b990c074d3 100644 --- a/tests/client/ayon_core/pipeline/traits/test_traits.py +++ b/tests/client/ayon_core/pipeline/traits/test_traits.py @@ -36,19 +36,27 @@ REPRESENTATION_DATA: dict = { }, } + class UpgradedImage(Image): """Upgraded image class.""" id = "ayon.2d.Image.v2" @classmethod def upgrade(cls, data: dict) -> UpgradedImage: # noqa: ARG003 - """Upgrade the trait.""" + """Upgrade the trait. + + Returns: + UpgradedImage: Upgraded image instance. + + """ return cls() + class InvalidTrait: """Invalid trait class.""" foo = "bar" + @pytest.fixture def representation() -> Representation: """Return a traits data instance.""" @@ -59,10 +67,11 @@ def representation() -> Representation: Planar(**REPRESENTATION_DATA[Planar.id]), ]) + def test_representation_errors(representation: Representation) -> None: """Test errors in representation.""" with pytest.raises(ValueError, - match="Invalid trait .* - ID is required."): + match=r"Invalid trait .* - ID is required."): representation.add_trait(InvalidTrait()) with pytest.raises(ValueError, @@ -70,9 +79,10 @@ def test_representation_errors(representation: Representation) -> None: representation.add_trait(Image()) with pytest.raises(ValueError, - match="Trait with ID .* not found."): + match=r"Trait with ID .* not found."): representation.remove_trait_by_id("foo") + def test_representation_traits(representation: Representation) -> None: """Test setting and getting traits.""" assert representation.get_trait_by_id( @@ -143,11 +153,12 @@ def test_representation_traits(representation: Representation) -> None: assert representation.contains_traits_by_id( trait_ids=[FileLocation.id, Bundle.id]) is False + def test_trait_removing(representation: Representation) -> None: """Test removing traits.""" - assert representation.contains_trait_by_id("nonexistent") is False + assert representation.contains_trait_by_id("nonexistent") is False with pytest.raises( - ValueError, match="Trait with ID nonexistent not found."): + ValueError, match=r"Trait with ID nonexistent not found."): representation.remove_trait_by_id("nonexistent") assert representation.contains_trait(trait=FileLocation) is True @@ -168,6 +179,7 @@ def test_trait_removing(representation: Representation) -> None: ValueError, match=f"Trait with ID {Image.id} not found."): representation.remove_trait(Image) + def test_representation_dict_properties( representation: Representation) -> None: """Test representation as dictionary.""" @@ -224,6 +236,7 @@ def test_get_version_from_id() -> None: assert TestMimeType(mime_type="foo/bar").get_version() is None + def test_get_versionless_id() -> None: """Test getting versionless trait ID.""" assert Image().get_versionless_id() == "ayon.2d.Image" @@ -271,7 +284,7 @@ def test_from_dict() -> None: }, } - with pytest.raises(ValueError, match="Trait model with ID .* not found."): + with pytest.raises(ValueError, match=r"Trait model with ID .* not found."): representation = Representation.from_dict( "test", trait_data=traits_data) @@ -302,6 +315,7 @@ def test_from_dict() -> None: "test", trait_data=traits_data) """ + def test_representation_equality() -> None: """Test representation equality.""" # rep_a and rep_b are equal @@ -348,7 +362,6 @@ def test_representation_equality() -> None: Planar(planar_configuration="RGBA"), ]) - # lets assume ids are the same (because ids are randomly generated) rep_b.representation_id = rep_d.representation_id = rep_a.representation_id rep_c.representation_id = rep_e.representation_id = rep_a.representation_id @@ -365,6 +378,3 @@ def test_representation_equality() -> None: assert rep_d != rep_e # because of the trait difference assert rep_d != rep_f - - -