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

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