Source

mana-core-pyjobtransformscore / python / trfutil.py

Full commit
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
import os,sys,re,exceptions,shutil
from AthenaCommon.Utils.unixtools import FindFile
from PyJobTransformsCore import trfconsts


def strip_suffix( aString, aSuffix ):
    """ Remove aSuffix from the end of aString (if present).
    Also first removed trailing whitespace"""
    # remove whitespace at the end
    newString = aString.rstrip()
    if newString.endswith( aSuffix ): newString = newString[:-len(aSuffix)]
    return newString


def append_path_env( env_name, what, sep=os.pathsep ):
    """Append <what> to pathlike environment variable <env_name>.
    (A list of paths separated by <sep>).
    If environment variable does not yet exist, it will
    be created and its value will be set that <what>"""
    try:
        var = os.environ[env_name]
    except KeyError:
        os.environ[env_name] = what
        return
    else:
        os.environ[env_name] = var + sep + what


def prepend_path_env( env_name, what, sep=os.pathsep ):
    try:
        var = os.environ[env_name]
    except KeyError:
        os.environ[env_name] = what
        return
    else:
        os.environ[env_name] = what + sep + var


def append_path_env_if( env_name, what, sep=os.pathsep ):
    """As append_path_env, except that <what> is only appended if it
    is not already present in the path."""
    try:
        var = os.environ[env_name]
    except KeyError:
        os.environ[env_name] = what
        return
    else:
        if what not in var.split(sep):
            os.environ[env_name] = var + sep + what
    

def prepend_path_env_if( env_name, what, sep=os.pathsep ):
    try:
        var = os.environ[env_name]
    except KeyError:
        os.environ[env_name] = what
        return
    else:
        if what not in var.split(sep):
            os.environ[env_name] = what + sep + var
    

def append_path_env_force( env_name, what, sep=os.pathsep ):
    """As append_path_env, except that <what> will be removed
    first if it is already present (not at the end)"""
    try:
        var = os.environ[env_name]
    except KeyError:
        os.environ[env_name] = what
        return
    else:
        varList = var.split(sep)
        try:
            idx = varList.index(what)
        except IndexError:
            os.environ[env_name] = var + sep + what
            return
        else:
            if idx != len(varList) - 1:
                varList.remove(what)
                varList.append(what)
                os.environ[env_name] = sep.join(varList)
                return


def prepend_path_env_force( env_name, what, sep=os.pathsep ):
    """As prepend_path_env, except that <what> will be removed
    first if it is already present (not at the beginning)"""
    try:
        var = os.environ[env_name]
    except KeyError:
        os.environ[env_name] = what
        return
    else:
        varList = var.split(sep)
        try:
            idx = varList.index(what)
        except IndexError:
            os.environ[env_name] = var + sep + what
            return
        else:
            if idx != 0:
                varList.remove(what)
                varList.insert(0,what)
                os.environ[env_name] = sep.join(varList)
                return



def find_file_env( filename, env_var_name ):
    try:
        env = os.environ[env_var_name]
    except KeyError:
        return None
    else:
        return FindFile( filename, re.split(',|' + os.pathsep, env ), os.R_OK )


def find_datafile( filename ):
    return find_file_env( trfconst.DATAPATH )


def find_joboptions( jobOptionsFile ):
    return find_file_env( os.path.expanduser( os.path.expandvars( jobOptionsFile ) ),
                          trfconsts.JOBOPTIONSPATH )


def find_library( lib ):
    return find_file_env( os.path.expandvars( lib ), trfconsts.LD_LIBRARY_PATH ) 


TRACEBACK_FILENAME = 0
TRACEBACK_LINENUMBER = 1
TRACEBACK_FUNCTION = 2
TRACEBACK_TEXT = 3
def find_in_stack( whatRE, where = TRACEBACK_TEXT ):
    import traceback
    stackList = traceback.extract_tb(sys.exc_info()[2])
    for s in stackList:
        if re.search( whatRE, s[where] ):
            return s

    return None



def load_module( module_filename ):
    """Extension of dynamic import. Load as module a python file with any path in the filename.
    File does not have to be in PYTHONPATH"""
    # mold the filename
    module_filename = os.path.normpath(module_filename)
    # remove .py suffix
    if module_filename.endswith('.py'): module_filename = module_filename[:-3]
    # explicit current dir
    if module_filename.startswith( os.curdir + os.sep ): module_filename = module_filename[2:]
    # updir somewhere
    if module_filename.startswith( os.pardir + os.sep ): module_filename = os.path.abspath( module_filename )
    
    # first try to load as is
    try:
        if os.path.isabs(module_filename): raise ImportError
        dotted = module_filename.replace(os.sep,'.')
        module = __import__( dotted )
        modlist = dotted.split('.')
        if len(modlist) > 1:
            for submod in modlist[1:]:
                module = getattr(module,submod) 
    except ImportError:
        # add path to system
        moddir,modname = os.path.split( module_filename )
        absdir = os.path.abspath( moddir )
        if moddir != os.curdir:
            # add module directory to search path
            inserted = False
            if absdir not in sys.path:
                sys.path.insert( 1, absdir )
                inserted = True
            module = __import__( modname )
            # cleanup search path
            if inserted: sys.path.remove( absdir )
        else:
            module = __import__( modname )

    return module


def load_transforms( trf_py, nameRE = r".*" ):
    """Return a list of JobTransform objects found in python file <trf_py>,
    whose names match the regular expression <nameRE>. Default is all objects in file."""
    # import transformation as module
    module = load_module( trf_py )
    trfs = [ ]
    for t in dir(module):
        obj = getattr(module,t)
        if isinstance(obj,module.JobTransform) and re.search( nameRE, obj.name() ):
            trfs.append( obj )

    return trfs


def load_errorhandlers( handler_py, nameRE = r".*" ):
    """Load a list of TransformErrorHandler objects found in python file handler_py,
    whose names (=class names) match the regular expression <nameRE>. Default is all objects in file."""
    # import errorhandler as module
    module = load_module( handler_py )
    handlers = [ ]
    for t in dir(module):
        obj = getattr(module,t)
        if isinstance(obj,module.TransformErrorHandler) and re.search( nameRE, obj.name() ):
            handlers.append( obj )

    return handlers


class Filename:
    """Utility class for file manipulation.
    It does not have the filename, but it can hold optionally the
    file type, contents and suffix."""
    def __init__(self,type=None,contents=None,suffix=None):
        """type: file type (root,txt,pool.root,doc,html,...)
        contents: file contents (evgen,simu,digi,esd,aod,...)
        suffix: the last part of the filename. If None (default),
                then the suffix is .<contents>.<type>
        Optional file attempt numbers are automatically added at the
        end of the type and suffix."""

        self._fileType = type
        if self._fileType is None:
            self._fileTypeRE = r".*"
        else:
            # Include the possibility of an attempt number at the end
            self._fileTypeRE = r"(.*)\.(%s)($|\.[0-9]+)" % type
        self._fileContents = contents
        self._fileSuffix = ''
        self._fileSuffixRE = r"(.*)"
        if suffix is None:
            # generate the default suffix: .<contents>.<type>
            if contents is not None:
                self._fileSuffix += '.' + contents
                self._fileSuffixRE += r"\.(%s)" % contents
            if type     is not None:
                self._fileSuffix += '.' + type
                self._fileSuffixRE += r"\.(%s)" % type
        else:
            self._fileSuffixRE += r"(%s)" % suffix

        if not self._fileSuffix:
            self._fileSuffix = None
            self._fileSuffixRE = r".*"
        else:
            # Add the possibility of an attempt number at the end
            self._fileSuffixRE += r"($|\.[0-9]+)"

            
    def hasFileType(self):
        return self._fileType is not None


    def fileType(self):
        return self._fileType


    def hasFileContents(self):
        return self._fileContents is not None


    def fileContents(self):
        return self._fileContents


    def hasFileSuffix(self):
        return self._fileSuffix is not None


    def fileSuffix(self):
        return self._fileSuffix


    def checkFileType(self,filename):
        """Check if filename ends with .<type>"""
        return not self.hasFileType() or re.search( self._fileTypeRE, filename )


    def checkFileSuffix(self,filename):
        """Check if filename ends with <suffix>"""
        return not self.hasFileSuffix() or re.search( self._fileSuffixRE, filename )


    def removeFileType(self,filename):
        return re.sub( self._fileTypeRE, r"\1", filename )


    def removeFileSuffix(self,filename):
        return re.sub( self._fileSuffixRE, r"\1", filename )


    def exists(filename): return os.path.exists(filename)
    exists = staticmethod(exists)


    def existsAttempt(filename):
        """Try if filename exists with attempt number appended. If file exists,
        return the filename including the attempt number, else return None"""
        pat = re.compile( r'%s\.[0-9]+' % filename )
        dir = os.path.dirname(filename) or os.curdir
        for f in os.listdir( dir ):
            if pat.search( f ): return f

        return None
    existsAttempt = staticmethod(existsAttempt)


    def isRegular(filename): return os.path.isfile(filename)
    isRegular = staticmethod(isRegular)
    

    def isReadable(filename):
        # best check is to try to open it. This covers all authorisation systems.
        try:
            f = open(filename)
        except IOError:
            return False
        else:
            f.close()
            return True
    isReadable = staticmethod(isReadable)


    def remove(filename):
        """Remove file if it exists"""
        if Filename.exists(filename): os.remove(filename)
    remove = staticmethod(remove)



class PoolDataFile( Filename ):
    defaultType = 'pool.root'
    defaultCatalogFilename = 'PoolFileCatalog.xml'
    defaultMessageLevel = 5

    def setMessageLevel(level,logger=None):
        os.environ['POOL_OUTMSG_LEVEL'] = str(level)
        if logger is not None:
            logger.info( "Setting POOL message level to %d" % level )
    setMessageLevel = staticmethod(setMessageLevel)



class EvgenFile:
    defaultContents = 'evgen'
    defaultType = PoolDataFile.defaultType


class HitsFile:
    defaultContents = 'hits'
    defaultType = PoolDataFile.defaultType


class RDOFile:
    defaultContents = 'rdo'
    defaultType = PoolDataFile.defaultType


class ESDFile:
    defaultContents = 'esd'
    defaultType = PoolDataFile.defaultType
   

class AODFile:
    defaultContents = 'aod'
    defaultType = PoolDataFile.defaultType
   

class HistogramFile:
    defaultContents = 'histo'
    defaultType = 'root'


class NtupleFile( Filename ):
    defaultContents = 'ntup'
    defaultType = 'root'
    


class CommentLine:
    """Utility class to generate python comment lines from a help string"""
    def __init__(self,comment):
        self._comment = comment

        
    def getLine( char, width = 80 ): return '#' + width * char
    getLine = staticmethod(getLine)


    def dashLine( width = 80 ): return '#' + width * '-'
    dashLine = staticmethod(dashLine)


    def hashLine( width = 80 ): return '#' + width * '#'
    hashLine = staticmethod(hashLine)


    def __str__(self):
        return self.smallComment()


    def smallComment(self):
        return '# ' + self._comment.replace(os.linesep, os.linesep + '# ')


    def bigComment(self,char='-',width=80):
        line = CommentLine.getLine(char,width)
        return line + os.linesep + \
                self.smallComment() + os.linesep + \
                line

#
# end of class CommentLine
#


class Author:
    def __init__(self,name,email):
        self._name = name
        self._email = email

    def __str__(self):
        return '%s <%s>' % (self._name, self._email)
    
    def name(self):
        return self._name

    def email(self):
        return self._email



class StringNumberList:
    """List of strings only differing by a number they contain, where the list of numbers is coded in a special syntax.
    <prefix>[<list>]<postfix> where '[' and ']' are litteral characters and <list> gives a recipe for a list of
    numbers to be generated (with <n> and <m> integers): 
    <n>,<m> (enumeration) or <n>-<m> (range including m) or any combination of those.
    A python list of strings is generated where the [<list>] is replaced by the actual integers that the list represents.
    The [<list>] part can also be omitted, in which case a list of one string is generated (as given).
    The width of the integers is determined by the integer with the most number of digits in the [<list>], where leading 0's
    are included in the count. The integers in the expanded filenames have leading 0's padded where needed."""

    def __init__(self, codedList=None):
        self.set( codedList )

        
    def set(self, codedString):
        self._codedString = codedString
        self._numbers = None
        self._prefix = None
        self._suffix = None
        self._numberList = None
        self._digits = 0
        # return self for easy chaining of commands
        return self


    def getPrefix(self,openBracket=-1):
        """Get everything up to (but not including) the [.
        If [ is not found, return the full string. In case of error, return None.
        The <openBracket> argument (optional) is there for optimisation and gives
        the position of the '[' in the coded string (or -1 if unknown)."""
        if self._prefix is not None: return self._prefix
        valIn = self._codedString
        if valIn is None: return None #signalling error
        if openBracket != -1:
            assert valIn[openBracket] == '['
            bopen = openBracket
        else:
            bopen = valIn.find('[')
            if bopen == -1: self._prefix = valIn
        if bopen > 0:
            self._prefix = valIn[:bopen]
        else:
            self._prefix = valIn

        return self._prefix


    def getSuffix(self,closeBracket=-1):
        """Get everything after the ].
        If ] is not found, return emptry string. In case of error, return None"""
        if self._suffix is not None: return self._suffix
        valIn = self._codedString
        if valIn is None: return None #signalling error
        if closeBracket != -1:
            assert valIn[closeBracket] == ']'
            bclose = closeBracket
        else:
            bclose = valIn.find(']')
            if bclose == -1: self._suffix = ""
        if bclose < len(valIn) - 1:
            self._suffix = valIn[bclose+1:]
        else:
            self._suffix = ""
        return self._suffix
    

    def getNumbers(self,openBracket=-1,closeBracket=-1):
        """Get the part in between [ and ], including the [].
        If [] part is not found, return empty string. In case of error, return None"""
        if self._numbers is not None: return self._numbers
        valIn = self._codedString
        if valIn is None: return None #signalling error
        if openBracket != -1:
            assert valIn[openBracket] == '['
            bopen = openBracket
        else:
            bopen = valIn.find('[')
        if closeBracket != -1:
            assert valIn[closeBracket] == ']'
            bclose = closeBracket
        else:
            bclose = valIn.find(']',bopen + 1)

        if bopen == -1 and bclose == -1:
            self._numbers = ""
        elif bopen == -1 or bclose == -1 or bclose < bopen + 2:
            self._numbers = None
        else:
            self._numbers = valIn[bopen:bclose+1]

        return self._numbers

                
    def getNumberList(self,openBracket=-1,closeBracket=-1):
        """Return a tuple of size 2, containing the list of integers in the first field and
        the number of digits in the second field. The list of integers is the result of
        the decoding of the numbers coded in the [] part of the input string. The number
        of digits is the maximum number of digits used in the numbers in the [] part,
        where leading 0's are included in the count.
        If no '[]' part is found, return an tuple with an empty list. In case of error,
        returns (None,None)."""
        if self._numberList is not None: return (self._numberList,self._digits)
        nums = self.getNumbers(openBracket,closeBracket)
        if nums is None: return (None,None)
        if nums is "": return (list(),0)
        numList = [ ]
        bclose = len(nums)
        posB = 1
        digits = 0
        while posB <= bclose:
            #always start with an digit
            posE = posB
            while posE < bclose and nums[posE].isdigit(): posE += 1
            # require at leaste one digit
            if posE == posB: return (None,None)
            # convert to integer
            digits = max(digits,posE - posB)
            iNum = int(nums[posB:posE])
            charE = nums[posE]
            if charE == ']': # last number
                numList.append( iNum )
                break
            elif charE == ',':  # single number
                numList.append( iNum )
                posB = posE + 1
                continue
            elif charE == '-':  # next comes end of range
                posB = posE + 1
                posE = posB
                while posE < bclose and nums[posE].isdigit(): posE += 1
                # require at leaste one digit
                if posE == posB: return (None,None)
                # convert to integer    
                digits = max(digits,posE - posB)
                iStop = int(nums[posB:posE])
                if iStop < iNum: return (None,None)
                numList += range(iNum,iStop+1)
                charE = nums[posE]
                if charE == ',':
                    posB = posE + 1
                    continue
                elif charE == ']':
                    break
                else:
                    return (None,None)
            else: # spurious character
                return (None,None)

        self._numberList = numList
        self._digits = digits

        return (self._numberList,self._digits)


    def convertStringList(self,codedString):
        return self.set(codedString).getStringList()


    def getStringList(self):
        """Convert coded string <valIn> into an expanded list. If <valIn> contains a syntax error, None is returned."""
        openBracket = self._codedString.find('[')
        closeBracket = self._codedString.find(']',openBracket + 1)
        numList,digits = self.getNumberList(openBracket,closeBracket)
        if numList is None: return None
        if len(numList) == 0: return [ self._codedString ]

        prefix = self.getPrefix(openBracket)
        if prefix is None: return None

        suffix = self.getSuffix(closeBracket)
        if suffix is None: return None
        return [ '%s%0*d%s' % (prefix,digits,i,suffix) for i in numList ]
  
    

class JobOptionsFile:
    def __init__(self,filename):
        self._filename = filename


    def filename(self):
        return self._filename


    def setFilename(self,filename):
        self._filename = filename


    def preRunAction(self):
        """Check that joboptions file can be found.
        Check is skipped if filename is empty"""
        if self._filename and not find_joboptions( self._filename ):
            raise JobOptionsNotFoundError( self._filename )
    


class PreJobOptionsFile(JobOptionsFile):
    def __init__(self,filename):
        JobOptionsFile.__init__(self,filename)


    def preRunAction(self):
        JobOptionsFile.preRunAction(self)



class PostJobOptionsFile(JobOptionsFile):
    def __init__(self,filename):
        JobOptionsFile.__init__(self,filename)


    def preRunAction(self):
        JobOptionsFile.preRunAction(self)




class RDBAccessOverride(PostJobOptionsFile):
    """Override settings of RDBAccessSvc. Properties that can be overridden:
    UseDBConnSvc,Technology,HostName.
    Optionally the name of an environment variable can be given. If none
    is given, the override is always applied. If one is given, the override is
    applied only if the environment variable is defined. If the contents of the
    environment variable is non-empty, it will be used to set the HostName field,
    otherwise the defaultHostname will be used."""

    def __init__(self,useDBConnSvc,technology,defaultHostname,environmentHostname=None):
        """
        useDBConnSvc: True or False (python objects, not strings). Will be used
                      to set UseDBConnSvc to TRUE or FALSE.
        technology:   string that is passed on to Technology
        defaultHostname: used for HostName if environmentHostname if set to None,
                      or if the value of the environment variable given by
                      environmentHostname contains an empty string.
        environmentHostname: If None (default), then override is always applied using
                      defaultHostname for HostName. If a (string) value is given, its
                      value is interpreted as a name of an environment variable. If
                      the environment variable is not set at runtime, no override is
                      applied. If the environment variable is set to an empty string,
                      the overrride is applied using defaultHostname for HostName.
                      If the environment variable is set to a non-empty string, the
                      string is assigned to HostName."""

        PostJobOptionsFile.__init__(self,"")
        self._useDBConnSvc = useDBConnSvc
        self._technology   = technology
        self._defaultHost  = defaultHostname
        self._environHost  = environmentHostname
        self._usedHost = None


    def setHostName(self,host):
        self._usedHost = host


    def getHostName(self):
        if self._usedHost is None:
            host = self._defaultHost
            environHost = self._environHost
            if environHost is not None:
                hostEnv = os.environ.get(environHost,"")
                if hostEnv: host = hostEnv
            self._usedHost = host

        return self._usedHost


    def preRunAction(self):
        """Create joboptions file to support SQLite for geometry database.
        file if T_SQLITEGEOM environment
        variable is set. Else set filename to empty string"""
        # set the hostname
        host = self.getHostName()
        if environHost is not None and self._environHost not in os.environ.keys():
            return

        if os.path.isabs(host):
            fulldb = host
        else:
            fulldb = find_datafile(host)
       
        if fulldb and os.path.exists(fulldb):
            # convert UseDBConnSvc
            if self._useDBConnSvc:
                useConn = 'TRUE'
            else:
                useConn = 'FALSE'
            # make jobOptions snippet
            jo = [ '# override DB Access' ,
                   'RDBAccessSvc = Service( "RDBAccessSvc" )' ,
                   'RDBAccessSvc.UseDBConnSvc = %s' % (useConn) ,
                   'RDBAccessSvc.Technology   = %r' % (self._technology) ,
                   'RDBAccessSvc.HostName     = %r' % (host) ]
            # make (hopefully) unique yet meaningful filename
            filename = 'RDBAccessSvc_%s_%s_%s.py' % (useConn, self._technology,
                                                     os.path.basename(host))
        
            joFile = open(filename)
            joFile.write( os.linesep.join(jo) + os.linesep )
            joFile.close()
            self.setFilename(filename)

        # only now call baseclass preRunAction()
        PostJobOptionsFile.preRunAction(self)



class SQLiteSupport(RDBAccessOverride):
    """Overrides some proporties of RDBAccessSvc, to use sqlite database.
    Makes and uses local copy of database to circumvent the problem that
    only one user can use a database at a time. See also class RDBAccessOverride."""
    def __init__(self,defaultHostname,environmentHostname=None):
        """For meaning of arguments, see class RDBAccessOverride."""
        RDBAccessOverride.__init__(self,False,"sqlite",defaultHostname,environmentHostname)


    def preRunAction(self):
        # an sqlite DB can only have one user at the time, so make a local copy of the db
        # and prepend current path to DATAPATH, so it will be found first.
        host = self.getHostName()
        if os.path.isabs(host):
            fulldb = host
        else:
            fulldb = find_datafile(host)
        if fulldb and os.path.exists(fulldb):
            host = os.path.basename(host)
            shutil.copyfile(fulldb,host)
            # prepend current path to DATAPATH, so this copy is found
            # by PathResolver in RDBAccessSvc.
            prepend_path_env_force(trfconsts.DATAPATH,os.curdir)
            # override hostName to correspond to new situation
            self.setHostName( host )

        # only now call baseclass preRunAction
        RDBAccessOverride.preRunAction(self)


class SQLiteGeomSupport(SQLiteSupport):
    def __init__(self):
        SQLiteSupport.__init__(self,'geomDB_sqlite','T_SQLITEGEOM')