# -*- test-case-name: mv3d.test.test_classgenerator -*- # Copyright (C) 2006-2012 Mortal Coil Games # See LICENSE for details. """ Class Generator generates classes """ import sys from twisted.spread import pb from twisted.internet import defer from mv3d.net.pb import Copyable class ConstructError(Exception): """ Raised when there is an issue constructing an object from a class generator """ def getClass(path): """ Assumes path is a module and class and will import the module and return the class """ moduleName = ".".join(path.split(".")[:-1]) className = path.split(".")[-1] return getattr(__import__(moduleName, fromlist=[className]), className) class ClassGenerator(Copyable): """ The ClassGenerator pretty much does what its name says. Its purpose in life is to construct a class via one of several means. In addition, it serves as a way to pass a class through PB or save it to disk. They can also be used to specify client side modules for cached PB refs. """ module = None def __init__(self, name=None, classname=None, modulename=None, sourceclass=None, assetid=None, constructor=None, assetService=None): self.classname = classname self.modulename = modulename if name is not None: self.modulename = ".".join(name.split(".")[:-1]) self.classname = name.split(".")[-1] self.sourceclass = sourceclass self.assetid = assetid self.constructor = constructor self.assetService = assetService if self.constructor is not None: self.setFrom(self.constructor) self.addExclude("constructor", "module") def __getstate__(self): state = {"classname":self.classname, "modulename":self.modulename, "sourceclass":self.sourceclass, "assetid":self.assetid} return state def __setstate__(self, state): self.__init__() self.__dict__.update(state) def __str__(self): return "ClassGenerator(%s)" % self.toString() def toString(self): """ Convert the class generator into a string (doesn't currently support assets) """ return ".".join([self.modulename, self.classname]) @classmethod def fromString(cls, name): """ Return the class generator from being a string (doesn't currently support assets) """ return ClassGenerator(name) def setFrom(self, o): """ You need to add a docstring here ! """ # if hasattr(o, "getClassGenerator"): # return o.getClassGenerator() if callable(o): self.classname = o.__name__ self.modulename = o.__module__ return self self.classname = o.__class__.__name__ self.modulename = o.__class__.__module__ return self def getClassName(self): """ You need to add a docstring here ! """ return self.classname def setClassName(self, n): """ You need to add a docstring here ! """ self.classname = n def observe_setClassName(self, n): """ You need to add a docstring here ! """ self.setClassName(n) def getModuleName(self): """ You need to add a docstring here ! """ return self.modulename def setModuleName(self, m): """ You need to add a docstring here ! """ self.modulename = m def observe_setModuleName(self, m): """ You need to add a docstring here ! """ self.setModuleName(m) def getSourceClass(self): """ You need to add a docstring here ! """ return self.sourceclass def setSourceClass(self, s): """ You need to add a docstring here ! """ self.sourceclass = s def observe_setSourceClass(self, s): """ You need to add a docstring here ! """ self.setSourceClass(s) def getAssetId(self): """ You need to add a docstring here ! """ return self.assetid def setAssetId(self, a): """ You need to add a docstring here ! """ self.assetid = a def observe_setAssetId(self, a): """ You need to add a docstring here ! """ self.setAssetId(a) def construct(self, *a, **kw): """ This will eventually return an instance of the class that the generator refers to. If that is not possible, returns 0. It may return a deferred. """ # print "Constructing a", self.classname # check if we have an asset.. if self.constructor is None: d = self.getConstructor() if isinstance(d, defer.Deferred): d.addCallback(self.construct_GotConstructor, a, kw) return d # if kw.has_key("server") or kw.has_key("store"): # print self.constructor try: c = self.constructor(* a, **kw) except: print self.constructor, a, kw raise #Exception("Stupid %s %s" % (self.constructor, kw.keys())) # c._constructed_from = self return c def construct_GotConstructor(self, r, a, kw): """ You need to add a docstring here ! """ c = self.constructor(* a, **kw) # c._constructed_from = self return c def getConstructor(self, r=1): """ This function should try various means to find our constructor. It returns 1 on success, 0 on fail """ if self.constructor is not None: return True #print "Get Constructor", self.assetid if self.assetid is not None: # print "We've got an asset", self.server d = self.assetService.acquireAsset(self.assetid) # print "CG Acquiring" return d.addCallback(self.getConstructor_GotAsset) return self.getConstructor_construct() def getConstructor_GotAsset(self, asset): """ You need to add a docstring here ! """ d = self.assetService.getAsset(self.assetid) def gotAsset(a): a.getCG() self.classname = a.getClass() self.modulename = a.getModule() return self.getConstructor_construct() return d.addCallback(gotAsset) def getConstructor_construct(self): """ You need to add a docstring here ! """ if self.module is not None: if not hasattr(self.module, self.classname): raise ConstructError("not self.classname in dir(self.module)") self.constructor = getattr(self.module, self.classname) return True # if we get here, we have to look up the module. # and to do that, we need a server or client... # though first, we should try loading the module. #print self.modulename if self.modulename in sys.modules.keys(): self.module = sys.modules[self.modulename] else: try: # print "importing", self.modulename self.module = __import__(self.modulename, globals(), locals(), [self.classname]) #print "done" except: print "Import Failed!", sys.path print "module=", self.modulename, "class=", self.classname raise if not self.classname in dir(self.module): raise ConstructError("Tried importing, got not self.classname" "in dir(self.module) (%s, %s)" % (self.classname, self.module.__name__)) self.constructor = getattr(self.module, self.classname) return True def readyJellyFor(self): """ This will call pb.setUnjellyableForClass for our class it may defer. Returns 1 on success, 0 on error. """ r = self.getConstructor() if isinstance(r, defer.Deferred): return r.addCallback(self.readyJellyFor_GotConstructor) j = self.readyJellyFor_GotConstructor(1) return j def readyJellyFor_GotConstructor(self, _): """ You need to add a docstring here ! """ if self.sourceclass is not None: # print "Jelly setup", self.sourceclass, self.constructor.__name__ pb.setUnjellyableForClass(self.sourceclass, self.constructor) else: # print "Jelly setup", self.constructor.__name__, self.constructor.__name__ pb.setUnjellyableForClass(self.constructor, self.constructor) return True setCopyableState = Copyable.setCopyableState getStateToCopy = Copyable.getStateToCopy pb.setUnjellyableForClass(ClassGenerator, ClassGenerator)