2002-10-12 11:37:38 +00:00
'''
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.5 final
98.03 .15 - - Fixed bug in items , values , etceteras with module - type
base objects .
'''
2003-01-06 17:27:43 +00:00
#import copy, types, string
import types , string
2002-10-12 11:37:38 +00:00
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, \n and math modules, and two simple variables " a " and " b " (== 23 and42 respectively) '
return a