Referencing module.instance

G

Gnarlodious

What I am doing is importing modules that have an identical instance
name. So I say:

import Grid

Grid has its instance:

Grid.Grid()

and this is the same for all modules of my webapp.
allowedPages is a list of modules to import, so they are quoted
strings:

for page in self.allowedPages:
setattr(self, page, __import__(page))

The problem is that the attribute name needs to reference the
Grid.Grid instance and not the Grid module. How would I do this?
I can do it literally:
setattr(self, 'Grid', Grid.Grid)

however doing it as a reference eludes me.

Or is there some nifty Pythonic way of bulk importing modules?

-- Gnarlie
 
S

Steven D'Aprano

What I am doing is importing modules that have an identical instance
name. So I say:

import Grid

Grid has its instance:

Grid.Grid()

and this is the same for all modules of my webapp. allowedPages is a
list of modules to import, so they are quoted strings:

for page in self.allowedPages:
setattr(self, page, __import__(page))

So if allowedPages looks like this: ['Grid', 'Spam', 'Ham', 'Cheese'] you
expect your instance to look like this:

self.Grid # has the value Grid.Grid
self.Spam # has the value Spam.Grid etc.
self.Ham
self.Cheese

Correct?

I'm not sure I like that design, it feels wrong, but perhaps I don't
understand the reason for it. Moving along...
The problem is that the attribute name needs to reference the Grid.Grid
instance and not the Grid module. How would I do this? I can do it
literally:
setattr(self, 'Grid', Grid.Grid)

however doing it as a reference eludes me.

__import__(page) returns a module, so just grab that attribute from the
module.

for page in self.allowedPages:
setattr(self, page, __import__(page).Grid)


But note that this is an binding operation, like an assignment. If you
rebind module.Grid, your instance self will not see the change:

Spam.Grid = 42 # some value
instance.read_pages() # whatever you call the method
assert instance.Spam == 42
Spam.Grid = 23 # rebind the original
assert instance.Spam == 23 # THIS WILL FAIL!!!


If you need these attributes to be aliases to the module attribute,
rather than independent bindings, you need to use computed properties
rather than plain attributes. I leave that as an exercise, mainly because
I suspect it will be very difficult.

Wait... no, I think it's easy! (Famous last words...) Try this untested
code:

class MyClass(object):
allowedPages = ('Grid', 'Spam', 'Ham', 'Cheese')
def __init__(self):
self.modules = {}
for name in allowedPages:
self.modules[name] = __import__(name)
def __getattribute__(self, name):
if name in self.allowedPages:
return self.modules[name].Grid
return super(MyClass, self).__getattribute__(name)
 
G

Gnarlodious

To explain my reasoning, this scheme will allow me to run the script three ways, as shell, as one-shot CGI or as a persistent mod_wsgi module.

So to be more exhaustive:

In __init__ I can say:

import Grid
self.Grid = Grid.Grid

self.Grid is now the instance of Grid inside the module Grid.

then later on I __call__ with the input data:

html=self.Grid(queryString, environ)

and this works great for one module, a default output which is now running on my server.


But now I want to use the query string to load the instance, which means it comes in as text. Say I load a list of modules:
allowedPages=['Gnomon', 'Grid', 'Lexicon', 'Harmonics']

I need to import them all at __init__, and set the instance for each like this:
self.Gnomon=Gnomon.Gnomon
self.Grid=Grid.Grid
self.Lexicon=Lexicon.Lexicon
self.Harmonics=Harmonics.Harmonics

Then dynamically, in __call_, I just use the CGI string to invoke my target object.

What I have now is:

self.modules = {}
page = 'Gnomon'
self.modules[page] = __import__(page)

which gives me

<module 'Gnomon' from '~/Sites/Sectrum/Gnomon.py'>

A solution is to set self.modules[page] to the Gnomon instance of the Gnomon module. How to?

-- Gnarlie
http://Sectrum.com
 
G

Gnarlodious

HA! After much experimenting I hit upon getattr(__import__(page), page):

for page in self.allowedPages:
scriptPath = '{}/{}.py'.format(os.path.dirname(__file__), page)
if os.path.exists(scriptPath):
self.modules[page] = getattr(__import__(page), page)


Then in __call_ I just say:

target = incoming CGI variable
self.modules[target](qList, environ)

and it works!

-- Gnarlie
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,281
Latest member
Pedroaciny

Latest Threads

Top