1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5from __future__ import absolute_import 

6import os 

7import os.path as op 

8from copy import deepcopy 

9import logging 

10 

11from . import util 

12from .guts import Object, Float, String, load, dump, List, Dict, TBase, \ 

13 Tuple, StringChoice, Bool 

14 

15 

16logger = logging.getLogger('pyrocko.config') 

17 

18guts_prefix = 'pf' 

19 

20pyrocko_dir_tmpl = os.environ.get( 

21 'PYROCKO_DIR', 

22 os.path.join('~', '.pyrocko')) 

23 

24 

25def make_conf_path_tmpl(name='config'): 

26 return op.join(pyrocko_dir_tmpl, '%s.pf' % name) 

27 

28 

29default_phase_key_mapping = { 

30 'F1': 'P', 'F2': 'S', 'F3': 'R', 'F4': 'Q', 'F5': '?'} 

31 

32 

33class BadConfig(Exception): 

34 pass 

35 

36 

37class PathWithPlaceholders(String): 

38 ''' 

39 Path, possibly containing placeholders. 

40 ''' 

41 pass 

42 

43 

44class VisibleLengthSetting(Object): 

45 class __T(TBase): 

46 def regularize_extra(self, val): 

47 if isinstance(val, list): 

48 return self.cls(key=val[0], value=val[1]) 

49 

50 return val 

51 

52 def to_save(self, val): 

53 return (val.key, val.value) 

54 

55 def to_save_xml(self, val): 

56 raise NotImplementedError() 

57 

58 key = String.T() 

59 value = Float.T() 

60 

61 

62class ConfigBase(Object): 

63 @classmethod 

64 def default(cls): 

65 return cls() 

66 

67 

68class SnufflerConfig(ConfigBase): 

69 visible_length_setting = List.T( 

70 VisibleLengthSetting.T(), 

71 default=[VisibleLengthSetting(key='Short', value=20000.), 

72 VisibleLengthSetting(key='Medium', value=60000.), 

73 VisibleLengthSetting(key='Long', value=120000.), 

74 VisibleLengthSetting(key='Extra Long', value=600000.)]) 

75 phase_key_mapping = Dict.T( 

76 String.T(), String.T(), default=default_phase_key_mapping) 

77 demean = Bool.T(default=True) 

78 show_scale_ranges = Bool.T(default=False) 

79 show_scale_axes = Bool.T(default=False) 

80 trace_scale = String.T(default='individual_scale') 

81 show_boxes = Bool.T(default=True) 

82 clip_traces = Bool.T(default=True) 

83 first_start = Bool.T(default=True) 

84 

85 def get_phase_name(self, key): 

86 return self.phase_key_mapping.get('F%s' % key, 'Undefined') 

87 

88 

89class PyrockoConfig(ConfigBase): 

90 cache_dir = PathWithPlaceholders.T( 

91 default=os.path.join(pyrocko_dir_tmpl, 'cache')) 

92 earthradius = Float.T(default=6371.*1000.) 

93 fdsn_timeout = Float.T(default=None, optional=True) 

94 gf_store_dirs = List.T(PathWithPlaceholders.T()) 

95 gf_store_superdirs = List.T(PathWithPlaceholders.T()) 

96 topo_dir = PathWithPlaceholders.T( 

97 default=os.path.join(pyrocko_dir_tmpl, 'topo')) 

98 tectonics_dir = PathWithPlaceholders.T( 

99 default=os.path.join(pyrocko_dir_tmpl, 'tectonics')) 

100 geonames_dir = PathWithPlaceholders.T( 

101 default=os.path.join(pyrocko_dir_tmpl, 'geonames')) 

102 crustdb_dir = PathWithPlaceholders.T( 

103 default=os.path.join(pyrocko_dir_tmpl, 'crustdb')) 

104 gshhg_dir = PathWithPlaceholders.T( 

105 default=os.path.join(pyrocko_dir_tmpl, 'gshhg')) 

106 leapseconds_path = PathWithPlaceholders.T( 

107 default=os.path.join(pyrocko_dir_tmpl, 'leap-seconds.list')) 

108 leapseconds_url = String.T( 

109 default='https://www.ietf.org/timezones/data/leap-seconds.list') 

110 earthdata_credentials = Tuple.T( 

111 2, String.T(), 

112 optional=True) 

113 gui_toolkit = StringChoice.T( 

114 choices=['auto', 'qt4', 'qt5'], 

115 default='auto') 

116 use_high_precision_time = Bool.T(default=False) 

117 

118 

119config_cls = { 

120 'config': PyrockoConfig, 

121 'snuffler': SnufflerConfig 

122} 

123 

124 

125def fill_template(tmpl, config_type): 

126 tmpl = tmpl .format( 

127 module=('.' + config_type) if config_type != 'pyrocko' else '') 

128 return tmpl 

129 

130 

131def expand(x): 

132 x = op.expanduser(op.expandvars(x)) 

133 return x 

134 

135 

136def rec_expand(x): 

137 for prop, val in x.T.ipropvals(x): 

138 if prop.multivalued: 

139 if val is not None: 

140 for i, ele in enumerate(val): 

141 if isinstance(prop.content_t, PathWithPlaceholders.T): 

142 newele = expand(ele) 

143 if newele != ele: 

144 val[i] = newele 

145 

146 elif isinstance(ele, Object): 

147 rec_expand(ele) 

148 else: 

149 if isinstance(prop, PathWithPlaceholders.T): 

150 newval = expand(val) 

151 if newval != val: 

152 setattr(x, prop.name, newval) 

153 

154 elif isinstance(val, Object): 

155 rec_expand(val) 

156 

157 

158def processed(config): 

159 config = deepcopy(config) 

160 rec_expand(config) 

161 return config 

162 

163 

164def mtime(p): 

165 return os.stat(p).st_mtime 

166 

167 

168g_conf_mtime = {} 

169g_conf = {} 

170 

171 

172def raw_config(config_name='config'): 

173 

174 conf_path = expand(make_conf_path_tmpl(config_name)) 

175 

176 if not op.exists(conf_path): 

177 g_conf[config_name] = config_cls[config_name].default() 

178 write_config(g_conf[config_name], config_name) 

179 

180 conf_mtime_now = mtime(conf_path) 

181 if conf_mtime_now != g_conf_mtime.get(config_name, None): 

182 g_conf[config_name] = load(filename=conf_path) 

183 if not isinstance(g_conf[config_name], config_cls[config_name]): 

184 with open(conf_path, 'r') as fconf: 

185 logger.warning('Config file content:') 

186 for line in fconf: 

187 logger.warning(' ' + line) 

188 

189 raise BadConfig('config file does not contain a ' 

190 'valid "%s" section. Found: %s' % ( 

191 config_cls[config_name].__name__, 

192 type(g_conf[config_name]))) 

193 

194 g_conf_mtime[config_name] = conf_mtime_now 

195 

196 return g_conf[config_name] 

197 

198 

199def config(config_name='config'): 

200 return processed(raw_config(config_name)) 

201 

202 

203def write_config(conf, config_name='config'): 

204 conf_path = expand(make_conf_path_tmpl(config_name)) 

205 util.ensuredirs(conf_path) 

206 dump(conf, filename=conf_path) 

207 

208 

209override_gui_toolkit = None 

210 

211 

212def effective_gui_toolkit(): 

213 return override_gui_toolkit or config().gui_toolkit