diff options
Diffstat (limited to 'intern/python/modules/vrml/utils')
-rw-r--r-- | intern/python/modules/vrml/utils/__init__.py | 1 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/collapse.py | 169 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/err.py | 37 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/namespace.py | 225 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/typeclasses.py | 50 |
5 files changed, 482 insertions, 0 deletions
diff --git a/intern/python/modules/vrml/utils/__init__.py b/intern/python/modules/vrml/utils/__init__.py new file mode 100644 index 00000000000..9d708a9084c --- /dev/null +++ b/intern/python/modules/vrml/utils/__init__.py @@ -0,0 +1 @@ +"""utilities""" diff --git a/intern/python/modules/vrml/utils/collapse.py b/intern/python/modules/vrml/utils/collapse.py new file mode 100644 index 00000000000..25da50c2adb --- /dev/null +++ b/intern/python/modules/vrml/utils/collapse.py @@ -0,0 +1,169 @@ +''' +Destructive Functions for "collapsing" Sequences into single levels + +>>> from mcf.utils import collapse + +>>> collapse.test([[[1],[2,3]],[[]],[4],5,[6]]) + +[1, 2, 3, 4, 5, 6] # note that is the same root list + +>>> collapse.collapse2([[[1],[2,3]],[[]],(4,()),(5,),[6]]) + +[1, 2, 3, 4, 5, 6] # note is the same root list +''' +import copy, types, sys +from types import ListType, TupleType # this now only supports the obsolete stuff... + +def hyperCollapse( inlist, allowedmap, type=type, list=list, itype=types.InstanceType, maxint= sys.maxint): + ''' + Destructively flatten a mixed hierarchy to a single level. + Non-recursive, many speedups and obfuscations by Tim Peters :) + ''' + try: + # for every possible index + for ind in xrange( maxint): + # while that index currently holds a list + expandable = 1 + while expandable: + expandable = 0 + if allowedmap.has_key( type(inlist[ind]) ): + # expand that list into the index (and subsequent indicies) + inlist[ind:ind+1] = list( inlist[ind]) + expandable = 1 + + # alternately you could iterate through checking for isinstance on all possible + # classes, but that would be very slow + elif type( inlist[ind] ) is itype and allowedmap.has_key( inlist[ind].__class__ ): + # here figure out some way to generically expand that doesn't risk + # infinite loops... + templist = [] + for x in inlist[ind]: + templist.append( x) + inlist[ind:ind+1] = templist + expandable = 1 + except IndexError: + pass + return inlist + + +def collapse(inlist, type=type, ltype=types.ListType, maxint= sys.maxint): + ''' + Destructively flatten a list hierarchy to a single level. + Non-recursive, and (as far as I can see, doesn't have any + glaring loopholes). + Further speedups and obfuscations by Tim Peters :) + ''' + try: + # for every possible index + for ind in xrange( maxint): + # while that index currently holds a list + while type(inlist[ind]) is ltype: + # expand that list into the index (and subsequent indicies) + inlist[ind:ind+1] = inlist[ind] + #ind = ind+1 + except IndexError: + pass + return inlist + +def collapse_safe(inlist): + ''' + As collapse, but works on a copy of the inlist + ''' + return collapse( inlist[:] ) + +def collapse2(inlist, ltype=(types.ListType, types.TupleType), type=type, maxint= sys.maxint ): + ''' + Destructively flatten a list hierarchy to a single level. + Will expand tuple children as well, but will fail if the + top level element is not a list. + Non-recursive, and (as far as I can see, doesn't have any + glaring loopholes). + ''' + ind = 0 + try: + while 1: + while type(inlist[ind]) in ltype: + try: + inlist[ind:ind+1] = inlist[ind] + except TypeError: + inlist[ind:ind+1] = list(inlist[ind]) + ind = ind+1 + except IndexError: + pass + return inlist + +def collapse2_safe(inlist): + ''' + As collapse2, but works on a copy of the inlist + ''' + return collapse( list(inlist) ) + +def old_buggy_collapse(inlist): + '''Always return a one-level list of all the non-list elements in listin, + rewritten to be non-recursive 96-12-28 Note that the new versions work + on the original list, not a copy of the original.''' + if type(inlist)==TupleType: + inlist = list(inlist) + elif type(inlist)!=ListType: + return [inlist] + x = 0 + while 1: + try: + y = inlist[x] + if type(y) == ListType: + ylen = len(y) + if ylen == 1: + inlist[x] = y[0] + if type(inlist[x]) == ListType: + x = x - 1 # need to collapse that list... + elif ylen == 0: + del(inlist[x]) + x = x-1 # list has been shortened + else: + inlist[x:x+1]=y + x = x+1 + except IndexError: + break + return inlist + + +def old_buggy_collapse2(inlist): + '''As collapse, but also collapse tuples, rewritten 96-12-28 to be non-recursive''' + if type(inlist)==TupleType: + inlist = list(inlist) + elif type(inlist)!=ListType: + return [inlist] + x = 0 + while 1: + try: + y = inlist[x] + if type(y) in [ListType, TupleType]: + ylen = len(y) + if ylen == 1: + inlist[x] = y[0] + if type(inlist[x]) in [ListType,TupleType]: + x = x-1 #(to deal with that element) + elif ylen == 0: + del(inlist[x]) + x = x-1 # list has been shortened, will raise exception with tuples... + else: + inlist[x:x+1]=list(y) + x = x+1 + except IndexError: + break + return inlist + + +def oldest_buggy_collapse(listin): + 'Always return a one-level list of all the non-list elements in listin' + if type(listin) == ListType: + return reduce(lambda x,y: x+y, map(collapse, listin), []) + else: return [listin] + +def oldest_buggy_collapse2(seqin): + + if type(seqin) in [ListType, TupleType]: + return reduce(lambda x,y: x+y, map(collapse2, seqin), []) + else: + return [seqin] + diff --git a/intern/python/modules/vrml/utils/err.py b/intern/python/modules/vrml/utils/err.py new file mode 100644 index 00000000000..3c6591a6873 --- /dev/null +++ b/intern/python/modules/vrml/utils/err.py @@ -0,0 +1,37 @@ +''' +err.py Encapsulated writing to sys.stderr + +The idea of this module is that, for a GUI system (or a more advanced UI), +you can just import a different err module (or object) and keep +your code the same. (For instance, you often want a status window +which flashes warnings and info, and have error messages pop up an +alert to get immediate attention. +''' + +import sys + +def err(message, Code=0): + ''' + report an error, with an optional error code + ''' + if Code: + sys.stderr.write('Error #%i: %s\n'%(Code,message)) + else: + sys.stderr.write('Error: %s\n'%message) +def warn(message, Code=0): + ''' + report a warning, with an optional error code + ''' + if Code: + sys.stderr.write('Warning #%i: %s\n'%(Code,message)) + else: + sys.stderr.write('Warning: %s\n'%message) +def info(message, Code=0): + ''' + report information/status, with an optional error code + ''' + if Code: + sys.stderr.write('Info #%i: %s\n'%(Code,message)) + else: + sys.stderr.write('Info: %s\n'%message) + diff --git a/intern/python/modules/vrml/utils/namespace.py b/intern/python/modules/vrml/utils/namespace.py new file mode 100644 index 00000000000..dd9f0b7dea6 --- /dev/null +++ b/intern/python/modules/vrml/utils/namespace.py @@ -0,0 +1,225 @@ +''' +NameSpace v0.04: + +A "NameSpace" is an object wrapper around a _base dictionary +which allows chaining searches for an 'attribute' within that +dictionary, or any other namespace which is defined as part +of the search path (depending on the downcascade variable, is +either the hier-parents or the hier-children). + +You can assign attributes to the namespace normally, and read +them normally. (setattr, getattr, a.this = that, a.this) + +I use namespaces for writing parsing systems, where I want to +differentiate between sources (have multiple sources that I can +swap into or out of the namespace), but want to be able to get +at them through a single interface. There is a test function +which gives you an idea how to use the system. + +In general, call NameSpace(someobj), where someobj is a dictionary, +a module, or another NameSpace, and it will return a NameSpace which +wraps up the keys of someobj. To add a namespace to the NameSpace, +just call the append (or hier_addchild) method of the parent namespace +with the child as argument. + +### NOTE: if you pass a module (or anything else with a dict attribute), +names which start with '__' will be removed. You can avoid this by +pre-copying the dict of the object and passing it as the arg to the +__init__ method. + +### NOTE: to properly pickle and/or copy module-based namespaces you +will likely want to do: from mcf.utils import extpkl, copy_extend + +### Changes: + 97.05.04 -- Altered to use standard hierobj interface, cleaned up + interface by removing the "addparent" function, which is reachable + by simply appending to the __parent__ attribute, though normally + you would want to use the hier_addchild or append functions, since + they let both objects know about the addition (and therefor the + relationship will be restored if the objects are stored and unstored) + + 97.06.26 -- Altered the getattr function to reduce the number of + situations in which infinite lookup loops could be created + (unfortunately, the cost is rather high). Made the downcascade + variable harden (resolve) at init, instead of checking for every + lookup. (see next note) + + 97.08.29 -- Discovered some _very_ weird behaviour when storing + namespaces in mcf.store dbases. Resolved it by storing the + __namespace_cascade__ attribute as a normal attribute instead of + using the __unstore__ mechanism... There was really no need to + use the __unstore__, but figuring out how a functions saying + self.__dict__['__namespace_cascade__'] = something + print `self.__dict__['__namespace_cascade__']` can print nothing + is a bit beyond me. (without causing an exception, mind you) + + 97.11.15 Found yet more errors, decided to make two different + classes of namespace. Those based on modules now act similar + to dummy objects, that is, they let you modify the original + instead of keeping a copy of the original and modifying that. + + 98.03.15 -- Eliminated custom pickling methods as they are no longer + needed for use with Python 1.5final + + 98.03.15 -- Fixed bug in items, values, etceteras with module-type + base objects. +''' +import copy, types, string + +from mcf.utils import hierobj + +class NameSpace(hierobj.Hierobj): + ''' + An hierarchic NameSpace, allows specification of upward or downward + chaining search for resolving names + ''' + def __init__(self, val = None, parents=None, downcascade=1,children=[]): + ''' + A NameSpace can be initialised with a dictionary, a dummied + dictionary, another namespace, or something which has a __dict__ + attribute. + Note that downcascade is hardened (resolved) at init, not at + lookup time. + ''' + hierobj.Hierobj.__init__(self, parents, children) + self.__dict__['__downcascade__'] = downcascade # boolean + if val is None: + self.__dict__['_base'] = {} + else: + if type( val ) == types.StringType: + # this is a reference to a module which has been pickled + val = __import__( val, {},{}, string.split( val, '.') ) + try: + # See if val's a dummy-style object which has a _base + self.__dict__['_base']=copy.copy(val._base) + except (AttributeError,KeyError): + # not a dummy-style object... see if it has a dict attribute... + try: + if type(val) != types.ModuleType: + val = copy.copy(val.__dict__) + except (AttributeError, KeyError): + pass + # whatever val is now, it's going to become our _base... + self.__dict__['_base']=val + # harden (resolve) the reference to downcascade to speed attribute lookups + if downcascade: self.__dict__['__namespace_cascade__'] = self.__childlist__ + else: self.__dict__['__namespace_cascade__'] = self.__parent__ + def __setattr__(self, var, val): + ''' + An attempt to set an attribute should place the attribute in the _base + dictionary through a setitem call. + ''' + # Note that we use standard attribute access to allow ObStore loading if the + # ._base isn't yet available. + try: + self._base[var] = val + except TypeError: + setattr(self._base, var, val) + def __getattr__(self,var): +## print '__getattr__', var + return self.__safe_getattr__(var, {}) # the {} is a stopdict + + def __safe_getattr__(self, var,stopdict): + ''' + We have a lot to do in this function, if the attribute is an unloaded + but stored attribute, we need to load it. If it's not in the stored + attributes, then we need to load the _base, then see if it's in the + _base. + If it's not found by then, then we need to check our resource namespaces + and see if it's in them. + ''' + # we don't have a __storedattr__ or it doesn't have this key... + if var != '_base': + try: + return self._base[var] + except (KeyError,TypeError), x: + try: + return getattr(self._base, var) + except AttributeError: + pass + try: # with pickle, it tries to get the __setstate__ before restoration is complete + for cas in self.__dict__['__namespace_cascade__']: + try: + stopdict[id(cas)] # if succeeds, we've already tried this child + # no need to do anything, if none of the children succeeds we will + # raise an AttributeError + except KeyError: + stopdict[id(cas)] = None + return cas.__safe_getattr__(var,stopdict) + except (KeyError,AttributeError): + pass + raise AttributeError, var + def items(self): + try: + return self._base.items() + except AttributeError: + pass + try: + return self._base.__dict__.items() + except AttributeError: + pass + def keys(self): + try: + return self._base.keys() + except AttributeError: + pass + try: + return self._base.__dict__.keys() + except AttributeError: + pass + def has_key( self, key ): + try: + return self._base.has_key( key) + except AttributeError: + pass + try: + return self._base.__dict__.has_key( key) + except AttributeError: + pass + def values(self): + try: + return self._base.values() + except AttributeError: + pass + try: + return self._base.__dict__.values() + except AttributeError: + pass + + def __getinitargs__(self): + if type( self._base ) is types.ModuleType: + base = self._base.__name__ + else: + base = self._base + return (base, self.__parent__, self.__downcascade__, self.__childlist__) + def __getstate__(self): + return None + def __setstate__(self,*args): + pass + def __deepcopy__(self, memo=None): + d = id(self) + if memo is None: + memo = {} + elif memo.has_key(d): + return memo[d] + if type(self._base) == types.ModuleType: + rest = tuple(map( copy.deepcopy, (self.__parent__, self.__downcascade__, self.__childlist__) )) + new = apply(self.__class__, (self._base,)+rest ) + else: + new = tuple(map( copy.deepcopy, (self._base, self.__parent__, self.__downcascade__, self.__childlist__) )) + return new +## def __del__( self, id=id ): +## print 'del namespace', id( self ) + + +def test(): + import string + a = NameSpace(string) + del(string) + a.append(NameSpace({'a':23,'b':42})) + import math + a.append(NameSpace(math)) + print 'The returned object should allow access to the attributes of the string,\nand math modules, and two simple variables "a" and "b" (== 23 and42 respectively)' + return a + + diff --git a/intern/python/modules/vrml/utils/typeclasses.py b/intern/python/modules/vrml/utils/typeclasses.py new file mode 100644 index 00000000000..ed798dfe3da --- /dev/null +++ b/intern/python/modules/vrml/utils/typeclasses.py @@ -0,0 +1,50 @@ +''' +Classes of Types + +Often you want to be able to say: + if type(obj) in MutableTypes: + yada + +This module is intended to make that easier. +Just import and use :) +''' +import types + +MutableTypes = [ types.ListType, types.DictType, types.InstanceType ] +MutableSequenceTypes = [ types.ListType ] +SequenceTypes = [ types.ListType, types.StringType, types.TupleType ] +NumericTypes = [ types.IntType, types.FloatType, types.LongType, types.ComplexType ] +MappingTypes = [ types.DictType ] + +def regarray(): + if globals().has_key('array'): + return 1 + try: + import array + SequenceTypes.append( array.ArrayType ) + MutableTypes.append( array.ArrayType ) + MutableSequenceTypes.append( array.ArrayType ) + return 1 + except ImportError: + return 0 + +def regnumpy(): + ''' + Call if you want to register numpy arrays + according to their types. + ''' + if globals().has_key('Numeric'): + return 1 + try: + import Numeric + SequenceTypes.append( Numeric.ArrayType ) + MutableTypes.append( Numeric.ArrayType ) + MutableSequenceTypes.append( Numeric.ArrayType ) + return 1 + except ImportError: + return 0 + +# for now, I'm going to always register these, if the module becomes part of the base distribution +# it might be better to leave it out so numpy isn't always getting loaded... +regarray() +regnumpy()
\ No newline at end of file |