Graham Dumpleton
grahamd at dscpl.com.au
Thu Oct 14 22:10:05 EDT 2004
On 14/10/2004, at 7:32 PM, Nicolas Lehuen wrote: >> FWIW, if using execfile() I have since found that it is >> probably better to use: >> >> import imp >> module = imp.new_module("z") >> module.__file__ = "z.py" >> execfile("z.py",module.__dict__) >> >> That way, executing type() on the module gives <type >> 'module'> and thus things one expects to work on modules >> will. The imp.new_module() method does not result in the >> module being listed in sys.modules. > > Ok, thanks for the tip, Graham. > > ... > > So I guess those two function do exactly the same thing. I'll modify my > ModuleCache to use imp.new_module instead of a fake Module object. Another thing I have done is to implement the cloning mechanism for data that I mentioned in my prior emails. Ie., module = imp.new_module(label) module.__file__ = file if hasattr(cache.module,"__clone__") and \ callable(cache.module.__clone__): cache.module.__clone__(module) execfile(file,module.__dict__) cache.module = module Thus, although the newly loaded module is constructed into a fresh module, there is the option of copying over selected data from the existing module. This is done by calling a __clone__() method if present in the existing module to populate the fresh module before loading the code in. Because it is a function, any necessary code could be put in it, including code which acquires any locks while copying the data. For example, a content handler might contain: from threading import Lock # This method prior to execfile() being run, but only # when the module had previously been loaded. def __clone__(module): _lock.acquire() module._lock = _lock module._data = _data _data["reloads"] = _data["reloads"] + 1 _lock.release() # This global scope code executed when execfile() run. # Check that data doesn't already exist, as don't want # to overwrite it if it does. This will be the case # when it is copied from existing module. if not globals().has_key("_lock"): _lock = Lock() _data = { "reloads" : 0 } # Data which should always be reset on fresh import # would still follow here. _cache = {} Using execfile() on a fresh module and using __clone__() in this way solves some of the problems I mentioned in my prior emails. First, because a fresh module is used, you don't get problems with code being modified while another thread may be executing in the context of the existing module that would have otherwise been overwritten. You can selectively copy over only the data which should be present in the context of the code being reconstructed. Ie., you could throw away certain types of cached data. If the modified code had removed functions, they will not show in the newly loaded module. The use of a method rather than an export list of data to copy across, means that locks can be acquired while data is being copied to prevent another thread accessing it at the same time. The intent with copying locks and data is that it is shared between old and new modules at least for the period that any existing thread may be executing in the context of the old module. Because the old module will no longer be referenced from the cache, once the thread executing in the context of the old module finishes, the old module will disappear and data will be exclusive to the new module. This does mean though that any data should really be dictionaries or class instances Ie., things that can be shared properly and a change through either reference is reflected in the other. Eg. >>> a={"a":1} >>> b=a >>> a {'a': 1} >>> b {'a': 1} >>> b["c"]=2 >>> a {'a': 1, 'c': 2} >>> b {'a': 1, 'c': 2} This isn't going to work if the data were simple integers or strings, as once a copy is made, a change from one module, by way of assignment, isn't going to be reflected in the other. You could also feasibly implement fixup code in a new module which would be executed upon the reload to convert data in an old form to a new form, but the danger of that is that you don't know whether a reload may occur for one fixup, before you later change the module again and where the subsequent fixup expects data in the intermediate format. If the format of data is going to change drastically, you should really stop Apache, load in your new code and restart. Otherwise, you would have to use a variable which tracks what version the data format is in and for fixup code be able to cope with various older versions of data when converting to a new format. Anyway, interesting stuff to play with. -- Graham Dumpleton (grahamd at dscpl.com.au)
|