Attachment 'lwickjr.Module.UT.py'
Download
Toggle line numbers
1 """Module UT exports the following functions,
2 which have their own help-strings:
3
4 UT.dump: dump an object with pprint, or dump a class, instance, or module,
5 using pprint to print the members.
6 UT.Load: A convenient wrapper to unpickle a single object from a file.
7 UT.Save: A convenient wrapper to pickle a single object into a file.
8 UT.ThisLine: Return the number of the line where this function was called.
9 UT.smartreload: a drop-in replacement for __builtins__.reload, only
10 smarter, exported directly into __builtins__.
11 """##"""Module UT *used to* export the following functions,
12 ##which are now obsolete or irrelavent:
13 ##UT.AppendDictionary: copy possibly multiple dictionaries into a new empty
14 ## dictionary and return the resultant dictionary.
15 ##UT.Call, UT.__Call__: an extension to apply() that allows the 'Function' to
16 ## include optional positional and keyword arguments, to be suplimented by
17 ## additional arguments to Call, and to allow 'Function' to be supplied in
18 ## the more-portably-pickleable format of [[Instance, 'Method']...].
19 ##UT.edit: edit and reload a module's source.
20 ##UT.Quote: preprocess strings to try to convince DOS.readargs() to retrieve
21 ## the original string.
22 ##
23 ##Module UT also exports one function directly
24 ##into module ARexx, which is only visible there:
25 ##
26 ##ARexx.CallArexxFunc: call an ARexx function, returning the result
27 ##"""
28
29 ##import ARexx
30 import cPickle
31 import os
32 import pprint
33 import sys
34 import types
35 import idlelib.PyShell as PyShell
36 from inspect import getmodule
37 from types import ClassType, DictionaryType, FloatType, FrameType, InstanceType
38 from types import IntType, ListType, LongType, ModuleType, StringType, TupleType
39 from types import TracebackType
40 NumericTypes=[FloatType, IntType, LongType]
41 SequenceTypes=[ListType, TupleType]
42
43 ##def AppendDictionary(*Args):
44 ## """
45 ##UT.AppendDictionary(*Args):
46 ##
47 ##Version: 1.0
48 ##
49 ##Argument(s):
50 ## one or more dictionaries to be concatenated.
51 ##
52 ##Result:
53 ## A single dictionary containing entries for each item in each dictionary
54 ## supplied in *Args, with duplicate entries represented by the entry in the
55 ## right-most dictionary.
56 ##
57 ##Notes:
58 ## None
59 ##
60 ##Author:
61 ## Lyster E. Wick Jr.
62 ##"""
63 ## Dictionary={}
64 ## for Arg in Args:
65 ## for Key in Arg.keys():
66 ## if not Dictionary.has_key(Key):
67 ## Dictionary[Key]=Arg[Key]
68 ## return Dictionary
69
70 ##def Call(Function, *Args, **KWArgs):
71 ## """
72 ##UT.Call(Function, *Args, **KWArgs)
73 ##
74 ##Version: 1.0
75 ##
76 ##Arguments:
77 ## Function: an object representing the object to be called.
78 ## Args: Optional positional arguments to be supplied to Function.
79 ## KWArgs: Optional keyword arguments to be supplied to Function.
80 ##
81 ##Result:
82 ## The result, if any, of the called function.
83 ##
84 ##Notes:
85 ## UT.Call() is a wrapper for UT.__Call__(), see its docstring for further notes.
86 ##
87 ##Author:
88 ## Lyster E. Wick Jr.
89 ##"""
90 ## return __Call__(Function, Args, KWArgs)
91
92 ##def __Call__(Function, Args, KWArgs):
93 ## """
94 ##UT.__Call__(Function, Args, KWArgs)
95 ##
96 ##Version: 1.0
97 ##
98 ##Arguments:
99 ## Function: an object representing the object to be called.
100 ## Args: Optional positional arguments to be supplied to Function.
101 ## KWArgs: Optional keyword arguments to be supplied to Function.
102 ##
103 ##Result:
104 ## The result, if any, of the called function.
105 ##
106 ##Notes:
107 ## Functionaly equivalent to built-in function apply(), __Call__() additionaly
108 ## handles "Function"s other than natively callable objects, ie. lists/tuples
109 ## of the following forms:
110 ##
111 ## - [[Object, 'Method']]
112 ## - [[Object, 'Method'], [Args]]
113 ## - [[Object, 'Method'], {KWArgs}]
114 ## - [[Object, 'Method'], [Args], {KWArgs}]
115 ## - [Callable]
116 ## - [Callable, [Args]]
117 ## - [Callable, {KWArgs}]
118 ## - [Callable, [Args], {KWArgs}]
119 ##
120 ## The Args and KWArgs passed explicitly to __Call__ will be appended to those
121 ## supplied by "Function", with any duplicate KWArgs taking on the value
122 ## supplied explicitly.
123 ##
124 ## Forms that mimic the listed forms but substitute tuples for lists are
125 ## accepted.
126 ##
127 ## Note that [Instance, 'Method'] and [Class, 'Method'] are pickleable,
128 ## whilst the equivalent [to __Call__()] Instance.Method and Class.Method are
129 ## not, unless copy_reg is used to extend Pickle/cPickle [my extension to
130 ## copy_reg handles Methods].
131 ##
132 ##Author:
133 ## Lyster E. Wick Jr.
134 ##"""
135 ## if type(Function) in SequenceTypes:
136 ## if len(Function)==0: raise TypeError, 'Empty sequence for "Function" is illegal'
137 ## if type(Function[0]) in SequenceTypes:
138 ## Callable=getattr(Function[0][0], Function[0][1])
139 ## else:
140 ## Callable=Function[0]
141 ## if len(Function)==2:
142 ## if not type(Function[1]) in (DictionaryType, ListType, TupleType):
143 ## raise TypeError, 'Single argument in "Function" must be either sequence or dictionary'
144 ## if type(Function[1]) in SequenceTypes:
145 ## Args=list(Function[1])+list(Args)
146 ## else:
147 ## KWArgs=AppendDictionary(KWArgs, Function[1])
148 ## if len(Function)==3:
149 ## if not type(Function[1]) in SequenceTypes:
150 ## raise TypeError, 'Non-sequence positional arguments in "Function"'
151 ## if not type(Function[2]) is DictionaryType:
152 ## raise TypeError, 'Non-dictionary keyword arguments in "Function"'
153 ## Args=List(Function[1])+list(Args)
154 ## KWArgs=AppendDictionary(KWArgs, Function[2])
155 ## if len(Function)>3: raise TypeError, 'Sequence for "Function" too long'
156 ## Function=Callable
157 ## return apply(Function, Args, KWArgs)
158
159 ##def CallARexxFunc(Func, *Args):
160 ## # Irmen de Jong copied this into Arexx.py without crediting me,
161 ## # but he did improve it in the process. This version, however,
162 ## # also by me, is even better than his version.
163 ## """
164 ##UT.CallARexxFunc(Func, *Args)
165 ##
166 ##Version: 0.3
167 ##
168 ##Arguments:
169 ## Func: The name of the ARexx function to be called.
170 ## *Args: The arguments to be passed to the ARexx function.
171 ##
172 ##Result:
173 ## Whatever the ARexx function returns, as a String.
174 ##
175 ##Notes:
176 ## CallARexxFunc can call any ARexx function, including functions defined by
177 ## the ARexx interpreter, functions defined in libraries on the ARexx
178 ## function library list, and functions defined by external ARexx scripts,
179 ## but NOT "internal" functions defined by labels inside of ARexx scripts.
180 ##
181 ## Irmen de Jong improved on the original version by Lyster E. Wick Jr., and
182 ## this is an improvement by the original author over Irmen's version.
183 ##
184 ##Author:
185 ## Lyster E. Wick Jr.
186 ##"""
187 ## return ARexx.SendARexxMsg('REXX', 'Return '+Func+`Args`)
188 ##
189 ### Export this function into module ARexx in place of the version defined there.
190 ##ARexx.CallARexxFunc=CallARexxFunc
191 ##del CallARexxFunc
192
193 def dump(Object):
194 """Prety-print objects, including some types not supported by `pprint`.
195
196 UT.dump(Object)
197
198 Version: 0.3
199
200 Argument:
201 Object: The object to be pretty-printed.
202
203 Author:
204 Lyster E. Wick Jr.
205 """
206 ##
207 ## improved the type-detection code somewhat (now catches new-style
208 ## subclasses, too)
209 ## 200307.26: New in v0.3:
210 ## restricted output to items in the Object's __dict__ when the
211 ## object has one (most classes and modules do)
212 if isinstance(Object, (
213 ClassType,
214 FrameType,
215 InstanceType,
216 ModuleType,
217 TracebackType,
218 )):
219 try:
220 PropertyList = Object.__dict__.keys()
221 PropertyList.sort()
222 except AttributeError:
223 PropertyList = dir(Object)
224 for Property in PropertyList:
225 print Property+': ',
226 pprint.pprint( getattr( Object, Property ))
227 else:
228 pprint.pprint( Object )
229
230 #
231 # The functionality of the following function was transfered into its
232 # own module, where it is configured as an I.D.L.E. extension module.
233 #
234
235 ##def edit(Object):
236 ## """
237 ##UT.edit(Object)
238 ##
239 ##Version: 0.5
240 ##
241 ##Argument:
242 ## Object: The object whose source is to be edited.
243 ##
244 ##Result:
245 ## None
246 ##
247 ##Notes:
248 ## Module must evaluate to a module whose source is available to be
249 ## edited.
250 ##
251 ## The I.D.L.E. editor is invoked, with the source positioned at the
252 ## object's definition, if possible.
253 ##
254 ##Author:
255 ## Lyster E. Wick Jr.
256 ##"""
257 ## ## 200309.17: New in v0.5:
258 ## ## * Support for I.D.L.E.'s internal editor added.
259 ## ## Old docstring:
260 ## ## An external editor is invoked on the source file,
261 ## ## and the module is reloaded when the editor returns.
262 ## ## * Type-checking converted to use isinstance() instead of directly
263 ## ## comparing the result of type() with specific hard-coded types.
264 ## global _LastEdited
265 ## if Object=='':
266 #### import Browse
267 #### Object=Browse.Browse()
268 ## Object = _LastEdited
269 ## FileName =None
270 ## Module =None
271 ## SourceLine=None
272 #### ObjectType=type(Object)
273 ## if isinstance(Object, types.ClassType):
274 ## Module =sys.modules[Object.__module__]
275 ## FileName =Module.__file__
276 #### SourceLine="Find (class %s)\n" % (Object.__name__,)
277 ## elif isinstance(Object, types.FunctionType):
278 ## FileName =Object.func_code.co_filename
279 ## SourceLine=Object.func_code.co_firstlineno
280 ## elif isinstance(Object, types.InstanceType):
281 ## Module =Object.__dict__.get('__module__') or Object.__class__.__module__
282 ## if isinstance(Module, basestring): Module=sys.modules[Module]
283 ## FileName =Object.__dict__.get('__file__') or Module.__file__
284 #### SourceLine=Object.__dict__.get('__DME_FindMe__') or "Find (class %s)\n" % (Object.__class__.__name__,)
285 ## elif isinstance(Object, types.MethodType):
286 ## Module =sys.modules[Object.im_class.__module__]
287 ## FileName =Module.__file__
288 ## SourceLine=Object.im_func.func_code.co_firstlineno
289 ## elif isinstance(Object, types.ModuleType):
290 ## Module =Object
291 ## FileName =Object.__dict__.get('__file__')
292 ## SourceLine=1
293 ## else:
294 #### dump(locals())
295 ## raise TypeError, "Can't identify source file of %s objects" % (type(Object).__name__,)
296 ## if FileName is None:
297 #### dump(locals())
298 ## raise ValueError, "Can't identify source file of %s" % (`Object`,)
299 ## if FileName[-1]=='c': FileName=FileName[:-1]
300 ## if FileName[-1]=='o': FileName=FileName[:-1]
301 #### SourcePath=os.path.join('Ram:T', os.path.basename(FileName))
302 #### if SourceLine: open(SourcePath, 'w').write(SourceLine+'Block Block ScreenTop While !cb ScrollDown UnBlock\n')
303 ## edit = PyShell.flist.open(FileName)
304 ## if SourceLine:
305 ## edit.gotoline(SourceLine)
306 #### os.system('DME '+FileName)
307 #### if not Module:
308 #### if FileName[-3:]=='.py': FileName=FileName[:-3]
309 #### if FileName[-9:]=='/__init__': FileName=FileName[:-9]
310 #### FileName=FileName.replace('/', '.')
311 #### Modules=sys.modules.keys()
312 #### Modules.sort(lambda x,y: -cmp(len(x), len(y)))
313 #### for Module in Modules:
314 #### if FileName[-len(Module):]==Module:
315 #### Module=sys.modules[Module]
316 #### break
317 #### reload(Module)
318 #### if not os.path.exists(self.file):
319 #### return
320 ##### if 'RawPython' in sys.modules.keys():
321 ##### try:
322 ##### Module.RawPythonInit
323 ##### except:
324 ##### pass
325 ##### else:
326 ##### if callable(Module.RawPythonInit):
327 ##### Module.RawPythonInit()
328 ##### try:
329 ##### Module.RawPythonCommands.keys
330 ##### except AttributeError:
331 ##### pass
332 ##### else:
333 ##### if callable(Module.RawPythonCommands.keys):
334 ##### from RawPython import RawPythonInternalCommands
335 ##### for Command in Module.RawPythonCommands.keys():
336 ##### RawPythonInternalCommands[Command]=Module.RawPythonCommands[Command]
337 #### if os.path.exists(SourcePath): os.unlink(SourcePath)
338 #### import linecache
339 #### linecache.checkcache()
340 ##_LastEdited = None
341 #
342 # Octal was created as an exercize, and was obsolete before it was written.
343 #
344 #def Octal(I, Width=7):
345 # MyI=I+0
346 # OctString=''
347 # for Octit in range(0, Width):
348 # OctString=`MyI - (MyI/8)*8`+OctString
349 # MyI=(MyI/8)
350 # return OctString
351 #
352
353 class Load:
354 """Load a pickled object from a named file, and unpickle it.
355
356 UT.Load(File)
357
358 Version: 0.1
359
360 Argument:
361 File: The name of the file whose pickled object is to be retrieved.
362
363 Result:
364 The pickled object in the file, unpickled.
365
366 Notes:
367 Implemented as a class to allow use of Load.Name and Load['Name'] as
368 well as the usual Load('Name'), Load retrieves a single pickled object
369 from the beginning of a named named file, such as is created by UT.Save().
370
371 Author:
372 Lyster E. Wick Jr.
373 """
374 def Load(
375 Load,
376 File,
377 ):
378 return cPickle.load(open(File))
379 def __call__(
380 Load,
381 File,
382 ):
383 return Load.Load(File)
384 def __getattr__(
385 Load,
386 File,
387 ):
388 return Load.Load(File)
389 def __getitem__(
390 Load,
391 File,
392 ):
393 return Load.Load(File)
394
395 Load=Load()
396
397 ##def ProtectBitsToText(Bits):
398 ## """UT.ProtectBitsToText(Bits)
399 ##
400 ##Version: 1.0
401 ##
402 ##Argument:
403 ## A file's protection bits as an integer.
404 ##
405 ##Result:
406 ## The same protection bits as a text string.
407 ##
408 ##Notes:
409 ## None
410 ##
411 ##Author:
412 ## Lyster E. Wick Jr.
413 ##"""
414 ## Text=['dewr-------------', '----apshdewrdewru']
415 ## BitNum=0
416 ## TextOut=''
417 ## for I in range(17):
418 ## Bits, Bit=divmod(Bits, 2)
419 ## TextOut=Text[Bit][BitNum]+TextOut
420 ## BitNum=BitNum+1
421 ## return TextOut[0]+TextOut[-8:]+' '+TextOut[1:5]+' '+TextOut[5:9]
422
423 def Save(
424 File,
425 Object,
426 Backup=True,
427 Protocol=-1,
428 ):
429 """Pickle an object and write it to a named file.
430
431 UT.Save(File, Object[, Backup[, Protocol]])
432
433 Version: 0.3
434
435 Arguments:
436 File: The name of the file into which the object is to be pickled.
437 Object: The object which is to be pickled into the file.
438 Backup: A flag specifying that a back-up copy of the file is to be saved,
439 or a string specifying where to save the back-up copy of the file.
440
441 Result:
442 None. The object is written into the file, pickled in the best available
443 format.
444
445 Notes:
446 A convenient wrapper for cPickle's dump function, Save writes a single pickled
447 object to a file, replacing the entire previous contents of the file, if any,
448 with a data value suitable for processing by UT.Load() or any similar code.
449
450 Any true value of Backup [which defaults to a true value] causes Save to
451 attempt to make a back-up copy of the old object file before creating the
452 new file. If the value of Backup is a string value, it is used as the name
453 of the back-up file, else the name is constructed by appending ".bak" to
454 the name contained in File.
455
456 Any errors encountered whilst making the back-up file are silently ignored.
457
458 Author:
459 Lyster E. Wick Jr.
460 """
461 ## 200308.20: New in v0.2:
462 ## Changed the documentation to represent the pickle format as "the best
463 ## available" format instead of "binary" format.
464 ## Changed the code to use the best available format instead of TEXT FORMAT!
465 ## The code and documentation actually *agree* on this now.
466 ## Changed the code to detect the type of the Backup flag using isinstance()
467 ## instead of direct type comparison.
468 ## 200310.07: New in v0.3:
469 ## Added an optional fourth parameter to specify the desired pickle protocol,
470 ## which defaults to "best available".
471 if Backup:
472 if not isinstance(Backup, basestring):
473 Backup=File+'.bak'
474 try: os.system('Copy "'+File+'" "'+Backup+'"')
475 except: pass
476 f=file(File, 'wb')
477 cPickle.dump(Object, f, Protocol)
478 f.close()
479
480 ##def Quote(String):
481 ## """UT.Quote(String)
482 ##
483 ##Version: 0.4
484 ##
485 ##Argument:
486 ## String: The string to process.
487 ##
488 ##Result:
489 ## The processed string.
490 ##
491 ##Notes:
492 ## None
493 ##
494 ##Author:
495 ## Lyster E. Wick Jr.
496 ##"""
497 ## if type(String) in NumericTypes: return `String`
498 ## import string
499 ## String=string.join(string.split(String, '*'), '**')
500 ## String=string.join(string.split(String, '"'), '*"')
501 ## String=string.join(string.split(String, '\012'), '*n')
502 ## return '"'+String+'"'
503
504 def ThisLine(depth=0):
505 """Return the number of the line where this function was called.
506
507 UT.ThisLine()
508
509 Version: 0.4
510
511 Optional Argument:
512 The depth of the frame to interogate for line number and function
513 name.
514
515 Result:
516 The number of the line where the function was called from, and the
517 name of the calling function.
518
519 Author:
520 Lyster E. Wick Jr.
521 """
522 ## 200402.20: New in v0.2:
523 ## * now also extracts and returns the name of the calling function.
524 ## 200402.21: New in v0.3:
525 ## * now uses sys._getframe() instead of raising an error and
526 ## extracting the frame.
527 ## * removed from the docstring the note about using a deliberate
528 ## error to achieve access to the required frame.
529 ## 200410.01: New in v0.4:
530 ## * added an optional parameter `depth`, specifying how deep in the
531 ## call stack to peek.
532 f = sys._getframe(depth + 1)
533 return [f.f_code.co_name, f.f_lineno]
534
535 def smartreload(Module):
536 """A 'smart' reload that auto-reloads modules listed in dependancy lists.
537
538 UT.smartreload()
539
540 Version: 0.3
541
542 Argument:
543 Module: The module to be reloaded.
544
545 Result:
546 The module whose reload was requested.
547
548 Side effects:
549 The module is reloaded, as are any modules listed in
550 Module.__reload_before__ and __reload_after__.
551
552 Notes:
553 The intended use of smartreload, setReloadBefore, and setReloadAfter
554 is as a set of development tools:
555
556 When one module depends upon another module in such a way that the
557 dependant module breaks when the other module is reloaded, the
558 dependant module can call UT.setReloadAfter(Other, Self) to ensure
559 that it gets reloaded every time the other module is reloaded.
560
561 When one module depends upon another module in such a way that the
562 dependant module breaks when reloaded unless another module is
563 re-initialized first, the dependant module can call
564 UT.setReloadBefore(Self, Other) to ensure that the other module gets
565 reloaded and re-initialized first every time the dependant module is
566 reloaded.
567
568 Author: Lyster E. Wick Jr.
569 """
570 # 200311.28: initial version.
571 # 200312.02: New in v0.1:
572 # * cache `__builtins__.reload` as `_reload`
573 # * call the cached _reload instead of the built-in `reload`
574 # * replace the built-in `reload` with `smartreload`
575 # 200402.19: New in v0.2:
576 # * use `inspect.getmodule` to allow reloading a module when given a
577 # non-module object defined there.
578 # 200507.03: New in v0.3:
579 # * Now returns the original module, for consistancy with the
580 # standard implimentation.
581 # * ***FIXED***: a small bug with *MAJOR* bad-mojo side-effects:
582 # Attempting to smartreload a module with __reload_before__ defined
583 # would trigger a recursive infinite loop of trying to smartreload
584 # the original module, terminating only when the depth of the call
585 # stack caused problems.
586 # * The documentation was extended with better[?] explanations.
587 ArgModule = Module
588 Module = getmodule(Module)
589 assert Module, "Can't find module for object %r." % (ArgModule)
590 if hasattr(Module, '__reload_before__'):
591 for m in Module.__reload_before__:
592 smartreload(m)
593 print _reload(Module)
594 if hasattr(Module, '__reload_after__'):
595 for m in Module.__reload_after__:
596 smartreload(m)
597 return Module
598
599 ## 200510.23 New:
600 ## * now uses the new sitecustomize.patch(Where, What) function,
601 ## with checks that we don`t cache a previous version of
602 ## smartreload().
603 import sitecustomize
604 while (sitecustomize.isPatched(reload)
605 and __file__ in (
606 reload.__previous_function__.func_code.co_filename,
607 reload.__previous_function__.func_code.co_filename+"w",
608 reload.__previous_function__.func_code.co_filename+"c",
609 reload.__previous_function__.func_code.co_filename+"o")
610 and reload.__name__ == "smartreload"):
611 sitecustomize.unpatch(sys.modules["__builtin__"], "reload")
612 try: _reload
613 except: _reload = __builtins__['reload']
614 sitecustomize.patch(sys.modules["__builtin__"], "reload", smartreload)
615 del sitecustomize
616 ##__builtins__['reload'] = smartreload
617
618 def setReloadBefore(Mod1, Mod2, Cancel=False):
619 """Set Mod2 to [not] be smartreloaded before Mod1.
620
621 UT.setReloadBefore(Mod1, Mod2, Cancel)
622
623 Version: 0.2
624
625 Arguments:
626 Mod1: The module whose __reload_before__ list is to be updated.
627 Mod2: The module to be inserted in that list.
628 Cancel: Flag to cause Mod2 to be removed from the list instead of
629 added.
630
631 Result:
632 None. Mod2 is inserted into Mod1's __reload_before__ list.
633
634 Notes:
635 See smartreload`s notes.
636
637 Author: Lyster E. Wick Jr.
638 """
639 # 200311.28: initial version.
640 # 200507.03: New in v0.1:
641 # * optional Cancel flag to cause removal of Mod2 from Mod1`s
642 # __reload_before__ list.
643 # 200510.23: New in v0.2:
644 # * documentation updated to include new Cancel flag.
645 if Cancel:
646 try: Mod1.__reload_before__.remove(Mod2)
647 except: pass
648 else:
649 try: Mod1.__reload_before__
650 except: Mod1.__reload_before__ = []
651 if Mod2 not in Mod1.__reload_before__:
652 Mod1.__reload_before__.append(Mod2)
653
654 def setReloadAfter(Mod1, Mod2, Cancel=False):
655 """Set Mod2 to be smartreloaded after Mod1.
656
657 UT.setReloadAfter(Mod1, Mod2, Cancel=False)
658
659 Version: 0.2
660
661 Arguments:
662 Mod1: The module whose __reload_after__ list is to be updated.
663 Mod2: The module to be inserted in that list.
664 Cancel: Flag to cause Mod2 to be removed from the list instead of
665 added.
666
667 Result:
668 None. Mod2 is inserted into Mod1's __reload_after__ list.
669
670 Notes:
671 See smartreload`s notes.
672
673 Author: Lyster E. Wick Jr.
674 """
675 # 200311.28: initial version.
676 # 200507.03: New in v0.1:
677 # * optional Cancel flag to cause removal of Mod2 from Mod1`s
678 # __reload_after__ list.
679 # 200510.23: New in v0.2:
680 # * documentation updated to include new Cancel flag.
681 # * bug fix: added Cancel flag to the formal argument list.
682 if Cancel:
683 try: Mod1.__reload_after__.remove(Mod2)
684 except: pass
685 else:
686 try: Mod1.__reload_after__
687 except: Mod1.__reload_after__ = []
688 if Mod2 not in Mod1.__reload_after__:
689 Mod1.__reload_after__.append(Mod2)
690
691 __Aliases__ = {
692 'dump': dump,
693 ## 'edit': edit,
694 'reload': smartreload,
695 'setReloadAfter': setReloadAfter,
696 'setReloadBefore':setReloadBefore,
697 'Save': Save,
698 }
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.