A problem about python script reloading(Bug or NOT)

Posted by Hairui on Mon 05 Jul 2004 05:56 AM — 18 posts, 78,651 views.

China #0
This post is related with

We can test it like this:
writing a main script named "MainTest.pys":
def OnWorldOpen():
	pass
def OnWorldConnect():
	pass
def OnWorldClose():
	pass
def OnWorldDisconnect():
	pass
def OnWorldGetFocus():
	pass
def OnWorldLoseFocus():
	pass
import sys
sys.path.append("C:\fy4")
import AddAliases
AddAliases.NoteTest(world)
world.note(str(AddAliases.AddSkAlias()))


And the script AddAliases.py's content is :
def AddSkAlias():
    return ["skillsAlias", "sk", "skills", 1, ""]

def NoteTest(world):
    world.note("Test")

if __name__=="__main__":
    print "AddAliases Test"
    print AddSkAlias()
else:
    print "AddAliases Imported"
    print AddSkAlias()


When you first run the script, the mushclient met no error and display text "Test" on the screen.

Then we changed the line 5 of AddAliases.py to :
   world.note("TestA")

and save the file.
Now press the button "Reload Script", the mushclient will still display text "Test" but not "TestA" on the screen.

Before rebooting the MushClient, the changes made to the AddAliases.py will not take affect.
Amended on Mon 05 Jul 2004 06:09 AM by Hairui
Australia Forum Administrator #1
From what you describe, this is not a problem in MUSHclient, but the Python scripting engine looks like it is caching the import file.

Try putting a display into the main script to confirm it is reloaded.

After all, the "reload script file" function only reloads the main script file. It is up to the scripting engine to reload imported files itself. There is no way MUSHclient can know to do that.

One thing you could test is to put the test into a plugin - then re-install the plugin. The plugin's script space should be reloaded when you do that. The problem may or may not go away then.
China #2
Same problem still can be found under MUSHClient 3.65 and ActivePython 2.4.

I had found a method to solve the problem,
which like this:
# Everytime we want to import a moudle
import ModuleA
reload(ModuleA)


It seems not a perfect method but really works.
Amended on Thu 14 Apr 2005 11:48 PM by Hairui
Russia #3
This is neither a Python, nor a Mushclient bug. This is perfectly valid and expected behavior of Python's "import" function. When you import the same module more than once per an interpreter session, all subsequent imports result in a copy of an already compiled and loaded module being returned. It's done that way for efficiency reasons, and I personally like it - it lets me organize interaction between plugin and is extremely useful. The official way around this feature is the "reload" function that you've already found.
China #4
Maybe I should add a DEBUGMODE flag into the code:

import ModuleA
if DEBUGMODE == 1:
    reload(ModuleA)

It seemed all the problems have beed solved, but the new problem is :
reload(ModuleA)
can not support the import clause like:
from ModuleA import *

And I can not assigned a function like :
ModuleA.FunctionA
to a trigger or an alias in MUSHClient.I think this is really a new problem.
Amended on Fri 15 Apr 2005 01:12 AM by Hairui
Greece #5
If you do a "from module import *", you have to use just the function name, FunctionA. If you do "import module" you have to use module.FunctionA. I don't know if I understood what you're saying correctly.
China #6
What I mean is if I use
import ModuleA

I can not assign the functions in ModuleA to the triggers of mushclient, because dot is forbidden for a function name of triggers , such as
ModuleA.FunctionA 
.
But if I use
from ModuleA import *
, the reload function would not work .
Amended on Fri 15 Apr 2005 12:56 PM by Hairui
Russia #7
Maybe try:


import ModuleA
reload(ModuleA)
from ModuleA import *


Hypothetically, it could work since the first import results in a module object, the reload() call returns the same (or newer) module, so "from ModuleA import *", if we follow the rationale of Python's import mechanism should load the contents of the already existing object - the one that was just reloaded one line prior. No guarantees though.
Greece #8
I didn't realise you could have MC triggers call functions in modules... It should be changed to allow the dot in the function name, I think, since it's valid...
China #9
I have tested the method from Ked in his recent post, it works well, though the codes :

import PassForest
if DEBUGMODE :
    reload(PassForest)
from PassForest import *

seem some strange. :)
I like it just because it works.
Amended on Sun 17 Apr 2005 06:10 AM by Hairui
Russia #10
Quote:
I didn't realise you could have MC triggers call functions in modules...


When you do "from module import *" it just takes every (almost every) key/value pair from the imported module's global dict and places it directly into the global dict of the importing module, so the contents of the imported module becomes the same thing as if it all was declared in the importing one. So technically speaking, you aren't calling a function in an imported module - you are calling one in the "top-level" script. Allowing the dot notation in triggers/aliases would mean allowing methods and properties access on objects, which might be a problem since different languages have different rules for doing that.
Greece #11
I have been bitten by this behaviour as well... Is there anything that can be done in MC to avoid this hackery (like restarting the interpreter whenever the script is reloaded)?
Netherlands #12
Take a look at my plugin MudStatus I posted a few days ago. I ran into the same problem and was too lazy to come up with a proper fix, so I pseudo-coded my way around it with the MUSHclient import directive since the plugin was small anyhow and the functionality not meant to be shared across other non-MUSHclient Pythonscripts anyhow.

However, I have since then figured (although not tested which you will need to do) that you can probably do the following to fix it (at the top of your plugin):

import sys
sys.modules = {}


I should really test to see if that theory holds, but I tend to have the habit of using the MUSHclient <import> tag anyhow because it allows me to split my triggers up also. Let us know if the above holds for you?
Amended on Thu 20 Nov 2008 05:41 PM by Worstje
Greece #13
Hmm, that just causes the scripting engine loading to fail:

Quote:

loading scripting engine
World: world
Error -2147467259 occurred when loading scripting engine:

Unspecified error
Amended on Thu 20 Nov 2008 05:58 PM by Poromenos
Netherlands #14
Bah, useful. From what I understood, clearing sys.modules would essentially wipe the cache of previously loaded modules, pushing Python to re-read them from disk. :/

In that case, I draw a blank for now. Sadly the issue isn't urgent enough for me to really go chase a fix down, although if I find a moment, I'll have a looker into it again.
Australia Forum Administrator #15
The "reload script file" menu item does exactly what it advertises - it reloads the script file.

To completely re-initialize the script engine you could turn scripting off, then turn it back on again.

However I found a similar problem with Lua, and with an extra line (similar to what Worstje suggested) it forced the module to be reloaded.

In case anyone is wondering, the Lua code is:


package.loaded.my_module = nil -- force reload

require "my_module"


For Python, if you can't find something similar, doesn't reloading the plugin work? That should give you a fresh copy. Failing that, delete the plugin and reinstall it. That would certainly reinitialize the script engine.
Greece #16
Ah, it's not a plugin, it's a script :/ Is there an easy way to do this? I'm writing my dissertation in MC and I wouldn't want to turn scripting off and on every time I change a few lines of code, while the automatic reload on save would be pretty handy...
Netherlands #17
Nick, unloading and loading again (not reinstalling) doesn't make a difference if I recall properly, although it's been ages since I tested. :( In essence, something hangs around in memory that says which modules have been loaded, and it won't re-read them. Somehow I suspect that the WSH-bindings (pywin32) are what make the sys.modules trick not work. But I'd need to do some serious testing to be sure.