Re: signal module patches [and metalbase] [really python db]

Ken Manheimer (ken.manheimer@NIST.GOV)
Thu, 28 Jul 1994 18:50:35 -0400 (EDT)

# An enhanced class wrapper for dict type object, which implements
# slice-style dict operations, including addition, deletion, and
# assignement of aggregate components. See bottom for some examples.

import sys

class Dict:
data = None
faux = None # For coercion fake-out vals.

def __init__(self, initVal={}):
# initVal for obvious purposes.
# fauxVal is for non-dict values that are being minted into dicts only
# because, eg, coerce requires it even though the operatoin coerce is
# serving may not.
if type(initVal) == type({}):
self.data = initVal
return # ===>
try:
initVal.index # do we have a list-like obj?
self.faux = initVal # generate a faux object, for
# coercion's sake.
except TypeError:
raise TypeError, ('Invalid initial %s val %s' %
(self.__class__.__name__, `initVal`[:10]))

def keys(self): return self.data.keys()
def has_key(self, key): return self.data.has_key(key)
def items(self): return self.data.items()
def values(self): return self.data.values()
def dict(self): return self.data

def __len__(self): return len(self.data)
def __getitem__(self, keyOrWhat):
# Like regular getitem, except composite keys produce subsets that
# match the components.
#
# keyOrWhat == self: return self.
# keyOrWhat == list: return self's items that have keys on keyOrWhat
# keyOrWhat == mapping: return interesection with keyOrWhat's dict

try:
# First try a regular dict reference, returning that if successful:
return self.data[keyOrWhat] # ===>
except TypeError: pass
except KeyError:
# Raise IndexError, which is caught by 'in' and 'not in' to
# recognize unestablished keys:
raise IndexError, sys.exc_value

if id(keyOrWhat) == id(self): # Degenerate entire self ref:
return self # ===>

# Try seqence-style reference:
result = {}
try:
for key in keyOrWhat:
try:
result[key] = self.data[key]
except KeyError: pass # Skip missing keys:
except TypeError:
raise relay, (TypeError, ('Invalid %s subkey %s' %
(self.__class__.__name__,
`key`[:20])))

return self.__class__(result) # ===>
except relay:
raise sys.exc_value[0], sys.exc_value[1]
except TypeError: # Must be dict-style ref
try:
for key, val in keyOrWhat.items():
try:
if self.data[key] == val:
result[key] = val
except KeyError: pass
except TypeError:
# Relay around enclosing TypeError handler:
raise relay, (TypeError, ('Invalid %s subkey %s' %
(self.__class__.__name__,
`key`[:20])))
return self.__class__(result) # ===>
except relay:
raise sys.exc_value[0], sys.exc_value[1]
except TypeError:
raise TypeError, ('Invalid %s key %s' % # ===>
(self.__class__.__name__, `keyOrWhat`[:20]))

def __delitem__(self, keyOrWhat):
# Like regular delitem, except aggregate key affects multiple elements:
#
# key == self: clear self.
# key == seq: delete items having keys in sequence.
# key is dictful: delete items in intersection.

try:
del self.data[keyOrWhat]
return # ===>
except TypeError: pass # Deal below with exotic keys.

if id(keyOrWhat) == id(self): # Delete everything in self:
for key in self.data.keys():
del self[key]
return # ===>

try: # Do as seq, skipping invalid keys:
for key in keyOrWhat:
try:
del self.data[key]
except (KeyError, TypeError): pass
except TypeError: # Do as mapping, skipping invalid keys:
for key, val in keyOrWhat.items():
try:
if self.data[key] == val:
del self.data[key]
except (KeyError, TypeError): pass

def __setitem__(self, keyOrWhat, valOrWhat):
# Regular setitem plus provisions for aggregate key references.
#
# Aggregate key's items are deleted from self and then aggregate
# vals are added. (Vals are assigned to individual item with normal
# keys.)
#
# key == self: del all items then merge items in val
# key == list: del items having keys on list and then merge val items
# key is dictful: del items in intersection and then merge val items
#
# Aggregate of val is copied before deletions are applied, so additions
# will not be changed by deletions (eg, if self is part of additions
# ref).

# Try the standard route:
try:
self.data[keyOrWhat] = valOrWhat
return # ===>
except TypeError: pass # Handle exotic keys below.

# Standard dict assignment no go. Try aggregate-style refs (whoopee).
# Parameters are sanity checked before any changes are applied.

# Get the deletions (or barf if invalid):
if id(keyOrWhat) == id(self):
deletions = self
else:
try: # ... as sequence:
deletions = keyOrWhat[:]
except TypeError:
deletions = {}
for key, val in keyOrWhat.items():
deletions[key] = val

# Get the additions (must be a dict-type obj):
additions = {}
for key, val in valOrWhat.items():
additions[key] = val

# At this point, deletions and additions are the aggregates we need.

del self[deletions] # Do deletions using __delitem__.
for key, val in additions.items():
self.data[key] = val # Do the additions.

def __repr__(self): return repr(self.dict())
def __cmp__(self, obj):
# Dict objects are compared with other objects exactly
# according to their dictionaries
if id(obj) == id(self):
return 0 # ===>
elif (type(self) == type(obj)) and (self.__class__ == obj.__class__):
return(cmp(self.data, obj.data))
elif type(self.data) == type(obj):
return(cmp(self.data, obj))
elif type(obj) == type({}):
return(cmp(self.dict(), obj))
else:
try: # try to compare as dicts:
objDict = obj.dict()
return(cmp(self.dict(), objDict))
except: # let the data try to do the compare:
return(cmp(self.data), obj)

def __add__(self, obj):
# Return a dictionary containing self's dictionary merged with obj's
# dictionary.
# Establish that obj has keys before duplicating self:
try:
objitems = obj.items()
except AttributeError:
objitems = obj # Ah, try assuming obj is dictful

result = {}
for key, val in self.items() + objitems:
result[key] = val
return self.__class__(result) # ===>

def __coerce__(self, other):
# It's unfortunate that coercion is mandatory for all numeric ops
# except '+' and '*', at least as of 1.0.2. This imposes what is often
# unnecessary conversions, but oh well.
return (self, self.__class__(other))

def __sub__(self, obj):
# Return a Dict containing self's dictionary sans the intersection with
# obj (which may have been dict-type or seq type; coercion will have
# forced it to be a Dict, but perhaps a faux one.
result = {}
if obj.data:
for key, val in self.items():
if (key not in obj) or (obj[key] != val):
result[key] = val
return result
elif obj.faux:
for key in self.data.keys():
if key not in obj.faux:
result[key] = self[key]
return result
else:
raise TypeError, 'invalid subtrand %s' % `obj`[:20]

# Uses:
#
# d[{}] = {1: 2} merges {1: 2} into dict, in place (no new dict minted)
# d + {1: 2} new dict that is the conjunction of d and {1: 2}
# (keys in latter obj take precedence)
# y = d + {} dup of d
# d[range(10)] returns a Dict containing all items with keys between 1 and 10
# d[{1: 2}] = {} makes d the intersection with {1: 2}, in place
# d[d] = {} clears Dict, and more generally:
# d[d] = nother replaces d's current dict with nother's