77import json
88import logging
99import os
10- import re
1110import requests
1211import shutil
1312import subprocess
1413import sys
15- import time
1614import urllib3
1715from pprint import pformat
1816
3129logger = logging .getLogger (__name__ )
3230
3331
32+ def halo_logging (closure ):
33+ class ClosureStreamHandler (logging .StreamHandler ):
34+ def emit (self , record ):
35+ # Call the closure with the log message
36+ closure (self .format (record ))
37+
38+ handler = ClosureStreamHandler ()
39+ formatter = logging .Formatter ('%(asctime)s - %(levelname)s - %(message)s' )
40+ handler .setFormatter (formatter )
41+ logger .addHandler (handler )
42+
43+
3444class SiliconTop (StepBase , Elaboratable ):
3545 def __init__ (self , config ):
3646 self ._config = config
@@ -122,6 +132,7 @@ def submit(self, rtlil_path, args):
122132 else :
123133 interval = - 1
124134 with Halo (text = "Submitting..." , spinner = "dots" , interval = interval ) as sp :
135+
125136 fh = None
126137 submission_name = self .determine_submission_name ()
127138 data = {
@@ -234,7 +245,6 @@ def network_err(e):
234245 headers ["Authorization" ] = "REDACTED"
235246 logger .debug (f"Request headers: { headers } " )
236247
237- logger .debug (f"Request data: { data } " )
238248 logger .debug (f"Response headers: { dict (resp .headers )} " )
239249 logger .debug (f"Response body: { resp_data } " )
240250 sp .text = ""
@@ -248,60 +258,52 @@ def network_err(e):
248258 exit (2 )
249259
250260 def _long_poll_stream (self , sp , network_err ):
251- steps = self ._last_log_steps
252- stream_event_counter = 0
253261 assert self ._chipflow_api_key
254262 # after 4 errors, return to _stream_logs loop and query the build status again
255- while (stream_event_counter < 4 ):
256- sp .text = "Build running... " + ' -> ' .join (steps )
257- try :
258- log_resp = requests .get (
259- self ._log_stream_url ,
260- auth = ("" , self ._chipflow_api_key ),
261- stream = True ,
262- timeout = (2.0 , 60.0 ) # fail if connect takes >2s, long poll for 60s at a time
263- )
264- if log_resp .status_code == 200 :
265- logger .debug (f"response from { self ._log_stream_url } :\n { log_resp } " )
266- for line in log_resp .iter_lines ():
267- line_str = line .decode ("utf-8" ) if line else ""
268- logger .debug (line_str )
269- match line_str [0 :8 ]:
270- case "DEBUG " :
271- sp .info (line_str ) if log_level <= logging .DEBUG else None
272- case "INFO " :
273- sp .info (line_str ) if log_level <= logging .INFO else None
274- # Some special handling for more user feedback
275- if line_str .endswith ("started" ):
276- steps = re .findall (r"([0-9a-z_.]+)\:+?" , line_str [18 :])[0 :2 ]
277- sp .text = "Build running... " + ' -> ' .join (steps )
278- case "WARNING " :
279- sp .info (line_str ) if log_level <= logging .WARNING else None
280- case "ERROR " :
281- sp .info (line_str ) if log_level <= logging .ERROR else None
282- sp .start ()
283- else :
284- stream_event_counter += 1
285- logger .debug (f"Failed to stream logs: { log_resp .text } " )
286- sp .text = "💥 Failed streaming build logs. Trying again!"
287- break
288- except requests .ConnectionError as e :
289- if type (e .__context__ ) is urllib3 .exceptions .ReadTimeoutError :
290- continue #just timed out, continue long poll
291- sp .text = "💥 Failed connecting to ChipFlow Cloud."
292- logger .debug (f"Error while streaming logs: { e } " )
293- break
294- except (requests .RequestException , requests .exceptions .ReadTimeout ) as e :
295- if type (e .__context__ ) is urllib3 .exceptions .ReadTimeoutError :
296- continue #just timed out, continue long poll
263+ logger .debug ("Long poll start" )
264+ try :
265+ log_resp = requests .get (
266+ self ._log_stream_url ,
267+ auth = ("" , self ._chipflow_api_key ),
268+ stream = True ,
269+ timeout = (2.0 , 60.0 ) # fail if connect takes >2s, long poll for 60s at a time
270+ )
271+ if log_resp .status_code == 200 :
272+ logger .debug (f"response from { self ._log_stream_url } :\n { log_resp } " )
273+ for line in log_resp .iter_lines ():
274+ message = line .decode ("utf-8" ) if line else ""
275+ level , time , step = message .split (maxsplit = 2 )
276+ match level :
277+ case "DEBUG" :
278+ sp .info (message ) if log_level <= logging .DEBUG else None
279+ case "INFO" | "INFO+" :
280+ sp .info (message ) if log_level <= logging .INFO else None
281+ case "WARNING" :
282+ sp .info (message ) if log_level <= logging .WARNING else None
283+ case "ERROR" :
284+ sp .info (message ) if log_level <= logging .ERROR else None
285+
286+ if step != self ._last_log_step :
287+ sp .text = f"Build running: { self ._last_log_step } "
288+ self ._last_log_step = step
289+ else :
290+ logger .debug (f"Failed to stream logs: { log_resp .text } " )
297291 sp .text = "💥 Failed streaming build logs. Trying again!"
298- logger .debug (f"Error while streaming logs: { e } " )
299- stream_event_counter += 1
300- continue
301-
302- # save steps so we coninue where we left off if we manage to reconnect
303- self ._last_log_steps = steps
304- return stream_event_counter
292+ return True
293+ except requests .ConnectionError as e :
294+ if type (e .__context__ ) is urllib3 .exceptions .ReadTimeoutError :
295+ return True
296+ sp .text = "💥 Failed connecting to ChipFlow Cloud."
297+ logger .debug (f"Error while streaming logs: { e } " )
298+ return False
299+ except (requests .RequestException , requests .exceptions .ReadTimeout ) as e :
300+ if type (e .__context__ ) is urllib3 .exceptions .ReadTimeoutError :
301+ return True
302+ sp .text = "💥 Failed streaming build logs. Trying again!"
303+ logger .debug (f"Error while streaming logs: { e } " )
304+ return False
305+
306+ return True
305307
306308 def _stream_logs (self , sp , network_err ):
307309 sp .start ("Streaming the logs..." )
@@ -310,18 +312,19 @@ def _stream_logs(self, sp, network_err):
310312 timeout = 10.0
311313 build_status = "pending"
312314 stream_event_counter = 0
313- self ._last_log_steps = []
315+ self ._last_log_step = ""
314316 assert self ._chipflow_api_key is not None
315- while fail_counter < 10 and stream_event_counter < 10 :
316- sp . text = f"Waiting for build to run... { build_status } "
317- time . sleep ( timeout ) # Wait before polling
317+ sp . text = f"Waiting for build to run... { build_status } "
318+
319+ while fail_counter < 5 :
318320 try :
321+ logger .debug (f"Checking build status, iteration { fail_counter } " )
319322 status_resp = requests .get (
320323 self ._build_status_url ,
321324 auth = ("" , self ._chipflow_api_key ),
322325 timeout = timeout
323326 )
324- except requests .exceptions .ReadTimeout as e :
327+ except ( requests .exceptions .ReadTimeout , requests . exceptions . ConnectionError ) as e :
325328 sp .text = "💥 Error connecting to ChipFlow Cloud. Trying again! "
326329 fail_counter += 1
327330 logger .debug (f"Failed to fetch build status{ fail_counter } times: { e } " )
@@ -337,22 +340,20 @@ def _stream_logs(self, sp, network_err):
337340 build_status = status_data .get ("status" )
338341 logger .debug (f"Build status: { build_status } " )
339342
340- sp .text = f"Polling build status... { build_status } "
341-
342343 if build_status == "completed" :
343344 sp .succeed ("✅ Build completed successfully!" )
344345 return 0
345346 elif build_status == "failed" :
346347 sp .succeed ("❌ Build failed." )
347348 return 1
348349 elif build_status == "running" :
349- stream_event_counter += self . _long_poll_stream ( sp , network_err )
350-
351- if fail_counter >= 10 or stream_event_counter >= 10 :
352- sp .text = ""
353- sp . fail ( "💥 Failed fetching build status. Perhaps you hit a network error? " )
354- logger . debug ( f"Failed to fetch build status { fail_counter } times and failed streaming { stream_event_counter } times. Exiting." )
355- return 2
350+ sp . text = f"Build status: { build_status } "
351+ if not self . _long_poll_stream ( sp , network_err ):
352+ sp . text = ""
353+ sp .fail ( "💥 Failed fetching build status. Perhaps you hit a network error?" )
354+ logger . debug ( f" Failed to fetch build status { fail_counter } times and failed streaming { stream_event_counter } times. Exiting. " )
355+ return 2
356+ # check status and go again
356357
357358 def determine_submission_name (self ):
358359 if "CHIPFLOW_SUBMISSION_NAME" in os .environ :
0 commit comments