This is a multi-part message in MIME format. ------=_NextPart_000_0149_01C267C9.4E13C0C0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit [me] > Maybe others will be less so, I have seen a module referred on c.l.p using the > "feature" escaped from the lab to make builtins observable <wink>. > the thread on c.l.p is google: watching mutables? group:comp.lang.python the package is at http://oomadness.tuxfamily.org/en/editobj/ author: Jean-Baptiste LAMY -- jiba@tuxfamily I have directly attached the "incriminated" module for the curious. Should I give them the bad news? regards. ------=_NextPart_000_0149_01C267C9.4E13C0C0 Content-Type: text/plain; name="eventobj.py" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="eventobj.py" # EventObj=0A= # Copyright (C) 2001-2002 Jean-Baptiste LAMY -- jiba@tuxfamily=0A= #=0A= # This program is free software; you can redistribute it and/or modify=0A= # it under the terms of the GNU General Public License as published by=0A= # the Free Software Foundation; either version 2 of the License, or=0A= # (at your option) any later version.=0A= #=0A= # This program is distributed in the hope that it will be useful,=0A= # but WITHOUT ANY WARRANTY; without even the implied warranty of=0A= # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the=0A= # GNU General Public License for more details.=0A= #=0A= # You should have received a copy of the GNU General Public License=0A= # along with this program; if not, write to the Free Software=0A= # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 = USA=0A= =0A= # This module is certainly my best hack ;-)=0A= # It is nothing else than a sequence of hacks, from the beginning to the = end !!!!!=0A= =0A= """EventObj -- Allow to add attribute-change, content-change or = hierarchy-change event to any Python instance.=0A= =0A= Provides the following functions (view their doc for more info):=0A= dumper_event(obj, attr, oldvalue, newvalue)=0A= addevent(obj, event)=0A= hasevent(obj[, event])=0A= removeevent(obj, event)=0A= addevent_rec(obj, event)=0A= removeevent_rec(obj, event)=0A= And the following constant for addition/removal events:=0A= ADDITION=0A= REMOVAL=0A= =0A= Events : An event is any callable object that take 4 parameters (=3D the = listened instance, the changed attribute name, the new value, the old = value).=0A= After registration with addevent, it will be called just after any = attribute is change on the instance.=0A= You can add many events on the same instance, and use the same event = many times.=0A= An instance can implement __addevent__(event, copiable =3D 0), = __hasevent__(event =3D None) and __removeevent__(event) to allow a = custom event system.=0A= =0A= Notice that the event is weakref'ed, so if it is no longer accessible, = the event is silently removed.=0A= =0A= Caution : As event management is performed by changing the class of the = instance, you use eventobj with critical objects at your own risk... !=0A= =0A= Quick example :=0A= >>> from editobj.eventobj import *=0A= >>> class C: pass=0A= >>> c =3D C()=0A= >>> def event(obj, attr, value, oldvalue):=0A= ... print "c." + attr, "was", `oldvalue` + ", is now", `value`=0A= >>> addevent(c, event)=0A= >>> c.x =3D 1=0A= c.x was None, is now 1=0A= =0A= Addition/Removal events : if you add them to a list / UserList or a dict = / UserDict, or a subclass, events are also called for addition or = removal in the list/dict.=0A= Addition or removal can be performed by any of the methods of = UserList/UserDict (e.g. append, extend, remove, __setitem__,...)=0A= In this case, name will be the ADDITION or REMOVAL constant, value the = added object for a list and the key-value pair for a dict, and oldvalue = None.=0A= =0A= Quick example :=0A= >>> from editobj.eventobj import *=0A= >>> c =3D []=0A= >>> def event(obj, attr, value, oldvalue):=0A= ... if attr is ADDITION: # Only for list / dict=0A= ... print `value`, "added to c"=0A= ... elif attr is REMOVAL: # Only for list / dict=0A= ... print `value`, "removed from c"=0A= ... else:=0A= ... print "c." + attr, "was", `oldvalue` + ", is now", `value`=0A= >>> addevent(c, event)=0A= >>> c.append(0)=0A= 0 added to c=0A= =0A= Hierachy events : such events are used with UserList or UserDict, and = are usefull to listen an entire hierarchy (e.g. a list that contains = other lists that can contain other lists...).=0A= The event apply to the registred instance, and any other item it = contains, and so on if the list/dict is a deeper hierarchy.=0A= If you want to automatically add or remove the event when the hierarchy = change (addition or removal at any level), use a HierarchyEvent (see = example below).=0A= =0A= Quick example :=0A= >>> from editobj.eventobj import *=0A= >>> c =3D []=0A= >>> def event(obj, attr, value, oldvalue):=0A= ... if attr is ADDITION:=0A= ... print `value`, "added to", `obj`=0A= ... elif attr is REMOVAL:=0A= ... print `value`, "removed from", `obj`=0A= >>> addevent_rec(c, event)=0A= >>> c.append([]) # This sub-list was not in c when we add = the event...=0A= [] added to [[]] # [[]] is c=0A= >>> c[0].append(12) # ...but the hierarchical event has been = automatically added !=0A= 12 added to [12] # [12] is c[0]=0A= """=0A= =0A= __all__ =3D [=0A= "addevent",=0A= "hasevent",=0A= "removeevent",=0A= "addevent_rec",=0A= "removeevent_rec",=0A= "dumper_event",=0A= "ADDITION",=0A= "REMOVAL",=0A= ]=0A= =0A= import new, weakref, types #, copy=0A= from UserList import UserList=0A= from UserDict import UserDict=0A= =0A= =0A= def to_list(obj):=0A= if isinstance(obj, list) or isinstance(obj, UserList): return obj=0A= if hasattr(obj, "children"):=0A= items =3D obj.children=0A= if callable(items): return items()=0A= return items=0A= if hasattr(obj, "items"):=0A= items =3D obj.items=0A= if callable(items): return items()=0A= return items=0A= if hasattr(obj, "__getitem__"): return obj=0A= return None=0A= =0A= def to_dict(obj):=0A= if isinstance(obj, dict) or isinstance(obj, UserDict): return obj=0A= return None=0A= =0A= def to_dict_or_list(obj):=0A= content =3D to_dict(obj) # Try dict...=0A= if content is None:=0A= content =3D to_list(obj) # Try list...=0A= if content is None: return None, None=0A= return list, content=0A= else:=0A= return dict, content=0A= =0A= def to_content(obj):=0A= content =3D to_dict(obj) # Try dict...=0A= if content is None: return to_list(obj) or () # Try list...=0A= return content.values()=0A= =0A= =0A= def dumper_event(obj, attr, value, old):=0A= """dumper_event -- an event that dump a stacktrace when called. = Usefull for debugging !=0A= =0A= dumper_event is the default value for add_event.=0A= """=0A= import traceback=0A= traceback.print_stack()=0A= =0A= if attr is ADDITION: print "%s now contains %s." % (obj, value)=0A= elif attr is REMOVAL : print "%s no longer contains %s." % (obj, value)=0A= else: print "%s.%s was %s, is now %s." % (obj, attr, = old, value)=0A= =0A= =0A= def addevent(obj, event =3D dumper_event):=0A= """addevent(obj, event =3D dumper_event)=0A= Add the given attribute-change event to OBJ. OBJ must be a class = instance (old or new style) or a list / dict, and EVENT a function that = takes 4 args: (obj, attr, value, old).=0A= EVENT will be called when any attribute of obj will be changed; OBJ is = the object, ATTR the name of the attribute, VALUE the new value of the = attribute and OLD the old one.=0A= =0A= EVENT defaults to the "dumper" event, which print each object = modification.=0A= =0A= Raise eventobj.NonEventableError if OBJ cannot support event.=0A= """=0A= =0A= event =3D _wrap_event(obj, event)=0A= =0A= try:=0A= obj.__addevent__(event)=0A= except:=0A= if hasattr(obj, "__dict__"):=0A= # Store the event and the old class of obj in an instance of = _EventObj_stuff (a class we use to contain that).=0A= # Do that BEFORE we link the event (else we'll get a change event = for the "_EventObj_stuff" attrib) !=0A= obj._EventObj_stuff =3D _EventObj_stuff(obj.__class__)=0A= =0A= # Change the class of the object.=0A= obj.__class__ =3D _create_class(obj, obj.__class__)=0A= else:=0A= # Change the class of the object.=0A= old_class =3D obj.__class__=0A= obj.__class__ =3D _create_class(obj, obj.__class__)=0A= =0A= stuff_for_non_dyn_objs[id(obj)] =3D _EventObj_stuff(old_class)=0A= =0A= obj.__addevent__(event)=0A= =0A= def hasevent(obj, event =3D None):=0A= """hasevent(obj[, event]) -> Boolean=0A= Return wether the obj instance has the given event (or has any event, if = event is None)."""=0A= return hasattr(obj, "__hasevent__") and obj.__hasevent__(event)=0A= =0A= def removeevent(obj, event =3D dumper_event):=0A= """removeevent(obj, event =3D dumper_event)=0A= Remove the given event from obj."""=0A= hasattr(obj, "__removeevent__") and obj.__removeevent__(event)=0A= =0A= ADDITION =3D "__added__"=0A= REMOVAL =3D "__removed__"=0A= =0A= NonEventableError =3D "NonEventableError"=0A= =0A= # Private stuff :=0A= =0A= # A dict to store the created classes.=0A= #_classes =3D weakref.WeakKeyDictionary() # old-style class cannot be = weakref'ed !=0A= _classes =3D {}=0A= =0A= # Create a with-event class for "clazz". the returned class is a "mixin" = that will extends clazz and _EventObj (see below).=0A= def _create_class(obj, clazz):=0A= try: return _classes[clazz]=0A= except:=0A= if hasattr(obj, "__dict__"):=0A= # The name of the new class is the same name of the original class = (mimetism !)=0A= if issubclass(clazz, list) or issubclass(clazz, UserList): cl = =3D new.classobj(clazz.__name__, (_EventObj_List, _EventObj, clazz), {})=0A= elif issubclass(clazz, dict) or issubclass(clazz, UserDict): cl = =3D new.classobj(clazz.__name__, (_EventObj_Dict, _EventObj, clazz), {})=0A= else:=0A= if issubclass(clazz, object): cl = =3D new.classobj(clazz.__name__, (_EventObj, clazz), {})=0A= else: cl = =3D new.classobj(clazz.__name__, (_EventObj_OldStyle, clazz), {})=0A= =0A= else:=0A= # list and dict were added in _classes at module initialization=0A= # Other types are not supported yet !=0A= raise NonEventableError, obj=0A= =0A= # Change also the module name.=0A= cl.__module__ =3D clazz.__module__=0A= _classes[clazz] =3D cl=0A= return cl=0A= =0A= =0A= # A container for _EventObj attribs.=0A= class _EventObj_stuff:=0A= def __init__(self, clazz):=0A= self.clazz =3D clazz=0A= self.events =3D []=0A= =0A= def __call__(self, obj, attr, value, oldvalue):=0A= # Clone the list, since executing an event function may add or = remove some events.=0A= for event in self.events[:]: event(obj, attr, value, oldvalue)=0A= =0A= def remove_event(self, event): self.events.remove(event)=0A= =0A= def has_event(self, event): return event in self.events=0A= =0A= def _wrap_event(obj, event, hi =3D 0):=0A= if not isinstance(event, WrappedEvent):=0A= #dump =3D repr(event)=0A= =0A= try: obj =3D weakref.proxy(obj) # Avoid cyclic ref=0A= except TypeError: pass=0A= =0A= def callback(o):=0A= #print "attention !", dump, "est mourant !"=0A= # This seems buggy since it is called when some objects are being = destructed=0A= try:=0A= ob =3D obj=0A= if ob:=0A= if removeevent and hasevent(ob, event): removeevent(ob, event)=0A= except: pass=0A= if hi:=0A= if type(event) is types.MethodType: event =3D WeakHiMethod(event, = callback)=0A= else: event =3D WeakHiFunc (event, = callback)=0A= else:=0A= if type(event) is types.MethodType: event =3D WeakMethod(event, = callback)=0A= else: event =3D WeakFunc (event, = callback)=0A= =0A= return event=0A= =0A= =0A= class WrappedEvent: pass=0A= =0A= class WeakFunc(WrappedEvent):=0A= def __init__(self, func, callback =3D None):=0A= if callback: self.func =3D weakref.ref(func, callback)=0A= else: self.func =3D weakref.ref(func)=0A= =0A= def original(self): return self.func()=0A= =0A= def __call__(self, *args): self.func()(*args)=0A= =0A= def __eq__(self, other):=0A= return (self.func() =3D=3D other) or (isinstance(other, WeakFunc) = and (self.func() =3D=3D other.func()))=0A= =0A= def __repr__(self): return "<WeakFunc for %s>" % self.func()=0A= =0A= class WeakMethod(WrappedEvent):=0A= def __init__(self, method, callback =3D None):=0A= if callback: self.obj =3D weakref.ref(method.im_self, callback)=0A= else: self.obj =3D weakref.ref(method.im_self)=0A= self.func =3D method.im_func=0A= =0A= def original(self):=0A= obj =3D self.obj()=0A= return new.instancemethod(self.func, obj, obj.__class__)=0A= =0A= def __call__(self, *args): self.func(self.obj(), *args)=0A= =0A= def __eq__(self, other):=0A= return ((type(other) is types.MethodType) and (self.obj() is = other.im_self) and (self.func is other.im_func)) or (isinstance(other, = WeakMethod) and (self.obj() is other.obj()) and (self.func is = other.func))=0A= =0A= def __repr__(self): return "<WeakMethod for %s>" % self.original()=0A= =0A= class HierarchyEvent:=0A= def __call__(self, obj, attr, value, oldvalue):=0A= if attr is ADDITION:=0A= try: =0A= if isinstance(obj, _EventObj_List): addevent_rec(value, = self.original())=0A= else: addevent_rec(value[1], = self.original())=0A= except NonEventableError: pass=0A= elif attr is REMOVAL:=0A= try: =0A= if isinstance(obj, _EventObj_List): removeevent_rec(value, = self.original())=0A= else: removeevent_rec(value[1], = self.original())=0A= except NonEventableError: pass=0A= =0A= class WeakHiFunc(HierarchyEvent, WeakFunc):=0A= def __call__(self, obj, attr, value, oldvalue):=0A= HierarchyEvent.__call__(self, obj, attr, value, oldvalue)=0A= WeakFunc.__call__(self, obj, attr, value, oldvalue)=0A= =0A= class WeakHiMethod(HierarchyEvent, WeakMethod):=0A= def __call__(self, obj, attr, value, oldvalue):=0A= HierarchyEvent.__call__(self, obj, attr, value, oldvalue)=0A= WeakMethod.__call__(self, obj, attr, value, oldvalue)=0A= =0A= =0A= # Mixin class used as base class for any with-event class.=0A= class _EventObj:=0A= stocks =3D []=0A= def __setattr__(self, attr, value):=0A= # Get the old value of the changing attrib.=0A= oldvalue =3D getattr(self, attr, None)=0A= if attr =3D=3D "__class__":=0A= newclass =3D _create_class(self, value)=0A= self._EventObj_stuff.clazz.__setattr__(self, "__class__", newclass)=0A= self._EventObj_stuff.clazz =3D value=0A= else:=0A= # If a __setattr__ is defined for obj's old class, call it. Else, = just set the attrib in obj's __dict__=0A= if hasattr(self._EventObj_stuff.clazz, "__setattr__"): = self._EventObj_stuff.clazz.__setattr__(self, attr, value)=0A= else: = self.__dict__[attr] =3D value=0A= =0A= # Comparison may fail=0A= try:=0A= if value =3D=3D oldvalue: return=0A= except: pass=0A= =0A= # Call registered events, if needed=0A= for event in self._EventObj_stuff.events:=0A= event(self, attr, value, oldvalue)=0A= =0A= def __addevent__(self, event):=0A= self._EventObj_stuff.events.append(event)=0A= l =3D to_list(self)=0A= if (not l is None) and (not l is self): addevent(l, event)=0A= def __hasevent__(self, event =3D None):=0A= return (event is None) or (self._EventObj_stuff.has_event(event))=0A= def __removeevent__(self, event):=0A= self._EventObj_stuff.remove_event(event)=0A= l =3D to_list(self)=0A= if (not l is None) and (not l is self): removeevent(l, event)=0A= if len(self._EventObj_stuff.events) =3D=3D 0: self.__restore__()=0A= =0A= def __restore__(self):=0A= # If no event left, reset obj to its original class.=0A= if hasattr(self._EventObj_stuff.clazz, "__setattr__"):=0A= self._EventObj_stuff.clazz.__setattr__(self, "__class__", = self._EventObj_stuff.clazz)=0A= else:=0A= self.__class__ =3D self._EventObj_stuff.clazz=0A= # And delete the _EventObj_stuff.=0A= del self._EventObj_stuff=0A= =0A= # Called at pickling time=0A= def __getstate__(self):=0A= try:=0A= dict =3D self._EventObj_stuff.clazz.__getstate__(self)=0A= =0A= if dict is self.__dict__: dict =3D dict.copy()=0A= except: dict =3D self.__dict__.copy()=0A= =0A= try:=0A= del dict["_EventObj_stuff"] # Remove what we have added.=0A= if dict.has_key("children"): dict["children"] =3D = list(dict["children"])=0A= elif dict.has_key("items" ): dict["items" ] =3D = list(dict["items" ])=0A= except: pass # Not a dictionary ??=0A= =0A= return dict=0A= =0A= def __reduce__(self):=0A= def rec_check(t):=0A= if t is self.__class__: return self._EventObj_stuff.clazz=0A= if type(t) is tuple: return tuple(map(rec_check, t))=0A= return t=0A= =0A= red =3D self._EventObj_stuff.clazz.__reduce__(self)=0A= =0A= if len(red) =3D=3D 2: return red[0], tuple(map(rec_check, red[1]))=0A= else: return red[0], tuple(map(rec_check, red[1])), = red[2]=0A= =0A= class _EventObj_OldStyle(_EventObj):=0A= def __deepcopy__(self, memo):=0A= if hasattr(self._EventObj_stuff.clazz, "__deepcopy__"):=0A= clone =3D self._EventObj_stuff.clazz.__deepcopy__(self, memo)=0A= if clone.__class__ is self.__class__:=0A= clone.__class__ =3D self._EventObj_stuff.clazz=0A= if hasattr(clone, "_EventObj_stuff"): del clone._EventObj_stuff=0A= return clone=0A= else:=0A= import copy=0A= =0A= if hasattr(self, '__getinitargs__'):=0A= args =3D self.__getinitargs__()=0A= copy._keep_alive(args, memo)=0A= args =3D copy.deepcopy(args, memo)=0A= y =3D apply(self._EventObj_stuff.clazz, args)=0A= else:=0A= y =3D copy._EmptyClass()=0A= y.__class__ =3D self._EventObj_stuff.clazz=0A= memo[id(self)] =3D y=0A= if hasattr(self, '__getstate__'):=0A= state =3D self.__getstate__()=0A= copy._keep_alive(state, memo)=0A= else:=0A= state =3D self.__dict__=0A= state =3D copy.deepcopy(state, memo)=0A= if hasattr(y, '__setstate__'): y.__setstate__(state)=0A= else: y.__dict__.update(state)=0A= return y=0A= =0A= =0A= class _EventObj_List(_EventObj):=0A= def __added__ (self, value): self._EventObj_stuff(self, ADDITION, = value, None)=0A= def __removed__(self, value): self._EventObj_stuff(self, REMOVAL , = value, None)=0A= =0A= def append(self, value):=0A= self._EventObj_stuff.clazz.append(self, value)=0A= self.__added__(value)=0A= def insert(self, before, value):=0A= self._EventObj_stuff.clazz.insert(self, before, value)=0A= self.__added__(value)=0A= def extend(self, list):=0A= self._EventObj_stuff.clazz.extend(self, list)=0A= for value in list: self.__added__(value)=0A= =0A= def remove(self, value):=0A= self._EventObj_stuff.clazz.remove(self, value)=0A= self.__removed__(value)=0A= def pop(self, index =3D -1):=0A= value =3D self._EventObj_stuff.clazz.pop(self, index)=0A= self.__removed__(value)=0A= return value=0A= =0A= def __setitem__(self, index, new):=0A= old =3D self[index]=0A= self._EventObj_stuff.clazz.__setitem__(self, index, new)=0A= self.__removed__(old)=0A= self.__added__ (new)=0A= def __delitem__(self, index):=0A= value =3D self[index]=0A= self._EventObj_stuff.clazz.__delitem__(self, index)=0A= self.__removed__(value)=0A= def __setslice__(self, i, j, slice):=0A= olds =3D self[i:j]=0A= self._EventObj_stuff.clazz.__setslice__(self, i, j, slice)=0A= for value in olds : self.__removed__(value)=0A= for value in slice: self.__added__ (value)=0A= def __delslice__(self, i, j):=0A= olds =3D self[i:j]=0A= self._EventObj_stuff.clazz.__delslice__(self, i, j)=0A= for value in olds : self.__removed__(value)=0A= def __iadd__(self, list):=0A= self._EventObj_stuff.clazz.__iadd__(self, list)=0A= for value in list: self.__added__(value)=0A= return self=0A= def __imul__(self, n):=0A= olds =3D self[:]=0A= self._EventObj_stuff.clazz.__imul__(self, n)=0A= if n =3D=3D 0:=0A= for value in olds: self.__removed__(value)=0A= else:=0A= for value in olds * (n - 1): self.__added__(value)=0A= return self=0A= =0A= =0A= class _EventObj_Dict(_EventObj):=0A= def __added__ (self, key, value): self._EventObj_stuff(self, = ADDITION, (key, value), None)=0A= def __removed__(self, key, value): self._EventObj_stuff(self, REMOVAL = , (key, value), None)=0A= =0A= def update(self, dict):=0A= old =3D {}=0A= for key, value in dict.items():=0A= if self.has_key(key): old[key] =3D value=0A= self._EventObj_stuff.clazz.update(self, dict)=0A= for key, value in old .items(): self.__removed__(key, value)=0A= for key, value in dict.items(): self.__added__ (key, value)=0A= def popitem(self):=0A= old =3D self._EventObj_stuff.clazz.popitem(self)=0A= self.__removed__(old[0], old[1])=0A= return old=0A= def clear(self):=0A= old =3D self.items()=0A= self._EventObj_stuff.clazz.clear(self)=0A= for key, value in old: self.__removed__(key, value)=0A= =0A= def __setitem__(self, key, new):=0A= if self.has_key(key):=0A= old =3D self[key]=0A= self._EventObj_stuff.clazz.__setitem__(self, key, new)=0A= self.__removed__(key, old)=0A= else:=0A= self._EventObj_stuff.clazz.__setitem__(self, key, new)=0A= self.__added__(key, new)=0A= def __delitem__(self, key):=0A= value =3D self[key]=0A= self._EventObj_stuff.clazz.__delitem__(self, key)=0A= self.__removed__(key, value)=0A= =0A= # EventObj class for plain list (e.g. []) and plain dict :=0A= =0A= # EventObj stuff is not stored in the object's dict (because no such = dict...)=0A= # but in this dictionary :=0A= #stuff_for_non_dyn_objs =3D weakref.WeakKeyDictionary()=0A= stuff_for_non_dyn_objs =3D {}=0A= =0A= class _EventObj_PlainList(_EventObj_List, list):=0A= __slots__ =3D []=0A= =0A= #__hash__ =3D object.__hash__ # Allows to hash it ! (needed to use = "self" as a dict key)=0A= =0A= def _get_EventObj_stuff(self): return stuff_for_non_dyn_objs[id(self)]=0A= def _set_EventObj_stuff(self, stuff): stuff_for_non_dyn_objs[id(self)] = =3D stuff=0A= _EventObj_stuff =3D property(_get_EventObj_stuff, _set_EventObj_stuff)=0A= =0A= def __restore__(self):=0A= # If no event left, delete the _EventObj_stuff and reset obj to its = original class.=0A= # Bypass the _EventObj.__setattr__ (it would crash since = _EventObj_stuff is no longer available after the class change)=0A= self._EventObj_stuff.clazz.__setattr__(self, "__class__", = self._EventObj_stuff.clazz)=0A= del stuff_for_non_dyn_objs[id(self)]=0A= =0A= def __getstate__(self): return None=0A= =0A= _classes[list] =3D _EventObj_PlainList=0A= =0A= class _EventObj_PlainDict(_EventObj_Dict, dict):=0A= __slots__ =3D []=0A= =0A= def _get_EventObj_stuff(self): return stuff_for_non_dyn_objs[id(self)]=0A= def _set_EventObj_stuff(self, stuff): stuff_for_non_dyn_objs[id(self)] = =3D stuff=0A= _EventObj_stuff =3D property(_get_EventObj_stuff, _set_EventObj_stuff)=0A= =0A= def __restore__(self):=0A= # If no event left, delete the _EventObj_stuff and reset obj to its = original class.=0A= # Bypass the _EventObj.__setattr__ (it would crash since = _EventObj_stuff is no longer available after the class change)=0A= self._EventObj_stuff.clazz.__setattr__(self, "__class__", = self._EventObj_stuff.clazz)=0A= del stuff_for_non_dyn_objs[id(self)]=0A= =0A= def __getstate__(self): return None=0A= =0A= _classes[dict] =3D _EventObj_PlainDict=0A= =0A= =0A= # Hierarchy stuff :=0A= =0A= def addevent_rec(obj, event =3D dumper_event):=0A= """addevent_rec(obj, event =3D dumper_event)=0A= Add event for obj, like addevent, but proceed recursively in all the = hierarchy : if obj is a UserList/UserDict, event will be added to each = instance obj contains, recursively.=0A= If the hierarchy is changed, the newly added items will DO have the = event, and the removed ones will no longuer have it."""=0A= if not hasevent(obj, event): # Avoid problem with cyclic list/dict=0A= # Wrap event in a hierarchy event=0A= if not isinstance(event, HierarchyEvent): wevent =3D = _wrap_event(obj, event, 1)=0A= =0A= addevent(obj, wevent)=0A= =0A= for o in to_content(obj):=0A= try: addevent_rec(o, event)=0A= except NonEventableError: pass=0A= =0A= def removeevent_rec(obj, event =3D dumper_event):=0A= """removeevent_rec(obj, event =3D dumper_event)=0A= Remove event for obj, like removeevent, but proceed recursively."""=0A= if hasevent(obj, event): # Avoid problem with cyclic list/dict=0A= removeevent(obj, event)=0A= =0A= for o in to_content(obj):=0A= if isinstance(o, _EventObj): removeevent_rec(o, event)=0A= =0A= def change_class(obj, newclass):=0A= """Change the class of OBJ to NEWCLASS, but keep the events it may = have."""=0A= events =3D obj._EventObj_stuff.events[:]=0A= for event in events: removeevent(obj, event)=0A= obj.__class__ =3D newclass=0A= for event in events: addevent(obj, event)=0A= =0A= ------=_NextPart_000_0149_01C267C9.4E13C0C0--
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4