source: SHX/trunk/SeismicHandler/config/runtime.py @ 1066

Revision 1066, 12.2 KB checked in by klaus, 4 years ago (diff)

forgot to check in other timeit changes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Rev Id Date
Line 
1# -*- coding: utf-8 -*-
2
3#    This file is part of Seismic Handler eXtended (SHX). For terms of use and
4#    license information please see license.txt and visit
5#    http://www.seismic-handler.org/portal/wiki/Shx/LicenseTerms
6
7"""
8This object carries all settings for runtime including global variables.
9
10E.g. Echo Channel, Cap Conversion, ...
11"""
12
13# Please note that in this file no logging takes place since configuration
14# options for logging are initialized here first.
15# Also _all_ code in modules/* use runtime.py so that any defined method under
16# "modules" should not be used here (to avoid import loops).
17
18
19import sys
20import os
21import ConfigParser
22import warnings
23from SeismicHandler.basics import Property, Singleton, AttribDict, timeit
24from SeismicHandler.basics.messages import msgs, log_message
25
26
27class runtime(object):
28    """
29    >>> Settings = runtime()
30
31    >>> print >> Settings.echo_channel, Settings.echo_channel
32    >>> Settings.echo_channel = "TEST"
33    >>> print >> Settings.echo_channel, Settings.echo_channel
34    >>> Settings.echo_channel = ""
35    >>> print >> Settings.echo_channel, Settings.echo_channel #doctest: +ELLIPSIS
36    <doctest...
37   
38    Now a file named TEST should contain
39    >>> open("TEST").read() #doctest: +ELLIPSIS
40    "<open file 'TEST', mode 'ab' at 0x..."
41
42    This is only for clean-up:
43    >>> import os
44    >>> os.unlink("TEST")
45    """
46    __metaclass__ = Singleton
47
48    # see echo_channel()
49    __echo_channel = sys.stdout
50   
51    # configuration is read from file
52    config = None
53
54    # global switches (see http://www.seismic-handler.org/portal/wiki/ShSwitch)
55    _Switches = AttribDict({
56        # convert to upper case
57        "Capcnv": True,
58        # echo command before execution
59        "Echo": False,
60        # verify command
61        "Verify": False,
62        # cancel script on error
63        "Cmderrstop": True,
64        # exit SH on error
65        "Sherrstop": False,
66        # no error message at all
67        "Noerrmsg": False,
68        # verbose
69        "Chatty": True,
70        # indicate startup file
71        "Startup": False,
72        # wait for prompt
73        "Step": False,
74        # include command in protocol
75        "Protocol": True,
76        # SHX: add new traces or discard old ones, to be altered only globally!
77        "Keeptraces": True,
78    })
79
80    # dictionary of global variables
81    Globals = AttribDict({"G1": "", "G2": "", "G3": "", "RET": ""})
82
83    # container for internal data like $status
84    # all lower case, except for prefixes
85    Runtime = AttribDict({
86        "status": 0,
87        "dsptrcs": 0,
88        "tottrcs": 0,
89        "RUNTIME:corrllo": -30.0,
90        "RUNTIME:corrlhi": 100.0,
91        # time-window borders
92        "RUNTIME:timewindow": (0., None),
93        # vertical borders
94        "RUNTIME:ywindow": (None, None),
95        # vertical display mode
96        "RUNTIME:yinfo": "none",
97        # extend of traces
98        "RUNTIME:extend": (0., 100.),
99        # q-file data directory
100        "RUNTIME:q_datadir": ".",
101        # normation type
102        "RUNTIME:normtype": "AW", # SW, AT, ST (all, single; window, total)
103        # correlation mode
104        "RUNTIME:corrmode": 1,
105        # styles
106        "RUNTIME:styles": {
107            "MARGINS": [0.1, 0.1, 0.4, 0.15],
108        },
109        # attribute blocks
110        "RUNTIME:attrib": {},
111        # default trace info values
112        "RUNTIME:zoom": 1.,
113        "RUNTIME:reduction": 1.,
114        "RUNTIME:norm": 0.5,
115        "RUNTIME:weight": 1.,
116    })
117
118    #@timeit
119    def __init__(self):
120        if not self.config:
121            self.readConfig()
122            msgs.subscribe(self._setRuntimeVar, 'setruntime')
123            msgs.subscribe(self._getRuntimeVar, 'getruntime')
124
125    def readConfig(self):
126        """
127        Read configuration file.
128
129        Possible locations of shx.conf
130        1. installation directory
131        2. /etc/
132        3. $HOME/.shx/
133        All configurations files found are processed in this order!
134
135        Finally the variable "conf" contains the full parsed configuration and
136        "fullconfig" contains the merged configuration with replacements.
137        """
138
139        # helper function
140        def __handlePlaceholdersVars(values, replacevars=False):
141            """
142            Replace placeholders in config file.
143            """
144
145            # split values
146            if type(values) != list:
147                values = map(str.strip, values.split(","))
148           
149            ret = []
150            for v in values:
151                if v.find("[") >= 0:
152                    # replacement requested
153                    for d in replace.keys():
154                        if v.find("[%s]" % d) >= 0:
155                            # do replacement
156                            ret.append(v.replace("[%s]" % d, replace[d]))
157                elif replacevars and v.startswith("$"):
158                    # variable substitution
159                    try:
160                        ret.extend(vars[v.lower()])
161                    except KeyError:
162                        warnings.warn("Variable '%s' not found!" % v, SyntaxWarning)
163                else:
164                    ret.append(v)
165
166            return ret
167
168        # "main" function
169        dirs_check = [
170            ["SHX", os.path.abspath(__file__).rsplit(os.path.sep, 2)[0]],
171            ["ETC", "/etc/SeismicHandler"], # XXX not portable!
172        ]
173
174        homeconfig = os.path.join(os.path.expanduser("~"), ".shx")
175
176        if os.path.exists(homeconfig):
177            dirs_check.append(["PRIVATE", homeconfig])
178
179        dirs = []
180        replace = {}
181        for d in dirs_check:
182            src = os.path.join(d[1], "shx.conf")
183            replace[d[0]] = d[1]
184            if os.path.exists(src):
185                dirs.append(src)
186
187        conf = AttribDict()
188        vars = AttribDict()
189
190        # includes merged configuration without replacements
191        # mainly useful for debugging
192        mergedConfig = ConfigParser.SafeConfigParser()
193
194        single = False
195        for src in dirs:
196            config = ConfigParser.SafeConfigParser()
197            config.read(src)
198
199            # check for single usage
200            if "single" in config.sections():
201                msg = "Using single configuration: %s" % src
202                log_message("info.runtime", msg)
203                single = True
204                # reset former configs
205                conf = AttribDict()
206                vars = AttribDict()
207                mergedConfig = ConfigParser.SafeConfigParser()
208
209            for s in config.sections():
210                if not mergedConfig.has_section(s):
211                    mergedConfig.add_section(s)
212
213                if s not in conf.keys():
214                    conf[s] = AttribDict()
215
216                for i in config.items(s):
217                    mergedConfig.set(s, *i)
218                   
219                    # check for variable definition
220                    if i[0].startswith("$"):
221                        v = i[0].lower()
222                        if v in vars:
223                            warnings.warn("Variable '%s' defined more "
224                                          "than once!" % v, SyntaxWarning)
225
226                        vars[v] = __handlePlaceholdersVars(i[1])
227                    else:
228                        conf[s][i[0]] = __handlePlaceholdersVars(i[1])
229
230                # check if include statement was defined -> process last
231                if "include" in conf[s]:
232
233                    # include sub configfile if requested
234                    extra = ConfigParser.SafeConfigParser()
235                    for e in conf[s]["include"]:
236                        extra.read(e)
237
238                        # only include current section
239                        if not extra.has_section(s):
240                            warnings.warn("To be included configuration file "
241                                          "'%s' not found." % e, SyntaxWarning)
242                            break
243
244                        for ie in extra.items(s):
245                            mergedConfig.set(s, *ie)
246                            conf[s][ie[0]] = __handlePlaceholdersVars(ie[1])
247
248                    del conf[s]["include"]
249                    mergedConfig.remove_option(s, "include")
250
251            if single:
252                break
253
254        # since configparser doesn't preserve order of entries, variables will
255        # be processed last.
256        for s in conf:
257            for i in conf[s]:
258                conf[s][i] = __handlePlaceholdersVars(conf[s][i], True)
259               
260        self.config = conf
261        self.__mergedConfig = mergedConfig
262
263    def __getattr__(self, name):
264        if name.startswith("sw"):
265            return getattr(self, "_Switches")[name[2:]]
266        else:
267            return object.__getattribute__(self, name)
268
269    def __setattr__(self, name, value):
270        if name.startswith("sw"):
271            if not name[2:] in self.__class__.__dict__["_Switches"].keys():
272                raise NameError("Unknown switch!")
273           
274            self.__class__.__dict__["_Switches"][name[2:]] = value
275        elif name == "echo_channel":
276            object.__setattr__(self, "echo_channel", value)
277        else:
278            self.__dict__[name] = value
279
280
281    def _setRuntimeVar(self, name, value):
282        self._setRuntimeVarTimed( name, value )
283       
284       
285    #@timeit
286    def _setRuntimeVarTimed(self, name, value):
287        log_message(
288            "debug.runtime",
289            "Received internal update for '%s' (%s)" % (name, str(value))
290        )
291        if ":" in name:
292            name = name.split(":", 1)
293            name = ":".join([name[0], name[1].lower()])
294        else:
295            name = name.lower()
296
297        self.Runtime[name] = value
298
299
300    def _getRuntimeVar(self, name, msgid):
301        self._getRuntimeVarTimed( name, msgid )
302       
303       
304    #@timeit
305    def _getRuntimeVarTimed(self, name, msgid):
306        """
307        Send out value of runtime var to listener @msgid
308        """
309        # if colon in name, let first part untouched
310        if ":" in name:
311            name = name.split(":", 1)
312            name = ":".join([name[0], name[1].lower()])
313        else:
314            name = name.lower()
315
316        value = self.Runtime.get(name, None)
317        msgs.sendMessage(msgid, value=value, msgid=msgid)
318
319        msg = "sent value of %s to %s" % (name, msgid)
320        log_message("debug.runtime.getruntime", msg)
321
322    #@timeit
323    def _getRuntimeVarDirect(self, name ):
324        """
325        Returns value of runtime var bypassing the message system.
326        """
327        # if colon in name, let first part untouched
328        if ":" in name:
329            name = name.split(":", 1)
330            name = ":".join([name[0], name[1].lower()])
331        else:
332            name = name.lower()
333        value = self.Runtime.get(name, None)
334        return value
335
336    @Property
337    def echo_channel():
338        """
339        The echo_channel is a special property used for data output. As
340        default all output goes to stdout. Using the ECHO_CH command
341        this can be redirected to a file.
342
343        This method cares for proper handling of file-like objects. New
344        feature: It's possible to use any object offering the methods
345        "write" and "close", e.g. a stream.
346
347        The actual target of output is stored in the class attribute
348        "__echo_channel".
349        """
350
351        def fget(self):
352            return self.__echo_channel
353
354        def fset(self, value = None):
355            # clear setting
356            if not value:
357                fdel(self)
358                return
359
360            # We assume that there's a file for output or a stream (actually I
361            # don't really care about it's true nature, since it offers the
362            # methods "write" and "close").
363            if hasattr(value, "write") and hasattr(value, "close"):
364                self.__echo_channel = value
365            elif isinstance(value, basestring):
366                # open file
367                self.__echo_channel = open(value, "ab")
368            else:
369                raise ValueError("This echo channel cannot be used.")
370
371        def fdel(self):
372            if self.__echo_channel == sys.stdout:
373                return
374
375            # close open files/stream
376            if not self.__echo_channel.closed:
377                self.__echo_channel.close()
378
379            self.__echo_channel = sys.stdout
380
381        return locals()
382
383
384if __name__ == "__main__":
385    import doctest
386    doctest.testmod(exclude_empty=True)
Note: See TracBrowser for help on using the repository browser.