NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

cfg.py (24772B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\script\sys\cfg.py
      2 import sys
      3 import types
      4 import copy
      5 import blue
      6 import carbon.common.script.sys.basesession as base
      7 import carbon.common.script.sys.crowset as crowset
      8 import carbon.common.script.sys.service as service
      9 import carbon.common.script.util.format as formatUtil
     10 import fsdBuiltData.common.character as fsdchar
     11 import localization
     12 import log
     13 import telemetry
     14 import uthread
     15 import utillib
     16 from carbon.common.script.sys.row import Row as utilRow
     17 from eve.common.script.sys import idCheckers
     18 from carbon.common.lib import const
     19 from eveprefs import boot
     20 from utillib import strx
     21 LogError = None
     22 LogInfo = None
     23 LogWarn = None
     24 LogNotice = None
     25 
     26 class DataConfig(service.CoreService):
     27     __guid__ = 'svc.dataconfig'
     28     __startupdependencies__ = []
     29     __servicename__ = 'dataconfig'
     30     __displayname__ = 'Data and Configuration Service'
     31     __exportedcalls__ = {'GetStartupData': [service.ROLE_SERVICE]}
     32     __notifyevents__ = ['OnCfgDataChanged', 'ProcessLoginProgressDone']
     33 
     34     def __init__(self):
     35         service.CoreService.__init__(self)
     36         self.cfg = None
     37 
     38     def _CreateConfig(self):
     39         return Config()
     40 
     41     def Run(self, memStream = None):
     42         global LogWarn
     43         global LogError
     44         global LogInfo
     45         global LogNotice
     46         service.CoreService.Run(self, memStream)
     47         self.state = service.SERVICE_START_PENDING
     48         import __builtin__
     49         if not hasattr(__builtin__, 'cfg'):
     50             __builtin__.cfg = self._CreateConfig()
     51             LogError = cfg.LogError = self.LogError
     52             LogInfo = cfg.LogInfo = self.LogInfo
     53             LogWarn = cfg.LogWarn = self.LogWarn
     54             LogNotice = cfg.LogNotice = self.LogNotice
     55             cfg.GetStartupData()
     56         self.state = service.SERVICE_RUNNING
     57 
     58     def Stop(self, memStream = None):
     59         tmp = cfg
     60         import __builtin__
     61         delattr(__builtin__, 'const')
     62         delattr(__builtin__, 'cfg')
     63         tmp.Release()
     64         service.CoreService.Stop(self)
     65 
     66     def ProcessLoginProgressDone(self):
     67         self.LogInfo('Login progress done, loading cfg data')
     68         cfg.LoadCfgData()
     69 
     70     def OnCfgDataChanged(self, what, data):
     71         uthread.new(cfg.OnCfgDataChanged, what, data)
     72 
     73     def DeleteRowTheHardWay(self, rs, keyIDs, keyCols):
     74         keyID, keyID2, keyID3 = keyIDs
     75         keyCol1, keyCol2, keyCol3 = keyCols
     76         delRow = None
     77         if keyCol3:
     78             for r in rs:
     79                 if r[keyCol1] == keyID and r[keyCol2] == keyID2 and r[keyCol3] == keyID3:
     80                     delRow = r
     81                     break
     82 
     83         elif keyCol2:
     84             for r in rs:
     85                 if r[keyCol1] == keyID and r[keyCol2] == keyID2:
     86                     delRow = r
     87                     break
     88 
     89         else:
     90             for r in rs:
     91                 if r[keyCol1] == keyID:
     92                     delRow = r
     93                     break
     94 
     95         if delRow:
     96             rs.remove(delRow)
     97 
     98     def GetStartupData(self):
     99         self.LogInfo('Getting startup data')
    100         cfg.GetStartupData()
    101 
    102 
    103 class ProxyServiceBroker():
    104 
    105     def ConnectToService(self, name):
    106         sess = base.GetServiceSession('cfg')
    107         return sess.ConnectToRemoteService(name)
    108 
    109 
    110 class Config():
    111     __guid__ = 'util.config'
    112 
    113     def __init__(self):
    114         self.servicebroker = None
    115         self.client = 0
    116         self.server = 0
    117         self.totalLogonSteps = 10
    118         self.fmtMapping = {}
    119         self.fmtMapping[const.UE_DATETIME] = lambda value, value2: formatUtil.FmtDate(value)
    120         self.fmtMapping[const.UE_DATE] = lambda value, value2: formatUtil.FmtDate(value, 'ln')
    121         self.fmtMapping[const.UE_TIME] = lambda value, value2: formatUtil.FmtDate(value, 'nl')
    122         self.fmtMapping[const.UE_TIMESHRT] = lambda value, value2: formatUtil.FmtDate(value, 'ns')
    123         self.fmtMapping[const.UE_MSGARGS] = lambda value, value2: cfg.GetMessage(value[0], value[1]).text
    124         self.fmtMapping[const.UE_LOC] = lambda value, value2: self._GetLocalization(value, value2)
    125         self.fmtMapping[const.UE_MESSAGEID] = lambda value, value2: self._GetLocalizationByID(value, value2)
    126         self.fmtMapping[const.UE_LIST] = lambda value, value2: self._GetList(value, value2)
    127 
    128     def UpdateCfgData(self, data, object, guid):
    129         if guid == Recordset.__guid__:
    130             keyidx = object.keyidx or object.header.index(object.keycolumn)
    131             object.data[data[keyidx]] = data
    132         elif guid == crowset.CFilterRowset.__guid__:
    133             keyidx = object.columnName
    134             index = data[keyidx]
    135             if index in object:
    136                 object[data[keyidx]].append(data)
    137             else:
    138                 object[data[keyidx]] = [data]
    139         else:
    140             raise RuntimeError('Call to OnCfgDataChanged unsupported container type.')
    141 
    142     def OnCfgDataChanged(self, what, data):
    143         if not hasattr(self, what):
    144             raise RuntimeError('Call to OnCfgDataChanged with invalid container reference.')
    145         object = getattr(self, what)
    146         if not object:
    147             raise RuntimeError('Call to OnCfgDataChanged failed to get a valid container.')
    148         if type(object) == types.DictType:
    149             object[data[0]] = data[1]
    150             return
    151         if not hasattr(object, '__guid__'):
    152             raise RuntimeError('Call to OnCfgDataChanged for unknown container type.')
    153         guid = getattr(object, '__guid__')
    154         if not guid:
    155             raise RuntimeError('Call to OnCfgDataChanged failed to get container type.')
    156         self.UpdateCfgData(data, object, guid)
    157         sm.ScatterEvent('OnPostCfgDataChanged', what, data)
    158 
    159     def Release(self):
    160         self.messages = None
    161         self.LogError = None
    162 
    163     def GetConfigSvc(self):
    164         if not self.servicebroker:
    165             sess = base.GetServiceSession('cfg')
    166             if 'config' in sm.services or boot.role == 'server':
    167                 config = sess.ConnectToService('config')
    168                 self.servicebroker = sess
    169                 self.server = 1
    170             elif boot.role == 'proxy':
    171                 self.servicebroker = ProxyServiceBroker()
    172                 self.client = 1
    173             else:
    174                 self.servicebroker = sess.ConnectToService('connection')
    175                 self.client = 1
    176         return self.servicebroker.ConnectToService('config')
    177 
    178     def GetSvc(self, serviceid):
    179         if not self.servicebroker:
    180             sess = base.GetServiceSession('cfg')
    181             self.servicebroker = sess
    182             self.server = 1
    183         return self.servicebroker.ConnectToService(serviceid)
    184 
    185     def GetStartupData(self):
    186         if boot.role != 'server':
    187             return
    188         self.AppGetStartupData()
    189 
    190     def AppGetStartupData(self):
    191         pass
    192 
    193     def ConvertData(self, src, dst):
    194         dst.data.clear()
    195         dst.header = src.columns
    196         keycol = src.columns.index(dst.keycolumn)
    197         for i in src:
    198             dst.data[i[keycol]] = i
    199 
    200     @telemetry.ZONE_METHOD
    201     def LoadCfgData(self):
    202         self.LogInfo('LoadCfgData')
    203         self.paperdollModifierLocations = fsdchar.GetModifierLocationRows()
    204         self.paperdollResources = fsdchar.GetResourceRows()
    205         self.paperdollSculptingLocations = fsdchar.GetSculptingLocationRows()
    206         self.paperdollColors = fsdchar.GetColorLocationRows()
    207         self.paperdollColorNames = fsdchar.GetColorNameRows()
    208         self.paperdollPortraitResources = fsdchar.GetPortraitResourceRows()
    209 
    210     def GetMessage(self, key, dict = None, onNotFound = 'return', onDictMissing = 'error'):
    211         if key not in self.messages:
    212             if onNotFound == 'return':
    213                 return self.GetMessage('ErrMessageNotFound', {'msgid': key,
    214                  'args': dict})
    215             if onNotFound == 'raise':
    216                 raise RuntimeError('ErrMessageNotFound', {'msgid': key,
    217                  'args': dict})
    218             else:
    219                 if onNotFound == 'pass':
    220                     return
    221                 raise RuntimeError('GetMessage: WTF', onNotFound)
    222         msg = self.messages[key]
    223         text, title, suppress = msg.messageText, None, 0
    224         if dict == -1:
    225             pass
    226         elif dict is not None:
    227             try:
    228                 if dict is not None and dict != -1:
    229                     dict = self.__prepdict(dict)
    230                 if text is not None:
    231                     text = text % dict
    232             except KeyError as e:
    233                 if onNotFound == 'raise':
    234                     raise
    235                 sys.exc_clear()
    236                 return self.GetMessage('ErrMessageKeyError', {'msgid': key,
    237                  'text': msg.messageText,
    238                  'args': dict,
    239                  'err': strx(e)})
    240 
    241         elif onDictMissing == 'error':
    242             if text and text.find('%(') != -1:
    243                 if onNotFound == 'raise':
    244                     raise RuntimeError('ErrMessageDictMissing', {'msgid': key,
    245                      'text': msg.messageText})
    246                 return self.GetMessage('ErrMessageDictMissing', {'msgid': key,
    247                  'text': msg.messageText})
    248         if text is not None:
    249             ix = text.find('::')
    250             if ix != -1:
    251                 title, text = text.split('::')
    252                 suppress = 0
    253             else:
    254                 ix = text.find(':s:')
    255                 if ix != -1:
    256                     title, text = text.split(':s:')
    257                     suppress = 1
    258         else:
    259             text, title, suppress = (None, None, 0)
    260         return utillib.KeyVal(text=text, title=title, type=msg.dialogType, audio=msg.urlAudio, icon=msg.urlIcon, suppress=suppress)
    261 
    262     def _GetLocalization(self, key, keyArgs = None):
    263         if boot.role == 'server':
    264             if keyArgs is None:
    265                 return key
    266             else:
    267                 return key + ' - ' + str(keyArgs)
    268         else:
    269             if keyArgs is None:
    270                 return localization.GetByLabel(key)
    271             for k, v in keyArgs.iteritems():
    272                 if type(v) != types.TupleType:
    273                     continue
    274                 value2 = None
    275                 if len(v) >= 3:
    276                     value2 = v[2]
    277                 keyArgs[k] = self.FormatConvert(v[0], v[1], value2)
    278 
    279             return localization.GetByLabel(key, **keyArgs)
    280 
    281     def _GetLocalizationByID(self, key, keyArgs = None):
    282         if boot.role == 'server':
    283             if keyArgs is None:
    284                 return 'MessageID: ' + str(key)
    285             else:
    286                 return 'MessageID: ' + str(key) + ' - ' + str(keyArgs)
    287         else:
    288             if keyArgs is None:
    289                 return localization.GetByMessageID(key)
    290             for k, v in keyArgs.iteritems():
    291                 if type(v) != types.TupleType:
    292                     continue
    293                 value2 = None
    294                 if len(v) >= 3:
    295                     value2 = v[2]
    296                 keyArgs[k] = self.FormatConvert(v[0], v[1], value2)
    297 
    298             return localization.GetByMessageID(key, **keyArgs)
    299 
    300     def _GetList(self, fmtList, separator = None):
    301         results = []
    302         for each in fmtList:
    303             value2 = None
    304             if len(each) >= 3:
    305                 value2 = each[2]
    306             results.append(self.FormatConvert(each[0], each[1], value2))
    307 
    308         if separator is not None:
    309             return separator.join(results)
    310         else:
    311             return localization.formatters.FormatGenericList(results)
    312 
    313     def __prepdict(self, dict):
    314         dict = copy.deepcopy(dict)
    315         for k, v in dict.iteritems():
    316             if type(v) != types.TupleType:
    317                 continue
    318             value2 = None
    319             if len(v) >= 3:
    320                 value2 = v[2]
    321             dict[k] = self.FormatConvert(v[0], v[1], value2)
    322 
    323         return dict
    324 
    325     __numberstrings__ = {1: 'one',
    326      2: 'two',
    327      3: 'three',
    328      4: 'four',
    329      5: 'five',
    330      6: 'six',
    331      7: 'seven',
    332      8: 'eight',
    333      9: 'nine',
    334      10: 'ten',
    335      11: 'eleven',
    336      12: 'twelve',
    337      13: 'thirteen',
    338      14: 'fourteen',
    339      15: 'fifteen',
    340      16: 'sixteen',
    341      17: 'seventeen',
    342      18: 'eighteen',
    343      19: 'nineteen',
    344      20: 'twenty',
    345      30: 'thirty',
    346      40: 'forty',
    347      50: 'fifty',
    348      60: 'sixty',
    349      70: 'seventy',
    350      80: 'eighty',
    351      90: 'ninety'}
    352     for each in range(19, 0, -1):
    353         __numberstrings__[each * 100] = __numberstrings__[each] + ' hundred'
    354         __numberstrings__[each * 1000] = __numberstrings__[each] + ' thousand'
    355         __numberstrings__[each * 100000] = __numberstrings__[each] + ' hundred thousand'
    356         __numberstrings__[each * 1000000] = __numberstrings__[each] + ' million'
    357         __numberstrings__[each * 1000000000] = __numberstrings__[each] + ' billion'
    358 
    359     def FormatConvert(self, formatType, value, value2 = None):
    360         if type(value) == types.TupleType:
    361             value2 = None
    362             if len(value) >= 3:
    363                 value2 = value[2]
    364             value = self.FormatConvert(value[0], value[1], value2)
    365         if formatType in self.fmtMapping:
    366             return self.fmtMapping[formatType](value, value2)
    367         else:
    368             return 'INVALID FORMAT TYPE %s, value is %s' % (formatType, value)
    369 
    370     def Format(self, key, dict = None):
    371         msg = self.GetMessage(key, dict, onNotFound='raise')
    372         if msg.title:
    373             return '%s - %s' % (msg.title, msg.text)
    374         else:
    375             return msg.text
    376 
    377 
    378 class Recordset():
    379     __guid__ = 'cfg.Recordset'
    380 
    381     def __init__(self, rowclass, keycolumn, dbfetcher = None, dbMultiFetcher = None, clientLocalFetcher = None):
    382         self.rowclass = rowclass
    383         self.dbfetcher = dbfetcher
    384         self.dbMultiFetcher = dbMultiFetcher
    385         if boot.role == 'client':
    386             self.clientLocalFetcher = clientLocalFetcher
    387         else:
    388             self.clientLocalFetcher = None
    389         self.header = []
    390         self.data = {}
    391         self.keyidx = None
    392         self.keycolumn = keycolumn
    393         self.locks = {}
    394         self.waitingKeys = set()
    395         self.knownLuzers = set([None])
    396         self.singlePrimeCount = 0
    397         self.singlePrimeTimestamp = 0
    398         if isinstance(self.dbfetcher, (list, tuple)):
    399             header, data = self.dbfetcher
    400             self.PopulateDataset(header, data)
    401 
    402     def PopulateDataset(self, header, data):
    403         self.header = header
    404         self.keyidx = self.header.index(self.keycolumn)
    405         self.__PopulateDataset(data)
    406 
    407     def GetKeyColumn(self):
    408         return self.keycolumn
    409 
    410     def GetLine(self, key):
    411         return self.data[key]
    412 
    413     def __len__(self):
    414         return len(self.data)
    415 
    416     def __str__(self):
    417         return self.__repr__()
    418 
    419     def __repr__(self):
    420         try:
    421             ret = '<Instance of Recordset.%s>\r\n' % (self.rowclass.__name__,)
    422             ret = ret + 'Key column: %s' % (self.keycolumn,)
    423             if len(self.header):
    424                 ret = ret + ', Cache entries: %d\r\n' % (len(self),)
    425                 ret = ret + 'Field names: '
    426                 for i in self.header:
    427                     ret = ret + i + ', '
    428 
    429                 ret = ret[:-2]
    430             else:
    431                 ret = ret + ', No cache entries.'
    432             return ret
    433         except:
    434             sys.exc_clear()
    435             return '<Instance of Recordset containing crappy data>'
    436 
    437     def __LoadData(self, key):
    438         if self.dbfetcher is None:
    439             return
    440         if type(self.dbfetcher) == types.StringType:
    441             conf = cfg.GetConfigSvc()
    442             fetch = getattr(conf, self.dbfetcher)
    443             fk = fetch(key)
    444             if isinstance(fk, Exception):
    445                 raise fk
    446             if type(fk) == types.TupleType:
    447                 self.header, data = fk
    448             elif hasattr(fk, '__columns__'):
    449                 self.header = fk.__columns__
    450                 data = [list(fk)]
    451             elif hasattr(fk, 'columns'):
    452                 self.header = fk.columns
    453                 data = fk
    454             else:
    455                 raise RuntimeError('_LoadData called with unsupported data type', fk.__class__)
    456         elif type(self.dbfetcher) in [types.ListType, types.TupleType]:
    457             self.header, data = self.dbfetcher
    458         else:
    459             fetch = self.dbfetcher
    460             self.header, data = fetch(key)
    461         if self.keyidx is None:
    462             self.keyidx = self.header.index(self.keycolumn)
    463         self.__PopulateDataset(data, key, None)
    464 
    465     def Hint(self, key, value):
    466         if key:
    467             if key in self.data and idCheckers.IsLocalIdentity(key):
    468                 return
    469             if len(value) < len(self.header):
    470                 log.LogError('Recordset.Hint is receiving a value that is less than the header length, key=', key, '| classType=', self.rowclass, '| value=', value)
    471             if len(self.header) == len(value):
    472                 self.data[key] = value
    473             else:
    474                 self.data[key] = value[:len(self.header)]
    475         elif hasattr(value, 'columns'):
    476             self.__PopulateDataset(value, None, list)
    477         else:
    478             raise RuntimeError('Hint called with unsupported data type', value.__class__)
    479 
    480     def Prime(self, keys, fuxorcheck = 1):
    481         if fuxorcheck:
    482             i = 0
    483             for each in keys:
    484                 if each not in self.data:
    485                     i += 1
    486 
    487             if i >= 50:
    488                 flag = [log.LGINFO, log.LGWARN][i >= 100]
    489                 log.general.Log('%s - Prime() called for %s items from %s' % (self.GetFetcherString(), i, log.WhoCalledMe()), flag)
    490             if boot.role == 'client' and i == 1:
    491                 NUM_SEC = 2
    492                 s = int(blue.os.GetWallclockTime() / (NUM_SEC * const.SEC))
    493                 if s > self.singlePrimeTimestamp + NUM_SEC:
    494                     self.singlePrimeCount = 0
    495                 self.singlePrimeTimestamp = s
    496                 self.singlePrimeCount += 1
    497                 if self.singlePrimeCount < 100:
    498                     tick = 10
    499                     flag = log.LGWARN
    500                 else:
    501                     tick = 50
    502                     flag = log.LGERR
    503                 if self.singlePrimeCount >= tick and self.singlePrimeCount % tick == 0:
    504                     log.general.Log('%s - Prime() called for %s single items. Last caller was %s. Caller might want to consider using Prime()' % (self.GetFetcherString(), self.singlePrimeCount, log.WhoCalledMe()), flag)
    505         if keys:
    506             self.waitingKeys.update(keys)
    507             uthread.Lock(self, 0)
    508             try:
    509                 self._Prime()
    510             finally:
    511                 uthread.UnLock(self, 0)
    512 
    513     def _Prime(self):
    514         if self.dbMultiFetcher is None:
    515             return
    516         if not self.waitingKeys:
    517             return
    518         keysToGet = self.waitingKeys
    519         localKeysToGet = set()
    520         keysIAlreadyHave = set()
    521         for key in keysToGet:
    522             if key in self.data:
    523                 keysIAlreadyHave.add(key)
    524             elif self.clientLocalFetcher is not None and idCheckers.IsLocalIdentity(key):
    525                 localKeysToGet.add(key)
    526 
    527         self.waitingKeys = set()
    528         keysToGet -= self.knownLuzers
    529         localKeysToGet -= self.knownLuzers
    530         keysToGet -= keysIAlreadyHave
    531         if len(localKeysToGet):
    532             localFetch = getattr(cfg, self.clientLocalFetcher)
    533             fk = localFetch(list(keysToGet))
    534             if isinstance(fk, Exception):
    535                 raise fk
    536             if type(fk) == types.TupleType:
    537                 self.header, data = fk
    538             elif hasattr(fk, 'columns'):
    539                 self.header = fk.columns
    540                 data = fk
    541             else:
    542                 data = []
    543                 cfg.LogWarn('Recordset: ', self.clientLocalFetcher, ' returned an empty record set from local data. Attempting to fetch from server to compensate!')
    544             if self.keyidx is None:
    545                 self.keyidx = self.header.index(self.keycolumn)
    546             ix = self.keyidx
    547             for i in data:
    548                 self.data[i[ix]] = i
    549                 keysToGet.remove(i[ix])
    550 
    551         if len(keysToGet) == 0:
    552             return
    553         conf = cfg.GetConfigSvc()
    554         fetch = getattr(conf, self.dbMultiFetcher)
    555         fk = fetch(list(keysToGet))
    556         if isinstance(fk, Exception):
    557             raise fk
    558         if type(fk) == types.TupleType:
    559             self.header, data = fk
    560         elif hasattr(fk, '__columns__'):
    561             self.header = fk.__columns__
    562             data = [list(fk)]
    563         elif hasattr(fk, 'columns'):
    564             self.header = fk.columns
    565             data = fk
    566         else:
    567             raise RuntimeError('_Prime called with unsupported data type', fk.__class__)
    568         if self.keyidx is None:
    569             self.keyidx = self.header.index(self.keycolumn)
    570         ix = self.keyidx
    571         for i in data:
    572             self.data[i[ix]] = i
    573             keysToGet.remove(i[ix])
    574 
    575         for luzer in keysToGet:
    576             self.knownLuzers.add(luzer)
    577             log.general.Log('Failed to prime ' + strx(luzer), 1, 1)
    578 
    579     def __PopulateDataset(self, data, key = None, factory = None):
    580         if hasattr(data, 'columns'):
    581             ix = data.columns.index(self.keycolumn)
    582         else:
    583             ix = self.header.index(self.keycolumn)
    584         gotit = 0
    585         for i in data:
    586             if factory is not None:
    587                 i = factory(i)
    588             if i[ix] not in self.data:
    589                 self.data[i[ix]] = i
    590             if key and i[ix] == key:
    591                 gotit = 1
    592             blue.pyos.BeNice()
    593 
    594         if key and not gotit:
    595             self.knownLuzers.add(key)
    596             log.LogTraceback()
    597 
    598     def RemoveFromKnowLuzers(self, key):
    599         self.knownLuzers.discard(key)
    600 
    601     def Get(self, key, flush = 0):
    602         key = int(key)
    603         if flush or key not in self.data:
    604             if boot.role != 'server':
    605                 self.Prime([key])
    606             else:
    607                 uthread.Lock(self, key)
    608                 try:
    609                     if flush or key not in self.data:
    610                         self.__LoadData(key)
    611                 finally:
    612                     uthread.UnLock(self, key)
    613 
    614         if key in self.data:
    615             return self.rowclass(self, key)
    616         raise KeyError('RecordNotFound', key)
    617 
    618     def GetIfExists(self, key):
    619         if key in self.data:
    620             return self.rowclass(self, key)
    621         else:
    622             return None
    623 
    624     def xxx__getitem__(self, index):
    625         if index > len(self.data):
    626             raise RuntimeError('RecordNotLoaded')
    627         return self.rowclass(self, self.data.keys()[index])
    628 
    629     def __getitem__(self, key):
    630         return self.rowclass(self, key)
    631 
    632     def __iter__(self):
    633 
    634         class RecordsetIterator:
    635 
    636             def next(self):
    637                 return self.rowset.rowclass(self.rowset, self.iter.next())
    638 
    639         it = RecordsetIterator()
    640         it.rowset = self
    641         it.iter = self.data.iterkeys()
    642         return it
    643 
    644     def __contains__(self, key):
    645         return key in self.data
    646 
    647     def GetFetcherString(self):
    648         if self.clientLocalFetcher is not None:
    649             if self.dbMultiFetcher is not None:
    650                 fetchers = '(Local: %s - Multi: %s)' % (self.clientLocalFetcher, self.dbMultiFetcher)
    651             else:
    652                 fetchers = '(Local: %s)' % self.clientLocalFetcher
    653         else:
    654             fetchers = '(Multi: %s)' % self.dbMultiFetcher
    655         return fetchers
    656 
    657 
    658 class Row(utilRow):
    659     __guid__ = 'cfg.Row'
    660     __persistvars__ = ['header', 'line', 'id']
    661     __slots__ = ('id',)
    662 
    663     def __init__(self, recordset = None, key = None):
    664         if recordset == None:
    665             self.header = None
    666             self.line = None
    667             self.id = None
    668         else:
    669             self.header = recordset.header
    670             self.line = recordset.data[key]
    671             self.id = key
    672 
    673     def __setattr__(self, name, value):
    674         if name in Row.__slots__ or name in utilRow.__slots__:
    675             object.__setattr__(self, name, value)
    676         else:
    677             raise RuntimeError('ReadOnly', name)
    678 
    679     def __setitem__(self, key, value):
    680         raise RuntimeError('ReadOnly', key)
    681 
    682     def __str__(self):
    683         return self.__repr__()
    684 
    685     def __repr__(self):
    686         try:
    687             ret = '<Instance of class ' + self.__guid__ + '>\r\n'
    688             header = self.header
    689             fields = self.line
    690             for i in xrange(len(header)):
    691                 ret = ret + '%s:%s%s\r\n' % (header[i], ' ' * (23 - len(header[i])), fields[i])
    692 
    693             return ret
    694         except:
    695             sys.exc_clear()
    696             return '<Instance of Row containing crappy data>'
    697 
    698     def __coerce__(self, object):
    699         if type(object) == type(0):
    700             return (self.id, object)
    701 
    702     def __int__(self):
    703         return self.id