A startup puzzle

Bengt Richter bokr at oz.net
Mon Sep 29 22:49:19 CEST 2003

On Mon, 29 Sep 2003 11:31:05 -0500, "Edward K. Ream" <edreamleo at charter.net> wrote:

>I've just about convinced myself there is no good, clean solution to the
>following puzzle.  I wonder if you agree.
>1. My app has a module called leoGlobals that contains often-used global
>functions.  All of Leo's source files start with:
>from leoGlobals import *
>I don't want to discuss whether this is good style: it is simple and it
>works well for me :-)
>2.  All code accesses the singleton application object using the app()
>method in leoGlobals.
>3.  To make the code cleaner looking, I would really like the code to be
>able to access an app global instead of the app() function.  This has
>nothing to do with speed: it's simply that there are lots of references to
>app() in the code, and there is soon going to be a lot more references to
>app.gui.x() and app.gui.y() etc.
>As a workaround, many methods and functions assign a = app(), but I would
>like to avoid this step.
How about an app proxy object that just passes through attribute accesses later?
That way you can immediately in leoGlobal create something that will have the
app name binding that all importers of leoGlobal can copy the binding to, but
which won't/shouldn't get used 'till the app behind it is initialized.

(see code at end)

>4. Alas, it does not seem possible to initialize an app global in
>leoGlobals.  The reason is simple:
>from leoGlobals import *
>caches the value of app at the time the  import is done.  But the startup
>code that creates the app "global" (really an attribute of the leoGlobals
>module) must do _other_ imports.
Ok, see code below

>For example, doing the following at the top of leoGlobals doesn't work:
>import leoApp
>app = leoApp.leoApp() # construct the app instance.

Well, plain vanilla it works, but not if leoApp() wants to import leoGlobals and expect app
to be already bound.

But see code below.

>Indeed, the leoApp module will be imported before the assignment of app.
Well, that's the order you wrote it ;-) I mean, no prob so far, right?

>Moreover, the leoApp module does other imports, and all the app variables in
>those modules will be uninitialized.
Because _they_ do from leoGlobals import * and leoGlobals is not ready
for that yet.
>It would be horrible style to place detailed constraints on the order in
>which modules get imported, and I'm not sure even that would work.
>Any ideas for a clean solution?  Thanks.

Well, I'm just exploring your problem in its particular form right now, so
I'm not sure what can be guaranteed, but it looks like when you import leoApp
and it triggers a rash of "from leoGlobals import *", the leoGlobals in effect
will be a snapshot of the leoGlobal module dict at that time, so what has been
defined/bound will be visible, but whatever has not, won't, e.g., the appInvisible
in leoGlobals is visible to my interactive from leoGlobals import *, but not
via the app method that returns the global dict _it_ sees. The latter does have bindings
for the other globals though.

There may be more to it than that, but that's what I get from experimenting as below:

===< leoGlobals.py >======================
class AppWrap(object):
    def setapp(self, theRealApp): self.theRealApp = theRealApp
    def __getattr__(self, name): return getattr(self.__dict__['theRealApp'], name)
app = AppWrap()
K1k = 1024
whatever = 'whatever value'
appInvisible = 'App import dependents do not see this in their leoGlobals'

===< leoApp.py >==========================
from leoGlobals import *
class leoApp(object):
    def foometh(self): print 'foometh of %r called.'%self
    def getapp(self): return app # from leoGlobals import above
    def peek(self): return locals(), globals()

Experimental result:

 >>> from leoGlobals import *
 >>> dir()
 ['AppWrap', 'K1k', '__builtins__', '__doc__', '__name__', 'app', 'appInvisible', 'whatever']
 >>> app
 <leoGlobals.AppWrap object at 0x008F9FD0>
 >>> app.foometh()
 foometh of <leoApp.leoApp object at 0x008F9E70> called.
 >>> app.getapp
 <bound method leoApp.getapp of <leoApp.leoApp object at 0x008F9E70>>
 >>> app.getapp().foometh()
 foometh of <leoApp.leoApp object at 0x008F9E70> called.

Now we'll peek at the globals in the leoApp module via the peek method

 >>> app.peek()[1]['whatever']
 'whatever value'
 >>> app.peek()[1]['K1k']

Those were visible, but let's try that last one ;-)

 >>> app.peek()[1]['appInvisible']
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 KeyError: 'appInvisible'

But here in the interactive environment, the import * got the final state of leoGlobals, so

 >>> appInvisible
 'App import dependents do not see this in their leoGlobals'

And the app binding seen in other modules should be ok (though not ok to use until app.setapp
has been called, so you will have to watch dependencies in the way you order things in leoGlobals --
you want app=AppWrap() at the top, and presumably app.setapp(...) at the end, if it's going
to trigger imports that want to see everything preceding).

Here is an access to an app method via the global imported in another (leoApp) module,
though of course this example is otherwise silly ;-)

 >>> app.peek()[1]['app']
 <leoGlobals.AppWrap object at 0x00902250>
 >>> app.peek()[1]['app'].foometh()
 foometh of <leoApp.leoApp object at 0x00902450> called.

I guess you will lose a little speedwise by going through the wrapped app name, but
does that let you spell things the way you wanted?


Bengt Richter

More information about the Python-list mailing list