Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python __import__ parameter confusion

I'm trying to import a module, while passing some global variables, but it does not seem to work:

File test_1:

test_2 = __import__("test_2", {"testvar": 1})

File test_2:

print testvar

This seems like it should work, and print a 1, but I get the following error when I run test_1:

Traceback (most recent call last):
  File ".../test_1.py", line 1, in <module>
    print testvar
NameError: name 'testvar' is not defined

What am I doing wrong?

EDIT:

As I commented later on, this is an attempt to replace the functions within a graphics library. Here is a sample program (that my teacher wrote) using that library:

from graphics import *
makeGraphicsWindow(800, 600)

############################################################
# this function is called once to initialize your new world

def startWorld(world):
    world.ballX = 50
    world.ballY = 300
    return world

############################################################
# this function is called every frame to update your world

def updateWorld(world):
     world.ballX = world.ballX + 3
     return world

############################################################
# this function is called every frame to draw your world

def drawWorld(world):
    fillCircle(world.ballX, world.ballY, 50, "red")

############################################################

runGraphics(startWorld, updateWorld, drawWorld)

Note that this code is designed such that people who have never (or almost never) seen ANY code before (not just python) would be able to understand with little effort.

Example for rewriting the function:

Original code:

def drawPoint(x, y, color=GLI.foreground):
    GLI.screen.set_at((int(x),int(y)), lookupColor(color))

Injected code:

# Where self is a window (class I created) instance.
def drawPoint(x, y, color = self.foreground):
  self.surface.set_at((int(x), int(y)), lookupColor(color))

I guess my real question is: how would I inject global functions/variables into an imported module before the module runs...?

like image 601
skeggse Avatar asked May 30 '10 18:05

skeggse


3 Answers

As the docs explain, the globals parameter to __import__ does not "inject" extra globals into the imported module, as you appear to believe -- indeed, what's normally passed there is the globals() of the importing module, so such injection would be highly problematic if it happened. Indeed, __import__'s docs specifically say:

The standard implementation does not use its locals argument at all, and uses its globals only to determine the package context of the import statement.

Edit: if you do need injection, e.g. into an empty imported module, as the OP suggested in a comment, that's easy as long as you're OK with doing it right after the import:

themod = __import__(themodname)
themod.__dict__.update(thedict)

The imported module's body will not be aware of still-to-happen injections, but that's clearly no matter if said body is empty anyway;-). Right after the import, you can inject to your heart's contents, and all following uses of that module will see your injections as if they were bona fide module-level bound names (because they are;-).

You could even, if you wished, save the need for an empty .py module to start with...:

import new, sys
themod = new.module(themodname)
sys.modules[themodname] = themod
themod.__dict__.update(thedict)

Edit: the OP tries to clarify in an edit of the Q...:

I guess my real question is: how would I inject global functions/variables into an imported module before the module runs...?

"The module runs" doesn't make much sense -- modules are loaded (which executes their body), just once (unless re-loaded explicitly later)... they never "run" per se. Given an empty module, you first import it (which executes nothing since the module's empty), then you can if you wish use a mix of themodule.__dict__.update and attribute assignment to populate the module -- functions are not automatically called when just mentioned (as the OP expresses fears they will be in a comment), so they can be treated just like any other variable in this respect (and most others, which is why Python's said to have first class functions).

like image 85
Alex Martelli Avatar answered Sep 22 '22 08:09

Alex Martelli


I was facing the same problem... And I convinced myself with following technique... I donno whtr this will be usefull for you or not!

File2: test1.py

global myvar
myvar = "some initial value"
print myvar
test = __import__("test2")
print myvar

File2: test2.py

import inspect
stk = inspect.stack()[1]
myvar = inspect.getmodule(frm[0]).myvar
print myvar
myvar = "i am changing its value."

Here variable is passed from module1 to module2 and that too it is passed by reference.

like image 36
Kaymatrix Avatar answered Sep 22 '22 08:09

Kaymatrix


As said, the __import__ function doesn't add globals to a new module. If you want to do that, I'd suggest adding some kind of initialize() function that assigns a value to a pre-declared global table, like this:

gTable = {}
gInited = False

def initialize(table):
    if(not gInited):
        gTable = table
        gInited = True

def getGlobal(name):
    if(name in gTable):
        return gTable[name]
    else:
        throw NameError("name '"+name+"' is not defined")

def setGlobal(name, value):
    gTable[name] = value

def mod_main():
    print getGlobal("testvar")

and import it like this:

import modGlobalsTest
modGlobalsTest.initialize({"testvar": 1})
modGlobalsTest.mod_main()

If you want to change the functions in the imported library, you can do something like this:

import foobar
def bar(x):
    return x+1
foobar.foo = bar

to replace foobar.foo with the custom function bar.

like image 28
nonpolynomial237 Avatar answered Sep 20 '22 08:09

nonpolynomial237