Source code for woo._monkey.io

# encoding: utf-8
'''Define IO routines for arbitrary objects.'''
# various monkey-patches for wrapped c++ classes
import woo.core
import woo.system
import woo.document
import woo.config
#import woo.dem
from minieigen import * # for recognizing the types

from io import StringIO
from woo.core import Object


import codecs
import pickle
import json
import minieigen
import numpy
import sys
import warnings
# we don't support h5py on Windows, as it is too complicated to install
# this is an ugly hack :|
try:
    # silence warnings from h5py
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')    
        import h5py
    haveH5py=True
except ImportError:
    haveH5py=False

nan,inf=float('nan'),float('inf') # for values in expressions

# ensure that the string is unicode
def _ensureUnicode(s): return s if isinstance(s,str) else s.decode('utf-8')


[docs]def Object_getAllTraits(obj): 'Return list of all trait objects for this instance, recursively including all parent classes.' ret=[]; k=obj.__class__ while k!=woo.core.Object: ret=k._attrTraits+ret k=k.__bases__[0] return ret
[docs]def Object_getAllTraitsWithClasses(obj): 'Return list of (trait,klass) relevant for *obj*, where *klass* is class (type object) to which each trait belongs.' ret=[]; k=obj.__class__ while k!=woo.core.Object: ret=[(trait,k) for trait in k._attrTraits]+ret k=k.__bases__[0] return ret
# this is not necessary in Python >= 3.0, and older versions of minieigen don't expose that function if sys.version_info<(3,0) and hasattr(minieigen,'float2str'): def float2str(f): return minieigen.float2str(f) else:
[docs] def float2str(f): return '%g'%f
htmlHead='<head><meta http-equiv="content-type" content="text/html;charset=UTF-8" /></head><body>\n'
[docs]def Object_dumps(obj,format,fragment=False,width=80,noMagic=False,stream=True,showDoc=False,hideWooExtra=False): if format not in ('html','expr','json','pickle','genshi'): raise IOError("Unsupported string dump format %s"%format) if format=='pickle': return pickle.dumps(obj) elif format=='json': return WooJSONEncoder().encode(obj) elif format=='expr': return SerializerToExpr(maxWd=width,noMagic=noMagic)(obj) elif format=='html': return ('' if fragment else htmlHead)+str(SerializerToHtmlTable(showDoc=showDoc,hideWooExtra=hideWooExtra)(obj))+('' if fragment else '</body>') elif format=='genshi': return SerializerToHtmlTable()(obj,dontRender=True,showDoc=showDoc,hideWooExtra=hideWooExtra)
[docs]def Object_dump(obj,out,format='auto',fallbackFormat=None,overwrite=True,fragment=False,width=80,noMagic=False,showDoc=False,hideWooExtra=False): '''Dump an object in specified *format*; *out* can be a str (filename) or a *file* object. Supported formats are: `auto` (auto-detected from *out* extension; raises exception when *out* is an object), `html`, `expr`.''' hasFilename=isinstance(out,str) if hasFilename: import os.path if os.path.exists(out) and not overwrite: raise IOError("File '%s' exists (use overwrite=True)"%out) if format=='auto': if not hasFilename: raise IOError("format='auto' is only possible when a fileName is given.") if out.endswith('.html'): format='html' elif sum([out.endswith(ext) for ext in ('.expr','expr.gz','expr.bz2')]): format='expr' elif sum([out.endswith(ext) for ext in ('.pickle','pickle.gz','pickle.bz2')]): format='pickle' elif sum([out.endswith(ext) for ext in ('.json','json.gz','json.bz2')]): format='json' elif sum([out.endswith(ext) for ext in ('.xml','.xml.gz','.xml.bz2','.bin','.gz','.bz2')]): format='boost::serialization' elif fallbackFormat is not None: format=fallbackFormat else: IOError("Output format not deduced for filename '%s' (and fallbackFormat not specified)"%out) if format not in ('auto','html','json','expr','pickle','boost::serialization'): raise IOError("Unsupported dump format %s"%format) #if not hasFilename and not hasattr(out,'write'): raise IOError('*out* must be filename or file-like object') if format in ('boost::serialization',) and not hasFilename: raise IOError("format='boost::serialization' needs filename.") # this will go away later if format=='boost::serialization': if format in ('boost::serialization',) and not isinstance(obj,woo.core.Object): raise IOError("Only instances of woo.core.Object can be saved via boost::serialization") if not hasFilename: raise NotImplementedError('Only serialization to files (not to strings) is supported with boost::serialization.') if obj.deepcopy.__module__!='woo._monkey.io': raise IOError("boost::serialization formats can only reliably save pure-c++ objects. Given object %s.%s seems to be derived from python. Save using some dump formats."%(obj.__class__.__module__,obj.__class__.__name__)) obj.save(str(out)) # must convert unicode to str here, so that it can be converted to std::string elif format=='auto': raise IOError("format='auto' could not guess format from extension (and fallback not given) -- say format='...' or pass an extension which is understood.") elif format=='pickle': if hasFilename: pickle.dump(obj,open(out,'wb')) else: out.write(pickle.dumps(obj)) elif format in ('expr','html','json'): if hasFilename: out=codecs.open(out,'wb','utf-8') if format=='expr': out.write(SerializerToExpr(maxWd=width,noMagic=noMagic)(obj)) elif format=='json': out.write(WooJSONEncoder().encode(obj)) elif format=='html': if not fragment: out.write(htmlHead) out.write(str(SerializerToHtmlTable(showDoc=showDoc,hideWooExtra=hideWooExtra)(obj))) if not fragment: out.write('</body>') else: assert False,'Unreachable.'
[docs]class SerializerToHtmlTableGenshi(object): 'Dump given object to HTML table, using the `Genshi <http://genshi.edgewall.org>`_ templating engine; the produced serialization is XHTML-compliant. Do not use this class directly, say ``object.dump(format="html")`` instead.' padding=dict(cellpadding='2px') splitStrSeq=1 splitIntSeq=5 splitFloatSeq=5 def __init__(self,showDoc=False,maxDepth=8,hideNoGui=False,hideWooExtra=False): self.maxDepth=maxDepth self.hideNoGui=hideNoGui self.hideWooExtra=hideWooExtra self.showDoc=showDoc
[docs] def htmlSeq(self,s,insideTable): from genshi.builder import tag table=tag.table(frame='box',rules='all',width='100%',**self.padding) if hasattr(s[0],'__len__') and not isinstance(s[0],(str,bytes)): # 2d array # disregard insideTable in this case for r in range(len(s) if type(s)!=AlignedBox3 else 2): # len(s) is sufficient, but some version of minieigen report erroneously that AlignedBox3 has length of 3 tr=tag.tr() for c in range(len(s[0])): tr.append(tag.td(float2str(s[r][c]) if isinstance(s[r][c],float) else str(s[r][c]),align='right',width='%g%%'%(100./len(s[0])-1.))) table.append(tr) return table splitLen=0 if len(s)>1: if isinstance(s[0],int): splitLen=self.splitIntSeq elif isinstance(s[0],str): splitLen=self.splitStrSeq elif isinstance(s[0],float): splitLen=self.splitFloatSeq # 1d array if splitLen==0 or len(s)<splitLen: ret=table if not insideTable else [] for e in s: ret.append(tag.td(float2str(e) if isinstance(e,float) else str(e),align='right',width='%g%%'%(100./len(s)-1.))) # 1d array, but with lines broken else: ret=table for i,e in enumerate(s): if i%splitLen==0: tr=tag.tr() tr.append(tag.td(float2str(e) if isinstance(e,float) else str(e),align='right',width='%g%%'%(100./splitLen-1.))) # last in the line, or last overall if i%splitLen==(splitLen-1) or i==len(s)-1: table.append(tr) return ret
def __call__(self,obj,depth=0,dontRender=False): from genshi.builder import tag if depth>self.maxDepth: raise RuntimeError("Maximum nesting depth %d exceeded"%self.maxDepth) kw=self.padding.copy() if depth>0: kw.update(width='100%') # was [1:] to omit leading woo./wooExtra., but that is not desirable objInExtra=obj.__class__.__module__.startswith('wooExtra.') if self.hideWooExtra and objInExtra: head=tag.span(tag.b(obj.__class__.__name__),title=_ensureUnicode(obj.__class__.__doc__)) else: head=tag.b('.'.join(obj.__class__.__module__.split('.')[0:])+'.'+obj.__class__.__name__) head=tag.a(head,href=woo.document.makeObjectUrl(obj),title=_ensureUnicode(obj.__class__.__doc__)) ret=tag.table(tag.th(head,colspan=3,align='left'),frame='box',rules='all',**kw) # get all attribute traits first traits=obj._getAllTraits() for trait in traits: if trait.hidden or (self.hideNoGui and trait.noGui) or trait.noDump or (trait.hideIf and eval(trait.hideIf,globals(),{'self':obj})): continue # start new group (additional line) if trait.startGroup: ret.append(tag.tr(tag.td(tag.i(u'▸ %s'%_ensureUnicode(trait.startGroup)),colspan=3))) attr=getattr(obj,trait.name) if self.showDoc: tr=tag.tr(tag.td(_ensureUnicode(trait.doc))) else: try: if self.hideWooExtra and objInExtra: label=tag.span(tag.b(trait.name),title=_ensureUnicode(trait.doc)) else: label=tag.a(trait.name,href=woo.document.makeObjectUrl(obj,trait.name),title=_ensureUnicode(trait.doc)) tr=tag.tr(tag.td(label)) except UnicodeEncodeError: print('ERROR: UnicodeEncodeError while formatting the attribute ',obj.__class__.__name__+'.'+trait.name) print('ERROR: the docstring is',trait.doc) raise # tr=tag.tr(tag.td(trait.name if not self.showDoc else trait.doc.decode('utf-8'))) # nested object if isinstance(attr,woo.core.Object): tr.append([tag.td(self(attr,depth+1),align='justify'),tag.td()]) # sequence of objects (no units here) elif hasattr(attr,'__len__') and len(attr)>0 and isinstance(attr[0],woo.core.Object): tr.append(tag.td(tag.ol([tag.li(self(o,depth+1)) for o in attr]))) else: # !! make deepcopy so that the original object is not modified !! import copy attr=copy.deepcopy(attr) if not trait.multiUnit: # the easier case if not trait.prefUnit: unit=u'−' else: unit=_ensureUnicode(trait.prefUnit[0][0]) # create new list, where entries are multiplied by the multiplier if type(attr)==list: attr=[a*trait.prefUnit[0][1] for a in attr] else: attr=attr*trait.prefUnit[0][1] else: # multiple units unit=[] wasList=isinstance(attr,list) if not wasList: attr=[attr] # handle uniformly for i in range(len(attr)): attr[i]=[attr[i][j]*trait.prefUnit[j][1] for j in range(len(attr[i]))] for pu in trait.prefUnit: unit.append(_ensureUnicode(pu[0])) if not wasList: attr=attr[0] unit=', '.join(unit) # sequence type, or something similar if hasattr(attr,'__len__') and not isinstance(attr,(str,bytes)): if len(attr)>0: tr.append(tag.td(self.htmlSeq(attr,insideTable=False),align='right')) else: tr.append(tag.td(tag.i('[empty]'),align='right')) else: tr.append(tag.td(float2str(attr) if isinstance(attr,float) else str(attr),align='right')) if unit: tr.append(tag.td(unit,align='right')) ret.append(tr) if depth>0 or dontRender: return ret r1=ret.generate().render('xhtml',encoding='ascii') if isinstance(r1,bytes): r1=r1.decode('ascii') return r1+u'\n'
SerializerToHtmlTable=SerializerToHtmlTableGenshi
[docs]class SerializerToExpr(object): ''' Represent given object as python expression. Do not use this class directly, say ``object.dump(format="expr")`` instead. ''' # if 'pybind11' in woo.config.features: unbreakableTypes=(Vector3,) unbreakableTypes=(Vector2i,Vector2,Vector3i,Vector3) def __init__(self,indent='\t',maxWd=120,noMagic=True): self.indent=indent self.indentLen=len(indent.replace('\t',3*' ')) self.maxWd=maxWd self.noMagic=noMagic def __call__(self,obj,level=0,neededModules=None): if neededModules is None: neededModules=set() # instantiate a new one every time if isinstance(obj,Object): attrs=[(trait.name,getattr(obj,trait.name)) for trait in obj._getAllTraits() if not (trait.hidden or trait.noDump or (trait.hideIf and eval(trait.hideIf,globals(),{'self':obj})))] delims=(obj.__class__.__module__)+'.'+obj.__class__.__name__+'(',')' neededModules.add(obj.__class__.__module__) elif isinstance(obj,dict): attrs=list(obj.items()) delims='{','}' # list or mutable list-like objects (NodeList, for instance) elif hasattr(obj,'__len__') and hasattr(obj,'__getitem__') and hasattr(obj,'append'): attrs=[(None,obj[i]) for i in range(len(obj))] delims='[',']' # tuple and tuple-like objects: format like tuples ## were additionally: Matrix3,Vector6,Matrix6,VectorX,MatrixX,AlignedBox2,AlignedBox3 elif sum([isinstance(obj,T) for T in (tuple,Vector2i,Vector2,Vector3i,Vector3)])>0: attrs=[(None,obj[i]) for i in range(len(obj))] delims='(',')' # use short representation of float as str elif isinstance(obj,float): return float2str(obj) # don't know what to do, use repr (unhandled or primive types) else: return repr(obj) lst=[(((attr[0]+'=') if attr[0] else '')+self(attr[1],level+1)) for attr in attrs] oneLiner=(sum([len(l) for l in lst])+self.indentLen<=self.maxWd or self.maxWd<=0 or type(obj) in self.unbreakableTypes) magic='' if not self.noMagic and level==0: magic='##woo-expression##\n' if neededModules: magic+='#: import '+','.join(neededModules)+'\n' oneLiner=False #magic=('##woo-expression##' if not self.noMagic and level==0 else '') if oneLiner: return delims[0]+', '.join(lst)+delims[1] indent0,indent1=self.indent*level,self.indent*(level+1) return magic+delims[0]+'\n'+indent1+(',\n'+indent1).join(lst)+'\n'+indent0+delims[1]+('\n' if level==0 else '')
# roughly following http://www.doughellmann.com/PyMOTW/json/, thanks!
[docs]class WooJSONEncoder(json.JSONEncoder): ''' Represent given object as JSON object. Do not use this class directly, say ``object.dump(format="json")`` instead. ''' def __init__(self,indent=3,sort_keys=True,oneway=False): 'oneway: allow serialization of objects which won\'t be properly deserialized. They are: numpy.ndarray, h5py.Dataset.' json.JSONEncoder.__init__(self,sort_keys=sort_keys,indent=indent) self.oneway=oneway
[docs] def default(self,obj): # Woo objects if isinstance(obj,woo.core.Object): d={'__class__':obj.__class__.__module__+'.'+obj.__class__.__name__} # assign woo attributes d.update(dict([(trait.name,getattr(obj,trait.name)) for trait in obj._getAllTraits() if not (trait.hidden or trait.noDump or (trait.hideIf and eval(trait.hideIf,globals(),{'self':obj})))])) return d # vectors, matrices: those can be assigned from tuples elif obj.__class__.__name__.endswith('List'): if hasattr(obj,'__len__'): return list(obj) else: raise TypeError("Unhandled type for JSON: "+obj.__class__.__module__+'.'+obj.__class__.__name__) # minieigen objects elif obj.__class__.__module__ in ('minieigen','minieigen11','_wooEigen11'): if isinstance(obj,minieigen.Quaternion): return obj.toAxisAngle() else: return tuple(obj[i] for i in range(len(obj))) # numpy arrays elif isinstance(obj,numpy.ndarray): if not self.oneway: raise TypeError('numpy.ndarray can only be serialized with WooJSONEncoder(oneway=True), since deserialization will yield only dict/list, not a numpy.ndarray.') # record array: dump as dict of sub-arrays (columns) if obj.dtype.names: return dict([(name,obj[name].tolist()) for name in obj.dtype.names]) # non-record array: dump as nested list return obj.tolist() # h5py datasets elif haveH5py and isinstance(obj,h5py.Dataset): if not self.oneway: raise TypeError('h5py.Dataset can only be serialized with WooJSONEncoder(oneway=True), since deserialization will yield only dict/list, not a h5py.Dataset.') return obj[...] # other types, handled by the json module natively else: return super(WooJSONEncoder,self).default(obj)
[docs]class WooJSONDecoder(json.JSONDecoder): ''' Reconstruct JSON object, possibly containing specially-denoted Woo object (:obj:`WooJSONEncoder`). Do not use this class directly, say ``object.loads(format="json")`` instead. The *onError* ctor parameter determines what to do with a dictionary defining '__class__', which nevertheless cannot be reconstructed properly: this happens when class attributes changed inthe meantime, or the class does not exist anymore. Possible values are: * ``error``: raise exception (default) * ``warn``: log warning and return dictionary * ``ignore``: just return dictionary, without any notification ''' def __init__(self,onError='error'): assert onError in ('error','warn','ignore') json.JSONDecoder.__init__(self,object_hook=self.dictToObject) self.onError=onError
[docs] def dictToObject(self,d): import sys, woo if not '__class__' in d: return d # nothing we know klass=d.pop('__class__') modPath=klass.split('.')[:-1] localns={} # import modules as needed so that __class__ can be eval'd # stuff the uppermost module to localns for i in range(len(modPath)): mname='.'.join(modPath[:i+1]) m=__import__(mname,level=0) # absolute imports only if i==0: localns[mname]=m try: return eval(klass,globals(),localns)(**d) except Exception as e: if self.onError=='error': raise elif self.onError=='warn': import logging if e.__class__ in (TypeError,AttributeError): reason='class definition changed?' elif e.__class__==NameError: reason='class does not exist anymore?' else: reason='unknown error' logging.warning('%s while decoding class %s from JSON (%s), returning dictionary: %s'%(e.__class__.__name__,klass,reason,str(e))) return d
# inject into the core namespace, so that it can be used elsewhere as well woo.core.WooJSONEncoder=WooJSONEncoder woo.core.WooJSONDecoder=WooJSONDecoder # call the arg __e to avoid clash with math.e if there is 'from math import *' in the magic string
[docs]def wooExprEval(__e,__f,__overrideHashPercent={}): ''' Evaluate expression created with :obj:`SerializerToExpr`. Comments starting with ``#%`` and ``#:`` are executed as python code before the evaluation happens, which is in particular useful for importing necessary modules. The rule is the following: #. Lines starting with ``#%`` are executed in the local context. #. Values passed through ``__overrideHashPercent`` override variable values created in the preceding steps (it is an error if some variable is not defined). #. Lines starting with ``%:`` are evaluated in the local context. The rationale for the 2-step evaluation is that variables defined in ``#%`` can be externally overridden in a controlled manner (if by mistake a non-existent variable is assigned externally, it is an error; if nothing is overridden externally, the default is used); and only after this override, ``#:`` lines can use those variables. :param __e: expression to be evaluated :param __f: filename (if any) where the expression was stored :param __overrideHashPercent: dictionary which will change local variables (defined in ``#%`` lines) before the expression itself is evaluated. ''' import woo,math,textwrap # exec all lines starting with #% and #: as a piece of code __codePercent=textwrap.dedent('\n'.join([l[2:] for l in __e.split('\n') if l.startswith('#%')])) __codeHash =textwrap.dedent('\n'.join([l[2:] for l in __e.split('\n') if l.startswith('#:')])) #print('EXECUTING #: CODE:\n'+__code) exec(__codePercent,locals()) # pass locals() here for __var,__val in __overrideHashPercent.items(): if __var not in locals(): raise NameError("Local (defined in #%%) variable '%s' does not exist, unable to override its value '%s' from __overrideHashPercent."%(__var,str(__val))) locals()[__var]=__val # re-evaluate the block with locals from overrideHashPercent exec(__codeHash,locals()) # pass locals() here # return the expression return eval(compile(__e,__f,'eval'),locals(),globals())
[docs]def Object_loads(typ,data,format='auto',overrideHashPercent={}): 'Load object from file, with format auto-detection; when *typ* is None, no type-checking is performed.' def typeChecked(obj,type): if type==None: return obj if not isinstance(obj,typ): raise TypeError('Loaded object of type '+obj.__class__.__name__+' is not a '+typ.__name__) return obj if format not in ('auto','pickle','expr','json'): raise ValueError('Invalid format %s'%format) elif format=='auto': if type(data)==bytes and data.startswith(b'##woo-expression##'): format='expr' elif type(data) in (str,) and data.startswith('##woo-expression##'): format='expr' else: # try pickle try: if type(data)==str: return typeChecked(pickle.loads(bytes(data,'utf-8')),typ) else: return typeChecked(pickle.loads(data),typ) except (IOError,KeyError,pickle.UnpicklingError,EOFError): pass # try json try: return typeChecked(WooJSONDecoder().decode(data),typ) except (IOError,ValueError,KeyError): pass if format=='auto': IOError("Format detection failed on data: "%data) if overrideHashPercent and format!='expr': raise ValueError("overrideHashPercent only applicable with the 'expr' format (not '%s')."%format) ## format detected now if format=='expr': return typeChecked(wooExprEval(data,'<string>',__overrideHashPercent=overrideHashPercent),typ) elif format=='pickle': return typeChecked(pickle.loads(data,typ)) elif format=='json': return typeChecked(WooJSONDecoder().decode(data),typ) assert False # impossible
[docs]def Object_load(typ,inFile,format='auto',overrideHashPercent={}): def typeChecked(obj,type): if type==None: return obj if not isinstance(obj,typ): raise TypeError('Loaded object of type '+obj.__class__.__name__+' is not a '+typ.__name__) return obj validFormats=('auto','boost::serialization','cereal','expr','pickle','json') if format not in validFormats: raise ValueError('format must be one of '+', '.join(validFormats)+'.') if format=='auto': format=None ## DO NOT use extensions to determine type, that is evil # check for compression first head=open(inFile,'rb').read(100) # we might want to pass this data to ObjectIO, which currently determines format by looking at extension if head[:2]==b'\x1f\x8b': import gzip head=gzip.open(inFile,'rb').read(100) elif head[:2]==b'BZ': import bz2 head=bz2.BZ2File(inFile,'rb').read(100) # detect real format here (head is uncompressed already) # the string between nulls is 'serialization::archive' # see http://stackoverflow.com/questions/10614215/magic-for-detecting-boostserialization-file # newer versions (1.51 and perhaps greater) put '\n' (\x0a) after serialization::archive instead of null, # so let's just not test the byte after "archive" if head.startswith(b'\x16\x00\x00\x00\x00\x00\x00\x00\x73\x65\x72\x69\x61\x6c\x69\x7a\x61\x74\x69\x6f\x6e\x3a\x3a\x61\x72\x63\x68\x69\x76\x65'): format='boost::serialization' elif head.startswith(b'<?xml version="1.0"'): format='boost::serialization' elif head.startswith(b'\x01\x00\x00\x80\x08\x00\x00\x00\x00\x00\x00\x00'): format='cereal' elif head.startswith(b'##woo-expression##'): format='expr' else: # test pickling by trying to load try: return typeChecked(pickle.load(open(inFile,'rb')),typ) # open again to seek to the beginning except (IOError,KeyError,pickle.UnpicklingError,EOFError): pass try: return typeChecked(WooJSONDecoder().decode(codecs.open(inFile,'rb','utf-8').read()),typ) except (IOError,ValueError): pass if not format: raise RuntimeError('File format detection failed on %s (head: %s, bin: %s)'%(inFile,''.join(["\\x%02x"%x for x in head]),str(head))) # in py3k, bytes contain integers rather than chars if format not in validFormats: raise RuntimeError("format='%s'??"%format) assert format in validFormats if overrideHashPercent and format!='expr': raise ValueError("overrideHashPercent only applicable with the 'expr' format (not '%s')"%format) if format==None: raise IOError('Input file format not detected') # loading boost::serialization with cereal or vice versa will fail # we can check that later (based on woo.features) elif format in ('boost::serialization','cereal'): # ObjectIO takes care of detecting binary, xml, compression independently return typeChecked(Object._boostLoad(str(inFile)),typ) # convert unicode to str, if necessary, as the c++ type is std::string elif format=='expr': buf=codecs.open(inFile,'rb','utf-8').read() return typeChecked(wooExprEval(buf,inFile,__overrideHashPercent=overrideHashPercent),typ) elif format=='pickle': return typeChecked(pickle.load(open(inFile,'rb')),typ) elif format=='json': return typeChecked(WooJSONDecoder().decode(codecs.open(inFile,'rb','utf-8').read()),typ) assert False
[docs]def Object_loadTmp(typ,name=''): obj=woo.master.loadTmpAny(str(name)) if not isinstance(obj,typ): raise TypeError('Loaded object of type '+obj.__class__.__name__+' is not a '+typ.__name__) return obj
[docs]def Object_saveTmp(obj,name='',quiet=False): woo.master.saveTmpAny(obj,name,quiet)
[docs]def Object_deepcopy(obj,**kw): 'Make object deepcopy by serializing to memory and deserializing.' return woo.master.deepcopy(obj,**kw)
[docs]def Object_sha1(obj): import hashlib return hashlib.sha1(obj.dumps(format='expr',width=0,noMagic=True).encode('utf-8')).hexdigest()
Object._getAllTraits=Object_getAllTraits Object._getAllTraitsWithClasses=Object_getAllTraitsWithClasses Object.dump=Object_dump Object.dumps=Object_dumps Object.saveTmp=Object_saveTmp Object.deepcopy=Object_deepcopy Object.load=classmethod(Object_load) Object.loads=classmethod(Object_loads) Object.loadTmp=classmethod(Object_loadTmp) Object.sha1=Object_sha1
[docs]def Master_save(o,*args,**kw): o.scene.save(*args,**kw)
[docs]def Master_load(o,*args,**kw): o.scene=woo.core.Scene.load(*args,**kw)
[docs]def Master_reload(o,quiet=None,*args,**kw): # this arg is deprecated 'Reload master scene, using its :obj:`woo.core.Scene.lastSave`; assigns ``woo.master.scene`` and returns the new scene object' f=o.scene.lastSave if not f: raise ValueError("Scene.lastSave is empty.") if f.startswith(':memory:'): o.scene=woo.core.Scene.loadTmp(f[8:]) else: o.scene=woo.core.Scene.load(f,*args,**kw) return o.scene
[docs]def Master_loadTmp(o,name='',quiet=None): # quiet deprecated 'Load scene from temporary storage, assign to it ``woo.master.scene`` and return it.' o.scene=woo.core.Scene.loadTmp(name) return o.scene
[docs]def Master_saveTmp(o,name='',quiet=False): o.scene.setLastSave(':memory:'+name) o.scene.saveTmp(name,quiet)
woo.core.Master.save=Master_save woo.core.Master.load=Master_load woo.core.Master.reload=Master_reload woo.core.Master.loadTmp=Master_loadTmp woo.core.Master.saveTmp=Master_saveTmp
[docs]def Master_run(o,*args,**kw): return o.scene.run(*args,**kw)
[docs]def Master_pause(o,*args,**kw): return o.scene.stop(*args,**kw)
[docs]def Master_step(o,*args,**kw): return o.scene.one(*args,**kw)
[docs]def Master_wait(o,*args,**kw): return o.scene.wait(*args,**kw)
[docs]def Master_reset(o): o.scene=woo.core.Scene()
#def Master_running(o.,*args,**kw): return o.scene.running woo.core.Master.run=Master_run woo.core.Master.pause=Master_pause woo.core.Master.step=Master_step woo.core.Master.wait=Master_wait woo.core.Master.running=property(lambda o: o.scene.running) woo.core.Master.reset=Master_reset