2424#
2525###############################################################################
2626import abc
27+ import os
28+ from typing import Optional
2729
2830from pydantic import BaseModel
2931
@@ -37,12 +39,103 @@ class CommandArtifact(BaseModel):
3739 exit_code : int
3840
3941
40- class FileArtifact (BaseModel ):
41- """Artifact to contains contents of file read into memory """
42+ class BaseFileArtifact (BaseModel , abc . ABC ):
43+ """Base class for files """
4244
4345 filename : str
46+
47+ @abc .abstractmethod
48+ def log_model (self , log_path : str ) -> None :
49+ """Write file to path
50+
51+ Args:
52+ log_path (str): Path for file
53+ """
54+ pass
55+
56+ @abc .abstractmethod
57+ def contents_str (self ) -> str :
58+ pass
59+
60+ @classmethod
61+ def from_bytes (
62+ cls ,
63+ filename : str ,
64+ raw_contents : bytes ,
65+ encoding : Optional [str ] = "utf-8" ,
66+ strip : bool = True ,
67+ ) -> "BaseFileArtifact" :
68+ """factory method
69+
70+ Args:
71+ filename (str): name of file to be read
72+ raw_contents (bytes): Raw file content
73+ encoding (Optional[str], optional): Optional encoding. Defaults to "utf-8".
74+ strip (bool, optional): Remove padding. Defaults to True.
75+
76+ Returns:
77+ BaseFileArtifact: _Returns instance of Artifact file
78+ """
79+ if encoding is None :
80+ return BinaryFileArtifact (filename = filename , contents = raw_contents )
81+
82+ try :
83+ text = raw_contents .decode (encoding )
84+ return TextFileArtifact (filename = filename , contents = text .strip () if strip else text )
85+ except UnicodeDecodeError :
86+ return BinaryFileArtifact (filename = filename , contents = raw_contents )
87+
88+
89+ class TextFileArtifact (BaseFileArtifact ):
90+ """Class for text file artifacts"""
91+
4492 contents : str
4593
94+ def log_model (self , log_path : str ) -> None :
95+ """Write file to disk
96+
97+ Args:
98+ log_path (str): Path for file
99+ """
100+ path = os .path .join (log_path , self .filename )
101+ with open (path , "w" , encoding = "utf-8" ) as f :
102+ f .write (self .contents )
103+
104+ def contents_str (self ) -> str :
105+ """Get content as str
106+
107+ Returns:
108+ str: Str instance of file content
109+ """
110+ return self .contents
111+
112+
113+ class BinaryFileArtifact (BaseFileArtifact ):
114+ """Class for binary file artifacts"""
115+
116+ contents : bytes
117+
118+ def log_model (self , log_path : str ) -> None :
119+ """Write file to disk
120+
121+ Args:
122+ log_path (str): Path for file
123+ """
124+ log_name = os .path .join (log_path , self .filename )
125+ with open (log_name , "wb" ) as f :
126+ f .write (self .contents )
127+
128+ def contents_str (self ) -> str :
129+ """File content
130+
131+ Returns:
132+ str: Str instance of file content
133+ """
134+ try :
135+ return self .contents .decode ("utf-8" )
136+ except UnicodeDecodeError :
137+ return f"<binary data: { len (self .contents )} bytes>"
138+
46139
47140class InBandConnection (abc .ABC ):
48141
@@ -63,14 +156,16 @@ def run_command(
63156 """
64157
65158 @abc .abstractmethod
66- def read_file (self , filename : str , encoding : str = "utf-8" , strip : bool = True ) -> FileArtifact :
67- """Read a file into a FileArtifact
159+ def read_file (
160+ self , filename : str , encoding : str = "utf-8" , strip : bool = True
161+ ) -> BaseFileArtifact :
162+ """Read a file into a BaseFileArtifact
68163
69164 Args:
70165 filename (str): filename
71166 encoding (str, optional): encoding to use when opening file. Defaults to "utf-8".
72167 strip (bool): automatically strip file contents
73168
74169 Returns:
75- FileArtifact : file artifact
170+ BaseFileArtifact : file artifact
76171 """
0 commit comments