#---------------------------------------------------------------------- # poo.py JJS 05/20/99 # # This is the main module for POO (Pythonic MOO). # See: http://www.strout.net/python/poo/ #---------------------------------------------------------------------- import string import pickle import sys import os from types import * from tostr import * from time import time import copy import marksub import msg from pooparse import HandleCommand, CmdDef gObjlist = {} # global dictionary of POO objects gHighID = 0 # highest ID used gProps = {} # maps an object to a property dictionary gCmdDefs = {} # maps a verb to a sequence of matching commands gContents = {} # maps an object to a list of contents gCmdLine = '' # copy of command typed by the user gSafeGlobals = {} # "safe" globals for user's environment gLocals = {} # maps user object to a dict of local vars gUpdateList = [] # list of objects which want updates gOwner = None # object which owns current function (etc.) gUser = None # object who invoked the current command gCallers = [None] # sort of a traceback list of calling objects gUnpickling = 0 # set to 1 when unpickling gLastSave = time() # time database was last saved gFilePath = os.path.join(os.curdir,'poofiles') # path for user-accessible files gTreasury = 100000 # credits for use by wizards flushNoCR = 0 # set to 1 if should flush() after tell_NoCR maxSaveTime = 1800 # maximum time between saves (seconds) kObjValue = 90 # standard "value" of objects, in credits kCreationTax = 10 # extra cost of creating an object -- goes to treasury kRecycleTax = 40 # tax levied when recycling an object kInbufLimit = 20 # maximum number of lines in "hear" buffer #---------------------------------------------------------------------- # FUNCTION: copyList #---------------------------------------------------------------------- def copyList( fromwhat, intowhat ): """FUNCTION copyList(fromwhat, intowhat): This function copies one list into another, WITHOUT creating a new list instance. This if you have A=B, and copy from C into B, A will also be changed. """ # LATER: check for list/tuple type; if not, coerce intowhat[:] = list(fromwhat) # (thanks, Aaron!) #---------------------------------------------------------------------- # CLASS: PooPickler #---------------------------------------------------------------------- class PooPickler(pickle.Pickler): """CLASS PooPickler: This class extends the normal Pickler object such that datatypes which can't by pickled are set to None, rather than crashing the server. """ # RADICALLY CHANGED on 4/20/98 to work with Python 1.5, # as per recommendation by our fearless leader (Guido) def save(self, object): try: pickle.Pickler.save(self, object) except pickle.PicklingError: pickle.Pickler.save(self, None) #---------------------------------------------------------------------- # CLASS: Func #---------------------------------------------------------------------- class Func: """CLASS Func: This class implements a POO function. Objects of this class walk like functions, but they are pickleable and do permission checks. """ #------------------------------------------------------------------ # Func constructor #------------------------------------------------------------------ def __init__(self,name='f',owner=None): """Func constructor.""" if gUnpickling: return name = string.replace(name,' ','') attributes = { 'owner':owner, # function owner 'name':name, # function delaration (name and args) 'x':1, # if 0, can only be called as command 'code':None, # compiled code 'source':[], # source (list of strings) 'presource':'', # extra code before 'source' 'desc':"", # brief description 'f':None, # function defined by code 'boundTo':None, # POO object to be considered 'self' 'definedOn':None } # POO object where this Func was found for k in attributes.keys(): self.__dict__[k] = attributes[k] #------------------------------------------------------------------ # Func call method #------------------------------------------------------------------ def __call__(self, *args): """Func call method.""" global gOwner, gUser, gCallers if not self.x: raise "PermError", self.name + " may only be called as a command" prevOwner = gOwner if self.owner: gOwner = self.owner if self.source and not self.code: self.compile() outval = None # prepare to attach "super" and other changables bdict = gSafeGlobals['__builtins__'].__dict__ prevInfo = (bdict['super'],bdict['caller']) # update the stack of callers gCallers.append(self.boundTo) try: if self.code: # attach "super" and other automatic globals bdict['super'] = self.doSuper bdict['caller'] = gCallers[-2] bdict['user'] = gUser bdict['permHolder'] = gOwner # call the function created and stored in compile(): if self.boundTo: # is this the most efficient way to do this? outval = apply(self.f, tuple( [self.boundTo]+list(args) )) else: outval = apply(self.f,args) finally: gCallers = gCallers[:-1] gOwner = prevOwner bdict['super'],bdict['caller'] = prevInfo gOwner = prevOwner return outval #------------------------------------------------------------------ # Func METHOD: compile #------------------------------------------------------------------ def compile(self): """Func METHOD: compile(self): compile self.source, store executable byte code.""" if not self.source: self.__dict__['code'] = None return pos = string.find(self.name,'(') paramdef = self.name[pos+1:-1] fullsource = 'def f(' + paramdef + '):\n ' \ + self.__dict__['presource'] + '\n ' \ + string.join(self.source,'\n ') + '\n' if gUnpickling: try: self.__dict__['code'] = compile(fullsource,'<'+self.name[:pos]+'>','exec') except: return else: self.__dict__['code'] = compile(fullsource,'<'+self.name[:pos]+'>','exec') # create a "dummy" function: locals = {'__builtins__':None} # (is this necessary?) exec(self.code) in gSafeGlobals,locals # copy (a reference to) the function for future use self.__dict__['f'] = locals['f'] #------------------------------------------------------------------ # Func METHOD: list #------------------------------------------------------------------ def list(self, fromLine=1, toLine=99999): """Func METHOD: list(self, fromLine=1, toLine=99999): list the source code [within the specified range].""" for line in self.source[fromLine-1:toLine]: print line #------------------------------------------------------------------ # Func METHOD: arg #------------------------------------------------------------------ def arg(self,num=0): """Func METHOD: arg(self, num=0): find the specified argument name amoung self.name.""" args = self.name[string.find(self.name,'(') +1 : \ string.rfind(self.name,')') ] try: return string.split(args,',')[num] except: return '' #------------------------------------------------------------------ # Func conversion to string #------------------------------------------------------------------ def __str__(self): """Func conversion to string.""" if self.boundTo: out = '' # str(self.boundTo) + '.' else: out = "unbound " if self.desc: out = out + self.name + " -- " + self.desc else: out = out + self.name return out #------------------------------------------------------------------ # Func attribute setter #------------------------------------------------------------------ def __setattr__(self,propname,value): """Func attribute setter.""" # owner can't be changed directly if gOwner and propname == 'owner': raise "PermError", "Can't directly change func owner" # other attributes can be changed only by owner if gOwner and not gOwner.wizard and gOwner != self.__dict__['owner']: raise "PermError", tostr(gOwner) \ + " can't modify " + propname + " of " + self.__str__() self.__dict__[propname] = value if propname == 'source': # when source changes, self.code = None # force a recompile #------------------------------------------------------------------ # Func pickling support #------------------------------------------------------------------ def __getstate__(self): """Func pickling support.""" temp = {} for key in self.__dict__.keys(): if key not in ['code','f']: temp[key] = self.__dict__[key] return temp #------------------------------------------------------------------ # Func unpickling support #------------------------------------------------------------------ def __setstate__(self,value): """Func unpickling support.""" for key in value.keys(): self.__dict__[key] = value[key] self.compile() #------------------------------------------------------------------ # Func METHOD: unbind #------------------------------------------------------------------ def unbind(self): """Func METHOD: unbind(self): set self.boundTo = None.""" self.__dict__['boundTo'] = None return self #------------------------------------------------------------------ # Func METHOD: doSuper #------------------------------------------------------------------ def doSuper(self,*args): """Func METHOD: doSuper(self,*args): Find the Func this overrides, and apply args to it. This is usually invoked via the 'super' variable.""" func = self.findSuper() func.__dict__['boundTo'] = self.boundTo return apply(func,args) #------------------------------------------------------------------ # Func METHOD: findSuper #------------------------------------------------------------------ def findSuper(self): """Func METHOD: findSuper(self): This method returns the Func which is apparently overridden by this one -- that is, it has the same name, but is defined further back in the ancestor chain. Return it unbound. """ if not self.boundTo or type(self.boundTo)!=InstanceType: raise "UseError", "Func.findSuper() must be called on bound Func" for p in self.definedOn.parents: super = self.findSameName( p ) if super: return super return None #------------------------------------------------------------------ # Func METHOD: findSelf #------------------------------------------------------------------ def findSameName(self,obj): """Func METHOD: findSameName(self,obj): Given an object reference, find a property (on it or its ancestors) which refers to a Func with the same name as this Func. """ matches = filter(lambda x,y=self.name: \ type(x.val) == InstanceType and \ x.val.__class__ == Func and x.val.name == y, gProps[obj].values()) if matches: return matches[0].val # not found here -- check ancestors for p in obj.parents: found = self.findSameName(p) if found: return found # still not found -- return None return None #---------------------------------------------------------------------- # CLASS: Prop #---------------------------------------------------------------------- class Prop: """CLASS Prop: This class implements a POO property. These are used instead of normal Python attributes to give us full access control. """ #------------------------------------------------------------------ # Prop constructor #------------------------------------------------------------------ def __init__(self,owner=None,val=None): """Prop constructor.""" if gUnpickling: return attributes = { 'owner':owner, 'val':val, 'r':1, # readable by others 'w':0, # writable by others 'c':1 } # ownership changes when inherited for k in attributes.keys(): self.__dict__[k] = attributes[k] if type(val) == InstanceType and val.__class__ == Func: val.__dict__['owner'] = owner #------------------------------------------------------------------ # Prop METHOD: permstr #------------------------------------------------------------------ def permstr(self): """Prop METHOD: permstr(self): return a string listing permissions as some combo of r,w,c,x.""" perm = '' if self.r: perm = 'r' if self.w: perm = perm + 'w' if self.c: perm = perm + 'c' if type(self.val) == InstanceType and self.val.__class__ == Func \ and self.val.x: perm = perm + 'x' return perm #------------------------------------------------------------------ # Prop conversion to string #------------------------------------------------------------------ def __str__(self): """Prop conversion to string.""" return "Prop(owner="+str(self.owner)+", perm="+self.permstr()+")" #------------------------------------------------------------------ # Prop attribute setter #------------------------------------------------------------------ def __setattr__(self,propname,value): """Prop attribute setter.""" # property flags can only be changed by the owner or a wizard if gOwner and gOwner!=self.owner and not gOwner.wizard and \ (propname=='r' or propname=='w' or propname=='c'): raise "PermError", "Can't change property permissions" if propname=='x': raise "UseError", "'x' flag should be set on Func, not Prop" # ownership can only be changed by a wizard if gOwner and not gOwner.wizard and propname=='owner': raise "PermError", "Can't change property owner" self.__dict__[propname] = value # make sure owner of a function and its property are in sync # (unless we're unpickling, in which case it takes care of itself) if gUnpickling: return if propname == 'val' and type(value) == InstanceType \ and value.__class__ == Func: value.__dict__['owner'] = self.owner elif propname == 'owner' and type(self.val) == InstanceType \ and self.val.__class__ == Func: self.val.__dict__['owner'] = self.owner #---------------------------------------------------------------------- # CLASS: Outfix #---------------------------------------------------------------------- class Outfix: """CLASS Outfix: This class is used to wrap an output stream so that output text is formatted to the user's preferences.""" def __init__(self, out=sys.stdout, user=None): self.out = out self.user = user def write(self,s): try: outlines = marksub.marksub(s, \ self.user.marksub, self.user.linelen) except: outlines = [s] try: sfx = self.user.linesuffix if type(sfx) != StringType: sfx = '\n' self.user.linesuffix = sfx except: sfx = '\n' self.out.write(string.join(outlines, sfx)) def flush(self): self.out.flush() def close(self): self.out.close() #---------------------------------------------------------------------- # CLASS: Obj #---------------------------------------------------------------------- class Obj: """CLASS: Obj This is the base class of any POO object -- rooms, users, widgets, etc. It implements a 'multiple superinheritance' (see POO docs!). """ #------------------------------------------------------------------ # Obj constructor #------------------------------------------------------------------ def __init__(self,id=0,*parents): """Obj constructor.""" global gObjlist, gHighID, gProps if not gProps.has_key(self): gProps[self] = {} gContents[self] = [] if gUnpickling: return if gOwner and not gOwner.wizard: raise "Hacker", "Hacking is grounds for termination of your account." if id and gObjlist.has_key(id): print "Error: object",id,"already exists." id = 0 if id: gObjlist[id] = self if gHighID < id: gHighID = id attributes = { '__outstream': None, # output stream 'id': id, # unique identifier '__isuser': 0, # flag indicating a User object '_inbuf': [], # input buffer '__value': kObjValue, # creation value '__credits': 0, # transferrable credits '__commands': {}, # commands defined on this object 'parents': parents, # parent objects 'name': '', # name of the object 'aliases': (), # name aliases 'owner': self, # object which controls this one 'location': None, # object that contains this one 'wantsUpdates': 0, # 1 if wants calls to update() 'wizard': 0, # 1 if has divine powers 'programmer': 0, # 1 if can use programmer commands 'r': 1, # 1 if publicly readable 'w': 0, # 1 if publicly writable 'f': 0 } # 1 if can have children ("fertile") for k in attributes.keys(): self.__dict__[k] = attributes[k] #------------------------------------------------------------------ # Obj conversion to string #------------------------------------------------------------------ def __str__(self): """Obj conversion to string.""" return "#" + str(self.__dict__['id']) #------------------------------------------------------------------ # Obj METHOD: tell #------------------------------------------------------------------ def tell(self, *what): """Obj METHOD: tell(self, *what): put the given string or list of strings in our auditory buffer. Terminate with a carriage return. """ bigstr = string.join(map(str,what),' ') + "\n" if self.__dict__['__outstream']: try: self.__dict__['__outstream'].write(bigstr) self.__dict__['__outstream'].flush() return except: # user appears to be disconnected; log them out self.Logout() # place in the input buffer (within INBUFLIMIT) if not callable(self.hear): return try: buf = self.__dict__['_inbuf'] except: buf = [] self.__dict__['_inbuf'] = buf if len(buf) >= kInbufLimit: return buf.append(bigstr) self.CheckUpdate() #------------------------------------------------------------------ # Obj METHOD: tell_noCR #------------------------------------------------------------------ def tell_noCR(self, *what): """Obj METHOD: tell_noCR(self, *what): put the given string or list of strings in our auditory buffer. Do not terminate with a carriage return. """ bigstr = string.join(map(str,what),' ') if self.__dict__['__outstream']: # try: self.__dict__['__outstream'].write(bigstr) if flushNoCR: self.__dict__['__outstream'].flush() return # except: # user appears to be disconnected; log them out self.Logout() # place in the input buffer (within INBUFLIMIT) if not callable(self.hear): return try: buf = self.__dict__['_inbuf'] except: buf = [] self.__dict__['_inbuf'] = buf if len(buf) >= kInbufLimit: return buf.append(bigstr) self.CheckUpdate() #------------------------------------------------------------------ # Obj METHOD: getprop #------------------------------------------------------------------ def getprop(self,propname): """Obj METHOD: getprop(self,propname): return the Prop defined on this object or an ancestor with the specified name. """ d = gProps[self] if propname in d.keys(): prop = d[propname] # check read permissions # if no owner, assume it's (safe) POO code if not gOwner or \ gOwner.__dict__['wizard'] or \ prop.owner == gOwner or \ prop.r: # we've found the property on self (noninherited) # return it as-is (make sure funcs are bound) if type(prop.val) == InstanceType and \ prop.val.__class__ == Func: prop.val.__dict__['boundTo'] = self prop.val.__dict__['definedOn'] = self return prop raise "PermError", "Can't read property "+propname for p in self.parents: prop = p.getprop(propname) if prop != None: # we've found it as an inherited value # if it's a function, return a COPY bound to this if type(prop.val) == InstanceType and \ prop.val.__class__ == Func: # do a quick copy of the Func, changing boundTo func2 = Func(prop.val.name,prop.val.owner) for i in prop.val.__dict__.keys(): func2.__dict__[i] = prop.val.__dict__[i] func2.__dict__['boundTo'] = self func2.__dict__['definedOn'] = prop.val.definedOn # do a quick copy of the prop, pointing to Func prop2 = Prop(prop.owner, func2) prop2.__dict__['r'] = prop.__dict__['r'] prop2.__dict__['w'] = prop.__dict__['w'] prop2.__dict__['c'] = prop.__dict__['c'] return prop2 return prop return None #------------------------------------------------------------------ # Obj attribute accessor #------------------------------------------------------------------ def __getattr__(self,propname): """Obj attribute accessor.""" # note: don't perform inheritance on Python props if propname[:2] == '__': try: return self.__dict__[propname] except: raise AttributeError, propname prop = self.getprop(propname) if prop: return prop.val else: return None #------------------------------------------------------------------ # Obj METHOD: canAddOrDeleteProp #------------------------------------------------------------------ def canAddOrDeleteProp(self, propname): """Obj METHOD: canAddOrDeleteProp(self, propname): return 1 if gOwner can add a property to this object """ if not propname: return 0 if (propname[0] < 'a' or propname[0] > 'z') and \ (propname[0] < 'A' or propname[0] > 'Z'): return 0 if ' ' in propname or '.' in propname: return 0 return not gOwner \ or gOwner==self.owner \ or gOwner.wizard \ or self.w #------------------------------------------------------------------ # Obj METHOD: canWriteProp #------------------------------------------------------------------ def canWriteProp(self, propname, useCflag=1): """Obj METHOD: canWriteProp(self, propname, useCflag=1): return 1 if gOwner can change the given property. """ # if no owner, assume it's (safe) POO code if not gOwner: return 1 # python attributes can't safely be changed by anyone if len(propname) > 1 and propname[:2] == "__": return 0 # location hierarchy can only be changed by move() if propname=='location': return gOwner==move # allow wizards to do anything if gOwner.wizard: return 1 # check for wizard-only properties if propname=='owner' or \ propname=='programmer' or \ propname=='wizard' or \ propname[0] == '_': return 0 # built-in properties are only writable by the object owner if propname in self.__dict__.keys(): return gOwner == self.owner # check owner & permission bit on the property itself if gProps[self].has_key(propname): prop = gProps[self][propname] owner = prop.owner else: # not defined here... prop = self.getprop(propname) # inherited? if not prop: return self.canAddOrDeleteProp(propname) # if inherited, check flag "c" if prop.c and useCflag: owner = self.owner else: owner = prop.owner if owner != gOwner and not prop.w: return 0 # if none of the above restrictions apply, must be OK return 1 #------------------------------------------------------------------ # Obj METHOD: setParents #------------------------------------------------------------------ def setParents(self,parents): """Obj METHOD: setParents(self,parents): change parents of this object to the given object or list. """ if not self.canWriteProp('parents'): raise "PermError", "Can't modify built-in parents" if type(parents) == ListType: parents = tuple(parents) elif type(parents) == InstanceType: parents = (parents,) elif type(parents) != TupleType: raise "ParamError", "parents must be an object, list, or tuple" for p in parents: if type(p) != InstanceType or \ (p.__class__ != Obj and p.__class__ != User and p.__class__ != Directory): raise "ParamError", `p` + " is not a POO object" # is proposed parent fertile? if not p.f: raise "ParamError", tostr(p) + " is infertile" # is proposed parent already one of our descendants? if self in p.ancestors(): raise "ParamError", tostr(p) + \ " cannot be both ancestor and descendant" self.__dict__['parents'] = parents #------------------------------------------------------------------ # Obj METHOD: ancestors #------------------------------------------------------------------ def ancestors(self): """Obj METHOD: ancestors(self): return list of all this object's ancestors. """ lst = [] for p in self.__dict__['parents']: lst = lst + [p] + p.ancestors() return lst #------------------------------------------------------------------ # Obj attribute setter #------------------------------------------------------------------ def __setattr__(self,propname,value): """Obj attribute setter.""" # if value is a Func, don't allow the assignment # unless caller is a wiz or the Func owner if type(value) == InstanceType and value.__class__ == Func: if gOwner and gOwner != value.owner and not gOwner.wizard: raise "PermError", "Can't store reference to " \ + str(value) + " owned by " + str(value.owner) # coerce types where appropriate if propname == "aliases": if type(value) == ListType: value = tuple(value) elif type(value) != TupleType: value = (value,) # if it's a built-in attribute, just check permission if self.__dict__.has_key(propname): if self.canWriteProp(propname): if propname == 'parents': self.setParents(value) else: self.__dict__[propname] = value if propname=='wantsUpdates': self.CheckUpdate() else: raise "PermError", "Can't modify built-in "+propname return # if it's an existing local property, change val if gProps[self].has_key(propname): if self.canWriteProp(propname): gProps[self][propname].val = value if propname=='update': self.CheckUpdate() else: raise "PermError", "Can't modify "+propname return # if it's an inherited property, add it anew here prop = self.getprop(propname) if prop: # implement "c" flag: when overriding a prop with c==1 # the owner of the object owns the overridden property if prop.c: owner = self.owner else: owner = prop.owner if gOwner != owner and not prop.w and not gOwner.wizard: raise "PermError", "Can't modify "+propname np = Prop(owner,value) for k in ['r','w','c']: np.__dict__[k] = prop.__dict__[k] gProps[self][propname] = np if propname=='update': self.CheckUpdate() return # check to see if this would actually add a new property if not gProps[self].has_key(propname): if not self.canAddOrDeleteProp(propname): raise "PermError", str(gOwner) + " can't add property (" \ + propname + ") to "+str(self) else: gProps[self][propname] = Prop(gOwner,value) if propname=='update': self.CheckUpdate() return #------------------------------------------------------------------ # Obj attribute deletion #------------------------------------------------------------------ def __delattr__(self,propname): """Obj attribute deletor.""" if self.__dict__.has_key(propname): raise "PermError", "Can't delete built-in "+propname elif self.canAddOrDeleteProp(propname): del gProps[self][propname] if propname=='update': self.CheckUpdate() else: raise "PermError", "Can't delete property "+propname #------------------------------------------------------------------ # Obj pickling support #------------------------------------------------------------------ def __getstate__(self): """Obj pickling support.""" temp = {} for key in self.__dict__.keys(): if key not in ['__outstream']: temp[key] = self.__dict__[key] return temp #------------------------------------------------------------------ # Obj unpickling support #------------------------------------------------------------------ def __setstate__(self,value): """Obj unpickling support.""" for key in value.keys(): self.__dict__[key] = value[key] self.__dict__['__outstream'] = None #------------------------------------------------------------------ # Obj METHOD: DoUpdate #------------------------------------------------------------------ def DoUpdate(self): """Obj METHOD: DoUpdate(self): update this object by calling self.hear or self.update as appropriate. """ global gOwner prevOwner = gOwner buf = self._inbuf if buf: nextline = buf[0] del buf[0] try: self.hear(nextline) except: pass if not buf: self.CheckUpdate() elif self.wantsUpdates: gOwner = self try: self.update() except: self.owner.tell("Error calling " + str(self) + ".update():") self.owner.handleErr(sys.exc_type, sys.exc_value, sys.exc_traceback) if (time()%10 < 1): self.__dict__['wantsUpdates'] = 0 self.owner.tell(str(self) + ".wantsUpdates has been set to 0") self.CheckUpdate() else: self.CheckUpdate() gOwner = prevOwner #------------------------------------------------------------------ # Obj METHOD: CheckUpdate #------------------------------------------------------------------ def CheckUpdate(self): """Obj METHOD: CheckUpdate(self): check whether this object should receive updates or not, and update gUpdateList accordingly. """ wantit = (callable(self.update) and self.wantsUpdates) \ or (callable(self.hear) and len(self._inbuf)) if wantit and self not in gUpdateList: gUpdateList.append(self) # self.owner.tell(self.name + " (#" + str(self.id) + \ # ") now receives updates.") elif not wantit and self in gUpdateList: gUpdateList.remove(self) # self.owner.tell(self.name + " (#" + str(self.id) + \ # ") no longer receives updates.") #------------------------------------------------------------------ # Obj METHOD: handleErr #------------------------------------------------------------------ def handleErr(self,e_type, e_val, e_traceback): """Obj METHOD: handleErr(self,e_type, e_val, e_traceback): crawl through the traceback and present only the most relevant line. """ # find the last traceback: tb = e_traceback # print tb.tb_frame.f_code.co_filename, tb.tb_lineno found = None if tb.tb_frame.f_code.co_filename[0] == '<': found = tb while tb.tb_next: tb = tb.tb_next if tb.tb_frame.f_code.co_filename[0] == '<': found = tb # print tb.tb_frame.f_code.co_filename, tb.tb_lineno if found: tb = found lineno = tb.tb_lineno codename = tb.tb_frame.f_code.co_filename if codename == '<string>': self.tell( e_type,'in command:', e_val) elif string.find(codename, ".py") > 0 and e_type == "SyntaxError": self.tell( "Compilation error: ", e_val ) else: self.tell( e_type,'in line',lineno-2, 'of ',codename, ':', e_val) return lineno #------------------------------------------------------------------ # Obj METHOD: do_cmd #------------------------------------------------------------------ def do_cmd(self,cmd): """Obj METHOD: do_cmd(self,cmd): attempt to execute the given POO command. """ global gCmdLine, gOwner, gUser # check permissions if gOwner and not gOwner.wizard and self.owner != gOwner: raise "PermError", "Must be wizard or owner to do_cmd()" gCmdLine = cmd gSafeGlobals['gCmdLine'] = gCmdLine if not cmd: return # change output and user characteristics prevInfo = (gUser, sys.stdout, gOwner) gUser, sys.stdout, gOwner = self, self.__dict__['__outstream'], self # if the cmd starts with ; or %, execute Python code if cmd[0] == '%' or cmd[0] == ';': try: locals = gLocals[self] except: locals = {'self':self,'this':self,'player':self, 'caller':self, 'me':self, 'user':self, '__builtins__':None} try: if not self.programmer: raise "PermError", "requires programmer privs" exec(cmd[1:]) in gSafeGlobals, locals except: self.handleErr(sys.exc_type, sys.exc_value, sys.exc_traceback) else: # otherwise, attempt to execute the named command try: sys.stdout = self.__dict__['__outstream'] HandleCommand(self,cmd) except: self.handleErr(sys.exc_type, sys.exc_value, sys.exc_traceback) if sys.stdout: sys.stdout.flush() # restore output and user characteristics gUser, sys.stdout, gOwner = prevInfo #------------------------------------------------------------------ # Obj METHOD: proplist #------------------------------------------------------------------ def proplist(self): """Obj METHOD: proplist(self) return a list of all property names. """ # you must be able to read the object to get a proplist if gOwner and gOwner != self.owner and not gOwner.wizard and not self.r: raise "PermError", "Can't list properties of this object" props = filter(lambda x:x[0]!='_' and x!='password', \ self.__dict__.keys() + gProps[self].keys()) props.sort() return props #------------------------------------------------------------------ # Obj METHOD: isa #------------------------------------------------------------------ def isa(self,what): """Obj METHOD: isa(self,what): return 1 if what is an ancestor of self. """ if type(what) != types.InstanceType: what = gProps[gObjlist[0]][what].val if what==self or what in self.parents: return 1 for p in self.parents: if p.isa(what): return 1 return 0 #------------------------------------------------------------------ # Obj METHOD: contents #------------------------------------------------------------------ def contents(self): """Obj METHOD: contents(self): return list of objects contained by self. """ if gOwner and gOwner != self.owner and not gOwner.wizard and not self.r: raise "PermError", "Can't list contents of this object" aCopy = [] aCopy[:] = gContents[self] return aCopy #---------------------------------------------------------------------- # Obj METHOD: getCmdDef #---------------------------------------------------------------------- def getCmdDef( self, verb, inherit=1 ): """Obj METHOD: getCmdDef(self, verb, inherit=1): This method returns a (possibly empty) tuple of commands which begin with the given verb from this object's command dictionary. If no verb is specified, return all defined commands. """ # first look for command defs on this object (overriding parents) out = () if gCmdDefs.has_key(self): defs = gCmdDefs[self] if not verb: for i in defs.values(): out = out + i elif defs.has_key(verb): out = defs[verb] # then, add commands from parents if not inherit: return out for p in self.parents: out = out + p.getCmdDef(verb,inherit) return out #---------------------------------------------------------------------- # Obj METHOD: setCmdDef #---------------------------------------------------------------------- def setCmdDef( self, verb, pattern, funcdef ): """Obj METHOD: setCmdDef( self, verb, pattern, funcdef ): This function sets a command definition in the global dictionary. If the given function call is None or an empty string, the command will be removed. If the given pattern matches an existing one exactly, the existing one will be replaced. This function requires wiz privs. """ global gOwner, gCmdDefs if gOwner and not gOwner.wizard and gOwner != self.owner : raise "PermError", "Must be owner or wizard to call setCmdDef()" if funcdef: newcmd = CmdDef(gOwner, pattern, funcdef) else: newcmd = None # make sure we have an entry for this object in gCmdDefs if not gCmdDefs.has_key(self): gCmdDefs[self] = {} defs = gCmdDefs[self] # get list of commands on this object for this verb try: cmds = list(defs[verb]) except: if newcmd: defs[verb] = (newcmd,) return for i in range(0,len(cmds)): if cmds[i].pat == pattern: # command exists already; replace it if newcmd: cmds[i] = newcmd else: del cmds[i] if cmds: defs[verb] = tuple(cmds) else: del defs[verb] if not gCmdDefs[self]: del gCmdDefs[self] return if newcmd: cmds.append(newcmd) else: return defs[verb] = tuple(cmds) #---------------------------------------------------------------------- # CLASS: User #---------------------------------------------------------------------- class User(Obj): """CLASS User: This subclass of Obj defines the class of Users in the game. They have all the properties of other POO objects, but also have facilities for being connected via the network. """ #------------------------------------------------------------------ # User constructor #------------------------------------------------------------------ def __init__(self,id=0,*parents): """User constructor.""" global gObjlist, gHighID, gProps if not gProps.has_key(self): gProps[self] = {} gContents[self] = [] if gUnpickling: return Obj.__init__(self,id) attributes = { '__outstream': None, # output stream 'id': id, # uniquet identifier '_editObj': None, # object whose property we're editing '_editProp': '', # name of property we're editing '_editBuf': [], # edit buffer '_editState': None, # edit state (temp storage) '_prompt': '<t>poo>', # user prompt '_oldprompt': '', # previous prompt '__isuser': 1 } # yes, we're a user for k in attributes.keys(): self.__dict__[k] = attributes[k] self.__dict__['parents'] = parents #------------------------------------------------------------------ # User METHOD: connected #------------------------------------------------------------------ def connected(self): """User METHOD: connected(self): return 1 if this User is currently logged in. """ return (self.__dict__['__outstream'] != None) #------------------------------------------------------------------ # User METHOD: Login #------------------------------------------------------------------ def Login(self, stream): """User METHOD: Login(self, stream): log into the POO server, and connect output to the given stream. """ global gOwner, gLocals if gOwner: raise "UseError", "Never call Login() directly!" gLocals[self] = {'self':self, 'this':self, 'player':self, 'caller':self, 'me':self, 'user':self, '__builtins__':None } self.__dict__['__outstream'] = stream self.loginTime = time() self.CheckUpdate() prevOwner = gOwner try: sys.stdout = stream gOwner = self gObjlist[0].login(self) except: pass gOwner = prevOwner self.tell_noCR(self._prompt) #------------------------------------------------------------------ # User METHOD: Logout #------------------------------------------------------------------ def Logout(self): """User METHOD: Logout(self): disconnect from the POO server.""" # beware of the case where the user is already logged out # (perhaps because her connection hung -- see Object.tell_noCR) if self not in gLocals.keys(): return global gOwner if gOwner and not gOwner.wizard and self.owner != gOwner: raise "PermError", "Must be wizard or owner to Logout()" prevOwner = gOwner sys.stdout = self.__dict__['__outstream'] self.__dict__['__outstream'] = None try: gOwner = self gObjlist[0].logout(self) except: pass gOwner = prevOwner self.CheckUpdate() del gLocals[self] #------------------------------------------------------------------ # User METHOD: handleMsg #------------------------------------------------------------------ def handleMsg(self,msg): """User METHOD: handleMsg(self,msg): handle some input from the real-life user. We assume msg is a single line. """ if gOwner and gOwner != self: raise "UseError", "Never call handleMsg() directly!" #lines = string.split(msg,'\n') #lines = map(lambda x:string.rstrip(x), lines) #self._inbuf = self._inbuf + lines self._inbuf = self._inbuf + [msg] self.activeTime = time() #------------------------------------------------------------------ # User METHOD: CheckUpdate #------------------------------------------------------------------ def CheckUpdate(self): """User METHOD: CheckUpdate(self): overrides Obj.CheckUpdate; a User should be updated if it is connected, and not otherwise. """ if self.__dict__['__outstream'] and self not in gUpdateList: gUpdateList.append(self) elif not self.__dict__['__outstream'] and self in gUpdateList: gUpdateList.remove(self) #------------------------------------------------------------------ # User METHOD: DoUpdate #------------------------------------------------------------------ def DoUpdate(self): """User METHOD: DoUpdate(self): overrides Obj.DoUpdate; this method gets a command from the inbuf and attempts to execute it. """ # if there's a command in the inbuf, do it if self._inbuf: cmd = self._inbuf[0] self._inbuf = self._inbuf[1:] if self._editObj != None: self.doEdit( cmd ) else: self.do_cmd( cmd ) self.tell_noCR(self._prompt) # if user wants update() calls, do it if self.wantsUpdates and callable(self.update): try: self.update() except: pass # otherwise, do nothing (but show prompt) #------------------------------------------------------------------ # User METHOD: startEdit #------------------------------------------------------------------ def startEdit(self,object,propname,postEdit=None): """User METHOD: startEdit(self,object,propname,postEdit=None): invoke the editor on the named object property. If present, postEdit should be a function which will receive the object reference, property name, and a "saved" parameter which will be 0 if user aborted, 1 if user saved changes. """ global gOwner if gOwner and not gOwner.wizard and self.owner != gOwner: raise "PermError", "Must be wizard or owner to startEdit()" # check permissions... # but don't apply C flag, since we're modifying it in place if not object.canWriteProp(propname,0): raise "PermError", "Can't modify property" self.__dict__['_editBuf'] = [] prop = getattr(object,propname) if prop == None: raise "NotFoundError", "No property named " + propname if type(prop) == InstanceType and prop.__class__ == Func: self.__dict__['_editObj'] = prop self.__dict__['_editProp'] = "source" copyList(prop.source, self._editBuf) elif type(prop) == ListType or type(prop) == TupleType: self.__dict__['_editObj'] = object self.__dict__['_editProp'] = propname copyList(prop, self._editBuf) else: raise "TypeError", "Can't edit "+ str(type(prop.val)) +" objects" self.__dict__['_postEditFunc'] = postEdit # set editor prompt self.__dict__['_oldprompt'] = self._prompt self.__dict__['_prompt'] = 'edit>' # now, invoke the built-in editor, or the user's preferred one prevOwner = gOwner gOwner = self try: self.__dict__['_editState'] = \ self.editor( self._editBuf, "", None ) except: try: self.__dict__['_editState'] = \ self.standardEdit( self._editBuf, "", None ) except: pass gOwner = prevOwner #------------------------------------------------------------------ # User METHOD: doEdit #------------------------------------------------------------------ def doEdit(self, msg): if gOwner and gOwner != self: raise "UseError", "doEdit() must only be called on oneself" # invoke the built-in editor, or the user's preferred one try: state = self.editor( self._editBuf, msg, self._editState ) except: state = self.standardEdit( self._editBuf, msg, self._editState ) # if it was an exit or abort command, make sure we really do! if msg == '.x' and state != 'DONE': state = 'DONE' if msg == '.q' and state != 'ABORT': state = 'ABORT' if state == 'DONE': self.endEdit(1) elif state == 'ABORT': self.endEdit(0) else: self.__dict__['_editState'] = state #------------------------------------------------------------------ # User METHOD: endEdit #------------------------------------------------------------------ def endEdit(self, save): if gOwner and gOwner != self: raise "UseError", "endEdit() must only be called on oneself" self.__dict__['_prompt'] = self._oldprompt if not self._editObj: raise "Error", "endEdit() called while not editing!" if save: setattr( self._editObj, self._editProp, tuple(self._editBuf) ) if self._postEditFunc: try: self._postEditFunc( self, self._editObj, self._editProp, save ) except: self.tell( "Error calling post-edit function " \ + str(self._postEditFunc) ) self.__dict__['_editObj'] = None self.__dict__['_editProp'] = '' self.__dict__['_editBuf'] = [] self.__dict__['_postEditFunc'] = None #------------------------------------------------------------------ # User METHOD: enterFunc #------------------------------------------------------------------ def enterFunc(self,object,funcname): """User METHOD: enterFunc(self,object,funcname): start the editor on the given function, creating it if it does not already exist. """ if gOwner and gOwner != self: raise "UseError", "enterFunc() must only be called on oneself" if gOwner and not gOwner.programmer: raise "PermError", "enterFunc requires programmer privs" lpos = string.find(funcname,'(') if lpos < 0: args = '(self)' # by default, make it a method else: args = string.strip(funcname[lpos:]) if not args or args == ')': raise "DeclarationError", \ "methods must have at least one parameter (self)" funcname = funcname[:lpos] setattr( object, funcname, Func(funcname+args, gOwner) ) self.tell('Enter function code below:') self.startEdit( object, funcname ) #------------------------------------------------------------------ # User METHOD: standardEdit #------------------------------------------------------------------ def standardEdit( self, buf, entry, state ): """User METHOD: standardEdit( self, buf, entry, state ): This method implements the simple, standard POO editor. """ if gOwner and gOwner != self: raise "UseError", "standardEdit() must only be called on oneself" if not state: # new editing session self.tell("[POO Editor -- enter .? for help]") return 'OK' if entry=='.?': # ? - help self.tell("Editing Commands:") self.tell(" .l List") self.tell(" .d Delete Last Line") self.tell(" .da Delete All Lines") self.tell(" .x Save & Exit ('x' is optional)") self.tell(" .q Quit (Abort)") elif entry=='.l': # l - list self.tell("---- Current Buffer ----") for line in buf: self.tell(line) self.tell("------------------------") elif entry=='.d': # d - delete last line if not buf: self.tell("Buffer is empty.") return del buf[-1] self.tell("Line deleted.") elif entry=='.da': # da - delete all buf[:] = [] self.tell("All lines deleted.") elif entry=='.' or entry == '.x': # x - save & exit self.tell("Saved.") return 'DONE' elif entry=='.q': # q - quit (abort) self.tell("Aborted.") return 'ABORT' else: # not a known command? Just append it to the buffer buf.append(entry) return 'OK' #---------------------------------------------------------------------- # CLASS: Directory #---------------------------------------------------------------------- class Directory(Obj): """CLASS: Directory This subclass of Obj defines the class of Directories in the game. These are POO objects which are only used for keeping references to other objects. They have the special characteristic that contents are treated like properties; that is, if Directory "foo" contains an object called "bar", it can be referred to as "foo.bar". Directories are severely restricted, so that they may be freely used within a quota system (i.e., they won't take up much memory). """ #------------------------------------------------------------------ # Directory constructor #------------------------------------------------------------------ def __init__(self,id=0,*parents): """Directory constructor.""" global gObjlist, gHighID, gProps if not gProps.has_key(self): gProps[self] = {} if not gContents.has_key(self): gContents[self] = [] if gUnpickling: return for p in parents: if gOwner and not gOwner.wizard and p != getObj('$dir'): raise "PermError", "Directories must be derived only from $dir" if not parents: raise "PermError", "Directories must be derived only from $dir" Obj.__init__(self,id) self.__dict__['parents'] = parents #------------------------------------------------------------------ # Directory attribute accessor #------------------------------------------------------------------ def __getattr__(self,propname): """Directory attribute gettor. Treats contents as if they were properties.""" # try usual methods first... val = Obj.__getattr__(self,propname) if val: return val # if those fail, look in our contents matches = filter( \ lambda x,y=propname:x.__dict__['name']==y, gContents[self]) if matches: return matches[0] return None #------------------------------------------------------------------ # Directory METHOD: canAddOrDeleteProp #------------------------------------------------------------------ def canAddOrDeleteProp(self, propname): """Directory METHOD: canAddOrDeleteProp(self, propname): overrides Obj.canAddorDeleteProp; only wizards can add properties to a Directory. """ if gOwner and not gOwner.wizard: return 0 return Obj.canAddOrDeleteProp(self, propname) #------------------------------------------------------------------ # Directory METHOD: canWriteProp #------------------------------------------------------------------ def canWriteProp(self, propname, useCflag=1): """Directory METHOD: canWriteprop(self, propname, useCflag=1): overrides the Obj method; prevents nonwizards from changing a directory's parents. """ # non-wizards can't change a directory's parents if propname == "parents" and gOwner and not gOwner.wizard: return 0 return Obj.canWriteProp( self, propname, useCflag ) #------------------------------------------------------------------ # Directory METHOD: proplist #------------------------------------------------------------------ def proplist(self): """Directory METHOD: proplist(self): return a list of property and contents names. """ # first, get normal props props = Obj.proplist(self) # then, add contents props = props + map(lambda x:x.name, gContents[self]) props.sort() return props #---------------------------------------------------------------------- # FUNCTION: users #---------------------------------------------------------------------- def users(): """FUNCTION: users(): This function returns a list of all connected Users. """ global gObjlist return filter( lambda x:x.__isuser and x.__outstream, gObjlist.values() ) #---------------------------------------------------------------------- # FUNCTION: globalkeys #---------------------------------------------------------------------- def globalkeys(): """FUNCTION: globalkeys(): This function returns a list of global variable names, like doing globals().keys(). """ return gSafeGlobals.keys() #---------------------------------------------------------------------- # FUNCTION: getObj #---------------------------------------------------------------------- def getObj(ref): """FUNCTION: getObj(ref): This function returns a POO object by number, #num, or $refname. """ global gObjlist if type(ref) == IntType: return gObjlist[ref] if type(ref) == StringType: ref = string.join(string.split(ref,'@'),'at_') periodpos = string.find(ref,'.') if periodpos > 0: parts = string.split(ref,'.') obj = getObj(parts[0]) for p in parts[1:]: obj = getattr(obj,p) return obj if ref[0] == '#': return gObjlist[ string.atoi(ref[1:]) ] if ref[0] == '$': return gProps[gObjlist[0]][ref[1:]].val if ref == "ALL": return gObjlist.values() raise "ParamError", "Invalid parameter (" + str(ref) + ") for getObj" #---------------------------------------------------------------------- # FUNCTION: treasury #---------------------------------------------------------------------- def treasury(): """FUNCTION: treasury(): This function returns the current value of the wizard treasury. (Wizards only.) """ global gTreasury, gOwner if gOwner and not gOwner.wizard: raise "PermError", "Must be wizard to view treasury" return gTreasury #---------------------------------------------------------------------- # FUNCTION: transfer #---------------------------------------------------------------------- def transfer(src, dest, amount): """FUNCTION: transfer(src, dest, amount): This function transfers credits from the source to the dest object. If source is None, credits come from the treasury; in this case, both gOwner and the dest object must be wizards. """ global gTreasury, gOwner if gOwner and not gOwner.wizard: raise "PermError", "Must be wizard to transfer funds" if src: if src.__dict__['__credits'] < amount: raise "CreditError", str(amount) + " credits requested, " \ + str(src.__dict__['__credits']) + " available" src.__dict__['__credits'] = src.__dict__['__credits'] - amount dest.__dict__['__credits'] = dest.__dict__['__credits'] + amount else: if not dest.wizard: raise "PermError", "Treasury funds may be transferred only to wizards" if gTreasury < amount: raise "CreditError", str(amount) + " credits requested, " \ + str(gTreasury) + " available" gTreasury = gTreasury - amount dest.__dict__['__credits'] = dest.__dict__['__credits'] + amount #---------------------------------------------------------------------- # FUNCTION: move #---------------------------------------------------------------------- def move(what,where): """FUNCTION: move(what,where): This function moves an object inside another one, if various checks are successfully negotiated. """ global gOwner # mover must be a wizard or owner of the object if gOwner and not gOwner.wizard: ### and gOwner!=what.owner: raise "PermError", "Must be wizard to move()" # if where is an object, it must return 1 from accept() if where: if callable(where.accept): OK = where.accept(what) else: OK = where.accept if not OK: raise "DeniedError", where.name + \ " refuses to accept " + what.name # disallow any loops in the containment hierarchy # this occurs if where is contained by what loc = where while loc: if loc == what: raise "ContainerLoopError", \ "Can't move "+what.name+" into "+where.name loc = loc.location # actually perform the move prevOwner = gOwner; gOwner = move oldwhere = what.location # ...remove from previous location's contents list if what.location: gContents[what.location].remove(what) # ...update location, and add to location's contents what.location = where if where: gContents[where].append(what) gOwner = prevOwner # call notification routines if oldwhere and callable(oldwhere.exitfunc): oldwhere.exitfunc(what) if where and callable(where.enterfunc): where.enterfunc(what) #---------------------------------------------------------------------- # FUNCTION: create #---------------------------------------------------------------------- def create(creator,parent,name=""): """FUNCTION: create(creator, parent, name=''): This function creates a new object and returns a reference to it. """ global gHighID, gTreasury, gOwner # check parent object type, fertility, and creator funds if type(parent) != InstanceType or \ (parent.__class__ != Obj and parent.__class__ != User and parent.__class__ != Directory): raise "ParamError", "Parent must be a POO object." if not parent.f: raise "ParamError", "Parent object must be fertile (flag f)." if gOwner and not gOwner.wizard: raise "PermError", "Object creation requires wizard privs." cost = kObjValue + kCreationTax if creator.__dict__['__credits'] < cost: raise "CreditError", str(cost) + " credits needed for object creation." # create a new object of the appropriate class oldclass = parent.__class__ newobj = oldclass(gHighID+1, parent) # set owner and name newobj.__dict__['owner'] = creator if name: newobj.name = name else: newobj.name = "new" + parent.name # move credits around newobj.__dict__['__value'] = kObjValue gTreasury = gTreasury + kCreationTax creator.__dict__['__credits'] = creator.__dict__['__credits'] - cost # place the object in the creator's inventory move(newobj,creator) return newobj #---------------------------------------------------------------------- # FUNCTION: destroy #---------------------------------------------------------------------- def destroy(object): """FUNCTION: destroy(object): The object is destroyed, and all references to it are removed. If it has any credits, they are given to its owner if possible, and otherwise to the treasury. The object must not have any children. This is a very time-consuming operation, and should be used rarely. """ global gObjlist, gOwner, gTreasury, gUpdateList # check permissions if gOwner and not gOwner.wizard and gOwner != object.owner: raise "PermError", "Caller (" + str(gOwner) + \ ") must be owner or wizard to destroy()" # refuse to make orphans children = filter(lambda x,a=object:a in x.parents,gObjlist.values()) if children: raise "OrphanError", "Object has children: "+tostr(children) # refuse to strand contents if object.contents(): raise "ContentsError", "Object has contents: "+tostr(object.contents()) # adjust credits gTreasury = gTreasury + kRecycleTax if (object.__dict__['__value'] > kRecycleTax): gained = object.__dict__['__value'] - kRecycleTax else: gained = 0 try: recipient = object.__dict__['owner'] recipient.__dict__['__credits'] = recipient.__dict__['__credits'] + gained except: gTreasury = gTreasury + gained # move into nothingness move(object,None) # destroy all properties, and its contents list del gProps[object] del gContents[object] # find all references to the object, anywhere, and remove them for ob in gObjlist.values(): for k in ob.__dict__.keys(): if ob.__dict__[k]==object: ob.__dict__[k] = None if gProps.has_key(ob): for k in gProps[ob].keys(): try: if gProps[ob][k] == object or \ gProps[ob][k].val==object: del gProps[ob][k] except: pass # remove from gObjlist for k in gObjlist.keys(): if gObjlist[k] == object: del gObjlist[k] # remove from gUpdateList for i in range(0,len(gUpdateList)): if gUpdateList[i] == object: del gUpdateList[i] #---------------------------------------------------------------------- # FUNCTION: show #---------------------------------------------------------------------- def show( message, parties, broadcast=1 ): """FUNCTION: show( message, parties, broadcast=1): message: string containing key tags e.g., "%1D %1:(gets) %2i." parties: dictionary mapping keys to objects e.g., {1:caller, 2:dobj} broadcast: also broadcast a message to the container of the object specified by this key This function displays an effect involving several parties. Each of the parties gets a customized message, and the location of the party specified by the broadcast key gets a general message sent to its broadcast function. """ # make lists out of the dictionary keys = parties.keys() values = parties.values() # can we assume this is the same order?!? # get various customized outputs outputs = msg.Msg().sub_parties(parties,message,values) # tell each object (if it's really an object) for i in range(0,len(values)): if type(values[i]) == InstanceType: values[i].tell(outputs[0][i]) # and finally, broadcast the general message if possible if not broadcast: return try: parties[broadcast].location.broadcast(outputs[1], values) except: pass #---------------------------------------------------------------------- # FUNCTION: POOopen #---------------------------------------------------------------------- def POOopen( filename, mode='r' ): """FUNCTION: POOopen( filename, mode='r' ): This function opens a file under the POO directory for reading. Filenames with slashes (either direction) or colons are disallowed. """ # check privs if gOwner and not gOwner.wizard: raise "PermError", "open() requires wizard privs" # check for dangerous characters if '/' in filename or '\\' in filename or ':' in filename: raise "ParamError", "filenames cannot contain /, \\, or :" # append it to the POO file directory, and return the file ref fullpath = os.path.join( gFilePath, filename ) return open( fullpath, mode ) #---------------------------------------------------------------------- # FUNCTION: gSave #---------------------------------------------------------------------- def gSave(filename='poo.dat'): """FUNCTION: gSave(filename='poo.dat'): This function saves the current database to a file (via pickle). """ global gObjlist, gProps, gContents, gCmdDefs, gTreasury, gLastSave # try saving the previous data file backupname = filename + ".bak" try: os.unlink(backupname) except: pass try: os.rename(filename, backupname) except: pass file = open(filename, 'w') PooPickler(file).dump((gObjlist,gProps,gContents,gCmdDefs,gTreasury)) file.close() gLastSave = time() #---------------------------------------------------------------------- # FUNCTION: gLoad #---------------------------------------------------------------------- def gLoad(filename='poo.dat'): """FUNCTION: gLoad(filename='poo.dat'): This function loads the database from a file (via pickle). """ global gObjlist, gProps, gContents, gCmdDefs, gSafeGlobals, \ gHighID, gUnpickling, gUpdateList, gTreasury # unpickle the file gUnpickling = 1 file = open(filename, 'r') # (gObjlist,gProps,gContents,gTreasury) = pickle.load(file) (gObjlist,gProps,gContents,gCmdDefs,gTreasury) = pickle.load(file) file.close() gUnpickling = 0 # build the index kys = filter(lambda x:type(x)==types.IntType, gObjlist.keys()) kys.sort() gHighID = kys[-1] # figure out which objects need updates gUpdateList = filter(lambda x:callable(x.update), gObjlist.values()) # set up any automatic globals which refer to the object set if getObj(0).pub: gSafeGlobals['pub'] = getObj(0).pub # apply corrections, if any # (currently none) #---------------------------------------------------------------------- # FUNCTION: gUpdate #---------------------------------------------------------------------- def gUpdate(): """FUNCTION: gUpdate(): This function updates all the objects in the database which need updating. It should be called periodically by the main program to make the engine crank. It also periodically saves the database. """ # update all objects in the update list for ob in gUpdateList: ob.DoUpdate() # consider saving the database t = time() - gLastSave if t > maxSaveTime: # later: do more sophisticated checks here! gSave() #---------------------------------------------------------------------- # FUNCTION: newDatabase #---------------------------------------------------------------------- def newDatabase(): """FUNCTION: newDatabase(): This function creates a new database from scratch ... used for bootstrapping the initial database. """ global gObjlist, gHighID print "Creating new database..." gObjlist = {} gObjlist[0] = Obj(0) gObjlist[0].name = "root" print str(gObjlist[0]), ':', gObjlist[0].name gObjlist[1] = Obj(1) gObjlist[1].name = "object" gObjlist[1].f = 1 print str(gObjlist[1]), ':', gObjlist[1].name gObjlist[2] = User(2,gObjlist[1]) gObjlist[2].name = "Implementor" gObjlist[2].wizard = 1 gObjlist[2].programmer = 1 print str(gObjlist[2]), ':', gObjlist[2].name gObjlist[3] = Obj(3,gObjlist[1]) gObjlist[3].name = "place" gObjlist[3].accept = 1 print str(gObjlist[3]), ':', gObjlist[3].name move(gObjlist[2],gObjlist[3]) gObjlist[4] = Directory(4,gObjlist[1]) gObjlist[4].name = "directory" print str(gObjlist[4]), ':', gObjlist[4].name gHighID = 4 #---------------------------------------------------------------------- # FUNCTION: makeSafeGlobals #---------------------------------------------------------------------- def makeSafeGlobals(): """FUNCTION: makeSafeGlobals(): This function returns a set of global variables that will be safe for POO coders to use. """ import md5 import imp from time import time,ctime from qsplit import * from whrandom import randint import poohelp poohelp.hLoad() safebuiltins = imp.new_module('safebuiltins') exceptions = ['__import__','open','reload','unload','__name__', 'execfile', 'globals'] for name in __builtins__.keys(): if name not in exceptions: safebuiltins.__dict__[name] = __builtins__[name] safebuiltins.__dict__['super'] = None safebuiltins.__dict__['caller'] = None d = { 'getObj':getObj, 'gCmdLine':gCmdLine, '__builtins__':safebuiltins, 'create':create, 'ctime':ctime, 'destroy':destroy, 'Directory':Directory, 'Func':Func, 'help':poohelp.help, 'globalkeys':globalkeys, 'marksub':marksub, 'md5':md5, 'move':move, 'msg':msg, 'Obj':Obj, 'open':POOopen, 'Prop':Prop, 'randint':randint, 'show':show, 'string':string, 'split':qsplit, 'time':time, 'tostr':tostr, 'treasury':treasury, 'transfer':transfer, 'User':User, 'users':users } for item in d.values(): if type(item) == ModuleType and hasattr(item,'__builtins__'): item.__builtins__ = safebuiltins g = globals() for k in g.keys(): if k[-4:] == "Type": d[k] = g[k] return d #---------------------------------------------------------------------- # FUNCTION: initialize #---------------------------------------------------------------------- def initialize(newDB=0): """FUNCTION: initialize(newDB=0): This function sets up POO globals variables, loads files, etc. """ global gSafeGlobals gSafeGlobals = makeSafeGlobals() if newDB: newDatabase() else: gLoad() #---------------------------------------------------------------------- # ----- end of poo.py ----- #----------------------------------------------------------------------