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

Revision 490, 9.6 KB checked in by marcus, 11 years ago (diff)
  • changed keeptraces default to SH standard (command line version keeps traces, but SHM replaces traces by default).
  • 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).
4#
5#    SHX is free software: you can redistribute it and/or modify
6#    it under the terms of the GNU Lesser General Public License as published
7#    by the Free Software Foundation, either version 3 of the License, or
8#    (at your option) any later version.
9#
10#    SHX is distributed in the hope that it will be useful,
11#    but WITHOUT ANY WARRANTY; without even the implied warranty of
12#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#    GNU Lesser General Public License for more details.
14#
15#    You should have received a copy of the GNU Lesser General Public License
16#    along with SHX.  If not, see <http://www.gnu.org/licenses/>.
17
18"""
19This object carries all settings for runtime including global variables.
20
21E.g. Echo Channel, Cap Conversion, ...
22"""
23
24# Please note that in this file no logging takes place since configuration
25# options for logging are initialized here first.
26# Also _all_ code in modules/* use runtime.py so that any defined method under
27# "modules" should not be used here (to avoid import loops).
28
29
30import sys
31import os
32import ConfigParser
33import warnings
34from SeismicHandler.basics import Property, Singleton, AttribDict
35from SeismicHandler.basics.messages import msgs
36
37
38class runtime(object):
39    """
40    >>> Settings = runtime()
41
42    >>> print >> Settings.echo_channel, Settings.echo_channel
43    >>> Settings.echo_channel = "TEST"
44    >>> print >> Settings.echo_channel, Settings.echo_channel
45    >>> Settings.echo_channel = ""
46    >>> print >> Settings.echo_channel, Settings.echo_channel #doctest: +ELLIPSIS
47    <doctest...
48   
49    Now a file named TEST should contain
50    >>> open("TEST").read() #doctest: +ELLIPSIS
51    "<open file 'TEST', mode 'ab' at 0x..."
52
53    This is only for clean-up:
54    >>> import os
55    >>> os.unlink("TEST")
56    """
57    __metaclass__ = Singleton
58
59    # see echo_channel()
60    __echo_channel = sys.stdout
61   
62    # configuration is read from file
63    config = None
64
65    # global switches (see http://www.seismic-handler.org/portal/wiki/ShSwitch)
66    _Switches = AttribDict({
67        # convert to upper case
68        "Capcnv": True,
69        # echo command before execution
70        "Echo": False,
71        # verify command
72        "Verify": False,
73        # cancel script on error
74        "Cmderrstop": True,
75        # exit SH on error
76        "Sherrstop": False,
77        # no error message at all
78        "Noerrmsg": False,
79        # verbose
80        "Chatty": True,
81        # indicate startup file
82        "Startup": False,
83        # SHX: add new traces or discard old ones, to be altered only globally!
84        "Keeptraces": True,
85    })
86
87    # dictionary of global variables
88    Globals = AttribDict()
89
90    # container for internal data like $status
91    Runtime = AttribDict({
92        "status": 0,
93        "dsptrcs": 0,
94        "tottrcs": 0,
95    })
96
97    def __init__(self):
98        if not self.config:
99            self.readConfig()
100            msgs.subscribe(self._setRuntimeVar, 'runtime')
101
102    def readConfig(self):
103        """
104        Read configuration file.
105
106        Possible locations of shx.conf
107        1. installation directory
108        2. /etc/
109        3. $HOME/.shx/
110        All configurations files found are processed in this order!
111
112        Finally the variable "conf" contains the full parsed configuration and
113        "fullconfig" contains the merged configuration with replacements.
114        """
115
116        # helper function
117        def __handlePlaceholdersVars(values, replacevars=False):
118            """
119            Replace placeholders in config file.
120            """
121
122            # split values
123            if type(values) != list:
124                values = map(str.strip, values.split(","))
125           
126            ret = []
127            for v in values:
128                if v.find("[") >= 0:
129                    # replacement requested
130                    for d in replace.keys():
131                        if v.find("[%s]" % d) >= 0:
132                            # do replacement
133                            ret.append(v.replace("[%s]" % d, replace[d]))
134                elif replacevars and v.startswith("$"):
135                    # variable substitution
136                    try:
137                        ret.extend(vars[v.lower()])
138                    except KeyError:
139                        warnings.warn("Variable '%s' not found!" % v, SyntaxWarning)
140                else:
141                    ret.append(v)
142
143            return ret
144
145        # "main" function
146        dirs_check = [
147            ["SHX", os.path.abspath(__file__).rsplit(os.path.sep, 2)[0]],
148            ["ETC", "/etc/SeismicHandler"], # XXX not portable!
149        ]
150
151        dir_home = os.getenv("HOME", None)
152
153        if dir_home:
154            dirs_check.append(["PRIVATE", os.path.join(dir_home, ".shx")])
155            # XXX not portable!
156
157        dirs = []
158        replace = {}
159        for d in dirs_check:
160            src = os.path.join(d[1], "shx.conf")
161            replace[d[0]] = d[1]
162            if os.path.exists(src):
163                dirs.append(src)
164
165        conf = AttribDict()
166        vars = AttribDict()
167
168        # includes merged configuration without replacements
169        # mainly useful for debugging
170        mergedConfig = ConfigParser.SafeConfigParser()
171
172        for src in dirs:
173            config = ConfigParser.SafeConfigParser()
174            config.read(src)
175
176            for s in config.sections():
177                if not mergedConfig.has_section(s):
178                    mergedConfig.add_section(s)
179
180                if s not in conf.keys():
181                    conf[s] = AttribDict()
182
183                for i in config.items(s):
184                    mergedConfig.set(s, *i)
185                   
186                    # check for variable definition
187                    if i[0].startswith("$"):
188                        v = i[0].lower()
189                        if v in vars:
190                            warnings.warn("Variable '%s' defined more "
191                                          "than once!" % v, SyntaxWarning)
192
193                        vars[v] = __handlePlaceholdersVars(i[1])
194                    else:
195                        conf[s][i[0]] = __handlePlaceholdersVars(i[1])
196
197                # check if include statement was defined -> process last
198                if "include" in conf[s]:
199
200                    # include sub configfile if requested
201                    extra = ConfigParser.SafeConfigParser()
202                    for e in conf[s]["include"]:
203                        extra.read(e)
204
205                        # only include current section
206                        if not extra.has_section(s):
207                            warnings.warn("To be included configuration file "
208                                          "'%s' not found." % e, SyntaxWarning)
209                            break
210
211                        for ie in extra.items(s):
212                            mergedConfig.set(s, *ie)
213                            conf[s][ie[0]] = __handlePlaceholdersVars(ie[1])
214
215                    del conf[s]["include"]
216                    mergedConfig.remove_option(s, "include")
217
218        # since configparser doesn't preserve order of entries, variables will
219        # be processed last.
220        for s in conf:
221            for i in conf[s]:
222                conf[s][i] = __handlePlaceholdersVars(conf[s][i], True)
223               
224        self.config = conf
225        self.__mergedConfig = mergedConfig
226
227    def __getattr__(self, name):
228        if name.startswith("sw"):
229            return getattr(self, "_Switches")[name[2:]]
230        else:
231            return object.__getattribute__(self, name)
232
233    def __setattr__(self, name, value):
234        if name.startswith("sw"):
235            if not name[2:] in self.__class__.__dict__["_Switches"].keys():
236                raise NameError("Unknown switch!")
237           
238            self.__class__.__dict__["_Switches"][name[2:]] = value
239        elif name == "echo_channel":
240            object.__setattr__(self, "echo_channel", value)
241        else:
242            self.__dict__[name] = value
243
244    def _setRuntimeVar(self, name, value):
245        self.Runtime[name] = value
246
247    @Property
248    def echo_channel():
249        """
250        The echo_channel is a special property used for data output. As
251        default all output goes to stdout. Using the ECHO_CH command
252        this can be redirected to a file.
253
254        This method cares for proper handling of file-like objects. New
255        feature: It's possible to use any object offering the methods
256        "write" and "close", e.g. a stream.
257
258        The actual target of output is stored in the class attribute
259        "__echo_channel".
260        """
261
262        def fget(self):
263            return self.__echo_channel
264
265        def fset(self, value = None):
266            # clear setting
267            if not value:
268                fdel(self)
269                return
270
271            # We assume that there's a file for output or a stream (actually I
272            # don't really care about it's true nature, since it offers the
273            # methods "write" and "close").
274            if hasattr(value, "write") and hasattr(value, "close"):
275                self.__echo_channel = value
276            elif isinstance(value, basestring):
277                # open file
278                self.__echo_channel = open(value, "ab")
279            else:
280                raise ValueError("This echo channel cannot be used.")
281
282        def fdel(self):
283            if self.__echo_channel == sys.stdout:
284                return
285
286            # close open files/stream
287            if not self.__echo_channel.closed:
288                self.__echo_channel.close()
289
290            self.__echo_channel = sys.stdout
291
292        return locals()
293
294
295if __name__ == "__main__":
296    import doctest
297    doctest.testmod(exclude_empty=True)
Note: See TracBrowser for help on using the repository browser.