REFACTOR Refactors simulationController: groups code into blocks,
[wrfxweb.git] / src / utils.py
blob6315f550ed3a7686d806e5ebb8dddfa79e636852
1 from __future__ import absolute_import
2 import fcntl, errno, logging, json, os, sys
3 import os.path as osp
5 class Dict(dict):
6 """
7 A dictionary that allows member access to its keys.
8 A convenience class.
9 """
11 def __init__(self, d):
12 """
13 Updates itself with d.
14 """
15 self.update(d)
17 def __getattr__(self, item):
18 return self[item]
20 def __setattr__(self, item, value):
21 self[item] = value
24 class lock():
25 """
26 Lock file for exclusive access
27 """
29 def __init__(self,path):
30 self.lock_path = path
31 logging.info('Initializing lock on %s' % self.lock_path)
32 self.lock_file=open(self.lock_path,'w',0)
33 self.locked=False
35 def islocked(self):
36 return(self.locked)
38 def acquire(self):
40 """
41 Block until exclusive lock can be acquired.
42 Used before code that should be executed by one process at a time only,
43 such as updating the catalog.
44 """
45 if self.locked:
46 logging.warning('lock.acquire: already locked %s' % self.lock_path)
47 try:
48 fcntl.flock(self.lock_file,fcntl.LOCK_EX|fcntl.LOCK_NB)
49 except IOError as e:
50 if e.errno == errno.EACCES or e.errno == errno.EAGAIN:
51 logging.warning('Waiting for lock on %s' % self.lock_path)
52 else:
53 logging.error("I/O error %s: %s" % (e.errno, e.strerror))
54 fcntl.flock(self.lock_file,fcntl.LOCK_EX)
55 logging.info('Acquired lock on %s' % self.lock_path)
56 self.locked=True
58 def release(self):
59 if not self.locked:
60 logging.warning('lock.release: not yet locked %s' % self.lock_path)
61 logging.info('Releasing lock on %s' % self.lock_path)
62 fcntl.flock(self.lock_file,fcntl.LOCK_UN)
63 self.locked=False
65 def update_nested_dict(d,u,level=0):
66 """
67 Recursively update nested dictionary. Does not overwrite any values.
68 Identical key is allowed only if both values are dictionaries and the
69 update can continue recursively.
71 :param d: update: dictionary to be updated
72 :param u: input: dictionary with the update
74 :param level: internal, for error reporting only
75 :param key: internal, for error reporting only
77 Example:
78 from utils import update_nested_dict
79 d = {1: {2: 3}, 2: {4: 5}}
80 u = {1: {8: 9}, 3: {10: 11}}
81 update_nested_dict(d,u)
83 {1: {8: 9, 2: 3}, 2: {4: 5}, 3: {10: 11}}
84 update_nested_dict(d,u)
85 ValueError: update_nested_dict: level 1: values for common key 8 must be dictionaries
86 """
88 # print ('update_nested_dict: level %s entering with d=%s u=%s' % (level,d,u))
89 if type(d) is not dict or type(u) is not dict:
90 raise ValueError ('update_nested_dict: level %s: both arguments must be dictionaries' % level)
91 for k in u.keys():
92 # print ('update_nested_dict: level %s found key %s in u' % (level,k))
93 if k in d:
94 # print ('update_nested_dict: level %s key %s in both u and d' % (level,k))
95 # print ('update_nested_dict: level %s recursive update in d=%s and u=%s' % (level,d,u))
96 if type(d[k]) is not dict or type(u[k]) is not dict:
97 raise ValueError ('update_nested_dict: level %s: values for common key %s must be dictionaries' % (level,k))
98 update_nested_dict(d[k],u[k],level+1)
99 # print ('update_nested_dict: level %s got updated d=%s' % (level,d))
100 else:
101 # print ('update_nested_dict: level %s key %s from u not found in d' % (level,k))
102 # print ('update_nested_dict: level %s adding item to d=%s from u=%s' % (level,d,u))
103 d[k]=u[k]
104 # print ('update_nested_dict: level %s got updated d=%s' % (level,d))
105 # print ('update_nested_dict: level %s exiting with updated d=%s' % (level,d))
108 def load_sys_cfg():
109 # load the system configuration
110 sys_cfg = None
111 try:
112 sys_cfg = Dict(json.load(open('etc/conf.json')))
113 except IOError:
114 logging.critical('Cannot find system configuration, have you created etc/conf.json?')
115 sys.exit(2)
116 # set defaults
117 sys = sys_cfg.sys_install_path = sys_cfg.get('sys_install_path',os.getcwd())
118 return sys_cfg