Module reload and wxPython

Bryan belred1 at yahoo.com
Sun May 4 13:24:19 EDT 2003


i totally agree with you that python module reloading is cool.... we did
something similar.    our custom developer tools which are written in python
will first try to checkout updated tools from source control (including
itself).   if any files were downloaded, then the tools reload themselves.
this way, a developers tools and configuration files are always guaranteed
to be up-to-date.  it's so fast and efficient, nobody really knows what's
happening :)  there were some tricky spots to overcome such as reloading
files with base classes and modules that load each other.  i was just so
impressed that i could do something as "advanced" as a self-upgrading tool
in such a short time.  python never ceases to amaze me.

bryan

"Hung Jung Lu" <hungjunglu at yahoo.com> wrote in message
news:8ef9bea6.0305031341.721f6e5a at posting.google.com...
> Hi,
>
> Python is cool in that it has a powerful module reloading scheme.
>
> I have written a cute little module reloader using wxPython. If you
> have wxPython installed, you can download the following two files and
> try it out. Don't just read the code, you have to run it to feel it
> and to understand it.
>
> (1) The module reloader itself was developed using itself. That is,
> bootstrapping. It's really cool. I did not need to restart the program
> while developing it, except for very few times. Theoretically it is
> possible in Python to write an entire wxPython application without
> ever shutting down the application itself. Developing UI (User
> Interface) this way is as comfortable as, or more comfortable than,
> using other GUI development systems. This way of programming is what
> many other programming languages cannot even dream of.
>
> (2) Upon reloading, the existing instances of classes can relink to
> the new classes. The trick is in (a) calling the "_dash()" method in
> the constructor to register an object instance, (b) the "reloader
> coda" code snippet added at the end of a module. (The name "_dash"
> does not  mean anything, I chose it just because it's easy to type!)
>
> (3) The above trick is good for development. In actual production, it
> will lead to memory leak if many transient instances are created and
> discarded elsewhere. So, there is a sys.__debug__ flag: when absent,
> the trick is suppressed.
>
> (4) Classes can also have the optional __reload__() method to execute
> statements during upon new reload.
>
> Try it out: you can modify the ModuleReloaderFrame object almost at
> will. (E.g.: modifying the position of the buttons, changing text,
> color, adding other wxPython widgets, etc.) Of course, I have written
> it in such a way that it can be used with other wxPython applications.
> Just remember to insert the _dash() calls in the class constructors,
> and append the "reloader coda" at the end of the modules. It's not too
> much extra work, and the payoff is great. Of course, I don't think the
> trick works for more esotheric modules/classes. But for "normal"
> modules and classes, it should work.
>
> Question: what I would like to know is, is there a simpler way of
> inserting the "reloader coda" snippet into modules?
>  I am thinking along the line of __import__ or ihooks. (All this leads
> back to the discussion of Aspect-Oriented Programming. I can really
> see that hooks are very important in modern programming, but that most
> programming languages don't handle hooks very well.) I guess it's
> possible to write something that grabs existing modules and
> automatically hook the "_dash()" to class constructors and also hook
> the "reloader coda" to modules. But the result could be a bit
> confusing/surprising to the module programmers.
>
> Have fun and comments are welcome. I am sure a lot of experts have
> done something along this line, and I'd like to see alternative
> approaches.
>
> Hung Jung
>
> #-----------------------------------------------
> # TestWxUtils.py
> #
> #   This is an example of using
> #   wxUtils.ModuleReloaderFrame.
> #-----------------------------------------------
> import sys
> sys.__debug__ = sys # comment out this line to suppress instance-class
> re-linking
>
> from wxPython.wx import *
> import wxUtils
> class App(wxApp):
>     def __init__(self):
>         wxApp.__init__(self, True)
>     def OnInit(self):
>         w = wxUtils.ModuleReloaderFrame(None)
>         self.SetTopWindow(w)
>         return True
>
> app = App()
> app.MainLoop()
>
> #--------------------------------------------------
> # wxUtils.py
> #
> #   This module contains a wxPython implementaion
> #   of a module reloader application window.
> #--------------------------------------------------
>
> import sys
> from wxPython.wx import *
>
> class ModuleReloaderFrame(wxFrame):
>
>     def __init__(self, parent):
>         _dash(self)
>         wxFrame.__init__(self, parent, wxNewId(), 'Module Reloader',
> size=(350, 240),
>                          style=wxDEFAULT_FRAME_STYLE ^
> (wxRESIZE_BORDER | wxMAXIMIZE_BOX))
>         EVT_CLOSE(self, self.OnFrameClose)
>         self.build_panel()
>         self.__reload__()
>         if parent is None: self.Show(True)
>
>     def __reload__(self):
>         self.build_listbox()
>         self.build_reload_button()
>         self.build_add_module_button()
>         self.build_about_button()
>
>     def build_panel(self):
>         self.panel = wxPanel(self, -1)
>         self.panel.SetBackgroundColour(wxWHITE)
>
>     def build_listbox(self):
>         try: self.modules
>         except: self.modules = {__name__: sys.modules[__name__]}
>         if hasattr(self, 'listbox'):
>             listbox_selection = self.listbox.GetSelection()
>             self.listbox.Destroy()
>         else:
>             listbox_selection = 0
>         # create new list box
>         id = wxNewId()
>         self.listbox = wxListBox(self.panel, id,
>                                  wxPoint(120,13),
>                                  wxSize(200, 180),
>                                  [], wxLB_SINGLE)
>         module_names = self.modules.keys()
>         module_names.sort()
>         for m in module_names:
>             self.listbox.Append(m, self.modules[m])
>         EVT_LISTBOX_DCLICK(self, id, self.OnListBoxDClick)
>         self.listbox.SetSelection(listbox_selection)
>
>     def build_reload_button(self):
>         if hasattr(self, 'reload_button'):
> self.reload_button.Destroy()
>         id = wxNewId()
>         self.reload_button = wxButton(self.panel, id, 'Reload',
> wxPoint(20, 50))
>         EVT_BUTTON(self, id, self.OnReloadButton)
>         self.reload_button.SetDefault()
>
>     def build_add_module_button(self):
>         if hasattr(self, 'add_module_button'):
> self.add_module_button.Destroy()
>         id = wxNewId()
>         self.add_module_button = wxButton(self.panel, id, 'Add
> Module', wxPoint(20, 90))
>         EVT_BUTTON(self, id, self.OnAddModuleButton)
>
>     def build_about_button(self):
>         if hasattr(self, 'about_button'): self.about_button.Destroy()
>         id = wxNewId()
>         self.about_button = wxButton(self.panel, id, 'About',
> wxPoint(20, 130))
>         EVT_BUTTON(self, id, self.OnAboutButton)
>
>     def OnListBoxDClick(self, event):
>         self.reload_selected_module()
>
>     def OnReloadButton(self, event):
>         self.reload_selected_module()
>
>     def OnAddModuleButton(self, event):
>         dlg = wxTextEntryDialog(self, 'Enter the module\'s name', 'Add
> Module', '')
>         module_name = ''
>         if dlg.ShowModal() == wxID_OK:
>             module_name = dlg.GetValue().strip()
>         dlg.Destroy()
>         if module_name != '':
>             try:
>                 exec 'import %s' % module_name
>             except:
>                 dlg = wxMessageDialog(self,
>                       'Failed to add module <%s>.\nPlease check the
> module name.' % module_name,
>                       'ModuleReloader Error', wxOK | wxICON_ERROR)
>                 dlg.ShowModal()
>                 dlg.Destroy()
>                 return
>             module = sys.modules[module_name]
>             self.modules[module_name] = module
>             self.refresh_listbox(module)
>
>     def OnAboutButton(self, event):
>         dlg = wxMessageDialog(self, 'Module Reloader\n\nHung Jung Lu,
> 2003',
>                               'Credit', style=wxOK |
> wxICON_INFORMATION)
>         dlg.ShowModal()
>         dlg.Destroy()
>
>     def refresh_listbox(self, selected_module=None):
>         self.listbox.Clear()
>         module_names = self.modules.keys()
>         module_names.sort()
>         i_selection = 0
>         for m in module_names:
>             module = self.modules[m]
>             self.listbox.Append(m, module)
>             if module is selected_module:
>                 self.listbox.SetSelection(i_selection)
>             i_selection += 1
>
>     def reload_selected_module(self):
>         module = self.listbox.GetClientData(self.listbox.GetSelection())
>         try:
>             reload(module)
>         except:
>             dlg = wxMessageDialog(self,
>                   'Failed to reload module <%s>.\nPlease check the
> stderr window.' % module.__name__,
>                   'ModuleReloader Error', wxOK | wxICON_ERROR)
>             dlg.ShowModal()
>             dlg.Destroy()
>             raise
>         dlg = wxMessageDialog(self,
>               'Done!\n<%s> reloaded.' % module.__name__,
>               'Module Reloader', wxOK | wxICON_INFORMATION)
>         dlg.ShowModal()
>         dlg.Destroy()
>
>     def OnFrameClose(self, event):
>         if event.CanVeto():
>             if self.GetParent() is None: # top window
>                 self.Destroy()
>             else: # not top window
>                 self.Show(False)
>                 event.Veto()
>         else:
>             self.Destroy()
>
> #-----------------
> # reloader coda
> #-----------------
> import sys
> try: sys.__debug__, _dashed_objects
> except: _dashed_objects = []
> try:
>     sys.__debug__
>     def _dash(obj): _dashed_objects.append(obj)
> except:
>     def _dash(obj): pass
> for obj in _dashed_objects:
>     try: obj.__class__ = eval(obj.__class__.__name__)
>     except: pass
>     if hasattr(obj, '__reload__'): obj.__reload__()






More information about the Python-list mailing list