1010import time
1111import datetime
1212
13+ from collections import defaultdict
1314from functools import wraps
15+
1416from .webtools import get_json
1517from .sitereadiness import site_list
1618
@@ -43,22 +45,21 @@ def function_wrapper(self, *args, **kwargs):
4345 :returns: Output of the originally decorated function
4446 :rtype: dict
4547 """
46- if not os .path .exists ('/tmp/workflowinfo' ):
47- os .mkdir ('/tmp/workflowinfo' )
48-
49- file_name = '/tmp/workflowinfo/%s_%s.cache.json' % (self , attribute )
48+ if not os .path .exists (self .cache_dir ):
49+ os .mkdir (self .cache_dir )
5050
5151 check_var = self .cache .get (attribute )
5252
5353 if check_var is None :
54+ file_name = self .cache_filename (attribute )
5455 if os .path .exists (file_name ) and \
5556 (timeout is None or time .time () - timeout < os .stat (file_name ).st_mtime ):
56- with open ( file_name , 'r' ) as cache_file :
57- try :
57+ try :
58+ with open ( file_name , 'r' ) as cache_file :
5859 check_var = json .load (cache_file )
59- except ValueError :
60- print 'JSON file no good. Delete %s and try again.' % file_name
61- exit ( 5 )
60+ except ValueError :
61+ print 'JSON file no good. Deleting %s. Try again later .' % file_name
62+ os . remove ( file_name )
6263
6364 else :
6465 check_var = func (self , * args , ** kwargs )
@@ -159,7 +160,48 @@ def explain_errors(workflow, errorcode):
159160 return output
160161
161162
162- class WorkflowInfo (object ):
163+ class Info (object ):
164+ """
165+ Implements shared operations on the cache
166+ """
167+
168+ def __init__ (self ):
169+ # Stores things using the cached_json decorator
170+ self .cache = {}
171+ self .cache_dir = os .path .join (os .environ .get ('TMPDIR' , '/tmp' ), 'workflowinfo' )
172+ self .bak_dir = os .path .join (self .cache_dir , 'bak' )
173+
174+ def __str__ (self ):
175+ pass
176+
177+ def cache_filename (self , attribute ):
178+ """
179+ Return the name of the file for caching
180+
181+ :param str attribute: The information to store in the file
182+ :returns: The full file name to store the cache
183+ :rtype: str
184+ """
185+ return os .path .join (self .cache_dir , '%s_%s.cache.json' % (self , attribute ))
186+
187+ def reset (self ):
188+ """
189+ Reset the cache for this object and clear out the files.
190+ """
191+ print 'Reseting %s' % self
192+
193+ if not os .path .exists (self .bak_dir ):
194+ os .mkdir (self .bak_dir )
195+
196+ for attribute in self .cache :
197+ cache_file = self .cache_filename (attribute )
198+ if os .path .exists (cache_file ):
199+ os .rename (cache_file , cache_file .replace (self .cache_dir , self .bak_dir ))
200+
201+ self .cache .clear ()
202+
203+
204+ class WorkflowInfo (Info ):
163205 """
164206 Class that holds methods for accessing various information about a workflow.
165207 """
@@ -172,11 +214,10 @@ def __init__(self, workflow, url='cmsweb.cern.ch'):
172214 :param str url: is the url to fetch information from
173215 """
174216
217+ super (WorkflowInfo , self ).__init__ ()
175218 self .workflow = workflow
176219 self .url = url
177220
178- # Stores things using the cached_json decorator
179- self .cache = {}
180221 # Is set first time get_explanation() is called
181222 self .explanations = None
182223
@@ -341,39 +382,19 @@ def get_explanation(self, errorcode, step=''):
341382 """
342383
343384 if self .explanations is None :
344- self .explanations = {}
385+ self .explanations = defaultdict ( lambda : defaultdict ( lambda : []))
345386 result = self ._get_jobdetail ()
346387 for stepname , stepdata in result ['result' ][0 ].get (self .workflow , {}).iteritems ():
347388 # Get the errors from both 'jobfailed' and 'submitfailed' details
348- for error , site in \
349- sum ([stepdata .get (status , {}).items () for status in \
350- ['jobfailed' , 'submitfailed' ]], []):
389+ for error , site in [(error , site ) for status in ['jobfailed' , 'submitfailed' ] \
390+ for error , site in stepdata .get (status , {}).items ()]:
351391 if error == '0' :
352392 continue
353393
354- if self .explanations .get (error ) is None :
355- self .explanations [error ] = {}
356-
357- if self .explanations [error ].get (stepname ) is None :
358- self .explanations [error ][stepname ] = []
359-
360394 for sitename , samples in site .iteritems ():
361-
362- #
363- # Flatten the following nested loops:
364- #
365- # for sample in samples['samples']:
366- # for values in sample['errors'].values():
367- # for detail in values:
368- #
369- # Both ways are equally unreadable, I think
370- #
371-
372- for detail in sum (
373- [values for values in sum (
374- [sample ['errors' ].values () for sample in samples ['samples' ]],
375- [])],
376- []):
395+ for detail in [values for sample in samples ['samples' ]
396+ for errs in sample ['errors' ].values ()
397+ for values in errs ]:
377398
378399 self .explanations [error ][stepname ].append ('\n \n ' .join (
379400 ['Site name: %s' % sitename ,
@@ -385,7 +406,7 @@ def get_explanation(self, errorcode, step=''):
385406 if step in explain .keys ():
386407 return explain [step ]
387408
388- return sum ( [val for val in explain .values ()], [])
409+ return [val for expl in explain .values () for val in expl ]
389410
390411 def get_prep_id (self ):
391412 """
@@ -396,19 +417,16 @@ def get_prep_id(self):
396417 return str (self .get_workflow_parameters ().get ('PrepID' , 'NoPrepID' ))
397418
398419
399- class PrepIDInfo (object ):
420+ class PrepIDInfo (Info ):
400421 """
401422 A class that just holds a small amount of information about a given PrepID.
402423 """
403424
404425 def __init__ (self , prep_id , url = 'cmsweb.cern.ch' ):
405-
426+ super ( PrepIDInfo , self ). __init__ ()
406427 self .prep_id = prep_id
407428 self .url = url
408429
409- # Stores things using the cached_json decorator
410- self .cache = {}
411-
412430 def __str__ (self ):
413431 return 'prepIDinfo_%s' % self .prep_id
414432
0 commit comments