@@ -56,7 +56,7 @@ def run_cmd(cmd, shell = False, quiet = False):
5656
5757def parse_mkt_uptime (time ):
5858 time_dict = re .match (r'((?P<weeks>\d+)w)?((?P<days>\d+)d)?((?P<hours>\d+)h)?((?P<minutes>\d+)m)?((?P<seconds>\d+)s)?' , time ).groupdict ()
59- delta = timedelta (** {key : int (value ) for key , value in time_dict .items () if value }).total_seconds ()
59+ delta = timedelta (** {key : int (value ) for key , value in time_dict .items () if value }).total_seconds ()
6060 return int (delta ) if delta else 0
6161
6262def str2bool (str_value , default = None ):
@@ -71,7 +71,7 @@ def str2bool(str_value, default = None):
7171 return default
7272 else :
7373 raise ValueError (f'Invalid truth value: { str_value } ' )
74-
74+
7575class FSHelper :
7676 ''' File System ops helper
7777 '''
@@ -229,15 +229,15 @@ def __init__(self, interval, func, args=[], kwargs={}, process_name = None, repe
229229 self .process_name = process_name
230230 self .interval = interval
231231 self .restartable = restartable
232-
232+
233233 self .func = func
234234 self .args = args
235235 self .kwargs = kwargs
236-
236+
237237 self .finished = Event ()
238238 self .run_once = Event ()
239239 if not repeatable :
240- self .run_once .set ()
240+ self .run_once .set ()
241241 self .process = Process (name = self .process_name , target = self ._execute )
242242
243243 def start (self ):
@@ -256,7 +256,7 @@ def _execute(self):
256256 self .func (* self .args , ** self .kwargs )
257257 if self .finished .is_set () or self .run_once .is_set ():
258258 break
259- self .finished .wait (self .interval )
259+ self .finished .wait (self .interval )
260260
261261class Benchmark :
262262 def __enter__ (self ):
@@ -281,29 +281,43 @@ def get_ttl_hash(seconds=3600):
281281 return round (time .time () / seconds )
282282
283283
284+ # Timeout for fetching update RSS feeds (in seconds)
285+ UPDATE_CHECK_TIMEOUT = 10
286+
284287@lru_cache (maxsize = 5 )
285288def get_available_updates (channel , ttl_hash = get_ttl_hash ()):
286289 """Check the RSS feed for available updates for a given update channel.
287290 This method fetches the RSS feed and returns all version from the parsed XML.
288- Version numbers are parsed into version.Version instances (part of setuptools)."""
291+ Version numbers are parsed into version.Version instances (part of setuptools).
292+
293+ Errors are handled internally so that lru_cache caches the (empty) result,
294+ preventing repeated blocking retries on transient failures."""
289295 del ttl_hash
290- rss_feed = CHANNEL_RSS_FEED_MAPPING [channel ]
296+ rss_feed = CHANNEL_RSS_FEED_MAPPING .get (channel )
297+ if not rss_feed :
298+ print (f'Unknown update channel: { channel } ' )
299+ return []
291300
292301 print (f'Fetching available ROS releases from { rss_feed } ' )
293302 versions = []
294- with urllib .request .urlopen (rss_feed ) as response :
295- result = response .read ()
296- root = ET .fromstring (result )
297- channel = root [0 ]
298-
299- for child in channel :
300- # iterate over all updates
301- if child .tag == 'item' :
302- title = child [0 ]
303- # extract and parse the version number from title
304- version_text = re .findall (r'[\d+\.]+' , title .text )[0 ]
305- version_number = parse (version_text )
306- versions .append (version_number )
303+ try :
304+ with urllib .request .urlopen (rss_feed , timeout = UPDATE_CHECK_TIMEOUT ) as response :
305+ result = response .read ()
306+ root = ET .fromstring (result )
307+ rss_channel = root [0 ]
308+
309+ for child in rss_channel :
310+ # iterate over all updates
311+ if child .tag == 'item' :
312+ title = child [0 ]
313+ # extract and parse the version number from title
314+ version_text = re .findall (r'[\d+\.]+' , title .text )[0 ]
315+ version_number = parse (version_text )
316+ versions .append (version_number )
317+ except urllib .error .HTTPError as err :
318+ print (f'Update feed returned: { err } ' )
319+ except Exception as err :
320+ print (f'Could not check for updates, because: { err } ' )
307321 return versions
308322
309323
@@ -315,7 +329,7 @@ def parse_ros_version(string):
315329 1.2.3, stable
316330
317331 >>> parse_ros_version('7.14.3 (long-term)')
318- 7.14.3, long-term
332+ 7.14.3, long-term
319333 """
320334
321335 match = re .findall (r'([\d\.]+).*?\(([\w-]+)\)' , string )
@@ -355,22 +369,13 @@ def check_for_updates(cur_version):
355369 """Try to check if there is a newer version available.
356370 If anything goes wrong, it returns the same version.
357371 Returns a tuple: (<current version>, <newest version>)"""
358- error = False
359372 try :
360373 cur_version , channel = parse_ros_version (cur_version )
361374 available_versions = get_available_updates (channel )
362- newest_version = sorted (available_versions )[- 1 ]
363- except KeyError :
364- print (f'unknown update channel { channel } ' )
365- error = True
366- except urllib .error .HTTPError as err :
367- print (f'update feed returned: { err } ' )
368- error = True
375+ if available_versions :
376+ newest_version = sorted (available_versions )[- 1 ]
377+ return cur_version , newest_version
369378 except Exception as err :
370- print (f'could not check for updates, because: { err } ' )
371- error = True
372-
373- if error :
374- return cur_version , cur_version
379+ print (f'Could not check for updates, because: { err } ' )
375380
376- return cur_version , newest_version
381+ return cur_version , cur_version
0 commit comments