mò ‹ã¸Ec@s%dZeZdklZdklZdkZdkZdk Tdk l Z l Z dk lZdfd„ƒYZd efd „ƒYZd efd „ƒYZdd „ZeƒZhZd„Zdeifd„ƒYZdeifd„ƒYZdfd„ƒYZd„ZedjondS(sÐ Patch references to auto-persistent objects in a module. When a persistent module is compiled, all classes and functions should be converted to persistent classes and functions. When a module is updated, it is compiled and its persistent functions and classes are updated in place so that clients of the module see the update. The specific semantics of the convert and update-in-place operations are still being determined. Here are some rough notes: - Classes and functions are not converted in place. New objects are created to replace the builtin functions and classes. - Every function object is converted to a PersistentFunction. - Every class is converted to a new class that is created by calling the PersistentClassMetaClass with the name, bases, and dict of the class being converted. - The conversion operation must preserve object identity. If an object created by a def or class statement is referenced elsewhere in the module, all references must be replaced with references to the converted object. Implementation notes -------------------- The conversion operation is implemented using a pickler. It wasn't possible to use the copy module, because it isn't possible to extend the copy module in a safe way. The copy module depends on module globals. The pickler uses a Wrapper object that creates the appropriate new object or updates an old one when it is unpickled. The wrapper also causes parts of the wrapped object's state to be traversed by the pickler, for example the func_defaults of a function object. This traversal is necessary because references to convertable objects could be contained in the state and must be updated to refer to the new objects. What semantics do we want for update-in-place in the presence of aliases? Semantics based on per-namespace updates don't work in the presence of aliases. If an update changes an alias, then the old binding will be updated with the state of the new binding. Semantics based on containing namespaces seem to work. The outermost namespace that contains a name is updated in place. Aliases are simple rebinding operations that do not update in place. The containment approach seems to have a problem with bound methods, where an instance can stash a copy of a bound method created via an alias. When the class is updated, the alias changes, but the bound method isn't. Then the bound method can invoke an old method on a new object, which may not be legal. It might sufficient to outlaw this case. Open issues ----------- Can we handle metaclasses within this framework? That is, what if an object's type is not type, but a subclass of type. How do we handle things like staticmethods? We'd like the code to be able to use them, but Python doesn't expose an introspection on them. What if the same object is bound to two different names in the same namespace? For example:: x = lambda: 1 y = x If the module is updated to:: x = lambda: 1 y = lambda: 2 What are the desired semantics? (sdispatch_table(sStringION(t*(sPersistentClassMetaClasssPersistentDescriptor(sPersistentFunctiontWrappercBs)tZdZeZed„Zd„ZRS(sÑImplement pickling reduce protocol for update-able object. The Pickler creates a Wrapper instance and uses it as the reduce function. The Unpickler calls the instance to recreate the object. cCs||_||_||_dS(N(tobjtselft_objtmodulet_moduletreplacet_replace(RRRR((t-/data/zmath/zope/lib/python/zodbcode/patch.pyt__init__ps  cGso|i|Œ}|idj oHt|idƒo|ii|ƒn|ii|i ƒƒ|iSn|SdS(Nt _p_newstate( RtunwraptargstnewRtNonethasattrR t __setstate__t __getstate__(RR R((R t__call__us (t__name__t __module__t__doc__tTruet__safe_for_unpickling__RR R(((R Rgs  tFunctionWrappercBstZd„ZRS(NcCs2||i_|iii|ƒt|i|iƒS(N( tdefaultsRRt func_defaultst func_dicttupdatetdicttPersistentFunctionR(RRR((R R …s (RRR (((R Rƒst TypeWrappercBstZd„ZRS(NcCst|ii||ƒS(N(tPersistentClassMetaClassRRRtbasesR(RR"R((R R Œs(RRR (((R R ŠscCs*titi|<|||fti|R!RR-tmarkertobjecttdescr(RRRLRPRM((R t persistent_idÉs 7        cCsƒ|i|ijo2|i|it|ƒ|i|ifd|ƒn;t |t ƒo|i |i |t ƒƒn|i|ƒdS(NR(R&RRR@t save_reduceRGR t __bases__R7RJR!t save_persRQRt save_global(RR&((R t save_typeêscCsrt||iƒ}||ijp |djo2|i|it|ƒ|i |i fd|ƒn|i |ƒdS(NR,R( R9R.RtmodnameRR@RRRGRRRRU(RR.RW((R t save_functionús cCsÀ|i|ijot|ƒ}|i|\}}}|dj o5|i |i ||ƒ||ƒ||ƒd|ƒq¼|i |i ||ƒ||ƒd|ƒn|i |i|tƒƒdS(NR(RRRR@ttypetobjtypeR*R'R(R)RRRRGRTRQR(RRR)R'R(RZ((R R$s   cCs½|i|ƒ}|dj o|i|ƒdSnt|ƒ} t|ƒ} | t joCt |ƒdjo0|i o|i |ƒn|i|ƒdSn| |ijo)|i|i|i| dƒƒdSny|i| } WnÆtj oºyt| tƒ} Wntj o d} nX| o|i|ƒdSnyt| }Wn0tj o$|i|i|tƒƒdSnX||ƒ}t|ƒtjo|i||ƒdSnt|ƒt j oti d|ƒ‚nt |ƒ} | djo$| djoti d|ƒ‚n|d}|d}| djo|d}nd}t|ƒt j o$|dj oti d|ƒ‚n|i%|||d|ƒdSnX| ||ƒdS( Nis$Value returned by %s must be a tupleiis<tuple returned by %s must contain only two or three elementsis6Second element of tuple returned by %s must be a tupleR(&RRQRtpidRRTRFtdRYttt TupleTypetlentbintsave_empty_tuplet save_tupleR=twriteR1R%tftKeyErrort issubclasstTypeTypetissct TypeErrorRUtdispatch_tabletreduceRttupt StringTypeR;t PicklingErrortltcallabletarg_tuptstateRR(RRtignoreRkRrRlR[RpRqR\RhRdRoR]((R tsaves`        !          cCs®|i}|i}||ƒ||ƒ|tiƒ|dj oBt |i ƒ}|i|i |ƒƒ||f|i t |ƒ(RR<R((R R ~scCs |i|S(N(RR>RM(RRM((R tpersistent_load‚s(RRR R€(((R R~|s t NameFindercBs“tZdZhee<ee<ee