source: SHX/trunk/SeismicHandler/modules/parse.py @ 491

Revision 491, 27.6 KB checked in by marcus, 11 years ago (diff)
  • status propagation
  • configurable limit history length
  • simplyfied warning message if issued via logMessage
  • Property svn:eol-style set to native
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"""
19These classes cover the processing of SH scripting language.
20
21- "script" processes a stream (open file, stringIO, ...) of commands
22- "parse" parses one command
23- "translate" manages variable substitution
24- "symbol" cares for local and global symbols
25
26It's intended to be compatible with the original C code SH. Please file a ticket
27at http://www.seismic-handler.org/ if there's something wrong or missing.
28"""
29
30import os
31import re
32import inspect
33import traceback
34from copy import copy
35import SeismicHandler.commands as commands
36from SeismicHandler.basics import AttribDict
37from SeismicHandler.basics.error import ShxError
38from SeismicHandler.config import Settings
39from SeismicHandler.basics.messages import logMessage, msgs
40from SeismicHandler.core import Traces, Hidden
41
42
43class Parse(object):
44    """
45    Parse SH scripting language.
46
47    After successful parsing these attributes are set:
48    - input (original input),
49    - input_conv (maybe changed to upper case, depends on global setting),
50    - parameter
51    - qualifier
52    - suspectedFilename (unix filenames contain slashes, it just a guess)
53
54    These values can be also accessed via:
55    - class attribute "parsed" (dictionary)
56    - class attribute with "shx_" prefix (e.g. shx_input)
57
58    The parser analyses the command string:
59    >>> cmd = 'COMMAND /SWITCH1 PARAMETER1 /SWITCH2=4 PARAMETER2'
60    >>> x = Parse(cmd).parsed
61
62    The parsed command name is always lower case. It's used for comparision with
63    the "provides" attribute of the command classes.
64    >>> x["shx_command"]
65    'command'
66
67    Switches may occur at any position, but are commonly placed following the
68    command name:
69    >>> sorted(x["shx_qualifiers"].keys())
70    ['SWITCH1', 'SWITCH2']
71    >>> x["shx_qualifiers"]["SWITCH1"]
72    True
73    >>> x["shx_qualifiers"]["SWITCH2"]
74    '4'
75
76    Parameter are returned as list in the order of occurrence:
77    >>> x["shx_parameter"]
78    ['PARAMETER1', 'PARAMETER2']
79
80    One new feature is that the algorithm guesses a file name (slashes normally
81    indicate qualifiers). This only works, if the switches follow directly after
82    the command name!
83    >>> cmd = 'DELAY+SUM/BLA/FOO=1 BAR /tmp/test /SW FOO'
84    >>> x = Parse(cmd).parsed
85    >>> x["shx_parameter"]
86    ['BAR', 'FOO']
87    >>> x["shx_qualifiers"]["FOO"]
88    '1'
89
90    Please note that the "suspected" file name is also present as switch:
91    >>> sorted(x["shx_qualifiers"].keys())
92    ['BLA', 'FOO', 'SW', 'TEST', 'TMP']
93    >>> x["shx_suspectedFilename"]
94    '/TMP/TEST'
95
96    If more than one "possible filename is given, only the first one is
97    recognized. All following are only processed as qualifiers.
98    >>> x = Parse('COMMAND /tmp/x /tmp/y').parsed
99    >>> x["shx_suspectedFilename"]
100    '/TMP/X'
101
102    The semicolon is an alternative separator that is mostly used to skip
103    parameters:
104    >>> x = Parse("ECHO;;2 3 4 ;; 6 7").parsed
105    >>> x["shx_parameter"]
106    ['', '2', '3', '4', '', '6', '7']
107
108    >>> x = Parse('ECHO;;;;foo;bar;;').parsed
109    >>> x["shx_parameter"]
110    ['', '', '', 'FOO', 'BAR', '']
111    """
112
113    # regular expressions for parsing
114    re_cmd = "[+\w]+"
115    re_qual = "/[\w=]+"
116    re_file = " (/\w+)+ ?"
117
118    def __init__(self, input, switches=None):
119        if switches is None:
120            switches = Switches()
121
122        self.switches = switches
123        self.input = input
124
125        converted = False
126        if self.switches.Capcnv:
127            # If a command starts with @, no conversion to upper case is
128            # performed but the indicator char is removed.
129            if input.startswith("@"):
130                input = input[1:]
131            else:
132                input = input.upper()
133                converted = True
134
135        c = re.compile(self.re_cmd)
136        q = re.compile(self.re_qual)
137        f = re.compile(self.re_file)
138
139        cmd = c.search(input).group(0)
140        qual = q.findall(input)
141
142        # guess file
143        try:
144            sfile = f.search(input).group(0).strip()
145        except:
146            sfile = None
147
148        qualifiers = {}
149        # remove cmd and qualifiers from string
150        cmd_par = input.replace(cmd, "")
151        for qq in qual:
152            cmd_par = cmd_par.replace(qq, "")
153
154            # build dict of qualifiers
155            i = qq.split("=")
156            if len(i) > 1:
157                qualifiers[i[0][1:]] = i[1]
158            else:
159                qualifiers[i[0][1:]] = True
160
161        par = cmd_par.split()
162
163        # check for semicolon placeholders
164        parameter = []
165        for j in par:
166            if ";" in j:
167                j = j.split(";")
168
169                # correct leading and trailing
170                if j[:2] == ['','']:
171                    j = j[1:]
172                if j[-2:] == ['','']:
173                    j = j[:-1]
174
175                parameter.extend(j)
176            else:
177                parameter.append(j)
178
179        self._parsed = {
180            "shx_input": self.input,
181            "shx_input_conv": input,
182            "shx_command": cmd.lower(), # command always in lower case
183            "shx_converted": converted, # indicates cap conversion
184            "shx_parameter": parameter,
185            "shx_qualifiers": qualifiers,
186            "shx_suspectedFilename": sfile,
187        }
188
189    def __getattr__(self, name):
190        try:
191            p = object.__getattribute__(self, "_parsed")
192
193            if name.startswith("shx_"):
194                return p[name]
195
196            if "shx_%s" % name in p.keys():
197                return p["shx_%s" % name]
198
199            if name == "parsed":
200                return p
201        except AttributeError:
202            pass
203
204        try:
205            return object.__getattribute__(self, name)
206        except AttributeError:
207            raise ShxError("Attribute '%s' not found!" % name)
208
209
210class Script(object):
211    """
212    Read commands from stream.
213
214    GOTO targets are cached and removed from stream so that any GOTO command
215    must be handled inside this class.
216
217    Also the IF condition is evaluated here.
218
219    All other commands are promoted to their handler or treated as new script.
220
221    For testing purposes we use a stream and fill it with commands. Also a
222    symbol set is needed. To echo the commands, the global "Echo" switch must
223    be turned on.
224    >>> from StringIO import StringIO
225    >>> strm = StringIO("echo line1\\necho line2\\n")
226    >>> symb = Symbol()
227    >>> sw = Switches()
228    >>> sw.Echo = True
229    >>> x = Script(strm, symb, sw)
230    >>> x.run()
231    echo line1
232    echo line2
233
234    It's possible to add more commands at runtime. The script will continue
235    there.
236    >>> x.feed(StringIO("echo line3\\n"))
237    >>> x.run()
238    echo line3
239    """
240
241    def __init__(self, inputdata, symbols, switches, parameters=None, **kwargs):
242        """
243        Read in stream, skip empty lines and comments. Remember GOTO targets.
244
245        Input parameters are:
246        - stream object having readline function
247        - symbol class instance (containing global and local variables
248        - switches class instance
249        """
250
251        self.content = []
252        self.targets = {}
253
254        self.pointer = 0
255        self.symbols = symbols
256        self.switches = switches
257
258        # If a script is initially called there might be parameters and
259        # also qualifiers.
260        if parameters:
261            self.parameters = parameters
262
263        # current working directory
264        self.searchpath = ["."]
265
266        # additional paths
267        if "searchpath" in kwargs:
268            self.searchpath += kwargs["searchpath"]
269
270        # use default search path from configuration
271        self.searchpath += Settings.config.paths.scripts
272
273        try:
274            if hasattr(inputdata, "read"):
275                stream = inputdata
276            elif type(inputdata) == str:
277                # no extension -> add default
278                try:
279                    _ = inputdata.index(".")
280                except ValueError:
281                    inputdata = inputdata + ".SHC"
282
283                if self.switches.Capcnv:
284                    inputdata = inputdata.upper()
285
286                self.filename = inputdata
287
288                # look for file in search path
289                for folder in self.searchpath:
290                    try:
291                        stream = open(os.path.join(folder, inputdata), "r")
292                    except IOError:
293                        pass
294                    else:
295                        break
296
297            _ = stream
298        except:
299            # cannot open
300            raise ShxError("Input not readable!")
301
302        # Cache self-handled commands. These methods begin with "command".
303        commands = []
304        for i in inspect.getmembers(self, lambda x: inspect.ismethod(x)):
305            if i[0].startswith("command"):
306                commands.append(i[0][7:].lower())
307
308        self.internal_commands = commands
309
310        self.feed(stream)
311
312    def feed(self, stream):
313        """
314        Method for populating command list.
315
316        This method can be run several times in order to run commands from
317        interactive input. The local symbol set is hereby not changed.
318
319        Execution of commands will automatically continue after the last
320        command.
321
322        So it's no possible to use loops also in interactive scripts. But be
323        warned: It's not easy to get a proper exit GOTO target!
324        """
325
326        content = self.content
327        targets = self.targets
328        skipped = 0
329
330        for line, cmdstr in enumerate(stream):
331            cmdstr = cmdstr.strip()
332
333            # skip empty lines or comments
334            if len(cmdstr) and cmdstr[0] not in "!#-":
335                # remember goto target line?
336                if cmdstr.split()[0][-1] == ":":
337                    targets[cmdstr.upper()] = line - skipped
338
339                content.append(cmdstr)
340            else:
341                skipped += 1
342
343        self.content = content
344        self.targets = targets
345
346    def traceBack(self, error, msg = ""):
347        # This allows debugging as python users are used to it.
348        print "\n" + "*"*72
349        print self.__recent
350        print "*"*72
351        traceback.print_exc()
352        print "*"*72
353        msg = ";".join(error.args)
354
355        # Respect global settings:
356        if self.switches.Sherrstop:
357            import sys
358            print >> sys.stderr, error, msg
359            quit()
360
361        if self.switches.Cmderrstop:
362            raise ShxError(msg)
363
364        if not self.switches.Noerrmsg:
365            logMessage("error.traceback", msg)
366
367    def run(self):
368        self.__recent = None
369        while True:
370            try:
371                cmd = self.next()
372            except StopIteration:
373                break
374
375            if self.switches.Echo:
376                print cmd
377
378            cmd = Parse(cmd, switches=self.switches).parsed
379
380            # translate variables
381            _ = Translate(cmd, self, switches=self.switches)
382            self.__recent = cmd.get("shx_translated", cmd["shx_input_conv"])
383
384            # Check for internal command ...
385            if cmd["shx_command"] in self.internal_commands:
386                try:
387                    x = getattr(self, "command"+cmd["shx_command"].capitalize())
388                    x(*cmd["shx_parameter"], **cmd["shx_qualifiers"])
389                except Exception, e:
390                    self.traceBack(e)
391
392            # or execute command...
393            elif cmd["shx_command"] in commands.list:
394                if self.switches.Verify:
395                    print cmd["shx_translated"]
396
397                try:
398                    # also supply recent symbolset
399                    commands.list[cmd["shx_command"]](
400                        shx_symbols=self.symbols,
401                        shx_switches=self.switches,
402                        *cmd["shx_parameter"],
403                        **cmd["shx_qualifiers"]
404                    )
405                except Exception, e:
406                    self.traceBack(e)
407
408            # .. or start script.
409            else:
410                # use new symbols and switches set
411                symb = Symbol()
412                swit = Switches()
413                try:
414                    ns = Script(
415                        cmd["shx_command"],
416                        symb,
417                        swit,
418                        parameters=cmd,
419                        searchpath=self.searchpath,
420                    )
421                    ns.run()
422                except Exception, e:
423                    msg = "Cannot run script '%s'!" % cmd["shx_command"]
424                    self.traceBack(e, msg)
425
426    def next(self):
427        """
428        Iterate over commands.
429        """
430
431        try:
432            # skip goto targets
433            while self.pointer in self.targets.values():
434                self.pointer += 1
435
436            pnt = self.pointer
437            self.pointer += 1
438
439            return self.content[pnt]
440        except IndexError:
441            # lower pointer
442            self.pointer -= 1
443            raise StopIteration
444
445    def commandReturn(self):
446        # jump to end of script
447        self.pointer = len(self.content)
448
449    def commandDefault(self, *args):
450        """
451        Query default values.
452
453        Since this statement manipulates script option variables, it's not done
454        as command class.
455
456        Note: No qualifiers are supported! Input is read from stdin.
457        """
458
459        # no parameters given at all -> request user input
460        # set as class attribute for caching.
461        try:
462            self.userreq = self.userreq and True or \
463                                     len(self.parameters["shx_parameter"]) == 0
464        except AttributeError:
465            self.userreq = len(self.parameters["shx_parameter"]) == 0
466
467        id = int(args[0])-1
468        default = args[1]
469
470        val = None
471        try:
472            val = self.parameters["shx_parameter"][id]
473        except IndexError:
474            if self.userreq:
475                # request user input
476                if len(default):
477                    q = " [default: %s]: " % default
478                else:
479                    q = " : "
480
481                userinput = raw_input(" ".join(args[2:]) + q)
482                if userinput:
483                    default = userinput
484
485        if not val:
486            # fill from default
487            try:
488                self.parameters["shx_parameter"][id] = default
489            except IndexError:
490                self.parameters["shx_parameter"].append(default)
491
492    def commandGoto(self, target):
493        try:
494            self.pointer = self.targets[target.upper()]
495            msgs.sendMessage("command.run", cmd="goto", args=[target,])
496        except KeyError:
497            raise ShxError("GOTO target not found: %s" % target.upper())
498
499    def commandQuit(self, *args):
500        msgs.sendMessage("command.run", cmd="quit")
501        quit()
502
503    def commandIf(self, cmd):
504        pass
505
506    def IfGoto(self, cmd):
507        # cast format
508        cast = {
509                "S": str,
510                "I": int,
511                "R": float
512                }
513
514        cmp, check = cmd.p[2].value, cmd.p[2].value[:2]
515
516        var1 = cast[cmp[-1]](cmd.p[1].value)
517        var2 = cast[cmp[-1]](cmd.p[3].value)
518
519        # comparison
520        try:
521            comp = {
522                    "EQ": var1.__eq__,
523                    "NE": var1.__ne__,
524                    "GT": var1.__gt__,
525                    "GE": var1.__ge__,
526                    "LT": var1.__lt__,
527                    "LE": var1.__le__,
528                    }
529        except AttributeError:
530            # in python 2.5 integers have no __eq__, __gt__, ... methods :(
531            comp = {
532                    "EQ": lambda x: var1 == x,
533                    "NE": lambda x: var1 != x,
534                    "GT": lambda x: var1 > x,
535                    "GE": lambda x: var1 >= x,
536                    "LT": lambda x: var1 < x,
537                    "LE": lambda x: var1 <= x,
538                    }
539
540        # check condition
541        if comp[check](var2):
542            self.Goto(cmd.p[5].value)
543
544
545class Translate(object):
546    """
547    Translate variables in command. If a script class is used as second
548    parameter, it's symbol set will be used (e.g. for global symbols).
549
550    There are five basic types of variables:
551    1. user-defined symbols start with a quote: "foo
552    2. system variables start with a dollar sign: $DSPTRCS
553    3. trace variables start with a caret: ^delta(3)
554    4. passed options to command procedures start with a hash: #1
555    5. data from file access start with a percent: %filename(1)
556
557    After translation all parameter and qualifiers are replaced.
558
559    If the global option "Verify" is set the translated command string is saved
560    into "shx_translated". This string is rebuild from the translated parts,
561    so qualifiers may appear not in original order.
562
563    In order to test this class, we define a dummy command object.
564    >>> sw = Switches()
565    >>> sw.Verify = True
566    >>> cmd = {
567    ...    'shx_command': 'echo',
568    ...    'shx_converted': True,
569    ...    'shx_parameter': ['$PI', '$EXCLAMATION'],
570    ...    'shx_qualifiers': {'FOO': '$DOLLAR', 'BAR': True},
571    ... }
572    >>> _ = Translate(cmd, switches=sw)
573    >>> cmd['shx_translated']
574    'ECHO 3.1415926535897931 ! /FOO=$ /BAR'
575    """
576
577    system = {
578        "BLANK": " ",
579        "EXCLAMATION": "!",
580        "QUOTES": '"',
581        "DOLLAR": "$",
582        "PERCENT": "%",
583        "HAT": "^",
584        "BAR": "|",
585        "SLASH": "/",
586        "NUMBER": "#",
587        "PI": "3.1415926535897931",
588        "SH_ID": lambda: "SH_%i_" % os.getpid(),
589        "X": "$X", # really no idea for what this is good for...
590        "DSPTRCS": "_internalvars",
591        "TOTTRCS": "_internalvars",
592        "STATUS": "_internalvars",
593
594        # XXX todo
595        "SYSTIME": None,
596        "VERSION": None,
597        "DSP_X": None,
598        "DSP_Y": None,
599        "DSP_W": None,
600        "DSP_H": None,
601        "DSP_XMAX": None,
602        "DSP_YMAX": None,
603        "TITLESTYLE": None,
604        "TRCINFOSTYLE": None,
605        "ZEROTRCSTYLE": None,
606        "TIMEAXISSTYLE": None,
607        "MARKSTYLE": None,
608        "PMSTYLE": None,
609
610        # special treatment necessary
611        # syntax is HEXCHAR3B
612        "HEXCHAR": None,
613    }
614
615    def __init__(self, cmd=None, script=None, switches=None):
616        self.script = script
617
618        if switches is None:
619            switches = Switches()
620
621        self.switches = switches
622
623        # If no command structure is found, return silently.
624        if not cmd:
625            return
626
627        # translate parameters
628        for i, p in enumerate(cmd["shx_parameter"]):
629            cmd["shx_parameter"][i] = self._translate(p)
630
631        # translate qualifiers
632        for q in cmd["shx_qualifiers"]:
633            p = cmd["shx_qualifiers"][q]
634
635            # Skip qualifiers that act as switches.
636            if type(p) == bool:
637                continue
638
639            cmd["shx_qualifiers"][q] = self._translate(p)
640
641        # Actually this is only for debugging purposes.
642        if not switches.Verify:
643            return
644
645        qual = []
646        for q in cmd["shx_qualifiers"]:
647            if type(cmd["shx_qualifiers"][q]) == bool:
648                qual.append(q)
649            else:
650                qual.append("%s=%s" % (q, cmd["shx_qualifiers"][q]))
651
652        cmd["shx_translated"] = " ".join([
653            cmd["shx_converted"] and cmd["shx_command"].upper() or \
654                                                             cmd["shx_command"],
655            " ".join(cmd["shx_parameter"]),
656            len(qual) and "/" + " /".join(qual) or "",
657        ])
658
659    def _translate(self, value):
660        if not value:
661            return ''
662
663        idmap = {
664            '$': self.__handleSystem,
665            '"': self.__handleSymbol,
666            '^': self.__handleTrace,
667            '#': self.__handleOption,
668            '%': self.__handleFile,
669        }
670
671        id = value[0]
672
673        # concat operator
674        if id == "|":
675            parts = filter(None, value.split("|"))
676            newparts = [self._translate(i) for i in parts]
677            return "".join(newparts)
678
679        if id not in idmap.keys():
680            return value
681
682        value = value[1:]
683
684        try:
685            return idmap[id](value)
686        except NotImplementedError:
687            raise NotImplementedError
688        except:
689            raise ShxError("'%s' could not be translated!" % value)
690
691    def __handleSystem(self, name):
692        try:
693            x = self.system[name.upper()]
694        except KeyError:
695            if not name.upper()[:-2] == "HEXCHAR":
696                raise ShxError("System variable '%s' not found!" % name)
697            else:
698                try:
699                    x = chr(int(name[-2:], 16))
700                except:
701                    raise ShxError("Invalid hexadecimal value: %s" % name[-2:])
702
703        if x == None:
704            raise NotImplementedError
705
706        if callable(x):
707            return x()
708        else:
709            x = getattr(self, x, x)
710            if callable(x):
711                return x(name)
712            else:
713                return x
714
715    def __handleSymbol(self, name):
716        try:
717            return getattr(self.script.symbols, name)
718        except:
719            raise ShxError("Symbol '%s' not found!" % name)
720
721    def __handleTrace(self, name):
722        raise NotImplementedError
723
724    def __handleOption(self, name):
725        """
726        Return parameters set on procedure call.
727
728        Possible values:
729        #0 - filename
730        #1..#99 - parameter number xx
731        #name - qualifier "name"
732        """
733        if name.isdigit():
734            id = int(name)-1
735            if id >= 0:
736                return self.script.parameters['shx_parameter'][id]
737            else:
738                return self.script.filename
739        else:
740            return self.script.parameters['shx_qualifiers'][name]
741
742    def __handleFile(self, name):
743        # split line count (if any)
744        try:
745            f, lineno = name.split("(")
746        except ValueError:
747            lineno = 1
748            f = name
749        else:
750            lineno = int(lineno[:-1])
751
752        f = self._translate(f)
753
754        # if file not found, try fallback
755        flist = [f, os.path.join(Settings.config.paths.globals[0], f)]
756
757        for i, f in enumerate(flist):
758            p, f = os.path.split(f)
759            try:
760                _ = f.index(".")
761            except ValueError:
762                f += ".STX"
763
764            flist[i] = os.path.join(p, f)
765
766        for f in flist:
767            try:
768                for cnt, line in enumerate(open(f, "r").readlines()):
769                    if cnt + 1 == lineno:
770                        return line.strip()
771            except IOError:
772                continue
773
774        if lineno == 0:
775            return str(cnt+1)
776        else:
777            raise ShxError("Line %d not found in file '%s'!" % (lineno, f))
778
779    @staticmethod
780    def _internalvars(name):
781        return Settings.Runtime[name.lower()]
782
783
784class Symbol(object):
785    """
786    This class holds locals symbols. Access to global symbols is granted.
787
788    Important note: All symbol names are converted into upper case!
789
790    >>> s = Symbol()
791    >>> s.foo = 1
792    >>> s.foo
793    1
794    >>> s.setGlobal("bar", "qux")
795    >>> s.bar
796    'qux'
797
798    Local symbols mask global ones! This can be undone by deleting the symbol.
799    >>> s.bar = 5
800    >>> s.bar
801    5
802    >>> del s.bar
803    >>> s.bar
804    'qux'
805
806    If no local symbol exists, the global one will be removed.
807    >>> del s.bar
808    >>> s.bar #doctest: +ELLIPSIS
809    Traceback (most recent call last):
810    ...
811    ShxError: Symbol 'BAR' not found!
812
813    If one wants to delete a global symbol but keep the local symbol:
814    >>> s.setGlobal("bar", "global")
815    >>> s.bar
816    'global'
817    >>> s.bar = "local"
818    >>> s.deleteGlobal("bar")
819    >>> s.bar
820    'local'
821    """
822
823    def __init__(self):
824        # this is a singleton
825        self.__dict__["_globals"] = Settings.Globals
826
827    def __getattr__(self, name):
828        """
829        This method is called only, if the requested name was not found in
830        __dict__
831        """
832        name = name.upper()
833
834        # local vars (but changed case)
835        try:
836            return self.__dict__[name]
837        except KeyError:
838            pass
839
840        # next try inside globals
841        try:
842            return self._globals[name]
843        except KeyError:
844            raise ShxError("Symbol '%s' not found!" % name)
845
846    def __setattr__(self, name, value):
847        self.__dict__[name.upper()] = value
848
849    def __delattr__(self, name):
850        name = name.upper()
851
852        # delete local symbol
853        try:
854            del self.__dict__[name]
855        except KeyError:
856            pass
857        else:
858            return
859
860        self.deleteGlobal(name)
861
862    def setGlobal(self, name, value=None):
863        self._globals[name.upper()] = value
864
865    def deleteGlobal(self, name):
866        try:
867            del self._globals[name.upper()]
868        except KeyError:
869            raise ShxError("Symbol %s not found!" % name)
870
871
872class Switches(object):
873    """
874    This class holds Seismic Handler's switches.
875
876    Support for global switches is present via runtime's attributes.
877
878    >>> sw = Switches()
879    >>> sw.keeptraces
880    False
881
882    Only certain switches are allowed:
883    >>> sw.test #doctest: +ELLIPSIS
884    Traceback (most recent call last):
885    ...
886    ShxError: no such switch: Test
887
888    It's not possible to create new switches:
889    >>> sw.bar = 1 #doctest: +ELLIPSIS
890    Traceback (most recent call last):
891    ...
892    ShxError: Unsupported switch: Bar
893
894    "on" and True indicate switch activation:
895    >>> sw.keeptraces = "on"
896    >>> sw.keeptraces
897    True
898
899    All other values will set switch to False:
900    >>> sw.capcnv = "foo"
901    >>> sw.capcnv
902    False
903    """
904
905    def __init__(self):
906        """
907        On init global switches are used by default.
908        """
909        self._switches = copy(Settings._Switches)
910
911    def __getattr__(self, name):
912        if name.startswith("_"):
913            return object.__getattribute__(self, name)
914        else:
915            name = name.capitalize()
916
917            try:
918                return object.__getattribute__(self, "_switches")[name]
919            except:
920                raise ShxError("no such switch: %s" % name)
921
922    def __setattr__(self, name, value):
923        """
924        Only certain switches are allowed.
925
926        "on" and True: True
927        other: False
928        """
929        name = name.capitalize()
930        if name.startswith("_"):
931            object.__setattr__(self, name, value)
932        elif name in self._switches.keys():
933            sw = self._switches
934            if value is True or (hasattr(value, "lower") and value.lower() == "on"):
935                sw[name] = True
936            else:
937                sw[name] = False
938        else:
939            raise ShxError("Unsupported switch: %s" % name)
940
941    def keys(self):
942        """
943        Simulate dict's keys method.
944        """
945        return self._switches.keys()
946
947
948def getVar(name, symbolset=None, switches=None):
949    """
950    Helper function to translate any variable.
951
952    name: variable to translate ($pi, "foo, ^length(1), ...
953    """
954
955    if not symbolset:
956        s = Translate(switches=switches)
957    else:
958        x = AttribDict()
959        x.symbols = symbolset
960        s = Translate(None, x, switches=switches)
961
962    return s._translate(name)
963
964
965if __name__ == "__main__":
966    import doctest
967#    doctest.testmod(exclude_empty=True, verbose=True)
968    doctest.testmod(exclude_empty=True)
Note: See TracBrowser for help on using the repository browser.