225 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			225 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 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 | ||
|  | 
 | ||
|  | 
 |