33from contextlib import contextmanager
44from datetime import timedelta
55from enum import Enum
6- from typing import ContextManager , Generator , Optional
6+ from typing import ContextManager , Generator , Literal , Optional
77
88import httpx
9- from pydantic import BaseModel , ValidationError
9+ from pydantic import BaseModel , Field , TypeAdapter , ValidationError
10+ from typing_extensions import Annotated
1011
1112from fastapi_cloud_cli import __version__
1213from fastapi_cloud_cli .config import Settings
1819BUILD_LOG_TIMEOUT = timedelta (minutes = 5 )
1920
2021
21- class BuildLogError (Exception ): ...
22+ class BuildLogError (Exception ):
23+ pass
2224
2325
2426class BuildLogType (str , Enum ):
@@ -30,11 +32,28 @@ class BuildLogType(str, Enum):
3032
3133
3234class BuildLogLine (BaseModel ):
33- type : BuildLogType
35+ type : Literal [
36+ BuildLogType .complete ,
37+ BuildLogType .failed ,
38+ BuildLogType .timeout ,
39+ BuildLogType .heartbeat ,
40+ ]
3441 message : Optional [str ] = None
3542 id : Optional [str ] = None
3643
3744
45+ class BuildLogLineMessage (BaseModel ):
46+ type : Literal [BuildLogType .message ] = BuildLogType .message
47+ message : str
48+ id : Optional [str ] = None
49+
50+
51+ # check pydantic v1
52+ BuildLogAdapter = TypeAdapter [BuildLogLineMessage | BuildLogLine ](
53+ Annotated [BuildLogLineMessage | BuildLogLine , Field (discriminator = "type" )]
54+ )
55+
56+
3857@contextmanager
3958def attempt (attempt_number : int ) -> Generator [None , None , None ]:
4059 def _backoff () -> None :
@@ -108,7 +127,7 @@ def __init__(self) -> None:
108127
109128 def stream_build_logs (
110129 self , deployment_id : str
111- ) -> Generator [BuildLogLine , None , None ]:
130+ ) -> Generator [BuildLogLineMessage | BuildLogLine , None , None ]:
112131 last_id = None
113132
114133 for attempt in attempts (BUILD_LOG_MAX_RETRIES , BUILD_LOG_TIMEOUT ):
@@ -158,9 +177,11 @@ def stream_build_logs(
158177 # Exhausted retries without getting any response
159178 raise BuildLogError (f"Failed after { BUILD_LOG_MAX_RETRIES } attempts" )
160179
161- def _parse_log_line (self , line : str ) -> Optional [BuildLogLine ]:
180+ def _parse_log_line (
181+ self , line : str
182+ ) -> Optional [BuildLogLineMessage | BuildLogLine ]:
162183 try :
163- return BuildLogLine . model_validate_json (line )
184+ return BuildLogAdapter . validate_json (line )
164185 except ValidationError as e :
165186 logger .debug ("Skipping malformed log: %s (error: %s)" , line [:100 ], e )
166187 return None
0 commit comments