diff options
Diffstat (limited to 'intern/python/modules/mcf')
27 files changed, 2297 insertions, 0 deletions
diff --git a/intern/python/modules/mcf/__init__.py b/intern/python/modules/mcf/__init__.py new file mode 100644 index 00000000000..a553f20ba1c --- /dev/null +++ b/intern/python/modules/mcf/__init__.py @@ -0,0 +1,6 @@ +#mcf 'vendor' packages + +#These packages are free software, provided without warranty or +#guarantee, if you use them, you must agree to use them at your +#own risk. Please see the file license.txt for full license +#details. diff --git a/intern/python/modules/mcf/utils/__init__.py b/intern/python/modules/mcf/utils/__init__.py new file mode 100644 index 00000000000..156e4dbe16b --- /dev/null +++ b/intern/python/modules/mcf/utils/__init__.py @@ -0,0 +1,6 @@ +''' +mcf.utils package + + +''' + diff --git a/intern/python/modules/mcf/utils/collapse.py b/intern/python/modules/mcf/utils/collapse.py new file mode 100644 index 00000000000..25da50c2adb --- /dev/null +++ b/intern/python/modules/mcf/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/mcf/utils/copy_extend.py b/intern/python/modules/mcf/utils/copy_extend.py new file mode 100644 index 00000000000..687564cf12c --- /dev/null +++ b/intern/python/modules/mcf/utils/copy_extend.py @@ -0,0 +1,83 @@ +''' +Module to allow for "copying" Numeric arrays, +(and thereby also matrices and userarrays) +standard arrays, classes and modules +(last two are not actually copied, but hey :) ). + +Could do approximately the same thing with +copy_reg, but would be inefficient because +of passing the data into and out of strings. + +To use, just import this module. +''' +# altered 98.11.05, moved copy out of NUMPY test +import copy +try: # in case numpy not installed + import Numeric + def _numpyarray_copy(somearray, memo=None): + ''' + Simple function for getting a copy of a NUMPY array + ''' + if memo == None: + memo = {} # yeah, I know, not _really_ necessary + # see if already done this item, return the copy if we have... + d = id(somearray) + try: + return memo[d] + except KeyError: + pass + temp = Numeric.array(somearray, copy=1) + memo[d] = temp + return temp + # now make it available to the copying functions + copy._copy_dispatch[Numeric.ArrayType] = _numpyarray_copy + copy._deepcopy_dispatch[Numeric.ArrayType] = _numpyarray_copy +except ImportError: # Numeric not installed... + pass + +try: # in case array not installed + import array + def _array_copy(somearray, memo = None): + ''' + Simple function for getting a copy of a standard array. + ''' + if memo == None: + memo = {} # yeah, I know, not _really_ necessary + # see if already done this item, return the copy if we have... + d = id(somearray) + try: + return memo[d] + except KeyError: + pass + newarray = somearay[:] + memo[d] = newarray + return newarray + + # now make it available to the copying functions + copy._copy_dispatch[ array.ArrayType ] = _array_copy + copy._deepcopy_dispatch[ array.ArrayType ] = _array_copy +except ImportError: + pass + +import types + +def _module_copy(somemodule, memo = None): + ''' + Modules we will always treat as themselves during copying??? + ''' + return somemodule + +# now make it available to the copying functions +copy._copy_dispatch[ types.ModuleType ] = _module_copy +copy._deepcopy_dispatch[ types.ModuleType ] = _module_copy + +def _class_copy(someclass, memo=None): + ''' + Again, classes are considered immutable, they are + just returned as themselves, not as new objects. + ''' + return someclass + +# now make it available to the copying functions +#copy._copy_dispatch[ types.ClassType ] = _class_copy +copy._deepcopy_dispatch[ types.ClassType ] = _class_copy diff --git a/intern/python/modules/mcf/utils/cpickle_extend.py b/intern/python/modules/mcf/utils/cpickle_extend.py new file mode 100644 index 00000000000..aaca41d51fb --- /dev/null +++ b/intern/python/modules/mcf/utils/cpickle_extend.py @@ -0,0 +1,190 @@ +''' +Extend cpickle storage to include modules, and builtin functions/methods + +To use, just import this module. +''' +import copy_reg + +### OBJECTS WHICH ARE RESTORED THROUGH IMPORTS +# MODULES +def pickle_module(module): + ''' + Store a module to a pickling stream, must be available for + reimport during unpickling + ''' + return unpickle_imported_code, ('import %s'%module.__name__, module.__name__) + +# FUNCTIONS, METHODS (BUILTIN) +def pickle_imported_code(funcmeth): + ''' + Store a reference to an imported element (such as a function/builtin function, + Must be available for reimport during unpickling. + ''' + module = _whichmodule(funcmeth) + return unpickle_imported_code, ('from %s import %s'%(module.__name__,funcmeth.__name__),funcmeth.__name__) + +import types, regex +import_filter = regex.compile('''\(from [A-Za-z0-9_\.]+ \)?import [A-Za-z0-9_\.]+''') # note the limitations on whitespace +getattr_filter = regex.compile('''[A-Za-z0-9_\.]+''') # note we allow you to use x.y.z here + +# MODULES, AND FUNCTIONS +def unpickle_imported_code(impstr,impname): + ''' + Attempt to load a reference to a module or other imported code (such as functions/builtin functions) + ''' + if import_filter.match(impstr) != len(impstr) or getattr_filter.match(impname)!= len(impname): + import sys + sys.stderr.write('''Possible attempt to smuggle arbitrary code into pickle file (see module cpickle_extend).\nPassed code was %s\n%s\n'''%(impstr,impname)) + del(sys) + else: + ns = {} + try: + exec (impstr) in ns # could raise all sorts of errors, of course, and is still dangerous when you have no control over the modules on your system! Do not allow for untrusted code!!! + return eval(impname, ns) + except: + import sys + sys.stderr.write('''Error unpickling module %s\n None returned, will likely raise errors.'''%impstr) + return None + +# Modules +copy_reg.pickle(type(regex),pickle_module,unpickle_imported_code) +# builtin functions/methods +copy_reg.pickle(type(regex.compile),pickle_imported_code, unpickle_imported_code) + +del(regex) # to keep the namespace neat as possible + +### INSTANCE METHODS +''' +The problem with instance methods is that they are almost always +stored inside a class somewhere. We really need a new type: reference +that lets us just say "y.this" + +We also need something that can reliably find burried functions :( not +likely to be easy or clean... + +then filter for x is part of the set +''' +import new + +def pickle_instance_method(imeth): + ''' + Use the (rather surprisingly clean) internals of + the method to store a reference to a method. Might + be better to use a more general "get the attribute + 'x' of this object" system, but I haven't written that yet :) + ''' + klass = imeth.im_class + funcimp = _imp_meth(imeth) + self = imeth.im_self # will be None for UnboundMethodType + return unpickle_instance_method, (funcimp,self,klass) +def unpickle_instance_method(funcimp,self,klass): + ''' + Attempt to restore a reference to an instance method, + the instance has already been recreated by the system + as self, so we just call new.instancemethod + ''' + funcimp = apply(unpickle_imported_code, funcimp) + return new.instancemethod(func,self,klass) + +copy_reg.pickle(types.MethodType, pickle_instance_method, unpickle_instance_method) +copy_reg.pickle(types.UnboundMethodType, pickle_instance_method, unpickle_instance_method) + +### Arrays +try: + import array + LittleEndian = array.array('i',[1]).tostring()[0] == '\001' + def pickle_array(somearray): + ''' + Store a standard array object, inefficient because of copying to string + ''' + return unpickle_array, (somearray.typecode, somearray.tostring(), LittleEndian) + def unpickle_array(typecode, stringrep, origendian): + ''' + Restore a standard array object + ''' + newarray = array.array(typecode) + newarray.fromstring(stringrep) + # floats are always big-endian, single byte elements don't need swapping + if origendian != LittleEndian and typecode in ('I','i','h','H'): + newarray.byteswap() + return newarray + copy_reg.pickle(array.ArrayType, pickle_array, unpickle_array) +except ImportError: # no arrays + pass + +### NUMPY Arrays +try: + import Numeric + LittleEndian = Numeric.array([1],'i').tostring()[0] == '\001' + def pickle_numpyarray(somearray): + ''' + Store a numpy array, inefficent, but should work with cPickle + ''' + return unpickle_numpyarray, (somearray.typecode(), somearray.shape, somearray.tostring(), LittleEndian) + def unpickle_numpyarray(typecode, shape, stringval, origendian): + ''' + Restore a numpy array + ''' + newarray = Numeric.fromstring(stringval, typecode) + Numeric.reshape(newarray, shape) + if origendian != LittleEndian and typecode in ('I','i','h','H'): + # this doesn't seem to work correctly, what's byteswapped doing??? + return newarray.byteswapped() + else: + return newarray + copy_reg.pickle(Numeric.ArrayType, pickle_numpyarray, unpickle_numpyarray) +except ImportError: + pass + +### UTILITY FUNCTIONS +classmap = {} +def _whichmodule(cls): + """Figure out the module in which an imported_code object occurs. + Search sys.modules for the module. + Cache in classmap. + Return a module name. + If the class cannot be found, return __main__. + Copied here from the standard pickle distribution + to prevent another import + """ + if classmap.has_key(cls): + return classmap[cls] + clsname = cls.__name__ + for name, module in sys.modules.items(): + if name != '__main__' and \ + hasattr(module, clsname) and \ + getattr(module, clsname) is cls: + break + else: + name = '__main__' + classmap[cls] = name + return name + +import os, string, sys + +def _imp_meth(im): + ''' + One-level deep recursion on finding methods, i.e. we can + find them only if the class is at the top level. + ''' + fname = im.im_func.func_code.co_filename + tail = os.path.splitext(os.path.split(fname)[1])[0] + ourkeys = sys.modules.keys() + possibles = filter(lambda x,tail=tail: x[-1] == tail, map(string.split, ourkeys, ['.']*len(ourkeys))) + + # now, iterate through possibles to find the correct class/function + possibles = map(string.join, possibles, ['.']*len(possibles)) + imp_string = _search_modules(possibles, im.im_func) + return imp_string + +def _search_modules(possibles, im_func): + for our_mod_name in possibles: + our_mod = sys.modules[our_mod_name] + if hasattr(our_mod, im_func.__name__) and getattr(our_mod, im_func.__name__).im_func is im_func: + return 'from %s import %s'%(our_mod.__name__, im_func.__name__), im_func.__name__ + for key,val in our_mod.__dict__.items(): + if hasattr(val, im_func.__name__) and getattr(val, im_func.__name__).im_func is im_func: + return 'from %s import %s'%(our_mod.__name__,key), '%s.%s'%(key,im_func.__name__) + raise '''No import string calculable for %s'''%im_func + + diff --git a/intern/python/modules/mcf/utils/dictbool.py b/intern/python/modules/mcf/utils/dictbool.py new file mode 100644 index 00000000000..cd549b7a681 --- /dev/null +++ b/intern/python/modules/mcf/utils/dictbool.py @@ -0,0 +1,80 @@ +''' +DictBool: +Simplistic (and slow) implementation of Boolean operations for +dictionaries... really these should be implemented in C, but I +can't do that till I have MSVC++, which I don't really want to +buy... this will have to do in the meantime. + +>>> from mcf.utils import dictbool + +>>> a = {1:2}; b = {2:3}; c={4:5,6:7,8:9,1:5} + +>>> dictbool.union(a,b,c) # overwrite a with b and the result with c + +{1: 5, 2: 3, 4: 5, 8: 9, 6: 7} + +>>> dictbool.collectunion(a,b,c) # collect all possible for each key + +{1: [2, 5], 2: [3], 4: [5], 8: [9], 6: [7]} + +>>> dictbool.intersect(a,b,c) # no common elements in all three + +{} + +>>> dictbool.intersect(a,c) # one element is common to both + +{1: [2, 5]} +''' + +def union(*args): + ''' + Build a new dictionary with the key,val from all args, + first overwritten by second, overwritten by third etc. + Rewritten for Python 1.5 on 98.03.31 + ''' + temp = {} + for adict in args: + # following is the 1.5 version + temp.update(adict) +# for key,val in adict.items(): +# temp[key] = val + return temp + +def collectunion(*args): + ''' + As union, save instead of overwriting, all vals are + returned in lists, and duplicates are appended to those + lists. + ''' + temp = {} + for adict in args: + for key,val in adict.items(): + try: + temp[key].append(val) + except KeyError: + temp[key] = [val] + return temp + +def intersect(*args): + ''' + Build a new dictionary with those keys common to all args, + the vals of the new dict are lists of length len(args), where + list[ind] is the value of args[ind] for that key. + ''' + args = map(lambda x: (len(x),x), args) + args.sort() + temp = {} + master = args[0][1] + rest = map(lambda x: x[1], args[1:]) + for var,val in master.items(): + tempval = [val] + for slave in rest: + try: + tempval.append(slave[var]) + except KeyError: + tempval = None + break + if tempval: + temp[var] = tempval + return temp + diff --git a/intern/python/modules/mcf/utils/dsort.py b/intern/python/modules/mcf/utils/dsort.py new file mode 100644 index 00000000000..cd6ad6b6c32 --- /dev/null +++ b/intern/python/modules/mcf/utils/dsort.py @@ -0,0 +1,91 @@ +nullval = (1,) + +class DSort: + ''' + A "dependency" sorting class, used to order elements + according to declared "dependencies" (many-to-one relationships) + Is not a beautiful algo, but it works (or seems to) + Requires hashable values for all elements. + + This is a quick hack, use at your own risk! + + Basic usage: + Create a DSort mysorter + for each element q which is part of the set to sort, call: + mysorter.rule( dsort.nullval, q) + # this is not strictly necessary for elements which are + # dependent on other objects, but it is necessary for + # those which are not. Generally it's easiest to call + # the null rule for each element. + for each rule x depends on y, call: + mysorter.rule( x, y) + when _all_ rules are entered, call + try: + sortedlist = mysorter.sort() + except ValueError: + handle recursive dependencies here... + + + For an example of real-life use, see the VRML lineariser. + + ''' + def __init__(self, recurseError=None ): + self.dependon = {nullval:[0]} + self.recurseError = recurseError + def rule( self, depon, deps): + ''' + Register a "rule". Both elements must be hashable values. + See the class' documentation for usage. + ''' +# print '''registering rule:''', depon, deps + if self.dependon.has_key( deps ) and depon is not nullval: + self.dependon[ deps ].append( depon ) + elif depon is not nullval: + self.dependon[ deps ] = [-1, depon] + elif not self.dependon.has_key( deps ): + self.dependon[ deps ] = [-1 ] + def sort( self ): + ''' + Get the sorted results as a list + ''' + for key, value in self.dependon.items(): + self._dsort( key, value) + temp = [] + for key, value in self.dependon.items(): + temp.append( (value[0], key) ) + temp.sort() + temp.reverse() + temp2 = [] + for x,y in temp: + temp2.append( y ) + # following adds the elements with no dependencies + temp2[len(temp2):] = self.dependon[ nullval ][1:] + return temp2 + def _dsort( self, key, value ): + if value[0] == -2: + if self.recurseError: + raise ValueError, '''Dependencies were recursive!''' + else: + if __debug__: + print '''Recursive dependency discovered and ignored in dsort.Dsort._dsort on %s:%s'''%(key, value) + return 1 # we know it has at least one reference... + elif value[0] == -1: # haven't yet calculated this rdepth + value[0] = -2 + tempval = [0] + for x in value[1:]: + try: + tempval.append( 1 + self._dsort( x, self.dependon[x]) ) + except KeyError: + self.dependon[ nullval ].append( x ) # is an unreferenced element + tempval.append( 1 ) + value[0] = max( tempval ) + return value[0] + else: + return value[0] +''' +from mcf.utils import dsort +>>> x = dsort.DSort() +>>> map( x.rule, [1,2,2,4,5,4], [2,3,4,5,6,3] ) +[None, None, None, None, None, None] +>>> x.sort() +'''
\ No newline at end of file diff --git a/intern/python/modules/mcf/utils/dummy.py b/intern/python/modules/mcf/utils/dummy.py new file mode 100644 index 00000000000..fb68c4049bf --- /dev/null +++ b/intern/python/modules/mcf/utils/dummy.py @@ -0,0 +1,91 @@ +''' +Dummy Class, intended as an abstract class for the creation +of base/builtin classes with slightly altered functionality +uses _base as the name of an instance of the base datatype, +mapping all special functions to that name. + +>>> from mcf.utils import dummy + +>>> j = dummy.Dummy({}) + +>>> j['this'] = 23 + +>>> j + +{'this': 23} + +>>> class example(dummy.Dummy): + +... def __repr__(self): + +... return '<example: %s>'%`self._base` + +>>> k = example([]) + +>>> k # uses the __repr__ function + +<example: []> + +>>> k.append # finds the attribute of the _base + +<built-in method append of list object at 501830> + +''' +import types, copy + +class Dummy: + 'Abstract class for slightly altering functionality of objects (including builtins)' + def __init__(self, val=None): + 'Initialisation, should be overridden' + if val and type(val)== types.InstanceType and hasattr(val, '_base'): + # Dict is used because subclasses often want to override + # the setattr function + self.__dict__['_base']=copy.copy(val.__dict__['_base']) + else: + self.__dict__['_base'] = val + def __repr__(self): + 'Return a string representation' + return repr(self._base) + def __str__(self): + 'Convert to a string' + return str(self._base) + def __cmp__(self,other): + 'Compare to other value' + # altered 98.03.17 from if...elif...else statement + return cmp(self._base, other) + def __getitem__(self, key): + 'Get an item by index' + return self._base[key] + def __setitem__(self, key, val): + 'Set an item by index' + self._base[key]=val + def __len__(self): + 'return the length of the self' + return len(self._base) + def __delitem__(self, key): + 'remove an item by index' + del(self._base[key]) + def __getslice__(self, i, j): + 'retrieve a slice by indexes' + return self._base[i:j] + def __setslice__(self, i, j, val): + 'set a slice by indexes to values' + self._base[i:j]=val + def __delslice__(self, i, j): + 'remove a slice by indexes' + del(self._base[i:j]) + def __nonzero__(self): + if self._base: + return 1 + else: + return 0 + def __getattr__(self, attr): + 'find an attribute when normal lookup fails, will raise a KeyError if missing _base attribute' + try: + return getattr( self.__dict__['_base'], attr) + except (AttributeError, KeyError): + try: + return self.__dict__['_base'][attr] + except (KeyError,TypeError): + pass + raise AttributeError, attr diff --git a/intern/python/modules/mcf/utils/err.py b/intern/python/modules/mcf/utils/err.py new file mode 100644 index 00000000000..3c6591a6873 --- /dev/null +++ b/intern/python/modules/mcf/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/mcf/utils/extpkl.py b/intern/python/modules/mcf/utils/extpkl.py new file mode 100644 index 00000000000..8ae52969281 --- /dev/null +++ b/intern/python/modules/mcf/utils/extpkl.py @@ -0,0 +1,19 @@ +''' +Make either cPickle or pickle available as the virtual +module mcf.utils.pickle. This allows you to use a single +import statement: + + from mcf.utils import extpkl, pickle + +and then use that pickle, knowing that you have the best +available pickling engine. +''' +defaultset = ('import cPickle', 'cPickle') +import sys, mcf.utils +from mcf.utils import cpickle_extend +try: + import cPickle + pickle = cPickle +except: + import pickle +sys.modules['mcf.utils.pickle'] = mcf.utils.pickle = pickle diff --git a/intern/python/modules/mcf/utils/fileassociation.py b/intern/python/modules/mcf/utils/fileassociation.py new file mode 100644 index 00000000000..55fd9735bfb --- /dev/null +++ b/intern/python/modules/mcf/utils/fileassociation.py @@ -0,0 +1,65 @@ +### WARNING: +# I don't have a clue what I'm doing here! + +import win32api +### Following is the "normal" approach, +### but it requires loading the entire win32con file (which is big) +### for two values... +##import win32con +##HKEY_CLASSES_ROOT = win32con.HKEY_CLASSES_ROOT +##REG_SZ = win32con.REG_SZ + +### These are the hard-coded values, should work everywhere as far as I know... +HKEY_CLASSES_ROOT = 0x80000000 +REG_SZ= 1 + +def associate( extension, filetype, description="", commands=(), iconfile="" ): + '''Warning: I don't have a clue what I'm doing here! + extension -- extension including "." character, e.g. .proc + filetype -- formal name, no spaces allowed, e.g. SkeletonBuilder.RulesFile + description -- human-readable description of the file type + commands -- sequence of (command, commandline), e.g. (("Open", "someexe.exe %1"),) + iconfile -- optional default icon file for the filetype + ''' + win32api.RegSetValue( + HKEY_CLASSES_ROOT, + extension, + REG_SZ, + filetype + ) + if description: + win32api.RegSetValue( + HKEY_CLASSES_ROOT , + filetype, + REG_SZ, + description + ) + if iconfile: + win32api.RegSetValue( + HKEY_CLASSES_ROOT , + "%(filetype)s\\DefaultIcon" % locals(), + REG_SZ, + iconfile + ) + for (command, commandline) in commands: + win32api.RegSetValue( + HKEY_CLASSES_ROOT , + "%(filetype)s\\Shell\\%(command)s" % locals(), + REG_SZ, + command, + ) + win32api.RegSetValue( + HKEY_CLASSES_ROOT , + "%(filetype)s\\Shell\\%(command)s\\Command" % locals(), + REG_SZ, + commandline + ) + +if __name__ == "__main__": + associate( + ".proc", + "SkeletonBuilder.Processing", + "SkeletonBuilder Processing File", + (("Open", '''z:\\skeletonbuilder\\skeletonbuilder.exe "%1" %*'''),), + '''z:\\skeletonbuilder\\bitmaps\\skeletonbuildericon.ico''', + )
\ No newline at end of file diff --git a/intern/python/modules/mcf/utils/findourfile.py b/intern/python/modules/mcf/utils/findourfile.py new file mode 100644 index 00000000000..1c70ff0dd70 --- /dev/null +++ b/intern/python/modules/mcf/utils/findourfile.py @@ -0,0 +1,30 @@ +''' +This utility allows a python system to find a file in it's +directory. To do this, you need to pass it a function object from +a module in the correct directory. I know there must be a better +way to do this, but I haven't seen it yet. Incidentally, the +current directory should be _different_ from the module in which +the function is contained, otherwise this function will go off into +the root directory. + +Currently this has to be called with the current directory a directory +other than the directory we're trying to find... need a better solution +for this kind of thing... a python registry would be great :) + +NOTE: as of Python 1.5, this module should be obsolete! As soon as I +have verified that all of my code is fixed, it will be moved to the unused +directories. +''' +import os,sys + +def findourfile(function, filename): + ''' + Given the function, return a path to the a file in the + same directory with 'filename'. We also let the caller + know if the file already exists. + ''' + ourfilename = os.path.split(function.func_code.co_filename)[0]+os.sep+filename + exists = os.path.exists(ourfilename) + return (exists,ourfilename) + + diff --git a/intern/python/modules/mcf/utils/hier_rx.py b/intern/python/modules/mcf/utils/hier_rx.py new file mode 100644 index 00000000000..3770f0bab22 --- /dev/null +++ b/intern/python/modules/mcf/utils/hier_rx.py @@ -0,0 +1,201 @@ +''' +Simple Hierarchic Walking functions for use with hierobj-type objects. + +Provide for recurse-safe processing. Currently only provide depth-first +processing, and don't provide means for ignoring branches of the tree +during processing. For an example of breadth-first processing, see +mcf.pars.int.index.indutils. For more complex hierarchic processing, +see the mcf.walker package. + +Originally these functions were only methods of the hierobj class (they +still are methods of it). I've split them out to allow them to be +imported selectively by other classes (some classes will only want +the simple walking functions, and not want to be bothered with the +methods which hierobj uses to keep track of its particular internal +structures. +''' + +def hier_rapply(self, function,arglist=None,argdict={},moreattr = '__childlist__'): + ''' + Safely apply a function to self and all children for + the function's side effects. Discard the return values + that function returns. + + function + function to apply + arglist + (self,)+arglist is the set of arguments passed to function + argdict + passed as namedargs to the function + moreattr + the attribute representing the children of a node + ''' + alreadydone = {} + tobedone = [self] + if arglist or argdict: + if not arglist: arglist=[self] + else: + arglist.insert(0,self) # we could insert anything... self is convenient + while tobedone: + object = tobedone[0] + try: + alreadydone[id(object)] + # We've already processed this object + except KeyError: + # We haven't processed this object + alreadydone[id(object)]=1 + arglist[0]=object + apply(function,tuple(arglist),argdict) + try: + tobedone[1:1]=getattr(object,moreattr) + except AttributeError: + # if the object isn't a hierobj, we don't need to recurse into it. + pass + del(tobedone[0]) + else: # no arglist or argdict + while tobedone: + object = tobedone[0] + try: + alreadydone[id(object)] + # We've already processed this object + except KeyError: + # We haven't processed this object + alreadydone[id(object)]=1 + function(object) + try: + tobedone[1:1]=getattr(object,moreattr) + except AttributeError: + # if the object isn't a hierobj, we don't need to recurse into it. + pass + del(tobedone[0]) +def hier_rreturn(self, function,arglist=None,argdict={},moreattr = '__childlist__'): + ''' + Safely apply a function to self and all children, + collect the results in a list and return. + + function + function to apply + arglist + (self,)+arglist is the set of arguments passed to function + argdict + passed as namedargs to the function + moreattr + the attribute representing the children of a node + ''' + alreadydone = {} + tobedone = [self] + results = [] + if arglist or argdict: + if not arglist: arglist=[self] + else: + arglist.insert(0,self) # or anything you feel like + while tobedone: + object = tobedone[0] + try: + alreadydone[id(object)] + # We've already processed this object + except KeyError: + # We haven't processed this object + alreadydone[id(object)]=1 + arglist[0]=object + results.append(apply(function,tuple(arglist),argdict)) + try: + tobedone[1:1]=getattr(object,moreattr) + except AttributeError: + # if the object isn't a hierobj, we don't need to recurse into it. + pass + del(tobedone[0]) + else: + while tobedone: + object = tobedone[0] + try: + alreadydone[id(object)] + # We've already processed this object + except KeyError: + # We haven't processed this object + alreadydone[id(object)]=1 + results.append(function(object)) + try: + tobedone[1:1]=getattr(object,moreattr) + except AttributeError: + # if the object isn't a hierobj, we don't need to recurse into it. + pass + del(tobedone[0]) + return results +def hier_rgetattr(self, attrname, multiple=1, moreattr = '__childlist__'): + ''' + Recursively collect the values for attrname and + return as a list. + + attrname + attribute to collect + arglist + (self,)+arglist is the set of arguments passed to function + argdict + passed as namedargs to the function + moreattr + the attribute representing the children of a node + ''' + alreadydone = {} + tobedone = [self] + results = [] + while tobedone: + object = tobedone[0] + try: + alreadydone[id(object)] + # We've already processed this object + except KeyError: + # We haven't processed this object + alreadydone[id(object)]=1 + try: + if multiple: + results.append(getattr(object, attrname)) + else: + return getattr(object, attrname) + except AttributeError: + pass + try: + tobedone[1:1]=getattr(object,moreattr) + except AttributeError: + # if the object isn't a hierobj, we don't need to recurse into it. + pass + del(tobedone[0]) + return results +def hier_rmethod(self, methodname,arglist=(),argdict={},moreattr = '__childlist__'): + ''' + return the result of calling every object's method methodname, + as for hier_rreturn otherwise. + + methodname + method to call + arglist + (self,)+arglist is the set of arguments passed to function + argdict + passed as namedargs to the function + moreattr + the attribute representing the children of a node + ''' + + alreadydone = {} + tobedone = [self] + results = [] + while tobedone: + object = tobedone[0] + try: + alreadydone[id(object)] + # We've already processed this object + except KeyError: + # We haven't processed this object + alreadydone[id(object)]=1 + try: + results.append(apply(getattr(object,methodname),arglist,argdict)) + except: + pass + try: + tobedone[1:1]=getattr(object,moreattr) + except AttributeError: + # if the object isn't a hierobj, we don't need to recurse into it. + pass + del(tobedone[0]) + return results + diff --git a/intern/python/modules/mcf/utils/hierdummy.py b/intern/python/modules/mcf/utils/hierdummy.py new file mode 100644 index 00000000000..0cf86e9e0c0 --- /dev/null +++ b/intern/python/modules/mcf/utils/hierdummy.py @@ -0,0 +1,16 @@ +''' +Hierarchic 'Dummy' objects +''' + +import hierobj, dummy + +class HierobjDummy(hierobj.Hierobj,dummy.Dummy): + ''' + An Hierarchic Dummy object, which provides direct access to its + children through object[x] interfaces, allows "index" "count" + etceteras by returning the corresponding attributes of the _base. + ''' + def __init__(self, parent=None, childlist=None): + hierobj.Hierobj.__init__(self, parent, childlist) + self._base = self.__childlist__ #set by init function above + diff --git a/intern/python/modules/mcf/utils/hierobj.py b/intern/python/modules/mcf/utils/hierobj.py new file mode 100644 index 00000000000..7730b4d3ba4 --- /dev/null +++ b/intern/python/modules/mcf/utils/hierobj.py @@ -0,0 +1,133 @@ +''' +Generic Hierarchic Objects Module +Hierobj's store their children (which can be anything) in their +__childlist__ attribute, and provide methods for walking the +hierarchy, either collecting results or not. + +The index function returns an index of the objects (effectively a +flattened copy of the hierarchy) + +97-03-17 Added ability to pass arguments to hier_rapply and hier_rreturn. +97-10-31 Removed dependencies on mcf.store +''' +import copy,types +import singletonlist, hier_rx + +class Hierobj: + ''' + An abstract class which handles hierarchic functions and information + # remade as a DAG 97-04-02, also reduced memory overhead for + hier-r* functions by using while-del-IndexError construct versus + for loop (probably makes it slower though) + If you require a true hierarchy, use the TrueHierobj class below... + ''' + def __init__(self, parent=None, childlist=None): + if parent is None: # passed no parents + self.__dict__['__parent__'] = [] + elif type(parent) == types.ListType: # passed a list of parents + self.__dict__['__parent__'] = parent + else: # passed a single parent + self.__dict__['__parent__'] = [parent] + self.__dict__['__childlist__'] = childlist or [] + for child in self.__childlist__: + try: + child.__parent__.append(self) + except: + pass + # import simple hierarchic processing methods + hier_rapply = hier_rx.hier_rapply + hier_rreturn = hier_rx.hier_rreturn + hier_rgetattr = hier_rx.hier_rgetattr + hier_rmethod = hier_rx.hier_rmethod + + + def hier_addchild(self, child): + ''' + Add a single child to the childlist + ''' + self.__childlist__.append(child) + try: + # Hierobj-aware child + child.__parent__.append(self) # raises error if not hier_obj aware + except (TypeError, AttributeError): + # Non Hierobj-aware child + pass + append = hier_addchild + def hier_remchild(self, child): + ''' + Breaks the child relationship with child, including the + reciprocal parent relationship + ''' + try: + self.__childlist__.remove(child) + try: + child.hier_remparent(self) # if this fails, no problem + except AttributeError: pass + except (AttributeError,ValueError): + return 0 # didn't manage to remove the child + return 1 # succeeded + def hier_remparent(self, parent): + ''' + Normally only called by hier_remchild of the parent, + just removes the parent from the child's parent list, + but leaves child in parent's childlist + ''' + try: + self.__parent__.remove(parent) + except (AttributeError,ValueError): + return 0 + return 1 + def hier_replacewith(self,newel): + ''' + As far as the hierarchy is concerned, the new element + is exactly the same as the old element, it has all + the same children, all the same parents. The old + element becomes completely disconnected from the hierarchy, + but it still retains all of its references + + For every parent, replace this as a child + For every child, replace this as the parent + ''' + for parent in self.__parent__: + try: + parent.hier_replacechild(self, newel) + except AttributeError: + pass + for child in self.__childlist__: + try: + child.hier_replaceparent(self,parent) + except AttributeError: + pass + def hier_replaceparent(self, oldparent, newparent): + ind = self.__parent__.index(oldparent) + self.__parent__[ind] = newparent + def hier_replacechild(self, oldchild, newchild): + ind = self.__childlist__.index(oldchild) + self.__childlist__[ind] = newchild + +class TrueHierobj(Hierobj): + ''' + An inefficient implementation of an Hierobj which limits the + __parent__ attribute to a single element. This will likely be + _slower_ than an equivalent Hierobj. That will have to be fixed + eventually. + ''' + def __init__(self, parent=None, childlist=[]): + if parent is None: # passed no parents + self.__dict__['__parent__'] = singletonlist.SingletonList() + else: # passed a single parent + self.__dict__['__parent__'] = singletonlist.SingletonList(parent) + self.__dict__['__childlist__'] = copy.copy(childlist) + for child in self.__childlist__: + try: + child.__parent__.append(self) + except: + pass + +def index(grove): + ''' + Returns a flattened version of the grove + ''' + return grove.hier_rreturn(lambda x: x) + + diff --git a/intern/python/modules/mcf/utils/in_place_ops.py b/intern/python/modules/mcf/utils/in_place_ops.py new file mode 100644 index 00000000000..7d64f196597 --- /dev/null +++ b/intern/python/modules/mcf/utils/in_place_ops.py @@ -0,0 +1,38 @@ +class inplace: + def __add__( self, num ): + self.base = self.base + num + return self.base + def __sub__( self, num ): + self.base = self.base - num + return self.base + def __init__(self, base ): + self.base = base + def __repr__(self ): + return repr( self.base) + def __str__(self ): + return str( self.base) + __radd__ = __add__ + def __mul__(self, num ): + return self.base * num + def __div__(self, num ): + return self.base / num + def __mod__(self, num ): + return self.base % num + def __neg__(self ): + return - abs( self.base) + def __pos__(self ): + return abs( self.base) + def __abs__(self ): + return abs( self.base ) + def __inv__(self ): + return -self.base + def __lshift__(self, num ): + return self.base << num + def __rshift__(self, num ): + return self.base >> num + def __and__(self, num ): + return self.base and num + def __or__(self, num ): + return self.base or num + def value( self ): + return self.base
\ No newline at end of file diff --git a/intern/python/modules/mcf/utils/namespace.py b/intern/python/modules/mcf/utils/namespace.py new file mode 100644 index 00000000000..819531e10db --- /dev/null +++ b/intern/python/modules/mcf/utils/namespace.py @@ -0,0 +1,224 @@ +''' +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 +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/mcf/utils/quote.py b/intern/python/modules/mcf/utils/quote.py new file mode 100644 index 00000000000..5f6dccdd511 --- /dev/null +++ b/intern/python/modules/mcf/utils/quote.py @@ -0,0 +1,78 @@ +''' +Generic quoting functions (very fast), +generalised to allow use in any number of +situations, but normally you'll want to create +a new function based on these patterns which +has the default args you need. This will +prevent an extra function call. +''' +import string, regex +# create a translator which is fully worked out... + +def _quote(somestring,trans,start='"',stop='"'): + ''' + Return a quoted version of somestring. + ''' + # would be _so_ much better if we could use the + # getitem, consider... + # return '%s%s%s'%(start,string.join(map(trans.__getitem__, somestring), ''),stop) + temp = list(somestring) + for charno in xrange(len(temp)): + temp[charno]= trans[temp[charno]] + return '%s%s%s'%(start,string.join(temp, ''),stop) + +def compilerex(trans): + ''' + Compiles a suitable regex from a dictionary + translation table. Should be used at design + time in most cases to improve speed. Note: + is not a very intelligent algo. You could + do better by creating a character-class [] + for the single-character keys and then the + groups for the or-ing after it, but I've not + got the time at the moment. + ''' + keyset = trans.keys() + multitrans = [] + for x in range(len(keyset)): + if len(keyset[x]) != len(trans[keyset[x]]): + multitrans.append((keyset[x],trans[keyset[x]])) + if len(keyset[x])!= 1: + keyset[x] = '\(%s\)'%keyset[x] + if multitrans: + return 1,regex.compile(string.join(keyset,'\|')) + + +def quote2(somestring,trans,rex,start='',stop=''): + ''' + Should be a faster version of _quote once + the regex is built. Rex should be a simple + or'ing of all characters requiring substitution, + use character ranges whereever possible (should + be in most cases) + ''' + temp = list(somestring) + curpos = 0 + try: + while rex.search(somestring,curpos) != -1: + pos = rex.regs[0] + print pos + replacement = list(trans[rex.group(0)]) + temp[pos[0]:pos[1]] = replacement + curpos = pos[0]+len(replacement) + except (IndexError,regex.error): + pass + return '%s%s%s'%(start,string.join(temp, ''),stop) +# compatability +_quote2 = quote2 + +def reprq(obj, qtype): + ''' + Return representation of a string obj as a string with qtype + quotes surrounding it. Usable when linearising Python objects + to languages which have only a particular type of string. (Such + as VRML). This is not a generalised nor a particularly reliable + solution. You should use the _quote2 function instead. + ''' + return '%s%s%s'%(qtype,string.join(string.split(string.join(string.split(obj, '\\'), '\\\\'), qtype), '\\%s'%qtype),qtype) + diff --git a/intern/python/modules/mcf/utils/rangeval.py b/intern/python/modules/mcf/utils/rangeval.py new file mode 100644 index 00000000000..dd166dbebfb --- /dev/null +++ b/intern/python/modules/mcf/utils/rangeval.py @@ -0,0 +1,64 @@ +''' Classes which match ranges, sets, or anything at all. ''' +import dummy # provides storage functions as well as a few others + +class BetwVal(dummy.Dummy): + ''' + Matches any object greater than smaller and less than larger + ''' + def __init__(self, first, second): + if first <= second: + dummy.Dummy.__init__(self, [first, second]) + else: + dummy.Dummy.__init__(self, [second, first]) + def __getinitargs__(self): + return (self._base[0], self._base[1]) + def __cmp__(self, object): + '''The Guts of the Class, allows standard comparison operators''' + if self._base[0]<=object: + if self._base[1] >=object: + return 0 + else: return 1 + else: return -1 + def __repr__(self): + return '%s(%s,%s)'% (self.__class__.__name__,`self._base[0]`,`self._base[1]`) + +class WInVal(dummy.Dummy): + ''' + Matches any value in the sequential object used as initialiser + Doesn't gracefully handle situations where not found, as it just + returns a -1 + ''' + def __init__(self,seq): + self._base = seq + def __cmp__(self, object): + ''' Standard comparison operators ''' + for x in self._base: + if x == object: + return 0 + return -1 + def __repr__(self): + return '%s(%s)'% (self.__class__.__name__,`self._base`) + +class ExceptVal(WInVal): + ''' + A negative Version of WInVal + ''' + def __cmp__(self, object): + for x in self._base: + if x == object: + return -1 + return 0 + +class AnyVal: + ''' + Matches anything at all + ''' + def __init__(self): + pass + def __getinitargs__(self): + return () + def __cmp__(self, object): + return 0 + def __repr__(self): + return 'AnyVal()' + diff --git a/intern/python/modules/mcf/utils/regutils_ex.py b/intern/python/modules/mcf/utils/regutils_ex.py new file mode 100644 index 00000000000..5ef48454e78 --- /dev/null +++ b/intern/python/modules/mcf/utils/regutils_ex.py @@ -0,0 +1,158 @@ +import win32api, win32con, string, types + +def _getDataType( data, coerce = 1 ): + ''' + Return a tuple of dataType, data for a given object + automatically converts non-string-or-tuple-data into + strings by calling pickle.dumps + ''' + if type( data ) is types.StringType: + return win32con.REG_SZ, data + elif type( data ) is types.IntType: + return win32con.REG_DWORD, data + # what about attempting to convert Longs, floats, etceteras to ints??? + elif coerce: + import pickle + return win32con.REG_SZ, pickle.dumps( data ) + else: + raise TypeError, '''Unsupported datatype for registry, use getDataType( data, coerce=1) to store types other than string/int.''' + +def _getBaseKey( fullPathSpec ): + ''' + Split a "full path specification" registry key + into its root and subpath components + ''' + key = '' + subkey = fullPathSpec + # while loop will strip off preceding \\ characters + while subkey and not key: + key, subkey = string.split( fullPathSpec, '\\', 1 ) + try: + return getattr( win32con, key ), subkey + except AttributeError: + raise '''Unknown root key %s in registry path %s'''% (key, fullPathSpec) + +def RegSetValue( key, valuename='', data='', allowPickling=1 ): + ''' + Set a registry value by providing a fully-specified + registry key (and an optional sub-key/value name), + and a data element. If allowPickling is true, the + data element can be any picklable element, otherwise + data element must be a string or integer. + ''' + root, subkey = _getBaseKey( key ) + dataType, data = _getDataType( data, allowPickling ) + try: + hKey = win32api.RegOpenKeyEx( root , subkey, 0, win32con.KEY_ALL_ACCESS) # could we use a lesser access model? + except: + hKey = win32api.RegCreateKey( root, subkey ) + try: + if not valuename: # the default value + win32api.RegSetValue( hKey, valuename, dataType, data ) + else: # named sub-value + win32api.RegSetValueEx( hKey, valuename, 0, dataType, data ) + finally: + win32api.RegCloseKey( hKey) + +def RegQueryValue( key, valuename='', pickling=0 ): + ''' + Get a registry value by providing a fully-specified + registry key (and an optional sub-key/value name) + If pickling is true, the data element will be + unpickled before being returned. + ''' + #print 'key', key + root, subkey = _getBaseKey( key ) + if not valuename: # the default value + data, type = win32api.RegQueryValue( root , subkey) + else: + try: + #print root, subkey + hKey = win32api.RegOpenKeyEx( root, subkey, 0, win32con.KEY_READ) + #print hKey, valuename + try: + data, type = win32api.RegQueryValueEx( hKey, valuename ) + except: # + data, type = None, 0 # value is not available... + pickling = None + finally: + win32api.RegCloseKey( hKey) + if pickling: + import pickle + data = pickle.loads( data ) + return data + +# following constants seem to reflect where path data is stored on NT machines +# no idea if it'll work on a 95 machine + +def AddPathEntry( newEntry, user = 1, prepend=0 ): + ''' + Add or remove path entry on NT, use prepend == -1 for removal, + use prepend == 0 for append, prepend= 1 for prepending to the + current path. + ''' + if user: + user = 'USER' + else: + user = 'MACHINE' + key, valuename = COMMON_KEYS[ (user, 'PATH') ] + _PathManager( key, valuename, newEntry, prepend ) + +def PyExecutables( user = 1, prepend=0 ): + ''' + Register/Deregister Python files as executables + ''' + if user: + user = 'USER' + else: + user = 'MACHINE' + key, valuename = COMMON_KEYS[ (user, 'PYEXECUTABLES') ] + # the default executables + Python scripts... + if prepend < 0: # are to eliminate only .py + newEntry = '.PY' + else: + newEntry = '.PY;.COM;.EXE;.BAT;.CMD' + _PathManager( key, valuename, newEntry, prepend ) + +def _PathManager( key, valuename, newEntry, prepend=0, eliminate_duplicates=1 ): + ''' + Create a new Path entry on NT machines (or kill an old one) + user determines whether to alter the USER or the Machine's path + prepend + 1 -> add newEntry to start + 0 -> add newEntry to end + -1 -> don't add newEntry + eliminate_duplicates determines whether to kill equal paths + + All values are converted to lower case + ''' + # get current value... + curval = RegQueryValue( key, valuename ) or '' + # split into elements + curval = string.split( string.lower(curval), ';' ) + if type( newEntry ) not in (types.ListType, types.TupleType): + newEntry = string.split( string.lower(newEntry), ';' ) + # eliminate duplicates of the newEntry + curval = filter( None, curval) # strip out null entries + if eliminate_duplicates: + newval = [] + for p in curval: + if p not in newEntry: + newval.append( p ) + curval = newval + if prepend == 1: + curval = list(newEntry) + curval + elif prepend == 0: + curval = curval + list( newEntry ) + elif prepend == -1: # this call is just killing the path entry + pass + #now do the recombination + curval = string.join( curval, ';' ) + RegSetValue( key, valuename, curval ) + +COMMON_KEYS = { +('USER','PATH') : ('''HKEY_CURRENT_USER\\Environment''', 'path'), +('MACHINE','PATH') : ('''HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment''', 'path'), +('USER','PYEXECUTABLES') : ('''HKEY_CURRENT_USER\\Environment''', 'pathext'), +('MACHINE','PYEXECUTABLES') : ('''HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment''', 'pathext') +} diff --git a/intern/python/modules/mcf/utils/reloader.py b/intern/python/modules/mcf/utils/reloader.py new file mode 100644 index 00000000000..2d7e2591ed2 --- /dev/null +++ b/intern/python/modules/mcf/utils/reloader.py @@ -0,0 +1,33 @@ +import sys, string + +class Reloader: + ''' + Class allows for reloading all modules imported + after the instance is created. Normally you will + use this by doing: + import <anything you don't want reloaded> + from mcf.utils import reloader + <do testing and rewriting> + reloader.go() + ''' + def __init__(self): + self.keys = sys.modules.keys() + def __call__(self, *args, **namedargs): + done = [] + for key, val in sys.modules.items(): + if key not in self.keys: + try: + reload( val ) + done.append( key ) + except (ImportError): + print '''Couldn't reload module:''', key + except (TypeError): # for None's + # is a flag to prevent reloading + pass + if done: + print '''Reloaded:''', string.join( done, ', ') + else: + print '''No modules reloaded''' + +# the default reloader... +go = Reloader()
\ No newline at end of file diff --git a/intern/python/modules/mcf/utils/singletonlist.py b/intern/python/modules/mcf/utils/singletonlist.py new file mode 100644 index 00000000000..5ca2f5000c2 --- /dev/null +++ b/intern/python/modules/mcf/utils/singletonlist.py @@ -0,0 +1,104 @@ +class SingletonList: + ''' + A SingletonList always has a length of one or 0, + appends overwrite the single element, iteration will + return precisely one element. Attempts to get any item + other than 0 will raise an IndexError or return the single + item depending on whether the 'raiseIndexError' flag is + true or false (generally it should be true except if the + for x in SingletonList: construct is known never to be + used, since this construct will create an infinite loop + if we never raise an IndexError). + ''' + def __init__(self,base=None,raiseIndexError=1): + self._base = base + self.raiseIndexError = raiseIndexError + def __len__(self): + ''' + The length is 0 if no _base, 1 if a base + ''' + if hasattr(self, '_base'): + return 1 + else: + return 0 + def __getitem__(self,ind): + ''' + Get the item if ind == 0, else raise an IndexError or return + the item, depending on the raiseIndexError flag + ''' + if ind == 0: + try: + return self._base + except AttributeError: + raise IndexError, ind + elif self.raiseIndexError: + raise IndexError, ind + else: + return self._base + def __setitem__(self,ind, item): + ''' + The item is to become the base + ''' + self._base = item + def __delitem__(self,ind): + ''' + Delete the base, regardless of the index used + ''' + try: + del(self._base) + except AttributeError: + raise IndexError, ind + def append(self,item): + ''' + Replace the base with the item + ''' + self._base = item + def index(self,item): + ''' + if the item is the base, return the only valid index (0) + ''' + try: + if item == self._base: + return 0 + except: + pass + raise ValueError, item + def count(self, item): + ''' + If the item is the base, we have one, else 0 + ''' + try: + if item == self._base: + return 1 + except: + pass + return 0 + insert = __setitem__ + def remove(self, item): + ''' + if the item is the base, delete the base, else ValueError + ''' + try: + if item == self._base: + del(self._base) + return + except: + pass + raise ValueError, item + def reverse(self): + pass + def sort(self): + pass + def __repr__(self): + try: + return '[%s]'%`self._base` + except AttributeError: + return '[]' + # store and copy functions +# def __getinitargs__(self): +# return (self._base,self.raiseIndexError) +# def __getstate__(self,*args,**namedargs): +# pass +# def __setstate__(self,*args,**namedargs): +# pass + diff --git a/intern/python/modules/mcf/utils/tempclassmodule.py b/intern/python/modules/mcf/utils/tempclassmodule.py new file mode 100644 index 00000000000..9fe6eed3918 --- /dev/null +++ b/intern/python/modules/mcf/utils/tempclassmodule.py @@ -0,0 +1,251 @@ +''' +Generate module for holding temporary classes which +will be reconstructed into the same module to allow +cPickle and the like to properly import them. + +Note: You _must_ pickle a reference to the tempclassmodule +_before_ you pickle any instances which use the classes stored +in the module! Also, the classes cannot reference anything +in their dictionary or bases tuples which are not normally +pickleable (in particular, you can't subclass a class in the +same tempclassmodule or a tempclassmodule which you cannot +guarantee will be loaded before the dependent classes. (i.e. +by guaranteeing they will be pickled first) +''' +import new, time, string, sys, types + +def buildModule(packagename, basename, rebuild=None, initialcontents=None): + ''' + Dynamically build a module or rebuild one, generates + a persistent ID/name if not rebuilding. The persistent + ID is the value of basename+`time.time()` with the decimal + point removed (i.e. a long string of digits). Packagename + must be an importable package! Will raise an ImportError + otherwise. Also, for easy reconstitution, basename must not + include any decimal points. + + initialcontents is a dictionary (or list) of elements which will be + added to the new module. + ''' + if rebuild == None: + timestamp = `time.time()` + decpos = string.find(timestamp,'.') + basename = basename+timestamp[:decpos]+timestamp[decpos+1:] + name = string.join((packagename, basename), '.') + a = {} + b = {} + try: # see if we've already loaded this module... + mod = __import__( name, {},{}, string.split( name, '.')) + if initialcontents: + _updateFrom(mod, initialcontents) + return mod.__name__, mod + except ImportError: + pass + mod = new.module(name) + sys.modules[name] = mod + # following is just to make sure the package is loaded before attempting to alter it... + __import__( packagename, {}, {}, string.split(packagename) ) +## exec 'import %s'%(packagename) in a, b ### Security Risk! + setattr(sys.modules[ packagename ], basename, mod) + # now do the update if there were initial contents... + if initialcontents: + _updateFrom(mod, initialcontents) + return name, mod + +def buildClassIn(module, *classargs, **namedclassargs): + ''' + Build a new class and register it in the module + as if it were really defined there. + ''' + print module, classargs, namedclassargs + namedclassargs["__temporary_class__"] = 1 + newclass = new.classobj(classargs[0], classargs[1], namedclassargs) + newclass.__module__ = module.__name__ + setattr(module, newclass.__name__, newclass) + return newclass + +def addClass(module, classobj): + ''' + Insert a classobj into the tempclassmodule, setting the + class' __module__ attribute to point to this tempclassmodule + ''' + classobj.__module__ = module.__name__ + setattr(module, classobj.__name__, classobj) + setattr( classobj, "__temporary_class__", 1) + +def delClass(module, classobj): + ''' + Remove this class from the module, Note: after running this + the classobj is no longer able to be pickled/unpickled unless + it is subsequently added to another module. This is because + it's __module__ attribute is now pointing to a module which + is no longer going to save its definition! + ''' + try: + delattr(module, classobj.__name__) + except AttributeError: + pass + +def _packageName(modulename): + decpos = string.rfind(modulename, '.') + return modulename[:decpos], modulename[decpos+1:] + +def _updateFrom(module, contentsource): + ''' + For dealing with unknown datatypes (those passed in by the user), + we want to check and make sure we're building the classes correctly. + ''' + # often will pass in a protoNamespace from which to update (during cloning) + if type(contentsource) in ( types.DictType, types.InstanceType): + contentsource = contentsource.values() + # contentsource should now be a list of classes or class-building tuples + for val in contentsource: + if type(val) is types.ClassType: + try: + addClass(module, val) + except: + pass + elif type(val) is types.TupleType: + try: + apply(buildClassIn, (module,)+val) + except: + pass + +def deconstruct(templatemodule): + ''' + Return a tuple which can be passed to reconstruct + in order to get a rebuilt version of the module + after pickling. i.e. apply(reconstruct, deconstruct(tempmodule)) + is the equivalent of doing a deepcopy on the tempmodule. + ''' +## import pdb +## pdb.set_trace() + classbuilder = [] + for name, classobj in templatemodule.__dict__.items(): + if type(classobj) is types.ClassType: # only copy class objects, could do others, but these are special-purpose modules, not general-purpose ones. + classbuilder.append( deconstruct_class( classobj) ) +## import pdb +## pdb.set_trace() + return (templatemodule.__name__, classbuilder) +## except AttributeError: +## print templatemodule +## print classbuilder + +def deconstruct_class( classobj ): + ''' + Pull apart a class into a tuple of values which can be used + to reconstruct it through a call to buildClassIn + ''' + if not hasattr( classobj, "__temporary_class__"): + # this is a regular class, re-import on load... + return (classobj.__module__, classobj.__name__) + else: + # this is a temporary class which can be deconstructed + bases = [] + for classobject in classobj.__bases__: + bases.append( deconstruct_class (classobject) ) + return (classobj.__name__, tuple (bases), classobj.__dict__) + + +def reconstruct(modulename, classbuilder): + ''' + Rebuild a temporary module and all of its classes + from the structure created by deconstruct. + i.e. apply(reconstruct, deconstruct(tempmodule)) + is the equivalent of doing a deepcopy on the tempmodule. + ''' +## import pdb +## pdb.set_trace() + mname, newmod = apply(buildModule, _packageName(modulename)+(1,) ) # 1 signals reconstruct + reconstruct_classes( newmod, classbuilder ) + return newmod + +def reconstruct_classes( module, constructors ): + ''' + Put a class back together from the tuple of values + created by deconstruct_class. + ''' + classes = [] + import pprint + pprint.pprint( constructors) + for constructor in constructors: + if len (constructor) == 2: + module, name = constructor + # this is a standard class, re-import + temporarymodule = __import__( + module, + {},{}, + string.split(module)+[name] + ) + classobject =getattr (temporarymodule, name) + else: + # this is a class which needs to be re-constructed + (name, bases,namedarguments) = constructor + bases = tuple( reconstruct_classes( module, bases )) + classobject = apply ( + buildClassIn, + (module, name, bases), # name and bases are the args to the class constructor along with the dict contents in namedarguments + namedarguments, + ) + classes.append (classobject) + return classes + + +def destroy(tempmodule): + ''' + Destroy the module to allow the system to do garbage collection + on it. I'm not sure that the system really does do gc on modules, + but one would hope :) + ''' + name = tempmodule.__name__ + tempmodule.__dict__.clear() # clears references to the classes + try: + del(sys.modules[name]) + except KeyError: + pass + packagename, modname = _packageName(name) + try: + delattr(sys.modules[ packagename ], modname) + except AttributeError: + pass + del( tempmodule ) # no, I don't see any reason to do it... + return None + + +def deepcopy(templatemodule, packagename=None, basename=None): + ''' + Rebuild the whole Module and it's included classes + (just the classes). Note: This will _not_ make instances + based on the old classes point to the new classes! + The value of this function is likely to be minimal given + this restriction. For pickling use deconstruct/reconstruct + for simple copying just return the module. + ''' + name, classbuilder = deconstruct( templatemodule ) + if packagename is None: + tp, tb = _packageName( name ) + if packagename is None: + packagename = tp + if basename is None: + basename = tb + newmod = buildModule(packagename, basename, initialcontents=classbuilder ) + return newmod + +if __name__ == "__main__": + def testPickle (): + import mcf.vrml.prototype + name, module = buildModule( 'mcf.vrml.temp', 'scenegraph' ) + buildClassIn( module, 'this', () ) + buildClassIn( module, 'that', (mcf.vrml.prototype.ProtoTypeNode,) ) +## import pdb +## pdb.set_trace() + import pprint + pprint.pprint( deconstruct( module )) + name,builder = deconstruct( module ) + destroy( module) + return reconstruct(name, builder) + t = testPickle() + print t + + +
\ No newline at end of file diff --git a/intern/python/modules/mcf/utils/typeclasses.py b/intern/python/modules/mcf/utils/typeclasses.py new file mode 100644 index 00000000000..ed798dfe3da --- /dev/null +++ b/intern/python/modules/mcf/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 diff --git a/intern/python/modules/mcf/utils/userquery.py b/intern/python/modules/mcf/utils/userquery.py new file mode 100644 index 00000000000..e1dc1bfeda5 --- /dev/null +++ b/intern/python/modules/mcf/utils/userquery.py @@ -0,0 +1,17 @@ +import string + +def userquery( prompt, choices, contextdata = '', defaultind=0 ): + if contextdata: + print 'Contextual Information:', contextdata + for x in range( len( choices ) ): + print '(%s)'%x, `choices[x]` + choice = raw_input( prompt+( '(%s):'%defaultind ) ) + if not choice: + return choices[ defaultind ] + try: + choice = string.atoi( choice ) + return choices[ choice] + except IndexError : + return choices[ defaultind ] + except ValueError: + return choice diff --git a/intern/python/modules/mcf/utils/ver.py b/intern/python/modules/mcf/utils/ver.py new file mode 100644 index 00000000000..1d36fcd122a --- /dev/null +++ b/intern/python/modules/mcf/utils/ver.py @@ -0,0 +1,17 @@ +''' +Module giving a float representation +of the interpreter major version (1.4, 1.5 etceteras) + +ver -- Float representation of the current interpreter version + +Note: Since I no longer have any Python 1.4 modules, this module is +no longer in use by me. I intend to leave it here for the next version +jump :) . +''' +import regex, sys, string +ver = string.atof(sys.version[:regex.match('[0-9.]*', sys.version)]) + +### Clean up namespace +del(regex) +del(sys) +del(string) diff --git a/intern/python/modules/mcf/utils/walkerable.py b/intern/python/modules/mcf/utils/walkerable.py new file mode 100644 index 00000000000..e4c18302097 --- /dev/null +++ b/intern/python/modules/mcf/utils/walkerable.py @@ -0,0 +1,46 @@ +''' +Really simplistic walker-processable hierobjects, doesn't +have parent attributes, every element has an __attrDict__ +item and a childlist. This is different from the mechanisms +we'll want to use for multi-tree systems, but it's fairly +close. Should be fairly simply worked with. +''' +class WalkerAble: + ''' + Simple hierarchic objects with the following elements + + __attrDict__ -- app-specific attributes + __childlist__ -- childen of this node + __gi__ -- "type" or Generic Indicator of this node + __childlist__append__ -- as you'd expect, method on childlist to add an element + ''' + def __init__(self, childlist=None, attrDict=None, gi=None): + self.__dict__['__attrDict__'] = attrDict or {} + self.__dict__['__childlist__'] = childlist or [] + self.__dict__['__gi__'] = gi or '' + self.__dict__['__childlist__append__'] = self.__childlist__.append + + def __getattr__(self, attrName): + ''' + Note: you can store attributes with the same names as + the reserved names, but to get them back, you'll need + to read it directly out of the attrDict + ''' + if attrName != '__attrDict__': + try: + return self.__attrDict__[attrName] + except KeyError: + pass + raise AttributeError, attrName + + def __setattr__(self, attrName, attrVal): + self.__attrDict__[attrName] = attrVal + def __setGI__(self, gi): + self.__dict__['__gi__'] = gi + def __repr__(self): + return '''<WalkerAble %(__gi__)s %(__attrDict__)s %(__childlist__)s>'''%self.__dict__ + + # copy functions +# def __getinitargs__(self): +# return (self.__childlist__, self.__attrDict__, self.__gi__) + |