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

Revision 1062, 12.0 KB checked in by klaus, 4 years ago (diff)

timeit decorator for runtime info

  • 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    })
112
113    @timeit
114    def __init__(self):
115        if not self.config:
116            self.readConfig()
117            msgs.subscribe(self._setRuntimeVar, 'setruntime')
118            msgs.subscribe(self._getRuntimeVar, 'getruntime')
119
120    def readConfig(self):
121        """
122        Read configuration file.
123
124        Possible locations of shx.conf
125        1. installation directory
126        2. /etc/
127        3. $HOME/.shx/
128        All configurations files found are processed in this order!
129
130        Finally the variable "conf" contains the full parsed configuration and
131        "fullconfig" contains the merged configuration with replacements.
132        """
133
134        # helper function
135        def __handlePlaceholdersVars(values, replacevars=False):
136            """
137            Replace placeholders in config file.
138            """
139
140            # split values
141            if type(values) != list:
142                values = map(str.strip, values.split(","))
143           
144            ret = []
145            for v in values:
146                if v.find("[") >= 0:
147                    # replacement requested
148                    for d in replace.keys():
149                        if v.find("[%s]" % d) >= 0:
150                            # do replacement
151                            ret.append(v.replace("[%s]" % d, replace[d]))
152                elif replacevars and v.startswith("$"):
153                    # variable substitution
154                    try:
155                        ret.extend(vars[v.lower()])
156                    except KeyError:
157                        warnings.warn("Variable '%s' not found!" % v, SyntaxWarning)
158                else:
159                    ret.append(v)
160
161            return ret
162
163        # "main" function
164        dirs_check = [
165            ["SHX", os.path.abspath(__file__).rsplit(os.path.sep, 2)[0]],
166            ["ETC", "/etc/SeismicHandler"], # XXX not portable!
167        ]
168
169        homeconfig = os.path.join(os.path.expanduser("~"), ".shx")
170
171        if os.path.exists(homeconfig):
172            dirs_check.append(["PRIVATE", homeconfig])
173
174        dirs = []
175        replace = {}
176        for d in dirs_check:
177            src = os.path.join(d[1], "shx.conf")
178            replace[d[0]] = d[1]
179            if os.path.exists(src):
180                dirs.append(src)
181
182        conf = AttribDict()
183        vars = AttribDict()
184
185        # includes merged configuration without replacements
186        # mainly useful for debugging
187        mergedConfig = ConfigParser.SafeConfigParser()
188
189        single = False
190        for src in dirs:
191            config = ConfigParser.SafeConfigParser()
192            config.read(src)
193
194            # check for single usage
195            if "single" in config.sections():
196                msg = "Using single configuration: %s" % src
197                log_message("info.runtime", msg)
198                single = True
199                # reset former configs
200                conf = AttribDict()
201                vars = AttribDict()
202                mergedConfig = ConfigParser.SafeConfigParser()
203
204            for s in config.sections():
205                if not mergedConfig.has_section(s):
206                    mergedConfig.add_section(s)
207
208                if s not in conf.keys():
209                    conf[s] = AttribDict()
210
211                for i in config.items(s):
212                    mergedConfig.set(s, *i)
213                   
214                    # check for variable definition
215                    if i[0].startswith("$"):
216                        v = i[0].lower()
217                        if v in vars:
218                            warnings.warn("Variable '%s' defined more "
219                                          "than once!" % v, SyntaxWarning)
220
221                        vars[v] = __handlePlaceholdersVars(i[1])
222                    else:
223                        conf[s][i[0]] = __handlePlaceholdersVars(i[1])
224
225                # check if include statement was defined -> process last
226                if "include" in conf[s]:
227
228                    # include sub configfile if requested
229                    extra = ConfigParser.SafeConfigParser()
230                    for e in conf[s]["include"]:
231                        extra.read(e)
232
233                        # only include current section
234                        if not extra.has_section(s):
235                            warnings.warn("To be included configuration file "
236                                          "'%s' not found." % e, SyntaxWarning)
237                            break
238
239                        for ie in extra.items(s):
240                            mergedConfig.set(s, *ie)
241                            conf[s][ie[0]] = __handlePlaceholdersVars(ie[1])
242
243                    del conf[s]["include"]
244                    mergedConfig.remove_option(s, "include")
245
246            if single:
247                break
248
249        # since configparser doesn't preserve order of entries, variables will
250        # be processed last.
251        for s in conf:
252            for i in conf[s]:
253                conf[s][i] = __handlePlaceholdersVars(conf[s][i], True)
254               
255        self.config = conf
256        self.__mergedConfig = mergedConfig
257
258    def __getattr__(self, name):
259        if name.startswith("sw"):
260            return getattr(self, "_Switches")[name[2:]]
261        else:
262            return object.__getattribute__(self, name)
263
264    def __setattr__(self, name, value):
265        if name.startswith("sw"):
266            if not name[2:] in self.__class__.__dict__["_Switches"].keys():
267                raise NameError("Unknown switch!")
268           
269            self.__class__.__dict__["_Switches"][name[2:]] = value
270        elif name == "echo_channel":
271            object.__setattr__(self, "echo_channel", value)
272        else:
273            self.__dict__[name] = value
274
275
276    def _setRuntimeVar(self, name, value):
277        self._setRuntimeVarTimed( name, value )
278       
279       
280    @timeit
281    def _setRuntimeVarTimed(self, name, value):
282        log_message(
283            "debug.runtime",
284            "Received internal update for '%s' (%s)" % (name, str(value))
285        )
286        if ":" in name:
287            name = name.split(":", 1)
288            name = ":".join([name[0], name[1].lower()])
289        else:
290            name = name.lower()
291
292        self.Runtime[name] = value
293
294
295    def _getRuntimeVar(self, name, msgid):
296        self._getRuntimeVarTimed( name, msgid )
297       
298       
299    @timeit
300    def _getRuntimeVarTimed(self, name, msgid):
301        """
302        Send out value of runtime var to listener @msgid
303        """
304        # if colon in name, let first part untouched
305        if ":" in name:
306            name = name.split(":", 1)
307            name = ":".join([name[0], name[1].lower()])
308        else:
309            name = name.lower()
310
311        value = self.Runtime.get(name, None)
312        msgs.sendMessage(msgid, value=value, msgid=msgid)
313
314        msg = "sent value of %s to %s" % (name, msgid)
315        log_message("debug.runtime.getruntime", msg)
316
317    @timeit
318    def _getRuntimeVarDirect(self, name ):
319        """
320        Returns value of runtime var bypassing the message system.
321        """
322        # if colon in name, let first part untouched
323        if ":" in name:
324            name = name.split(":", 1)
325            name = ":".join([name[0], name[1].lower()])
326        else:
327            name = name.lower()
328        value = self.Runtime.get(name, None)
329        return value
330
331    @Property
332    def echo_channel():
333        """
334        The echo_channel is a special property used for data output. As
335        default all output goes to stdout. Using the ECHO_CH command
336        this can be redirected to a file.
337
338        This method cares for proper handling of file-like objects. New
339        feature: It's possible to use any object offering the methods
340        "write" and "close", e.g. a stream.
341
342        The actual target of output is stored in the class attribute
343        "__echo_channel".
344        """
345
346        def fget(self):
347            return self.__echo_channel
348
349        def fset(self, value = None):
350            # clear setting
351            if not value:
352                fdel(self)
353                return
354
355            # We assume that there's a file for output or a stream (actually I
356            # don't really care about it's true nature, since it offers the
357            # methods "write" and "close").
358            if hasattr(value, "write") and hasattr(value, "close"):
359                self.__echo_channel = value
360            elif isinstance(value, basestring):
361                # open file
362                self.__echo_channel = open(value, "ab")
363            else:
364                raise ValueError("This echo channel cannot be used.")
365
366        def fdel(self):
367            if self.__echo_channel == sys.stdout:
368                return
369
370            # close open files/stream
371            if not self.__echo_channel.closed:
372                self.__echo_channel.close()
373
374            self.__echo_channel = sys.stdout
375
376        return locals()
377
378
379if __name__ == "__main__":
380    import doctest
381    doctest.testmod(exclude_empty=True)
Note: See TracBrowser for help on using the repository browser.