11from __future__ import annotations
22
3- from typing import TYPE_CHECKING , Any , ClassVar , Protocol
3+ from typing import TYPE_CHECKING , Any , ClassVar
44
55if TYPE_CHECKING :
66 from pathlib import Path
77
8- from sift_client .client import SiftClient
8+ from sift_client .sift_types . _base import BaseTypeProtocol
99 from sift_client .sift_types .file_attachment import FileAttachment
1010
1111
12- class _SupportsFileAttachments (Protocol ):
13- """Protocol for types that support file attachments."""
14-
15- @property
16- def client (self ) -> SiftClient : ...
17-
18- @property
19- def id_ (self ) -> str | None : ...
20-
21-
2212class FileAttachmentsMixin :
2313 """Mixin for sift_types that support file attachments (remote files).
2414
2515 This mixin assumes the class also inherits from BaseType, which provides:
2616 - id_: str | None
2717 - client: SiftClient property
28-
29- The entity type is automatically determined from the class name:
30- - Asset -> assets
31- - Run -> runs
32- - TestReport -> test_reports
3318 """
3419
3520 # Mapping of class names to entity types (REST API format)
3621 _ENTITY_TYPE_MAP : ClassVar [dict [str , str ]] = {
3722 "Asset" : "assets" ,
3823 "Run" : "runs" ,
3924 "TestReport" : "test_reports" ,
25+ "TestStep" : "test_steps" ,
4026 }
4127
28+ @staticmethod
29+ def check_is_supported_entity_type (cls ):
30+ """Check if the entity type is supported for file attachments.
31+
32+ Returns:
33+ True if the entity type is supported, False otherwise.
34+ """
35+ if not cls .__class__ .__name__ in FileAttachmentsMixin ._ENTITY_TYPE_MAP :
36+ raise ValueError (f"{ cls .__name__ } does not support file attachments" )
37+
4238 def _get_entity_type_name (self ) -> str :
4339 """Get the entity type string.
4440
@@ -49,7 +45,7 @@ def _get_entity_type_name(self) -> str:
4945 ValueError: If the class name is not in the entity type mapping.
5046 """
5147 class_name = self .__class__ .__name__
52- entity_type = self ._ENTITY_TYPE_MAP .get (class_name )
48+ entity_type = FileAttachmentsMixin ._ENTITY_TYPE_MAP .get (self . __class__ . __name__ )
5349
5450 if not entity_type :
5551 raise ValueError (
@@ -60,24 +56,19 @@ def _get_entity_type_name(self) -> str:
6056 return entity_type
6157
6258 @property
63- def attachments (self : _SupportsFileAttachments ) -> list [FileAttachment ]:
59+ def attachments (self : BaseTypeProtocol ) -> list [FileAttachment ]:
6460 """Get all file attachments for this entity.
6561
6662 Returns:
6763 A list of FileAttachments associated with this entity.
6864 """
69- from sift_client .sift_types .asset import Asset
70- from sift_client .sift_types .run import Run
71- from sift_client .sift_types .test_report import TestReport
72-
73- if not isinstance (self , (Asset , Run , TestReport )):
74- raise ValueError ("Entity is not a valid entity type" )
65+ FileAttachmentsMixin .check_is_supported_entity_type (self )
7566 return self .client .file_attachments .list_ (
76- entities = [self ],
67+ entities = [self ], # type: ignore
7768 )
7869
7970 def delete_attachment (
80- self : _SupportsFileAttachments ,
71+ self : BaseTypeProtocol ,
8172 file_attachment : list [FileAttachment | str ] | FileAttachment | str ,
8273 ) -> None :
8374 """Delete one or more file attachments.
@@ -88,7 +79,7 @@ def delete_attachment(
8879 self .client .file_attachments .delete (file_attachments = file_attachment )
8980
9081 def upload_attachment (
91- self : _SupportsFileAttachments ,
82+ self : BaseTypeProtocol ,
9283 path : str | Path ,
9384 metadata : dict [str , Any ] | None = None ,
9485 description : str | None = None ,
@@ -105,15 +96,10 @@ def upload_attachment(
10596 Returns:
10697 The uploaded FileAttachment.
10798 """
108- from sift_client .sift_types .asset import Asset
109- from sift_client .sift_types .run import Run
110- from sift_client .sift_types .test_report import TestReport
111-
112- if not isinstance (self , (Asset , Run , TestReport )):
113- raise ValueError ("Entity is not a valid entity type" )
99+ FileAttachmentsMixin .check_is_supported_entity_type (self )
114100 return self .client .file_attachments .upload (
115101 path = path ,
116- entity = self ,
102+ entity = self , # type: ignore
117103 metadata = metadata ,
118104 description = description ,
119105 organization_id = organization_id ,
0 commit comments