Files
blender/intern/python/modules/vrml/utils/namespace.py
Michel Selten c95d631b83 Removed references to modules that do not exist. The python interface is
broken, but it should give 0 errors when building.

Michel
2003-01-06 17:27:43 +00:00

227 lines
7.8 KiB
Python

'''
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 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